js prototype 定义作用

var index = function(){};
index.prototype = {
    btnActive: function (){@#¥%……&*#¥%……},
    roleHover: function (){@#¥%……&*#¥%……}
}
index.prototype.constructor = index;
var index = new index();
index.btnActive();
index.roleHover();

请问一下定义一个函数之后 再去定义函数的prototype 是有什么作用?
然后最后几句也还没看懂什么意思
求高手解答一下

阅读 4.5k
3 个回答
  1. 构造函数用函数表达式……唔……倒不是不行……只是比较少见……

  2. 对象实例和构造函数的名字居然一样……写这代码的人到底在想什么……

  3. prototype的问题光是SF就能搜索出好几页来……提问之前先搜一下了嘛……

  4. 这算是非常基础的问题了,讲js的书百分之百都会讲这东西,去翻翻书吧。

  5. 推荐一本《JavaScript高级程序设计》

回答你这个问题需要具备几点知识

  1. prototype是用来做什么的?

  2. 原型链查找

  3. 原型链和prototype的关系

首先说第一个,prototype在ECMAScript标准里明确说明了设计prototype的用途的,它是用来实现实例间的属性共享和基于原型的继承的。这里不谈继承,只谈前者。什么是属性共享哦?你可能会这么问。这么说吧,如果定义了一个类(音乐播放器类Player),这个类的实例(ipod)都有播放,暂停等方法(方法是特殊的属性,它的属性值是函数是一般叫方法),这些方法都是使用播放器实例的东西来实现音乐的播放。所有的播放器的这两个方法都干的是几乎相同的事,唯一的不同就只是因为是不同的播放器(实例)而造成的。如果不存在共享属性,则每个播放器都有自己的播放暂停方法,在内存里面就有多少播放器就会有多少播放暂停的方法。这样就消耗了大量的内存来存储这些方法,为了节省这部分内存,所以设计了prototype来实现属性共享。至于为什么prototype就实现了,需要后面2个知识点。且听我慢慢道来。

第二点,原型链查找.
当访问一个实例对象的属性或方法时,是判断实例本身是否含有这个属性或方法,如果存在,则直接返回这个属性的值,如果没有,则在这个对象的[[proto]]属性的值上查找是否有这个属性或方法,以此类推,直到找到或[[proto]]属性的值为null为止。这个[[proto]]在浏览器里以前是隐藏属性,现在部分浏览器开放了这个属性,它就是__proto__(前后各2个下划线),任何typeof值为'object'的都有__proto__属性,除了null。这是原型链是有限的重要依据.

第三点,原型链和prototype的关系
从第二点可以看出,如果一个变量a的typeof值为'object',且它不是null,则它有__proto__属性,换句话说就是任何非null对象都有__proto__。prototype不是任何对象都有的,prototype是函数才具备的,任何一个函数,定义之后,都会拥有prototype属性,这个属性的值默认是个对象,且这个对象默认就拥有2个属性constructor, __proto__。也就是说如果你定义了一个函数A,则
A.prototype = { constructor: A, __proto__: Function.prototype }
当然可能还有别的默认属性,这里只列举了和题干相关的属性。说了大堆,你发现是不是还没有进入正题?
OK,马上来。原型链__proto__和prototype的关系。若果var a = new A();
a.__proto__ = A.prototype,就这个关系。

上面你的代码里面,你直接对原prototype属性赋予了另外一个对象,这时会覆盖默认的prototype,从而断掉函数的原型到函数的原有的关系,即A.prototype.constructor不再指向A了,从而从原型里面再也不能找回函数本身了。造成的后果就是这个函数A之后生成的实例都不知道自己的构造函数是什么了,就像孩子生出来之后不知道爹是谁了一样,所以后面需要补回这个。你可能会问,为啥__proto__就用补回呢?因为覆盖原__proto__造成的影响远不如constructor的大,且__proto__这个属于浏览器私有属性,部分浏览器并未开放的。

下面补坑(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,说明它们占据了不同的内存空间。

understand?

javascript中的每个对象都有prototype属性,Javascript中对象的prototype属性的解释是:返回对象类型原型的引用。
A.prototype = new B();
理解prototype不应把它和继承混淆。A的prototype为B的一个实例,可以理解A将B中的方法和属性全部克隆了一遍。A能使用B的方法和属性。这里强调的是克隆而不是继承。可以出现这种情况:A的prototype是B的实例,同时B的prototype也是A的实例。
任何一个函数的声明,它都将会具有上面所述的5个property(方法或者属性).
详情参见javascript必知必会之prototype

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题