Javascript中的prototype
Javacript中的Prototype应该算是一个非常容易混淆的概念,就算是经验丰富的开发者也会对这个概念了解不足。我相信这些麻烦的根源来自于我们早期对prototype的使用,这些使用基本上就是new,constructor以及非常容易误解的那种隶属于某个方法的prototype属性。事实上,prototype是一个非常简单的概念。为了更好的理解它,我们需要忘记我们已经学过的在constructor时使用的prototype,从prototype的第一条原则开始。
什么是prototype?
Prototype就是一个特殊的对象,其他对象从它这边继承属性。
是不是任何对象都可以作为一个prototype?
是的
哪些对象拥有prototype?
Javascript默认任何对象都拥有一个prototype。由于prototype本身是一个对象,所以所有的prototype都拥有自己的prototype。(只有一个例外:在prototype链顶端的默认对象的prototype。后面会介绍prototype链)
什么是对象?
Javascript中的对象指的是任何没有排序的key-value键值对的集合。只要它不是一个javascript原语(undefined,null,boolean,number或者string),它就是一个对象。
你说所有的对象都有prototype,但是当我编写({}).prototype的时候,我获得的是undefined,你是不是疯了?
忘记你所学过的关于prototype属性的所有东西,这些东西正是使你对prototype产生疑惑的源泉。一个对象的真正的prototype存放在对象内部的[[Prototype]]属性中。ECMA 5中介绍了一个标准的访问方法Object.getPrototypeOf(object)。目前为止,firefox,safari,chrome和IE9中都实现了这个方法。除此之外,除了IE以外的所有浏览器都支持非标准的访问方法__proto__。如果仍然不行,我们可以通过访问对象的constructor来获取对象的prototype属性。
1 var a = {}; 2 //在Opera和 IE<=8时失败 3 Object.getPrototypeOf(a); // [object Object] 4 5 //在IE中失败 6 a.__proto__; // [object Object] 7 8 //所有浏览器 9 a.constructor.prototype; // [object Object]
false是一个原语,那为什么false.__proto__可以返回值?
当请求一个原语的prototype时,这个原语会被强制转换成对象。
1 False.__proto__ === Boolean(false).__proto__;//true
我想用prototype来实现继承,应该怎么做?
对一个实例设置prototype基本上是没有意义的,这样做的效率和在实例中直接添加属相基本一样。如果我们想要一个对象去共享一个已有对象的功能,我们可以这么做:
1 //在IE<=8时失败 2 var a = {}; 3 a.__proto__ = Array.prototype; 4 a.length; //0
prototype真正强大的地方在于多个实例共享一个通用的prototype。Prototype的属性只需要定义一次就可以被所有的实例所继承。
这个时候是不是该介绍constructor的概念了?
是的。Constructor提供了一种在实例创建的时候设置通用prototype的方便的跨浏览器机制。
首先,在javascript中不区分constructor和其他的function,所以每一个function都拥有一个prototype属性。反过来说,任何不是function的东西都没有这样一个属性。
1 //永远不会成为constructor的function,拥有prototype属性 2 Math.max.prototype; //[object Object] 3 4 //可以成为constructor的function,也拥有prototype属性 5 var A = function(name){ 6 This.name = name; 7 }; 8 A. prototype;//[object Object] 9 10 //Math不是function,所以没有prototype属性 11 Math.prototype; //null
现在prototype的定义就变成了:一个function的prototype属性,是在所有以这个function为constructor而创建出来的实例所共享的prototype对象。
比较重要的一点就是,function的prototype属性和它本身的prototype是没有关系的,比如下面的例子。
1 //(例子在IE中无法运行) 2 var A = function(name){ 3 This.name = name; 4 }; 5 A.prototype ==A.__proto__; //false 6 A.__proto__ == Function.prototype;
prototype使用的常见例子
常规prototype使用,添加方法:
1 var circle = function(radius){ 2 This.radius = radius; 3 } 4 5 Circle.prototype.area = function(){ 6 Return Math.PI*this.radius*this.radius; 7 } 8 9 var a = new Circle(3), b = new Circle(4); 10 a.area().toFixed(2); //28.27 11 b.area().toFixed(2); //50.27
实例共享prototype的属性:
var A = function(name) { this.name = name; } var a = new A('alpha'); a.name; //'alpha' A.prototype.x = 23; a.x; //23
如果将A的prototype属性设置为一个新的对象,a.__prototype__仍然引用原来的对象:
1 var A = function(name) { 2 this.name = name; 3 } 4 var a = new A('alpha'); 5 a.name; //'alpha' 6 A.prototype = {x:23}; 7 a.x; //null
默认的prototype只有一个constructor属性:
1 var A = function() {}; 2 A.prototype.constructor == A; //true 3 var a = new A(); 4 a.constructor == A; //true (a的constructor属性继承于它的prototype)
prototype链(链顶的对象没有prototype)
1 a.__proto__ = b; 2 b.__proto__ = c; 3 c.__proto__ = {}; //默认的 object 4 {}.__proto__.__proto__; //null