继承是 OOP 语言中一个比较重要的概念,继承可使得子类具备父类的属性和方法或者从新定义、新追加属性和方法等,因为 Javascript 语言没有真正的对象类,因此其实现继承的方法相对而言会比较特殊,实现继承主要是依靠原型链来实现的。 实现继承的方法主要有如下几种:vue
将一个原型对象的实例赋值给另外一个原型对象的原型,从而继承该原型对象的属性和方法。git
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValeu = function () {
return this.property;
}
function SubType() {
this.subproperty = false;
}
// 建立 SuperType 实例,并将该实例赋值给 SubType.prototype
SubType.prototype.getSubValvue = function () {
return this.subproperty;
}
var instance = new SubType();
console.log(instance.getSuperValue()); // true
复制代码
【注意事项】github
优势:闭包
缺点:函数
包含引用类型值的原型属性会被全部实例共享,多个实例对引用类型的操做会被篡改this
function SuperType() {
this.colors = ['red', 'blue', 'green'];
}
function SubType() {
}
// 继承 SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push('black');
console.log(instance1.colors); // 'red, blue, green, black'
var instance2 = new SubType();
console.log(instance2.colors); // 'red, blue, green, black'
复制代码
在子类构造函数中调用执行父类构造函数,并将this指针指向子类的构造函数的做用域, 使得子类的每一个实例都会复制一份父类函数中的属性。spa
function SuperType() {
this.colors = ['red', 'blue' , 'green'];
}
function SubType() {
// 执父类构造函数,继承父类
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push('blck');
console.log(instance1.colors); // 'red, blue, green, black'
var instance2 = new SubType();
console.log(instance2.colors); // 'red, blue, green'
复制代码
优势:prototype
在子类的构造函数中能够向父类函数传递参数设计
function SuperType(name) {
this.name = name;
}
function SubType() {
// 继承 SuperType,同时传递参数
SuperType.call(this, 'Nicholas');
// 实例属性
this.age = 29;
}
var instance = new SubType()
console.log(instance.name); // 'Nicholas'
console.log(instance.age); // 29
复制代码
缺点:指针
组合继承指的是组合原型链和借用构造函数技术的继承方法,使用原型链实现对原型属性和方法的继承, 经过借用构造函数来实现对实例属性的继承。
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function () {
console.log(this.name);
};
function SubType(name, age) {
// 继承属性
// 第二次借用构造函数,调用 SuperType
SuperType.call(this, name);
this.age = age;
}
// 继承方法
// 第一次构造原型链,调用 SuperType
SubType.prototype = new SuperType();
// 重写 SubType.prototype 的 constructor 属性,指向本身的构造函数 SubType
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
console.log(this.age);
};
var instance1 = new SubType('Nicholas', 29);
instance1.colors.push('black');
console.log(instance1.colors); // 'red, blue, green, black'
instance1.sayName(); // 'Nicholas'
instance1.sayAge(); // 29
var instance12 = new SubType('Greg', 27);
console.log(instance2.colors); // 'red, blue, green'
instance2.sayName(); // 'Greg'
instance2.sayAge(); // 27
复制代码
缺点:
利用空对象做为中介,将某个对象直接复制给空对象搞糟函数的原型。
function object(obj) {
function F() {}
F.prototype = obj;
return new F();
}
复制代码
object()对传入的对象执行了一次浅复制,将构造函数的原型直接指向传入的对象。
var person = {
name: 'Nicholas',
friends: ['Shelby', 'Court', 'Van']
};
var anotherPerson = object(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Rob');
var yetAnotherPerson = object(person);
yetAnotherPerson.name = 'Linda';
yetAnotherPerson.friends.push('Barbie');
console.log(person.friends); // 'Shelby, Court, Van, Rob, Barbie'
复制代码
另外,ES5 存在 Object.create() 的方法,可以代替上面的 object 方法。
在原型式继承的基础上,加强对象,返回构造函数
funciton createAnother(original) {
var clone = object(orginal); // 经过调用 object() 函数建立一个新对象
clone.sayHi = function () { // 以某种方式来加强对象
console.log('HI);
}
return clone; // 返回这个对象
}
复制代码
经过函数的做用加强新对象,即给新对象添加属性和方法
var person = {
name: 'Nicholas',
friends: ['Shelby', 'Court', 'Van']
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // 'HI'
复制代码
缺点:
结合借用构造函数传递参数和寄生模实现继承
function inheritPrototype(subType, superType) {
var prototype = Object.create(superType.prototype); // 建立对象
prototype.constructor = subType; // 加强对象
subType.prototype = prototype; // 指定对象
}
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function () {
console.log(this.name);
};
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function () {
console.log(this.age);
};
复制代码
优势:
寄生组合式继承式引用类型最理想的继承范式。
圣杯模式:其原理依然遵循的是寄生组合式
// 圣杯模式
function inherit(subType, superType) {
function F() {};
F.prototype = superType.prototype;
subType.prototype = new F();
subType.prototype.constructor = superType;
subType.prototype.uber = superType.prototype;
}
// 高级圣杯
// 经过闭包函数实现属性私有化的做用
var inherit = (function () {
var F = function () {};
return function (subType, superType) {
// 定义私有属性
//var prop
F.prototype = superType.prototype;
subType.prototype = new F();
subType.prototype.constructor = superType;
subType.prototype.uber = superType.prototype;
// 获取私有属性
subType.prototype.getProp = function () {
// get prop
}
}
})
复制代码
经过 Object.assign 把其余原型构造函数拷贝到实例子类原型上。
function MyClass() {
SuperClass.call(this);
OtherSuperClass.call(this);
}
// 继承 SuperClass
MyClass.prototype = Object.create(SuperClass.prototype);
// 混合其余类
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
// 从新指定 constructor
MyClass.prototype.constructor = MyClass;
MyClass.prototype.myMethod = function () {
// do something
}
复制代码
extends关键字主要用于类声明或者类表达式中,以建立一个类,该类是另外一个类的子类。其中constructor表示构造函数,一个类中只能有一个构造函数,有多个会报出SyntaxError错误,若是没有显式指定构造方法,则会添加默认的 constructor方法
class Rectangle {
// constructor
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea()
}
// Method
calcArea() {
return this.height * this.width;
}
}
const rectangle = new Rectangle(10, 20);
console.log(rectangle.area);
// 输出 200
-----------------------------------------------------------------
// 继承
class Square extends Rectangle {
constructor(length) {
super(length, length);
// 若是子类中存在构造函数,则须要在使用“this”以前首先调用 super()。
this.name = 'Square';
}
get area() {
return this.height * this.width;
}
}
const square = new Square(10);
console.log(square.area);
// 输出 100
复制代码
extends 继承的核心代码以下,其实现和上述的寄生组合式继承方式同样
function _inherits(subType, superType) {
// 建立对象,建立父类原型的一个副本
// 加强对象,弥补因重写原型而失去的默认的constructor 属性
// 指定对象,将新建立的对象赋值给子类的原型
subType.prototype = Object.create(superType && superType.prototype, {
constructor: {
value: subType,
enumerable: false,
writable: true,
configurable: true
}
});
if (superType) {
Object.setPrototypeOf
? Object.setPrototypeOf(subType, superType)
: subType.__proto__ = superType;
}
}
复制代码
ES5继承和ES6继承的区别
ES5的继承实质上是先建立子类的实例对象,而后再将父类的方法添加到this上(Parent.call(this)).
ES6的继承有所不一样,实质上是先建立父类的实例对象this,而后再用子类的构造函数修改this。由于子类没有本身的this对象,因此必须先调用父类的super()方法,不然新建实例报错。
NicholasC.Zakas. JavaScript高级程序设计. JAVASCRIPT高级程序设计. 2012.