JavaScript .prototype 如何工作? [英] How does JavaScript .prototype work?

查看:32
本文介绍了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

还有这些幻灯片真的很有帮助.

Also these slides really helped a lot.

推荐答案

每个 JavaScript 对象 有一个名为 [[Prototype]] 的内部slot",其值为 nullobject.您可以将插槽视为对象上的一个属性,位于 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]]没有这样的属性,则依次检查其[[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:

  1. new 操作符(在构造函数返回的默认对象上配置原型链),
  2. extends 关键字(使用类语法时配置原型链),
  3. Object.create 将提供的参数设置为结果对象的 [[Prototype]]
  4. Object.getPrototypeOfObject.setPrototypeOf(获取/设置 [[Prototype]] after 对象创建),和
  5. 名为 __proto__ 的标准化访问器(即 getter/setter)属性(类似于 4.)
  1. The new operator (configures the prototype chain on the default object returned from a constructor function),
  2. The extends keyword (configures the prototype chain when using the class syntax),
  3. Object.create will set the supplied argument as the [[Prototype]] of the resulting object,
  4. Object.getPrototypeOf and Object.setPrototypeOf (get/set the [[Prototype]] after object creation), and
  5. The standardized accessor (ie. getter/setter) property named __proto__ (similar to 4.)

Object.getPrototypeOfObject.setPrototypeOf 优于 __proto__,部分原因是 o.__proto__ 的行为code> 当对象的原型为 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]] 内部槽.原型"这个词的双重使用是该语言新手之间无尽困惑的根源.

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.

这篇关于JavaScript .prototype 如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆