JavaScript 继承方式有以下五种:
构造函数、原型和实例的关系是:每个构造函数都有一个原型对象 protptype
,原型有一个属性 constructor
指向构造函数,而实例有一个内部指针 __proto__
指向原型。如果原型是另一个类型的实例呢?原型链就是基于此构想。
function Parent () {
this.name = "kevin";
}
Parent.prototype.getName = function () {
console.log(this.name);
return this.name
}
function Child () {
}
Child.prototype = new Parent();
let child1 = new Child();
console.log(child1.getName()) // kevin
缺点:
function Parent () {
this.names = ["kevin", "daisy"];
}
function Child () {
Parent.call(this);
}
let child1 = new Child();
child1.names.push("Lucy");
console.log(child1.names); // ["kevin", "daisy", "Lucy"]
let child2 = new Child();
console.log(child2.names); // ["kevin", "daisy"]
优点:
解决了原型链继承存在的两个缺点。
缺点:
方法都在构造函数中定义,每次创建实例都会创建一遍方法。
function Parent (name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
let child1 = new Child("kevin", "18");
child1.colors.push("black");
console.log(child1.name); // kevin
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]
let child2 = new Child("daisy", "20");
console.log(child2.name); // daisy
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]
优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
function createObj (o) {
let clone = Object.create(o);
clone.sayName = function () {
console.log("hi");
}
return clone;
}
缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。
function Parent (name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
// 封装创建新的对象的方法,以传入的原型作为新对象的原型
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function prototype(child, parent) {
var prototype = object(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
prototype(Child, Parent);
let child1 = new Child("kevin", "18");
child1.colors.push("black");
console.log(child1.name); // kevin
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]
let child2 = new Child("daisy", "20");
console.log(child2.name); // daisy
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]
这种方式的高效率体现它只调用了一次 Parent
构造函数,并且因此避免了在 Parent.prototype
上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof
和 isPrototypeOf
。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
小练习
谈谈 JavaScript 的几种继承方式各有什么优缺点。
答:见正文。
判断对象属于某个类有四种方式:
typeof
instanceof
Object.prototype.construcor()
Object.prototype.toString()
我们具体分析一下这四种方式。
typeof
typeof
是用于判断数据的基本类型的,判断对象属于某个类不是特别准确。它可以判断的对象只有函数和对象。除此之外,需要注意的 typeof null
的结果是 "object"
,这是一个历史设计缺陷。
typeof "1"; // "string"
typeof 1; // "number"
typeof true; // "boolean"
typeof null; // "object"
typeof undefined; // "undefined"
typeof 11n; // "bigint"
typeof Symbol(); // "symbol"
typeof function() {}; // "function"
typeof {}; // "object"
typeof []; // "object"
instanceof
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);
console.log(auto instanceof Car); // true
console.log(auto instanceof Object); // true
我们可以利用原型链来模拟实现 instanceof
:
function instanceOf(objA, objB) {
// 获取构造函数的原型对象
objB = objB.prototype;
// 获取对象的原型
let proto = Object.getPrototypeOf(objA);
// 查找,直到 null 为止
while (proto !== null) {
if (proto === objB) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
Object.prototype.construcor()
MDN 上对 Object.prototype.construcor()
定义:
constructor
属性返回 Object 的构造函数(用于创建实例对象)。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串。
const o = {}
o.constructor === Object // true
const o = new Object
o.constructor === Object // true
const a = []
a.constructor === Array // true
const a = new Array
a.constructor === Array // true
const n = new Number(3)
n.constructor === Number // true
Object.prototype.toString()
toString()
方法返回一个表示该对象的字符串。可以使用它检查对象类。
const toString = Object.prototype.toString;
toString.call(new Date()); // [object Date]
toString.call(new String()); // [object String]
// Math has its Symbol.toStringTag
toString.call(Math); // [object Math]
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]
以这种方式检查类是不可靠的,因为继承自 Object
的对象可能用它们自己的实现重写它。
const myDate = new Date();
Object.prototype.toString.call(myDate); // [object Date]
myDate[Symbol.toStringTag] = "myDate";
Object.prototype.toString.call(myDate); // [object myDate]
请模拟实现 instanceof
。
答案:见正文。
阅读量:923
点赞量:0
收藏量:0