我试图用简单、清晰、专业的方式讲清楚js继承这么一个有点复杂的问题javascript
由于须要复用java
经过复用咱们能够实现共享。函数
从对象的角度来讲,咱们不想重复写同一段逻辑,因此逻辑须要复用;可是咱们不但愿一个对象掌管的变量被其它对象修改。因此变量不能共享。也就是要共享函数,不共享属性。this
原型链能够用来实现共享,构造函数可让对象拥有本身的属性,二者结合的组合的组合继承
即是最经常使用
的继承方式。spa
组合继承中有一个弊端,经过简单的改进,寄生组合式继承
能够实现最有效
的继承。接下来将展开讲解组合继承
和寄生组合式继承
。prototype
若是已经了解了这些知识,能够直接跳过设计
function Person () { } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function () { console.log(this.name); }; var person1=new Person(); person1.sayName();//Nicholas
构造函数(Person
)能够用来建立实例(person1
),建立的实例和构造函数共同指向一个原型(Person.prototype
),原型的构造器又指向了构造函数。
若是没有指定原型,则默认的原型为Object.prototype
。code
new操做符具体干了什么呢?对象
建立一个空对象,而且 this 变量引用该对象,同时还继承了该函数的原型。var obj = {};
blog
属性和方法被加入到 this 引用的对象中。obj.__proto__ = Base.prototype;
(图示中用[[Prorotype]]
表明__proto__
)
新建立的对象由 this 所引用,而且最后隐式的返回 this 。Base.call(obj);
屏蔽:
//...上面的代码,此处省略 var person2=new Person(); person1.name='Mike'; person1.sayName();//Mike person2.sayName();//Nicholas
属性值沿着原型链,遵循就近原则。就近的属性值会屏蔽上层原型链的属性值
动态追加而非断开:
误区:javaScript高级程序设计第三版 中指出了 修改原型时 这样一种断开情景
function Person () { } var friend=new Person(); Person.prototype={ constructor:Person, name:'Nicholas', age:29, job:'Software Engineer', sayName:function () { console.log(this.name); } } friend.sayName();//error
friend的原型丢失了,和它断开了联系。
其实如今已经有所改进,运行friend.sayName();
,应该输出为:Nicholas
,当修改原型时,语言的设计意图是什么?若是你是语言的设计者到底应该为什么种输出?
我认为最好的效果是确保其稳妥性的同时,又不失灵活是最佳的,在这个例子中,建立完原型关系后又想追加原型中的属性,我但愿他可以追加成功,所以,但愿它输出为:Nicholas
。
可是,我不但愿原型中的属性随意修改,先开始的时候我给他赋予了值,也定义了相应的方法使用这个值,若是这个值在后期随意修改的话,使用这个方法的时候会以为不知所措。
因此我但愿的涉及是原型属性值能够追加但定义后值不能随意修改,以下:
function Person () { } Person.prototype.name = "Greg"; Person.prototype.age = 24; Person.prototype.job="Doctor"; var friend=new Person(); Person.prototype={ constructor:Person, name:'Nicholas', age:29, job:'Software Engineer', sayName:function () { console.log(this.name); } } friend.sayName();//Greg
打印friend.__proto__
:
Object {name: "Greg", age: 24, job: "Doctor"} age:24 job:"Doctor" name:"Greg" sayName:function()
横向上,每一个原型的属性 在原型自己被修改时 只是动态追加而不会修改原值;纵向上,原型链继承遵循就近原则。
让一个构造函数A的原型为另外一个构造函数B的实例,便造成了A继承B的原型链。
function SuperType() { this.property=true; } SuperType.prototype.getSuperValue=function () { return this.property; }; function SubType() { this.subproperty=false; }; SubType.prototype=new SuperType(); SubType.prototype.getSubValue=function () { return this.subproperty; } var instance=new SubType();
不易理解的地方:subproperty的属性在实例中而不在SubType构造函数中;property的属性在 SubType.prototype中而不在SuperType构造函数中。
讲解:subproperty和property都是实例属性,在哪里建立了实例就出如今哪里,
优点:函数共享
缺陷:原型链体现出了共享的特性,当一个实例改变了其从原型那里继承来的引用属性值时,其它继承自这个原型属性的值都将被改变。
function SuperType() { this.colors=['red','blue','green']; } function SubType() { }; 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"]
function SuperType(name) { this.name=name; } function SubType() { SuperType.call(this,'Nicholas'); this.age=29; } var instance=new SubType(); console.log(instance.name+' '+instance.age);//Nicholas 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.call(this,name); this.age=age; } SubType.prototype=new SuperType(); SubType.prototype.sayAge=function () { console.log(this.age); } var instance1=new SubType('Nicholas',29); instance1.colors.push('black'); console.log(instance1.colors); instance1.sayName(); instance1.sayAge(); var instance2=new SubType('Greg',27); console.log(instance2.colors); instance2.sayName(); instance2.sayAge();
组合继承虽然实现了需求:共享函数,但不共享属性,但是它是有不足之处:咱们在独立属性时只是但愿实例有各自的属性就行了,不须要原型(SubType.prototype)中也存在属性,这就多余了。
SubType.prototype存在属性是由于它对SuperType作了实例化继承,咱们将实例话继承换成前拷贝继承即可以解决问题:
//将组合继承中的实例化继承: //SubType.prototype=new SuperType(); //改成浅拷贝继承: function inheritPrototype(subType,superType){ var prototype=Object.create(superType.prototype);//建立对象 prototype.constructor=subType; subType.prototype=prototype; } inheritPrototype(SubType,SuperType);
Object.create()代码说明:
function Object.create(o){ function F() {}; F.prototype=o; return new F(); }
javascript高级程序设计 第三版