原型
没错,第一次看到这张图的我,也呆了。但经过这几天对原型的了解,已经可以完全理解。
构造函数created的时候,就会有一个prototype属性,指向其原型对象。原型对象有一个属性constructor,指回其构造函数。用构造函数new出来的实力对象,有_proto_属性,指向其构造函数的原型。
于是,对于每一个对象来说,都有一个这样的三角关系。
构造函数,本质上是Function new出来的,所以他们的_proto_都指向Function.prototype(Foo, Object, Function)。
xxx.prototype,原型对象,本质上是对象,所以他们的proto都指向Object.prototype(Foo.prototype, Function.prototype)。普通对象,是Object new出来的,所以_proto_也指向Object.prototype。
Object.prototype._proto_ === null
由_proto_指向串联起来的,就是原型链。当我们访问对象的一个属性时,会先看对象本身是否有这个属性,没有的话顺着原型链一层层查找,直到null为止。若找到,则返回该值;找不到,则返回undefinded。
继承
//parent构造函数
function Parent(name) {
this.name = name || 'Adam';
}
//给原型增加方法
Parent.prototype.say = function () {
return this.name;
};
//空的child构造函数
function Child(name) {}
//继承
inherit(Child, Parent);
- 原型继承
function inherit(C, P) {
C.prototype = new P();
}
var kid = new Child();
kid.say(); // "Adam"
kid会拿到Parent构造函数自身&原型上的值,且无法传递参数给父函数。
- 借用构造函数
function Child(a, c, b, d) {
Parent.apply(this, arguments);
}
子对象复制了一套父构造函数自身的属性,改动不会影响父函数。
可以传参给父函数。
子无法拿到父原型上的属性。
- 借用并设置原型
function Child(a, c, b, d) {
Parent.apply(this, arguments);
}
Child.prototype = new Parent();
子对象获得了父对象自己的成员,也获得了父对象中可复用的(在原型中实现的)方法。
子对象也可以传递任何参数给父构造函数。
一个弊端是父构造函数被调用了两次,所以不是很高效。最后,(父对象)自己的属性(比如这个例子中的name)也被继承了两次。
- 共享原型
function inherit(C, P) {
C.prototype = P.prototype;
}
这种模式的原型链很短并且查找很快,因为所有的对象实际上共享着同一个原型。但是这样也有弊端,那就是如果子对象或者在继承关系中的某个地方的任何一个子对象修改这个原型,将影响所有的继承关系中的父对象。(译注:这里应该是指会影响到所有从这个原型中继承的对象。)
- 临时构造函数
function inherit(C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
}
打断父对象和子对象原型的直接链接解决了共享原型时的问题,子对象只继承原型中的属性。