此文在帮助笔者理解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__,如果这两者都不起作用的,我们需要从对象的构造函数中找到的它原型属性。下面的代码展示了获取对象原型的方法
04
|
Object.getPrototypeOf(a);
|
10
|
a.constructor.prototype;
|
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.
当你试图获取一个主数据类型的原型时,它被强制转化成了一个对象
2
|
false
.__proto__ === Boolean(
false
).__proto__;
|
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__ 的浏览器中)
3
|
a.__proto__ =
Array.prototype;
|
-----------------------------------------------------------------------------------------------------------
译者注:上面这个例子中,首先创建了一个对象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)和其他函数之间做区分,所以说每个函数都有一个原型属性。反过来,如果不是函数,将不会有这样一个属性。请看下面的代码
05
|
var
A =
function
(name) {
|
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)没有关系对我们来说至关重要
2
|
var
A =
function
(name) {
|
6
|
A.prototype == A.__proto__;
|
7
|
A.__proto__ == Function.prototype;
|
Example
please?
给个例子撒
You’ve
probably seen and used this a hundred times but here it is once again, maybe now
with added
perspective.
你可能曾经上百次的像这样使用javascript,现在当你再次看到这样的代码的时候,你或许会有不同的理解。
02
|
var
Circle =
function
(radius) {
|
03
|
this
.radius = radius;
|
09
|
Circle.prototype.area =
function
() {
|
10
|
return
Math.PI*
this
.radius*
this
.radius;
|
14
|
var
a =
new
Circle(3), b =
new
Circle(4);
|
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) {
|
05
|
var
a =
new
A(
'alpha'
);
|
------------------------------------------------------------------------------------------------------
译者注:这个和上例中的一样,实例对象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) {
|
05
|
var
a =
new
A(
'alpha'
);
|
------------------------------------------------------------------------------------------------------------------
译者注:即如果在实例被创建之后,改变了函数的原型属性所指向的对象,也就是改变了创建实例时实例原型所指向的对象
但是这并不会影响已经创建的实例的原型。
-------------------------------------------------------------------------------------------------------------------
What
does a default prototype look
like?
一个默认的原型是什么样子的?
An object with one property, the constructor.
1
|
var
A =
function
() {};
|
2
|
A.prototype.constructor == A;
|
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
() {}
|
04
|
a.__proto__ == A.prototype;
|
08
|
a.__proto__ =
Function.prototype;
|
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
);
|
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.
因为每个对象和原型都有一个原型(注:原型也是一个对象
),对象的原型指向对象的父,而父的原型又指向父的父,我们把这种通过原型层层连接起来的关系撑为原型链。这条链的末端一般总是默认的对象原型。
4
|
{}.__proto__.__proto__;
|
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所著的文章,非常优秀。
分享到:
相关推荐
深入理解javascript原型和闭包(01)——一切都是对象 深入理解javascript原型和闭包(02)——函数和对象的关系
理解Javascript原型继承原理
在咱们的第一节(深入理解javascript原型和闭包(1)——一切都是对象)中说道,函数也是一种对象。他也是属性的集合,你也可以对函数进行自定义属性。不用等咱
javascript原型和闭包
对于javascript原型链,以前都觉得是个很深的东西,一直没有理解很明白,今天看了一些介绍后,发现这张图,表示再没有什么语言能比这张图说得清楚了。 看了这张图后突然对javascript有了质的理解。 javascript的...
面向对象概念(对象封装,各种继承,闭包原理,this作用域等)介绍清晰易懂
在上一篇文章中,介绍了原型的概念,了解到在javascript中构造函数、原型对象、实例三个好基友之间的关系:每一个构造函数都有一个“守护神”——原型对象,原型对象心里面也存着一个构造函数的“位置”,两情相悦,...
深入理解JavaScript系列(5):强大的原型和原型链 深入理解JavaScript系列(6):S.O.L.I.D五大原则之单一职责SRP 深入理解JavaScript系列(7):S.O.L.I.D五大原则之开闭原则OCP 深入理解JavaScript系列(8):...
简单说一说对JavaScript原型链的理解,希望对大家学习JavaScript原型链有所帮助,具体内容如下
主要介绍了javascript原型和原型链,学习javascript面向对象,感兴趣的小伙伴们可以参考一下
深入理解JavaScript系列(5):强大的原型和原型链 深入理解JavaScript系列(6):S O L I D五大原则之单一职责SRP 深入理解JavaScript系列(7):S O L I D五大原则之开闭原则OCP 深入理解JavaScript系列(8):...
理解JavaScript的运行上下文及原型
我所理解的从原型到原型链
当今 JavaScript 大行其道,各种应用...要掌握好 JavaScript,首先一点是必须摒弃一些其他高级语言如 Java、C# 等类式面向对象思维的干扰,全面地从函数式语言的角度理解 JavaScript 原型式面向对象的特点。把握好这一
要掌握好JavaScript,首先一点是必须摒弃一些其他高级语言如Java、C#等类式面向对象思维的干扰,全面地从函数式语言的角度理解JavaScript原型式面向对象的特点。当今JavaScript大行其道,各种应用对其依赖日深。web...