prototype

来源:互联网 发布:灵异知乎 编辑:程序博客网 时间:2024/05/16 16:12

根据一个回答整理:https://segmentfault.com/q/1010000008004290/a-1020000008019852

一 为什么觉得prototype比较难以理解,原因是学习的时候首先问这个东西有什么作用,不知道有什么用就去问这个原理是什么,那怎么搞得清楚


二 prototype的作用:

 1 ECMAScript标准里明确说明了设计prototype的用途的,它是用来实现实例间的属性共享和基于原型的继承的。但是我觉得本质的作用就是实现面向对象:

(1)动态添加属性和方法(这个比java的面向对象更加灵活)

(2)实现继承,通过sub.prototype = parent.prototype实现

(3)原型链查找

2 我们能够用的就是(1)(2),第(3)点还未用到过

三 第二点中的一些解释:原型链和prototype的关系

1 如果一个变量a的typeof值为'object',且它不是null,则它有__proto__属性,换句话说就是任何非null对象都有__proto__。prototype不是任何对象都有的,prototype是函数才具备的,任何一个函数,定义之后,都会拥有prototype属性,这个属性的值默认是个对象,且这个对象默认就拥有2个属性constructor, __proto__。也就是说如果你定义了一个函数A,则
A.prototype = { constructor: A, __proto__: Function.prototype }
当然可能还有别的默认属性,这里只列举了和题干相关的属性。
原型链__proto__和prototype的关系。若果var a = new A();
则a.__proto__ = A.prototype,就这个关系。
2 为什么要定义一个原型链__proto__还要定义一个prototype

你直接对原prototype属性赋予了另外一个对象,这时会覆盖默认的prototype,从而断掉函数的原型到函数的原有的关系,即A.prototype.constructor不再指向A了,从而从原型里面再也不能找回函数本身了。造成的后果就是这个函数A之后生成的实例都不知道自己的构造函数是什么了,就像孩子生出来之后不知道爹是谁了一样,所以后面需要补回这个。你可能会问,为啥__proto__就用补回呢?因为覆盖原__proto__造成的影响远不如constructor的大,且__proto__这个属于浏览器私有属性,部分浏览器并未开放的。
3 prototype实现属性共享
首先,prototype的值只是一个对象,在内存里只有一份,回到上面的播放器类(Player)和播放暂停方法(play, pause)。首先得到一个播放器实例var ipod = new Player('ipod');
这时ipod变量的值应该是
{
    // ipod.__proto__ = Player.prototype;
    // 下面是补全的代码,实际上__proto__的值只是一个引用(占据的内存可以忽略不计).
    __proto__: {
        play: function () {},
        pause: function () {}
    }
}
当访问ipod.play()时,由于在ipod上找不到,然后从原型链上找到play方法。也就是说prototype上的方法在ipod上都能访问到,除非ipod上定义了同名的属性(属性覆盖)。在外部看起来就像是play也是ipod的属性一样。这样便保证了属性能添加到实例上,至于有没有节约内存呢?答案是有的,比如再来一个实例var mp3 = new Player('mp3');
如果ipod的play和mp3的play占据不同的内存,则ipod.play === mp3.play会为false,但实际上比较的结果是true。
如果把play或pause写到构造函数内部的this属性上,则ipod.play === mp3.play的值就是false,说明它们占据了不同的内存空间。