`
三月沙
  • 浏览: 617657 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

理解javascript原型

阅读更多

此文在帮助笔者理解javascript的原型所起的作用很大,花费了不少业余时间来做此文的翻译,希望可以帮助一些对此概念还很困惑的童鞋,如有纰漏和错误敬请指正。

 

 

Understanding JavaScript Prototypes.

理解javascript原型 作者 Angus Croll

JavaScript’s prototype object generates confusion wherever it goes. Seasoned JavaScript professionals, even authors frequently exhibit a limited understanding of the concept. I believe a lot of the trouble stems from our earliest encounters with prototypes, which almost always relate to new , constructor and the very misleading prototype property attached to functions. In fact prototype is a remarkably simple concept. To understand it better, we just need to forget what we ‘learned’ about constructor prototypes and start again from first principles.

javascript的原型总会给人产生一些困惑,无论是经验丰富的专家,还是作者自己也时常表现出对这个概念某些有限的理解,我认为这样的困惑在我们一开始接触原型时就已经产生了,它们常常和new,constructor相关,特别是函数(function)的原型(prototype)属性(property)。事实上,原型是一种非常简单的概念。为了更好的理解它,我们应该首先记住这个原则,那就是忘记我们已经学到的关于构造原型(construtor prototypes)的认识

What is a prototype?
什么是原型


A prototype is an object from which other objects inherit properties
原型是一个对象,其他对象可以通过它实现属性继承


Can any object be a prototype?
任何一个对象都可以成为原型么?


Yes.


Which objects have prototypes?
哪些对象有原型


Every object has a prototype by default. Since prototypes are themselves objects, every prototype has a prototype too. (There is only one exception, the default object prototype at the top of every prototype chain. More on prototype chains later)
所有的对象在默认的情况下都有一个原型,因为原型本身也是对象,所以每个原型自身又有一个原型(只有一种例外,默认的对象原型在原型链的顶端。更多关于原型链的将在后面介绍)


OK back up, what is an object again?
好吧,再绕回来,那什么又是对象呢?


An object in JavaScript is any unordered collection of key-value pairs. If its not a primitive (undefined, null, boolean, number or string) its an object.
在javascript中,一个对象就是任何无序键值对的集合,如果它不是一个主数据类型(undefined,null,boolean,number,or string),那它就是一个对象


You said every object has a prototype. But when I write
({}).prototype I get null. Are you crazy?
你说每个对象都有一个原型,可是我当我写成({}).prototype 我得到了一个null,你说的不对吧?

Forget everything you learned about the prototype property – it’s probably the biggest source of confusion about prototypes. The true prototype of an object is held by the internal[[Prototype]] property. ECMA 5 introduces the standard accessor Object.getPrototypeOf(object) which to-date is only implemented in Firefox and Chrome. In addition all browsers except IE support the non-standard accessor __proto__ . Failing that we need to ask the object’s constructor for its prototype property.

忘记你已经学到的关于原型属性的一切,它可能就是你对原型困惑的根源所在。一个对象的真正原型是被对象内部的[[Prototype]]属性(property)所持有。ECMA引入了标准对象原型访问器Object.getPrototype(object),到目前为止只有Firefox和chrome实现了此访问器。除了IE,其他的浏览器支持非标准的访问器__proto__,如果这两者都不起作用的,我们需要从对象的构造函数中找到的它原型属性。下面的代码展示了获取对象原型的方法

01 var a = {};
02
03 //Firefox 3.6 and Chrome 5
04 Object.getPrototypeOf(a); //[object Object]
05
06 //Firefox 3.6, Chrome 5 and Safari 4
07 a.__proto__; //[object Object]
08
09 //all browsers
10 a.constructor.prototype; //[object Object]


Ok fine, but
false is a primitive, so why does false.__proto__ return a value?
ok,一切都进行的很好,但是false明明是一个主数据类型,可是false.__proto__却返回了一个值


When a primitive is asked for it’s prototype it will be coerced to an object.
当你试图获取一个主数据类型的原型时,它被强制转化成了一个对象

1 //(works in IE too, but only by accident)
2 false .__proto__ === Boolean( false ).__proto__; //true


I want to use prototypes for inheritance. What do I do now?
我想在继承中使用原型,那我该怎么做?


It rarely makes sense to set a prototype for one instance and only one instance, since it would be equally efficient just to add properties directly to the instance itself. I suppose if we have created a one off object which we would like to share the functionality of an established object, such as Array, we might do something like this (in __proto__ supporting browsers).
如果仅仅只是因为一个实例而使用原型是没有多大意义的,这和直接添加属性到这个实例是一样的,假如我们已经创建了一个实例对象 ,我们想要继承一个已经存在的对象的功能比如说Array,我们可以像下面这样做( 在支持__proto__ 的浏览器中)

1 //unusual case and does not work in IE
2 var a = {};
3 a.__proto__ = Array.prototype;
4 a.length; //0

-----------------------------------------------------------------------------------------------------------
译者注:上面这个例子中,首先创建了一个对象a,然后通过a的原型来达到继承Array 这个已经存在的对象的功能
-----------------------------------------------------------------------------------------------------------

But the real power of prototype is seen when multiple instances share a common prototype. Properties of the prototype object are defined once but inherited by all instances which reference it. The implications for performance and maintenance are obvious and significant.

原型真正魅力体现在多个实例共用一个通用原型的时候。原型对象(注:也就是某个对象的原型所引用的对象 )的属性一旦定义,就可以被多个引用它的实例所继承(注:即这些实例对象的原型所指向的就是这个原型对象 ),这种操作在性能和维护方面其意义是不言自明的

So is this where constructors come in?
这也是构造函数的存在的原因么?

Yes. Constructors provide a convenient cross-browser mechanism for assigning a common prototype on instance creation.

是的。构造函数提供了一种方便的跨浏览器机制,这种机制允许在创建实例时为实例提供一个通用的原型

Just before you give an example I need to know what this constructor.prototype property is all about?
在你能够提供一个例子之前,我需要知道constructor.prototype 属性究竟是什么?

OK. Firstly JavaScript makes no distinction between constructors and other functions, so every function gets a prototype property. Conversely, anything that is not a function does not have such a property.
首先,javascript并没有在构造函数(constructor)和其他函数之间做区分,所以说每个函数都有一个原型属性。反过来,如果不是函数,将不会有这样一个属性。请看下面的代码

01 //function will never be a constructor but it has a prototype property anyway
02 Math.max.prototype; //[object Object]
03
04 //function intended to be a constructor has a prototype too
05 var A = function (name) {
06 this .name = name;
07 }
08 A.prototype; //[object Object]
09
10 //Math is not a function so no prototype property
11 Math.prototype; //null

So now the definition: A function’s prototype property is the object that will be assigned as the prototype to all instances created when this function is used as a constructor.
现在我们可以下个定义了:函数A的原型属性(prototype property )是一个对象,当这个函数被用作构造函数来创建实例时,该函数的原型属性将被作为原型赋值给所有对象实例(注:即所有实例的原型引用的是函数的原型属性 )
----------------------------------------------------------------------------------------------------------
译者注:以下的代码更详细的说明这一切

//创建一个函数b
var b = function(){ var one; }
//使用b创建一个对象实例c
var c = new b();
//查看b 和c的构造函数
b.constructor; // function Function() { [native code]}
b.constructor==Function.constructor; //true
c.constructor; //实例c的构造函数 即 b function(){ var one; }
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.constructor.prototype; //false c的原型和b的原型比较
c.constructor.prototype==b.prototype; //true c的原型和b的原型属性比较

//为函数b的原型属性添加一个属性max
b.prototype.max = 3

//实例c也有了一个属性max
c.max //3
上面的例子中,对象实例c的原型和函数的b的原型属性是一样的,如果改变b的原型属性,则对象实例c
的原型也会改变

----------------------------------------------------------------------------------------------------------

It’s important to understand that a function’s prototype property has nothing to do with it’s actual prototype.
理解一个函数的原型属性(function’s prototype property )其实和实际的原型(prototype)没有关系对我们来说至关重要

1 //(example fails in IE)
2 var A = function (name) {
3 this .name = name;
4 }
5
6 A.prototype == A.__proto__; //false
7 A.__proto__ == Function.prototype; //true - A's prototype is set to its constructor's prototype property

Example please?
给个例子撒

You’ve probably seen and used this a hundred times but here it is once again, maybe now with added perspective.
你可能曾经上百次的像这样使用javascript,现在当你再次看到这样的代码的时候,你或许会有不同的理解。

01 //Constructor. <em>this</em> is returned as new object and its internal [[prototype]] property will be set to the constructor's default prototype property
02 var Circle = function (radius) {
03 this .radius = radius;
04 //next line is implicit, added for illustration only
05 //this.__proto__ = Circle.prototype;
06 }
07
08 //augment Circle's default prototype property thereby augmenting the prototype of each generated instance
09 Circle.prototype.area = function () {
10 return Math.PI* this .radius* this .radius;
11 }
12
13 //create two instances of a circle and make each leverage the common prototype
14 var a = new Circle(3), b = new Circle(4);
15 a.area().toFixed(2); //28.27
16 b.area().toFixed(2); //50.27

That’s great. And if I change the constructor’s prototype, even existing instances will have access to the latest version right?
棒极了。如果我更改了构造函数的原型,是否意味着已经存在的该构造函数的实例将获得构造函数的最新版本?

Well….not exactly. If I modify the existing prototype’s property then this is true, becausea.__proto__ is a reference to the object defined by A.prototype at the time it was created.
不一定。如果修改的是原型属性,那么这样的改变将会发生。因为在a实际被创建之后,a.__proto__是一个对A.prototype 的一个引用,。

01 var A = function (name) {
02 this .name = name;
03 }
04
05 var a = new A( 'alpha' );
06 a.name; //'alpha'
07
08 A.prototype.x = 23;
09
10 a.x; //23

------------------------------------------------------------------------------------------------------
译者注:这个和上例中的一样,实例对象a的原型(a.__proto__)是对函数A的原型属性(A.prototype)的引用,所以如果修改的是A的原型属性,

改变将影响由A创建的对象实例a 在下面的例子中,但是对函数A的原型进行了修改,但是并没有反应到A所创建的实例a中

var A = function(name)
{
this.name = name;
}
var a = new A('alpha');
a.name;
//'alpha'

A.__proto__.max = 19880716;

a.max //undefined

------------------------------------------------------------------------------------------------------

But if I replace the prototype property with a new object, a.__proto__ still references the original object.
但是如果我现在替换A的原型属性为一个新的对象,实例对象的原型a.__proto__却仍然引用着原来它被创建时A的原型属性

01 var A = function (name) {
02 this .name = name;
03 }
04
05 var a = new A( 'alpha' );
06 a.name; //'alpha'
07
08 A.prototype = {x:23};
09
10 a.x; //null

------------------------------------------------------------------------------------------------------------------
译者注:即如果在实例被创建之后,改变了函数的原型属性所指向的对象,也就是改变了创建实例时实例原型所指向的对象

但是这并不会影响已经创建的实例的原型。
-------------------------------------------------------------------------------------------------------------------
What does a default prototype look like?
一个默认的原型是什么样子的?

An object with one property, the constructor.

1 var A = function () {};
2 A.prototype.constructor == A; //true
3
4 var a = new A();
5 a.constructor == A; //true (a's constructor property inherited from it's prototype)

What does instanceof have to do with prototype?
instance of 和原型有什么关系

The expression a instanceof A will answer true if a’s prototype falls within the same prototype chain as A’s prototype property. This means we can trick instanceof into failing
如果a的原型属于A的原型链,表达式 a instance of A 值为true。这意味着 我们可以对instance of 耍个诡计让它不在起作用

01 var A = function () {}
02
03 var a = new A();
04 a.__proto__ == A.prototype; //true - so instanceof A will return true
05 a instanceof A; //true;
06
07 //mess around with a's prototype
08 a.__proto__ = Function.prototype;
09
10 //a's prototype no longer in same prototype chain as A's prototype property
11 a instanceof A; //false

So what else can I do with prototypes?
还能使用原型做些什么呢?

Remember I said that every constructor has a prototype property which it uses to assign prototypes to all instances it generates? Well that applies to native constructors too such as Function and String. By extending (not replacing!) this property we get to update the prototype of every instance of the given type.

记住我曾经所提到过的每个构造函数都有一个原型属性,它用来为每一个它所创建的实例提供原型。这同样也适用原生态的构造函数Function,String等,扩展这个属性,我们可以达到扩展指定构造函数的所有实例

I’ve used this technique in numerous previous posts to demonstrate function augmentation. For example the
tracer utility I introduced in my last post needed all string instances to implement times , which returns a given string duplicated a specified number of times
我曾经在之前的很多文章中使用过这个技巧来演示函数的拓展。在tracer utility 这篇文章中所有的string实例都实现了times这个方法,对字符串本身进行指定数目的复制

1 String.prototype.times = function (count) {
2 return count < 1 ? '' : new Array(count + 1).join( this );
3 }
4
5 "hello!" .times(3); //"hello!hello!hello!";
6

"please..." .times(6); //"please...please...please...please...please...please...

 

Tell me more about how inheritance works with prototypes. What’s a prototype chain?
告诉我继承是怎样通过原型来工作的。什么是原型链?

Since every object and every prototype (bar one ) has a prototype, we can think of a succession of objects linked together to form a prototype chain. The end of the chain is always the default object’s prototype.
因为每个对象和原型都有一个原型(注:原型也是一个对象 ),对象的原型指向对象的父,而父的原型又指向父的父,我们把这种通过原型层层连接起来的关系撑为原型链。这条链的末端一般总是默认的对象原型。

1 a.__proto__ = b;
2 b.__proto__ = c;
3 c.__proto__ = {}; //default object
4 {}.__proto__.__proto__; //null


The prototypical inheritance mechanism is internal and non-explicit. When object a is asked to evaluate property foo , JavaScript walks the prototype chain (starting with object a itself), checking each link in the chain for the presence of property foo . If and when foo is found it is returned, otherwise undefined is returned.

原型的继承机制是发生在内部且是隐式的.当想要获得一个对象a的属性foo的值,javascript会在原型链中查找foo的存在,如果找到则返回foo的值,否则undefined被返回。

What about assigning values?
赋值呢?

Prototypical inheritance is not a player when property values are set. a.foo = ‘bar’ will always be assigned directly to the foo property of a. To assign a property to a prototype you need to address the prototype directly.
原型的继承 is not a player 当属性值被设置成a.foo='bar'是直接给a的属性foo设置了一个值bar。为了把一个属性添加到原型中,你需要直接指定该原型。

And that about covers it. I feel I have the upper hand on the prototype concept but my opinion is by no means the final word. Please feel free to tell me about errors or disagreements.

以上就是这篇文章所要讲述的。我个人感觉自己对原型概念的理解还是比较深刻的,但我的观点并不能代表一切。如有纰漏和异议,敬请告知。

Where can I get more information on protoypes?
我在哪里还能获得关于原型更多的信息?

I recommend this excellent article by Dmitry A. Soshnikov
我推荐这篇由Dmitry A. Soshnikov所著的文章,非常优秀。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics