此文在帮助笔者理解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中,原型继承是一种非常核心且独特的机制,它使得对象能够继承其他对象的属性和方法。本文旨在深入探讨这一机制,并通过具体的示例代码帮助读者更好地...
JavaScript原型和闭包是这门语言中两个比较难以理解且与其他面向对象语言区别较大的概念。理解这两个概念,不仅能让我们更深层次地理解JavaScript,而且有助于我们了解编程语言的设计思路,拓宽我们的视野。 首先,...
JavaScript是一种动态类型的...通过这些深入的讲解,你应该能更好地理解JavaScript中对象、原型、函数和闭包的工作原理,为你的JavaScript开发打下坚实的基础。在实际编程中,灵活运用这些知识可以解决许多复杂的问题。
在深入理解JavaScript原型链和继承的概念之前,首先要了解JavaScript是一种基于对象的语言,而非传统的面向对象语言。它没有类的概念,函数可以被视为构造器,而对象则是通过构造函数、原型对象和实例之间的特殊关系...
理解JavaScript原型继承是深入学习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中,...
JavaScript原型链是JavaScript语言中的一个核心特性,它关乎对象之间的继承关系。在JavaScript中,一切皆为对象,而原型链则是实现对象间属性和方法共享的一种机制。理解原型链对于深入学习JavaScript至关重要。 ...
JavaScript原型链是这门语言的核心概念之一,它关系到对象属性的继承机制。在JavaScript中,所有对象都有一个原型,原型也是一个对象,从而形成了一条原型链。每个对象通过内部属性[[Prototype]](ES6后建议使用...
JavaScript是Web开发中不可或缺的一部分,尤其在前端领域,它的强大在于动态性和灵活性。在这个"基于js原型链的小游戏"中,我们...这不仅是一个理解JavaScript原型链的好例子,也是实践前端开发技能的绝佳实践项目。
以下是五句话帮助你理解JavaScript原型链: 1. `Function`和`Object`都是JavaScript的构造函数。`Function`用于创建其他函数,而`Object`则用于创建所有类型的实例对象。 2. 所有的构造函数都是通过`Function`构造...
深入理解JavaScript系列(5):强大的原型和原型链 深入理解JavaScript系列(6):S O L I D五大原则之单一职责SRP 深入理解JavaScript系列(7):S O L I D五大原则之开闭原则OCP 深入理解JavaScript系列(8):...