bangrenzhuce 2019-06-21
原文地址:JavaScript实现继承
众所周知,JavaScript 这门语言在 ES6 出来之前是没有类(class)这一概念的,所以 JavaScript 中的类都是通过原型链来实现的。同样,使用 JavaScript 也能实现面向对象的实现继承。以下是《高程》(第三版)的读书笔记。
通过原型链实现继承很容易理解,也很简单。将子类的原型指向父类的实例即可。写个简单的例子:
// 父类
function SuperType() {
this.property = "super";
}
// 在父类的原型上定义方法
SuperType.prototype.getSuperVal = function () {
console.log(this.property);
};
// 子类
function SubType() {
this.property = "sub";
}
// 子类继承父类
SubType.prototype = new SuperType();
var instance = new SubType();
console.log(instance.getSuperVal()); // "sub"对于子类来讲,其原型的指向应该是这样的:SubType -> new SuperType() -> SuperType.prototype -> Object.prototype -> null。
注意:
原型链的问题:
这个方法是为了解决原型链方式带来的问题,使用十分巧妙,利用了 call 方法。代码实现:
// 父类
function SuperType() {
this.users = ["Jack", "Tom"];
}
// 子类
function SubType() {
// 继承
SuperType.call(this);
}
var instance1 = new SubType();
var instance2 = new SubType();
instance1.users.pop(); // "Tom"
console.log(instance2.users); // ["Jack", "Tom"]通过借用构造函数解决了共享原型属性导致的问题。同时也可以通过 call 方法给父类传递参数。
借用构造函数的问题:
组合继承有时也叫伪经典继承,该继承模式将原型链和借用构造函数的技术结合在一起实现。示例代码:
// 父类
function SuperType(company) {
this.company = company;
this.staffs = ["Jack", "Tom"];
}
// 父类方法
SuperType.prototype.getCompany = function () {
console.log(this.company);
};
// 子类
function SubType(company, product) {
// 继承属性
SuperType.call(this, company);
this.product = product;
}
// 继承方法
SubType.prototype = new SuperType();
// 指向正确的constructor
SubType.prototype.constructor = SubType;
SubType.prototype.getProduct = function () {
console.log(this.product);
};
// SubType实例
var instance1 = new SubType("A", "tellphone");
instance1.getCompany(); // "A"
instance1.getProduct(); // "tellphone"
instance1.staffs.push("Amy"); // ["Jack", "Tom", "Amy"]
var instance2 = new SubType("B", "toy");
instance2.getCompany(); // "B"
instance2.getProduct(); // "toy"
console.log(instance2.staffs); // ["Jack", "Tom"]从代码的例子可以观察到,组合继承模式可以让子类的多个实例既能拥有自己的属性,又能使用相同的方法,融合了原型链和借用构造函数的优点。
原型式继承是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。使用如下函数实现:
function object(obj) {
function F() {}
F.prototype = obj;
return new F();
}object 函数对传入的对象实现了浅复制。所以对所有由其创建的实例都共享了 obj 对象中的引用属性。
寄生式继承模式和原型式继承模式很相似,创建了一个仅用于封装继承过程的函数,在函数内部增强对象的功能:
function createAnother(obj) {
var clone = object(obj);
clone.saySomething = function () {
alert("Hello world!");
};
return clone;
}
function object(obj) {
function F() {}
F.prototype = obj;
return new F();
}通过 createAnother 函数,对对象的功能进行增强,然而这种方式也没有达到函数复用的效果,这一点和构造函数模式一样。
通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。寄生组合模式使用寄生模式来实现对父类原型的继承,再将结果指定给子类的原型。其基本模式如下:
function inheritPrototype(subType, superType) {
// 返回父类原型副本并赋值给子类原型
subType.prototype = object(superType.prototype);
subType.prototype.constructor = subType;
}再来看一个例子:
// 父类
function SuperType(name) {
this.name = name;
}
// 在父类的原型上定义方法
SuperType.prototype.getName = function () {
console.log(this.name);
};
// 子类
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
// 寄生组合继承
inheritPrototype(SubType, SuperType);
// 添加子类方法
SubType.prototype.getAge = function () {
console.log(this.age);
};和组合继承模式相比,寄生组合式继承模式只调用了一次 SuperType 构造函数,也避免了在 SubType.prototype 上创建多余的属性。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
《JavaScript高级程序设计》(第三版)