继承的意义
当我们想要一个对象能够访问另一个对象的属性,同时,这个对象还能够添加自己新的属性或是覆盖可访问的另一个对象的属性,我们实现这个目标的方式叫做“继承”。
更重要的是,继承可以在大型项目中简化代码,高效运作。
继承的实现
一般来说,继承有六种实现方式。
1. 原型链继承
作为对面向对象语言中对类的模仿,我们可以使用原型链的形式实现继承。
function Father(){
this.name = "Nicholas";
this.tShirtColor = ['blue',"black","brown"];
}
function Child(){
this.age = 12;
}
//Child继承Father
Child.prototype = new Father();
var cherry = new Child();
var sunny = new Child();
console.log(cherry.name); //"Nicholas"
这样就形成了原型链,完成了Father到Child的继承。
但是这样的副作用也很明显,比如
cherry.tShirtColor.push("pink");
console.log(cherry.tShirtColor); //[ 'blue', 'black', 'brown', 'pink' ]
console.log(sunny.tShirtColor); //[ 'blue', 'black', 'brown', 'pink' ]
出现了引用值共享的问题。
也不能向父级传递参数。
构造函数继承
function Father(){
this.tShirtColors = ['red','blue','yellow'];
}
function Child(){
Father.call(this);
}
var son1 = new Child();
var son2 = new Child();
son1.tShirtColors.push('black');
alert(son1.tShirtColors); //'red','blue','yelllow','black'
alert(son2.tShirtColors); //'red','blue','yelllow'
这种方法利用call函数改变了this的指向。
而且可以实现参数的传递,如下
function Father(){
this.name = name;
}
function Son(){
Father.call(this,'john');
}
var son = new Son();
alert(son.name); //john
但是使用这种方法的话,不能继承父级原型上的属性和方法。
原型链+构造函数
将以上两种方法融合,我们得到了一种更好的方法。
既不会导致引用值共享,还可以传递参数,而且又能继承父级的原型链。
function Father(){
this.name = name;
this.tShirtColors = ['red','blue','yellow'];
}
Father.prototype.sayName= function(){
alert(this.name);
};
function Son(){
//继承了属性
Father.call('this'); // 1
}
//继承了方法
Son.prototype = new Father(); // 2
var son1 = new Son();
var son2 = new Son();
son1.tShirtColors.push('black');
alert(son1.TshirtColors); //'red','blue','yelllow','black'
alert(son2.TshirtColors); //'red','blue','yelllow'
以上的代码中调用了两次构造函数Father,这是组合继承明显的一个缺点。
原型式继承
首先在object()函数内部创建一个临时性的构造函数 ,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。如下:
function object(o){
function F(){}
var F.prototype = o;
return new F();
}
var person = {
name : 'john',
tShirtColors : ['red','blue','yellow']
};
var person1 = object(person);
person1.name = 'merry';
var person2 = object(person);
person2.tShirtColors.push('black');
在ES5中,函数object可被Object.create()取代。
但是这种方法中会使得引用值共享
寄生式继承
寄生式和上面的原型式继承紧密相连。
function create(o){
var clone = object(o); //这里的object函数即是原型式里自定义的函数
//在这个基础上给clone添加方法或属性
clone.sayName = function(){
alert(this.name);
}
}
原型式+寄生式
当把寄生式和原型式相结合,我们得到一种高效的引用类型的继承范式。这也是最为常用的一种继承方式。解决了二次调用的问题。
function inherit(Target,Origin){
function F(){ };
F.prototype = Origin.prototype //这一行与下面一行不能颠倒,否则F的原型不能改变。
Target.prototype = new F();
Target.prototype.construct = Target; //完善代码,使Target的构造器指向自己。
Target.prototypr.uber = Origin.prototype //知道真正继承自谁。
}
Father.prototype.lastName = 'xxx';
function Father(){}
function Son(){}
inherit(Son,Father);
var son = new Son();
var father = new Father();
此时赋给son的值不会传到father里。
这种方法最通用的写法如下:
var inherit = (function (){
var = function (){};
return = function (Target,Origin){
F.prototype = Origin.prototype
Target.prototype = new F();
//Target.prototype.construct = Target;
//Target.prototypr.uber = Origin.prototype
}
}());
以上就是继承的几种常见方式。