blog
blog copied to clipboard
JavaScript 常用继承方式总结
基于prototype方式
function Super(){
this.val = 1;
this.arr = [];
}
function Sub(){
// ...
}
Sub.prototype = new Super();
var sub1 = new Sub();
var sub2 = new Sub();
sub1.val = 2;
sub1.arr.push(2);
alert(sub1.val); // 2
alert(sub2.val); // 1
alert(sub1.arr); // 2
alert(sub2.arr); // 2
优缺点
-
简单,易于实现
-
原型对象的引用属性是所有实例共享
执行sub1.arr.push(2);先对sub1进行属性查找,找遍了实例属性(在本例中没有实例属性),没找到,就开始顺着原型链向上找,拿到了sub1的原型对象,发现有arr属性。于是给arr末尾插入了2,所以sub2.arr也变了 创建子类实例时,无法向父类构造函数传参
借用构造函数
function Super(val){
this.val = val;
this.arr = [];
this.say = function() { alert(1) }
}
function Sub(val){
Super.call(this, val);
// ...
}
var sub1 = new Sub(1);
var sub2 = new Sub(2);
sub1.arr.push(2);
alert(sub1.val); // 1
alert(sub2.val); // 2
alert(sub1.arr); // [2]
alert(sub2.arr); // []
alert(sub1.say === sub2.say); // false
优缺点
- 解决了子类实例共享父类引用属性的问题
- 创建子类实例时,可以向父类构造函数传参
- 无法实现函数复用,每个子类实例都持有一个新的say函数,会影响性能,内存增加
组合继承
function Super(){
this.val = 1;
this.arr = [];
}
// 在此处声明函数
Super.prototype.fun1 = function(){};
Super.prototype.fun2 = function(){};
function Sub(){
Super.call(this);
// ...
}
Sub.prototype = new Super();
var sub1 = new Sub(1);
var sub2 = new Sub(2);
alert(sub1.fun === sub2.fun); // true
优缺点
- 不存在引用属性共享问题
- 可传参
- 函数可复用
- 父类构造函数被调用了两次,子类原型上有一份多余的父类实例属性
寄生组合继承
function create(obj) {
var F = function(){};
F.prototype = obj;
return new F();
}
function Super(){
this.val = 1;
this.arr = [1];
}
Super.prototype.fun1 = function(){};
Super.prototype.fun2 = function(){};
function Sub(){
Super.call(this);
// ...
}
var proto = create(Super.prototype);
proto.constructor = Sub;
Sub.prototype = proto;
var sub = new Sub();
alert(sub.val);
alert(sub.arr);
优缺点
- 最佳方式
原型式继承
function create(obj){
var F = function(){};
F.prototype = obj;
return new F();
}
function Super(){
this.val = 1;
this.arr = [];
}
// 拿到父类对象
var sup = new Super();
var sub = create(sup);
// 增强
sub.attr1 = 1;
sub.attr2 = 2;
//sub.attr3...
alert(sub.val); // 1
alert(sub.arr); // 1
alert(sub.attr1); // 1
优缺点
- 用
create
函数得到得到一个“纯洁”的新对象(“纯洁”是因为没有实例属性),再逐步增强之(填充实例属性), ES5提供了Object.create()函数,内部就是原型式继承 - 无法实现函数复用
- 原型引用属性会被所有实例共享