前面文章有提到过原型和原型链的文章,总结下,对象的proto指向的是函数的原型。通过这种方式,可以做到原始的继承方法。
- 理解概念constructor构造函数,是一种用于创建和初始化创建的对象的特殊方法。一般理解constructor指向的都是谁来创建的。
1、constructor是一种函数模板,用于创建对象。
2、constructor是一个属性,告诉实例,是谁创建了实例。

- 原型链就是通过这种方式,可以一层一层的找。constructor就像是族谱一样,开发中我们希望谁是对象的爸爸,谁是对象的爷爷,是清晰的,明确的。
接下来进入正题,关于es5的原型链继承是怎么回事,我们先看下如果不加constructor是什么一个情况。

打印出来的constructor指向的是father的类。然而我们new出来的对象其实是child。我们可以看到这个对象的构造函数已经不存在了,所以我们找constructor的时候根据原型链会一层层往上找,找到了它爷爷的构造函数。

原型链继承
function Father() {
this.name = ['我是父亲'];
this.sex = '男'
}
Father.prototype.getName = function () {
console.log(this.name);
}
function Child(params) {
this.name.push(params);
this.age = 18;
}
Child.prototype = new Father();
// 所有涉及到原型链继承的继承方式都要修改子类构造函数的指向,否则子类实例的构造函数会指向SuperType。
Child.prototype.constructor = Child;
Child.prototype.say = function () {
console.log(this.name + 'say hi');
}
let child1 = new Child('child1');
let child2 = new Child('child2');
child1.name[0] = '我是change child2';
console.log(child1.getName()) // ["我是change child2", "child1", "child2"]
console.log(child2.getName()) // ["我是change child2", "child1", "child2"]
- 上面的代码就是经典的原型链继承,原型链继承有一个很致命的缺点。
- 父属性是共用的
- 子类构造函数的时候,没办法给父类传参数。
上面的代码运行结果,会发现,child1添加值,child2也会变,child2修改值,child1也会变。这个不是我们想要的。
构造函数继承
- 通过call方法,创建的对象指向对象本身。这样就实现了每个对象不会共用父属性。
- 但是也有一个致命的缺点,原型全部丢失了。就是没办法调用到getName的方法
function Father(type) {
this.name = ['我是父亲'];
this.sex = '男'
this.type = type;
}
Father.prototype.getName = function () {
console.log(this.name);
}
function Child(name, type) {
Father.call(this, type);
this.name.push(name);
this.age = 18;
}
let child1 = new Child('child1', 1);
let child2 = new Child('child2', 2);
child2.name[0] = '我是change child2';
console.log(child1.name) // ["我是父亲", "child1"]
console.log(child2.name) // ["我是change child2", "child2"]
组合继承
- 故名思议就是把两个的优点都拿过来,缺点都摒弃。
function Father(type) {
this.name = ['我是父亲'];
this.sex = '男'
this.type = type;
}
Father.prototype.getName = function () {
console.log(this.name);
}
function Child(name, type) {
Father.call(this, type);
this.name.push(name);
this.age = 18;
}
Child.prototype = new Father();
Child.prototype.constructor = Child;
let child1 = new Child('child1', 1);
let child2 = new Child('child2', 2);
child2.name[0] = '我是change child2';
console.log(child1.name) // ["我是父亲", "child1"]
console.log(child2.name) // ["我是change child2", "child2"]
console.log(child1.getName()) // ["我是父亲", "child1"]
console.log(child2.getName()) // ["我是change child2", "child2"]
- 优点就是原型链继承和构造函数继承的共同有点
- 父类的方法可以被复用
- 父类的引用属性不会被共享
- 子类构建实例时可以向父类传递参数
- 缺点调用了两次父类的构造函数。
网友评论