js继承模式
更新日期:
文章目录
一切的继承都只是为了复用,为了解放劳动力!
然而遗忘是我们的天性,曾经懂得!
开篇废话
其实很不想写这篇,关于js继承的文章遍地皆是,继承应该是一种思想模式,很多时候我们并不会去使用它,然而我们却必须懂它。我也很少用它,因为大部分直接扩展属性,$.extend()就直接解决了。可能在大型架构,尤其是游戏中,继承是一个很重要的模式,比如精灵类Sprite,说到底,一切都是提高代码复用率。
废话还没结束,申明一下,这里针对的 javascript 的继承,假如我们已经理解了js中的 对象 的概念,还知道一个叫做 prototype 的属性的东西。
#模式1. 设置原型
1 | // 父构造函数 |
可以看到,这个模式的继承只有一行代码,把父类的实例 赋值 给子类的prototype,然后子类 就可以获取到父类的 属性和方法,这是为什么呢?这是什么原理呢?这里需要画一张图:
根据上图,我们来分析:
(1) Child构造函数为null,除了一个隐式属性proto
(2) c.say()调用,可是c并没有say方法,但是有原型链
(3) 根据图中的原型链向上找,发现 父类的实例也没有say方法,然后接着向上,找到父类的原型中的say
(4) say中调用了this.name,然后依照上面的顺序重新开始解析name属性
ok,是不是已经明白了,那么接下来我们给子类添加一个name属性:
1 | c.name = 'child'; |
是不是很好理解了呢。
#模式1. 缺点
- 此模式完全继承了父类的 私有属性(this.) 和 原型属性(prototype)
- 无法在子类中传参数
- 如果父类的属性是一个引用类型的,那么子类的修改会影响到父类的对象,demo
1
2
3
4
5
6
7
8
9
10
11
12
13function Parent(name){
this.name = name || "parent";
this.info = ['a', 'b', 'c'];
}
Parent.prototype.say = function (){
return this.name;
}
var p = new Parent();
function Child(name){}
Child.prototype = p;
var c = new Child();
c.info.push('d');
console.log(p.info); // ["a", "b", "c", "d"]#模式2. 借用构造函数
熟悉 apply 和 call的同学都明白上面那行代码发生了什么,子类直接获取了父类对象的属性副本。1
2
3function Child(){
Parent.apply(this, arguments);
}
记住,副本这个概念很重要。所以只要你愿意,你可以在子类的构造函数中借用n多父类都可以,类似于多重继承。#模式2. 缺点
此种模式有一个致命的缺陷,就是子类和父类相对独立,子类无法获取父类 prototype中的方法和属性,然而一般公用的方法都会放置在 prototype中,不是吗?
#模式3. 借用和设置原型
相信聪明的你看到这里,瞬间就明白要做什么了,对,就是模式1和模式2的合体。
1 | function Parent(name){ |
#模式3. 缺点
父构造函数被调用两次
#模式4. 直接共享原型
1 | function extend(C, P){ |
经过上面几种模式的分析,相信聪明的你一眼就能看出此种模式的优劣。造成父类和子类原型强引用,互相影响。
#模式5. 借用临时构造函数
我们真正想继承的可能只是prototype里面的方法和属相,而不需要添加到 this的,这里还是用模式1和4的合体.
1 | function extend(C, P){ |