prototype 是哪里来的?
1 | function Person() {} |
这是一个简单的函数声明,除了声明函数,还发生了什么?
1 | Person.prototype = { constructor: Person }; |
这里我们发现函数 Person 声明完以后,多了一个叫 prototype 的属性,它也是一个对象,包含一个 constructor 属性,constructor 也是一个引用,指向了 Person 函数本身
..proto.. 是什么鬼?
1 | var xwill = new Person(); |
我们使用 new 关键字生成一个 Person 的实例对象 xwill,然后打印了这个对象,我们发现,xwill 对象中出现了一个 proto 属性,而这个 proto 属性的值是一个对象。展开这个对象
很清楚了吧,xwill 中的 proto 对象和 Person.prototype 对象实际上是同一个,xwill 的 proto 对象指向了 Person.prototype 对象
我们来验证一下
嗯,确实是这样
沿着 prototype 寻找属性
思考一个问题,当我们去获取 xwill 对象的某个属性的时候,在 js 引擎中,到底发生了什么?
1 | // what happend in js engine ??? |
这个问题有点高级,我们先来回答一个简单的问题,console.log(xwill.name) 会输出什么?
毫无疑问, xwill 暂时只有一个 proto 属性,并没有 name 属性,所以输出 undefined
如果我们给 xwill.name 赋值,那么显然 xwill.name 就会等于所赋的值
实际上,当我们在获取 xwill.name 的值的时候,js 引擎做了两件事
- 寻找 xwill 对象上有没有 name 属性,有,则返回
- 如果没有,则寻找 xwill 的 proto 对象中有没有 name 属性(由于 xwill 的 proto 对象等于 Person 的 prototype 对象,所以就相当于寻找 Person 的 prototype 对象中是否有 name 属性),有,则返回
现在我们可以回答一个常见的问题,为什么使用 prototype 定义方法 ?
1 | function Person(name) { |
p1 和 p2 对象并没有 say 方法,但是它们的 proto 对象中有 say 方法(因为 Person 的 prototype 对象定义了 say 方法,而 p1,p2 的 proto 对象又都等于 Person 的 prototype 对象),所以,相当于实现了方法共享,这样做的好处就是如果有100个对象,那么这100个对象共享一个方法,而不是各自有一个一样的方法
prototype 继承 (classic inheritance)
有了上面的基础,我们就可以通过改变 prototype 的指向,从而达到继承的效果
1 | function Person(name, jobName) { |
这时候看看 Person.prototype 的 proto 属性,指向的对象已经是 Job 的 prototype 了
现在 p1 和 p2 既是 Person 的实例,又是 Job 的实例
1 | console.log(p1 instanceof Person); //true |
Prototypal Inheritance
这是另一种原型继承模式
1 | // Prototypal Pattern |
连续继承:
1 | // prototypal |
这里 xwill 继承了 musician,而 musician 继承了 human,所以 xwill 即可以使用 musician 上的属性,也可以使用 human 上的属性
Prototypal Pattern Video
Prototypal inheritance
Others
nodejs 0.1.0 使用的继承工具方法
1 | function inherits(ctor, superCtor) { |
node 5.9.1 使用的继承方法已经改成了使用 Object.setPrototypeOf(ES6)
1 | function inherits(ctor, superCtor) { |