Ryan Shang

生死看淡,不服就干

0%

ES5继承

面向对象编程有三大特征:封装、继承、多态。

严格意义上来说,JavaScript是没有继承和多态的,所以需要我们自己来实现,今天来讲讲ES5的继承实现方法。

我个人认为,最好的继承方案,应该是父类私有的属性和方法拿到子类上用,父类公有的属性和方法直接通过__proto__查找到。

要实现父类的私有属性和方法拿到子类上来用,最简单的方式就是用call方法。

1
2
3
4
5
6
7
8
9
10
11
function Father() {
this.x = 10;
this.y = 20;
}
Father.prototype.fn=function () {
console.log(this.x);
};
function Son() {
//this->实例
Father.call(this); //父类上的私有属性拷贝一份放到子类的实例上
}

这样,私有属性和方法的继承就完成了,接下来,我们看怎么继承公有属性和方法。

首先,把父类的prototype直接赋值给子类:

1
Son.prototype = Father.prototype;

这样会导致互相污染,肯定是行不通的。接着,能想到的方式肯定是把父类实例化一个实例出来,然后子类的prototype改为这个实例:

1
Son.prototype = new Father();

这样确实子类可以拿到父类的公有属性和方法,但是这个做法会导致子类的实例和原型上都会有父类的私有属性和方法。这个方式明显就不合适了。

为了只拿到公有属性和方法,试试直接简单粗暴的把父类原型上的属性和方法拷贝一份放在子类原型上:

1
2
3
4
5
6
function copy(Father, Son) {
for(var attr in Father){
Son[attr] = Father[attr];
}
}
copy(Father.prototype, Son.prototype);

这样就不会对子类的原型造成污染,但是同样有问题:比较占空间,尤其是父类越来越复杂的时候。

这样的话,貌似借助一个临时的空类是一个不错的选择。

1
2
3
4
function Temp () {}
Temp.prototype = Father.prototype;
Son.prototype = new Temp(); // 将子类的原型变成临时类的实例
Son.prototype.constructor = Son; // 最后别忘了把constructor改回来

这样既可以拿到父类的私有方法和属性,子类的原型上也会有父类的公有属性和方法。

最后来整体看下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function myCreate(obj) {
//obj指的是父类的原型
function Temp () {}
Temp.prototype = obj;
return new Temp;
}
function Father() {
this.x = 10;
this.y = 20;
}
Father.prototype.fn=function () {
console.log(this.x);
};
function Son() {
Father.call(this);
}
Son.prototype = myCreate(Father.prototype);
Son.prototype.constructor = Son;

这样就完成了一个比较不错的继承,其实myCreate方法应该可以看作一个简化的Object.create()方法