JavaScript .prototype 是如何工作的?
我不太喜欢动态编程语言,但我编写了相当多的 JavaScript 代码.我从来没有真正理解过这种基于原型的编程,有人知道它是如何工作的吗?
I'm not that into dynamic programming languages but I've written my fair share of JavaScript code. I never really got my head around this prototype-based programming, does any one know how this works?
var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();
我记得不久前我与人们进行了很多讨论(我不确定我在做什么),但据我所知,没有类的概念.它只是一个对象,这些对象的实例是原始对象的克隆,对吧?
I remember a lot discussion I had with people a while back (I'm not exactly sure what I'm doing) but as I understand it, there's no concept of a class. It's just an object, and instances of those objects are clones of the original, right?
但是这个.prototype"的确切目的是什么?JavaScript 中的属性?它与实例化对象有何关系?
But what is the exact purpose of this ".prototype" property in JavaScript? How does it relate to instantiating objects?
var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!
function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK
这些幻灯片真的很有帮助.
推荐答案
每个 JavaScript 对象 有一个名为 [[Prototype]]
的内部slot",其值为 null
或 object
.您可以将插槽视为对象的属性,位于 JavaScript 引擎内部,对您编写的代码隐藏.[[Prototype]]
周围的方括号是经过深思熟虑的,是表示内部槽的 ECMAScript 规范约定.
Every JavaScript object has an internal "slot" called [[Prototype]]
whose value is either null
or an object
. You can think of a slot as a property on an object, internal to the JavaScript engine, hidden from the code you write. The square brackets around [[Prototype]]
are deliberate, and are an ECMAScript specification convention to denote internal slots.
对象的[[Prototype]]
所指向的值,俗称该对象的原型".
The value pointed at by the [[Prototype]]
of an object, is colloquially known as "the prototype of that object."
如果您通过点 (obj.propName
) 或方括号 (obj['propName']
) 表示法访问属性,并且对象不直接具有此类一个属性(即一个自己的属性,可通过obj.hasOwnProperty('propName')
检查),运行时在被引用的对象上查找具有该名称的属性[[Prototype]]
代替.如果[[Prototype]]
also没有这样的属性,则依次检查其[[Prototype]]
,以此类推.这样,原始对象的 原型链 会一直走下去,直到找到匹配项,或者到达其末端.原型链的顶部是 null
值.
If you access a property via the dot (obj.propName
) or bracket (obj['propName']
) notation, and the object does not directly have such a property (ie. an own property, checkable via obj.hasOwnProperty('propName')
), the runtime looks for a property with that name on the object referenced by the [[Prototype]]
instead. If the [[Prototype]]
also does not have such a property, its [[Prototype]]
is checked in turn, and so on. In this way, the original object's prototype chain is walked until a match is found, or its end is reached. At the top of the prototype chain is the null
value.
现代 JavaScript 实现允许通过以下方式对 [[Prototype]]
进行读取和/或写入访问:
Modern JavaScript implementations allow read and/or write access to the [[Prototype]]
in the following ways:
new
操作符(在构造函数返回的默认对象上配置原型链),extends
关键字(使用类语法时配置原型链),Object.create
将提供的参数设置为结果对象的[[Prototype]]
,Object.getPrototypeOf
和Object.setPrototypeOf
(获取/设置[[Prototype]]
after 对象创作),以及- 名为
__proto__
的标准化访问器(即getter/setter)属性(类似于4.)
- The
new
operator (configures the prototype chain on the default object returned from a constructor function), - The
extends
keyword (configures the prototype chain when using the class syntax), Object.create
will set the supplied argument as the[[Prototype]]
of the resulting object,Object.getPrototypeOf
andObject.setPrototypeOf
(get/set the[[Prototype]]
after object creation), and- The standardized accessor (ie. getter/setter) property named
__proto__
(similar to 4.)
Object.getPrototypeOf
和 Object.setPrototypeOf
优于 __proto__
,部分原因是 o.__proto__代码> 异常 当对象的原型为
null
.
Object.getPrototypeOf
and Object.setPrototypeOf
are preferred over __proto__
, in part because the behavior of o.__proto__
is unusual when an object has a prototype of null
.
对象的 [[Prototype]]
最初是在对象创建期间设置的.
An object's [[Prototype]]
is initially set during object creation.
如果你通过new Func()
创建一个新对象,对象的[[Prototype]]
会默认设置为Func.prototype
.
If you create a new object via new Func()
, the object's [[Prototype]]
will, by default, be set to the object referenced by Func.prototype
.
请注意,因此,所有可以与 new
运算符一起使用的类和函数,除了它们的属性之外,还有一个名为 .prototype
的属性.拥有自己的 [[Prototype]]
内部插槽.原型"一词的双重使用是该语言新手无休止的困惑的根源.
Note that, therefore, all classes, and all functions that can be used with the new
operator, have a property named .prototype
in addition to their own [[Prototype]]
internal slot. This dual use of the word "prototype" is the source of endless confusion amongst newcomers to the language.
在构造函数中使用 new
可以让我们模拟 JavaScript 中的经典继承;尽管 JavaScript 的继承系统——正如我们所见——是原型的,而不是基于类的.
Using new
with constructor functions allows us to simulate classical inheritance in JavaScript; although JavaScript's inheritance system is - as we have seen - prototypical, and not class-based.
在将类语法引入 JavaScript 之前,构造函数是模拟类的唯一方法.我们可以将构造函数的 .prototype
属性所引用的对象的属性视为共享成员;IE.每个实例都相同的成员.在基于类的系统中,每个实例的方法都以相同的方式实现,因此方法在概念上被添加到 .prototype
属性中;但是,对象的字段是特定于实例的,因此在构造过程中会添加到对象本身.
Prior to the introduction of class syntax to JavaScript, constructor functions were the only way to simulate classes. We can think of properties of the object referenced by the constructor function's .prototype
property as shared members; ie. members which are the same for each instance. In class-based systems, methods are implemented the same way for each instance, so methods are conceptually added to the .prototype
property; an object's fields, however, are instance-specific and are therefore added to the object itself during construction.
如果没有类语法,开发人员必须手动配置原型链以实现与经典继承类似的功能.这导致了实现这一目标的不同方法的优势.
Without the class syntax, developers had to manually configure the prototype chain to achieve similar functionality to classical inheritance. This led to a preponderance of different ways to achieve this.
这是一种方法:
function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }
function inherit(child, parent) {
child.prototype = Object.create(parent.prototype)
child.prototype.constructor = child
return child;
}
Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'
...还有另一种方式:
...and here's another way:
function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }
function inherit(child, parent) {
function tmp() {}
tmp.prototype = parent.prototype
const proto = new tmp()
proto.constructor = child
child.prototype = proto
return child
}
Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'
ES2015 中引入的类语法通过提供 extends
作为一种真正的方式"来配置原型链以模拟 JavaScript 中的经典继承,从而简化了事情.
The class syntax introduced in ES2015 simplifies things, by providing extends
as the "one true way" to configure the prototype chain in order to simulate classical inheritance in JavaScript.
所以,和上面的代码类似,如果你像这样使用类语法来创建一个新对象:
So, similar to the code above, if you use the class syntax to create a new object like so:
class Parent { inheritedMethod() { return 'this is inherited' } }
class Child extends Parent {}
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'
...结果对象的 [[Prototype]]
将设置为 Parent
的实例,其 [[Prototype]]
,反过来,是Parent.prototype
.
...the resulting object's [[Prototype]]
will be set to an instance of Parent
, whose [[Prototype]]
, in turn, is Parent.prototype
.
最后,如果你通过Object.create(foo)
创建一个新对象,结果对象的[[Prototype]]
将被设置为foo代码>.
Finally, if you create a new object via Object.create(foo)
, the resulting object's [[Prototype]]
will be set to foo
.
相关文章