构造函数的继承
function parent() {
this.name = 1
this.play = [3,4]
}
parent.prototype.say = function () {
console.log('say')
}
function children() {
parent.call(this)
this.age = 2
}
console.log(new children().name)
console.log(new children().say())
结果如图:这样的继承只可以继承父函数的自身属性,不可以继承原型链say属性,也就是说只可以部分继承
原型链继承
function parent2() {
this.name = 1
this.play = [3,4]
}
parent2.prototype.say = function () {
console.log('say')
}
function children2() {
this.age = 2
}
children2.prototype = new parent2();
console.log(new children2().name)
console.log(new children2().say())
var m1 = new children2()
var m2 = new children2()
m1.play.push(5)
console.log(m1.play)
console.log(m2.play)
结果如图:这样的继承虽然全部继承了,可以找到name和say属性,但是由于m2.__proto__ === m1.__proto__ 为true,都指向同一个原型链children2.prototype,所以当m1数组改变的时候,m2也跟着变
组合继承
function parent2() {
this.name = 1
this.play = [3,4]
}
parent2.prototype.say = function () {
console.log('say')
}
function children2() {
parent2.call(this)
this.age = 2
}
children2.prototype = new parent2();
console.log(new children2().name)
console.log(new children2().say())
var m1 = new children2()
var m2 = new children2()
m1.play.push(5)
console.log(m1.play)
console.log(m2.play)
结果如下:上面代码就是组合上面两种继承,因为他是通过parent2.call(this)改变this继承的,m1和m2是new出来的两个不同的实例,这样即使m1的数组改变了,m2也不会变,但是这样子写parent2执行了两次
优化方式
function parent2() {
this.name = 1
this.play = [3,4]
}
parent2.prototype.say = function () {
console.log('say')
}
function children2() {
parent2.call(this)
this.age = 2
}
children2.prototype = parent2.prototype;
console.log(new children2().name)
console.log(new children2().say())
var m1 = new children2()
var m2 = new children2()
m1.play.push(5)
console.log(m1.play)
console.log(m2.play)
结果如下:可以看到m2.__proto__.constructor居然是parent2,按道理应该是children2的,因为m2.__proto__.constructor === children2.prototype.constructor,但是children2.prototype = parent2.prototype直接指向了parent2,所以m2.__proto__.constructor直接指向parent2,所以这个优化方式也有缺点
最终优化
function parent2() {
this.name = 1
this.play = [3,4]
}
parent2.prototype.say = function () {
console.log('say')
}
function children2() {
parent2.call(this)
this.age = 2
}
children2.prototype = Object.create(parent2.prototype);
children2.prototype.constructor = children2;
console.log(new children2().name)
console.log(new children2().say())
var m1 = new children2()
var m2 = new children2()
m1.play.push(5)
console.log(m1.play)
console.log(m2.play)
结果如下:可以看到m2.__proto__.constructor是children2的
也许会有疑问为什么不直接如下代码这样,为什么要用Object.create?因为children2.prototype.constructor = children2,如下代码所以parent2.prototype.constructor = children2,这样就连parent2一起改了
children2.prototype = parent2.prototype;
children2.prototype.constructor = children2;
然而children2.prototype = Object.create(parent2.prototype)这行代码意味着children2.prototype._proto_ = parent2.prototype,为什么呢?我们先来看看Object.create发生了什么,关键代码如下:
Object.create = function (o) {
var F = function () {};
F.prototype = o;
var newObj=new F();
return newObj;
};
可以看到Object.create内部创建了一个新对象newObj,
默认情况下newObj.__proto__== F.prototype,
在本例中则重写了构造函数F的原型属性,
最终的原型关系链为newObj.__proto__== F.prototype == o
Object.create()
方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__,所以最终版本是使用Object.create()来创建children2.prototype