注:本文来自微博网友@三月沙 的翻译投稿。
Javascript原型总会给人产生一些困惑,无论是经验丰富的专家,还是作者自己也时常表现出对这个概念某些有限的理解,我认为这样的困惑在我们一开始接触原型时就已经产生了,它们常常和new、constructor相关,特别是函数(function)的原型(prototype)属性(property)。事实上,原型是一种非常简单的概念。为了更好的理解它,我们应该首先记住这个原则,那就是忘记我们已经学到的关于构造原型(construtor prototypes)的认识。
什么是原型?
原型是一个对象,其他对象可以通过它实现属性继承。
任何一个对象都可以成为原型么?
是
哪些对象有原型
所有的对象在默认的情况下都有一个原型,因为原型本身也是对象,所以每个原型自身又有一个原型(只有一种例外,默认的对象原型在原型链的顶端。更多关于原型链的将在后面介绍)
好吧,再绕回来,那什么又是对象呢?
在javascript中,一个对象就是任何无序键值对的集合,如果它不是一个主数据类型(undefined,null,boolean,number,or string),那它就是一个对象
你说每个对象都有一个原型,可是我当我写成({}).prototype 我得到了一个null,你说的不对吧?
忘记你已经学到的关于原型属性的一切,它可能就是你对原型困惑的根源所在。一个对象的真正原型是被对象内部的[[Prototype]]属性(property)所持有。ECMA引入了标准对象原型访问器Object.getPrototype(object),到目前为止只有Firefox和chrome实现了此访问器。除了IE,其他的浏览器支持非标准的访问器__proto__,如果这两者都不起作用的,我们需要从对象的构造函数中找到的它原型属性。下面的代码展示了获取对象原型的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var a = {}; //Firefox 3.6 and Chrome 5 Object.getPrototypeOf(a); //[object Object] //Firefox 3.6, Chrome 5 and Safari 4 a.__proto__; //[object Object] //all browsers a.constructor.prototype; //[object Object] |
ok,一切都进行的很好,但是false明明是一个主数据类型,可是false.__proto__却返回了一个值
当你试图获取一个主数据类型的原型时,它被强制转化成了一个对象
1 2 3 |
//(works in IE too, but only by accident) false.__proto__ === Boolean(false).__proto__; //true |
我想在继承中使用原型,那我该怎么做?
如果仅仅只是因为一个实例而使用原型是没有多大意义的,这和直接添加属性到这个实例是一样的,假如我们已经创建了一个实例对象 ,我们想要继承一个已经存在的对象的功能比如说Array,我们可以像下面这样做( 在支持__proto__ 的浏览器中)
1 2 3 4 |
//unusual case and does not work in IE var a = {}; a.__proto__ = Array.prototype; a.length; //0 |
———————————————————————————————————–
译者注:上面这个例子中,首先创建了一个对象a,然后通过a的原型来达到继承Array 这个已经存在的对象的功能
———————————————————————————————————–
原型真正魅力体现在多个实例共用一个通用原型的时候。原型对象(注:也就是某个对象的原型所引用的对象)的属性一旦定义,就可以被多个引用它的实例所继承(注:即这些实例对象的原型所指向的就是这个原型对象),这种操作在性能和维护方面其意义是不言自明的
这也是构造函数的存在的原因么?
是的。构造函数提供了一种方便的跨浏览器机制,这种机制允许在创建实例时为实例提供一个通用的原型
在你能够提供一个例子之前,我需要知道constructor.prototype 属性究竟是什么?
首先,javascript并没有在构造函数(constructor)和其他函数之间做区分,所以说每个函数都有一个原型属性。反过来,如果不是函数,将不会有这样一个属性。请看下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//function will never be a constructor but it has a prototype property anyway Math.max.prototype; //[object Object] //function intended to be a constructor has a prototype too var A = function(name) { this.name = name; } A.prototype; //[object Object] //Math is not a function so no prototype property Math.prototype; //null |
现在我们可以下个定义了:函数A的原型属性(prototype property )是一个对象,当这个函数被用作构造函数来创建实例时,该函数的原型属性将被作为原型赋值给所有对象实例(注:即所有实例的原型引用的是函数的原型属性)
———————————————————————————————————-
译者注:以下的代码更详细的说明这一切
var b = function(){ var one; }
var c = new b();
c.constructor==b //true
//b是一个函数,查看b的原型如下
b.constructor.prototype // function (){}
b.__proto__ //function (){}
//b是一个函数,由于javascript没有在构造函数constructor和函数function之间做区分,所以函数像constructor一样,
//有一个原型属性,这和函数的原型(b.__proto__ 或者b.construtor.prototype)是不一样的
b.prototype //[object Object] 函数b的原型属性
b.prototype==b.constructor.prototype //fasle
b.prototype==b.__proto__ //false
b.__proto__==b.constructor.prototype //true
//c是一个由b创建的对象实例,查看c的原型如下
c.constructor.prototype //[object Object] 这是对象的原型
c.__proto__ //[object Object] 这是对象的原型
c.constructor.prototype==b.prototype; //true c的原型和b的原型属性比较
//为函数b的原型属性添加一个属性max
的原型也会改变
———————————————————————————————————-
理解一个函数的原型属性(function’s prototype property )其实和实际的原型(prototype)没有关系对我们来说至关重要
1 2 3 4 5 6 7 8 9 10 11 |
//(example fails in IE) var A = function(name) { this.name = name; } A.prototype == A.__proto__; //false A.__proto__ == Function.prototype; //true - A's prototype is set to its constructor's prototype property |
给个例子撒
你可能曾经上百次的像这样使用javascript,现在当你再次看到这样的代码的时候,你或许会有不同的理解。