本文首次发布于 tinypoint`s blog, 作者 @张小点(tinypoint) ,转载请保留原文链接.
前言
今天是10月1日国庆节,我要在这里先祝福祖国更加的国泰民安,繁荣昌盛。由于今年的十一与中秋连在一起,放假时间比过年还长,所以身边的朋友回家的回家,出去浪的出去浪,留我自己苦逼的在宿舍敲代码,不过没关系,误敲代码也要敲出风度来,让我们回顾一下在js中是如何实现继承的,话不多说,直接开始
原型链继承
缺点
- 引用类型被实例共享,每个实例对引用类型的操作,会直接影响到其他实例
- 父类无法传参(或者说,无法再不影响其他实例的情况下向父类传参)
function Super() {
this.hobby = ['ball', 'game'];
}
Super.prototype.addHobby = function (hobby) {
this.hobby.push(hobby);
}
function Sub(name) {
this.name = name;
}
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
var xiaoming = new Sub('xiaoming');
xiaoming.addHobby('read');
console.log(xiaoming.hobby); //"ball", "game", "read"
var xiaohong = new Sub('xiaohong');
console.log(xiaohong.hobby); //"ball", "game", "read"
借用构造函数继承
优点
- 可以向父类传参
缺点
- 构造函数老毛病,方法被每个实例创建一次,无法复用,造成内存浪费
- 父类无法传参(或者说,无法再不影响其他实例的情况下向父类传参)
- 父类原型中的方法,子类实例不可用
function Super(name, hobby) {
this.name = name;
this.hobby = hobby;
}
Super.prototype.sayHi = function () {
console.log('Hi');
}
function Sub(name, age, hobby) {
Super.call(this, name, hobby);
this.age = age;
}
Sub.prototype.addHobby = function (hobby) {
this.hobby.push(hobby)
}
var a = new Sub('a', 22, ['ball', 'game']);
a.addHobby('read');
console.log(a.hobby); //"ball", "game", "read"
var b = new Sub('b', 23, ['ball', 'game']);
console.log(b.hobby); //"ball", "game"
a.sayHi(); //Error 没有此方法
组合继承(伪经典继承)
- 将原型链继承和借用构造函数继承两种技术组合到一块,发挥二者所长
- 使用原型链方法实现原型的属性和方法继承
- 使用借用构造函数实现实例属性的继承
function Super(name, hobby) {
this.name = name;
this.hobby = hobby;
}
Super.prototype.sayHi = function () {
console.log('Hi');
}
function Sub(name, age, hobby) {
Super.call(this, name, hobby);
this.age = age;
}
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
Sub.prototype.addHobby = function (hobby) {
this.hobby.push(hobby)
}
var a = new Sub('a', 22, ['ball', 'game']);
a.addHobby('read');
console.log(a.hobby); //"ball", "game", "read"
var b = new Sub('b', 23, ['ball', 'game']);
console.log(b.hobby); //"ball", "game"
a.sayHi(); //'Hi'
原型式继承
将想要继承的对象放入函数中,从而不用创建自定义类型实现继承
缺点
- 依旧是引用类型共享问题
function object(obj) {
function F() {}
F.prototype = obj;
return new F();
}
var person = {
name: 'xiaoming',
hobby: ['ball', 'game']
}
var a = object(person);
a.name = 'xiaohong';
a.hobby.push('read');
console.log(a.hobby); //"ball", "game", "read"
var b = object(person);
b.name = 'xiaolu';
console.log(b.hobby); //"ball", "game", "read"
//上面的object函数等同于ES5中的Object.create()函数
var person = {
name: 'xiaoming',
hobby: ['ball', 'game']
}
var a = Object.create(person);
a.name = 'xiaohong';
a.hobby.push('read');
console.log(a.hobby); //"ball", "game", "read"
var b = Object.create(person);
b.name = 'xiaolu';
console.log(b.hobby); //"ball", "game", "read"
寄生式继承
与原型式继承紧密相关,在函数内部克隆要继承的对象,并强化新对象
缺点
- 类似构造函数模式的函数无法复用的问题
var person = {
name: 'xiaoming',
hobby: ['ball', 'game']
}
function object(obj) {
function F() {}
F.prototype = obj;
return new F();
}
function createSubObject (obj) {
var clone = object(obj); //object可以替换为任意返回新对象的函数
clone.sayHi = function () {
console.log('sayHi');
}
return clone;
}
var a = createSubObject(person);
a.name = 'xiaohong';
a.sayHi();
寄生组合式继承
优点
- 解决了组合式继承的二次调用父类构造函数的问题
- 使用借用构造函数继承属性
- 使用原型链混成的形式继承方法
- 不必在指定原型而实例化父类,只需要父类原型的一个副本
function object(obj) {
function F() {}
F.prototype = obj;
return new F();
}
function Super(name) {
this.name = name
}
Super.prototype.sayName = function () {
console.log(this.name);
}
function Sub(name, age) {
Super.call(this, name);
this.age = age;
}
Sub.prototype = object(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function () {
console.log(this.age)
}
var a = new Sub('xiaohong', 22);
a.sayName(); //'xiaohong' 小红成功继承了父类原型的方法
参考