__proto__ VS。 JavaScript 中的原型

Posted

技术标签:

【中文标题】__proto__ VS。 JavaScript 中的原型【英文标题】:__proto__ VS. prototype in JavaScript 【发布时间】:2012-04-15 03:03:36 【问题描述】:

这个图再次表明每个对象都有一个原型。构造函数 function Foo 也有自己的__proto__,即 Function.prototype, 反过来又通过其__proto__ 属性再次引用 Object.prototype。因此,重复一遍, Foo.prototype 只是一个显式的 Foo 的属性,指的是 b 和 c 对象的原型。

var b = new Foo(20);
var c = new Foo(30);

__proto__prototype 有什么区别?

图取自dmitrysoshnikov.com。

注意:上面 2010 年的文章现在有a 2nd edition (2017)。

【问题讨论】:

另见How does __proto__ differ from constructor.prototype? 我认为自上而下或自下而上是一个偏好问题。我实际上更喜欢这种方式,所以我可以追踪图表,直到找到某些东西的来源。 我喜欢 javascript 如何使用原型继承将 y.constructor 解析为 y.__proto__.constructor。我也喜欢 Object.prototype 如何位于原型继承链的顶部,其中 Object.prototype.__proto__ 设置为 null。我还喜欢该图如何对程序员如何将对象视为 1. 实例、2. 构造函数、3. 原型进行三列概念可视化,构造函数在通过 new 关键字实例化时与这些实例相关联。 在您观看 youtube.com/watch?v=_JJgSbuj5VI 之类的内容后,图表立即有意义,顺便说一句 现在,当我通读答案时,觉得有义务真的推荐上面的视频,因为它确实有一个清晰的(和非 WTFy)解释发生了什么:) 【参考方案1】:

我认为您需要了解 __proto__[[prototype]]prototype 之间的区别。

接受的答案很有帮助,但它可能暗示(不完全)__proto__仅与在构造函数上使用 new 创建的对象相关,这是不正确的。 p>

更准确地说:__proto__ 存在于每个对象上

但是__proto__ 到底是什么?

嗯,它是一个对象引用另一个对象,它也是所有对象的属性,称为[[prototype]]。 值得一提的是,[[prototype]] 是 JavaScript 在内部处理的,开发人员无法访问

为什么我们需要一个指向属性[[prototype]](在所有对象中)的引用对象?

因为 JavaScript 不希望直接获取/设置[[prototype]],所以它允许它通过__proto__ 的中间层。因此,您可以将__proto__ 视为[[prototype]] 属性的getter/setter。

那么prototype 是什么?

它是特定于函数的东西(最初在Function 中定义,即Function.prototype,然后由创建的原型继承函数,然后这些函数又将其传递给它们的子函数,形成一个原型继承链)。

当父函数使用new 运行时,JavaScript 使用父函数的prototype 设置其子函数的[[prototype]](记得我们说过所有 对象都有[[prototype]]?好吧,函数也是对象,所以它们也有[[prototype]])。所以当一个函数(子)的[[prototype]]被设置为另一个函数(父)的prototype时,你最终会得到这个:

let child = new Parent();
child.__proto__ === Parent.prototype // --> true.

(记住child.[[prototype]] 不可访问,因此我们使用__proto__ 进行了检查。)


注意 1: 只要属性不在子项中,就会“隐式”搜索其 __proto__。因此,例如,如果child.myprop 返回一个值,您不能说“myprop”是孩子的属性,还是其父母原型之一的属性。这也意味着您永远不需要自己执行以下操作:child.__proto__.__proto__.myprop,只需 child.myprop 会自动为您执行此操作。

注意 2: 即使父母的原型中有项目,孩子自己的prototype 最初也会是一个空对象。但是,如果您想进一步扩展继承链(将 child[ren] 添加到 child),您可以手动添加或从中删除项目。或者它可以被隐式操作,例如,使用class syntax。)

注意 3: 如果您需要自己设置/获取 [[prototype]],使用 __proto__ 有点 outdated 和现代 JavaScript 建议使用 @改为 987654354@ 和 Object.getPrototypeOf

【讨论】:

【参考方案2】:

这个问题有很多好的答案,但是对于有很好细节的概括和紧凑的答案形式,我添加以下内容:

我们首先要考虑的是,在 JS 发明的时候,计算机的内存非常低,所以如果我们需要一个进程来创建新的对象类型,就必须考虑内存性能。

因此他们将根据特定object type 创建的对象所需的方法定位在内存的单独部分,而不是每次我们创建一个新对象时,存储对象之外的方法。 因此,如果我们用 JS 的新特性重新发明 new 运算符和 constructor 函数概念,我们有以下步骤:

    和空对象。 (这将是对象类型实例化的最终结果)
let empty=
    我们已经知道,出于内存性能原因,object type 实例所需的所有方法都位于构造函数的prototype 属性中。 (函数也是对象,因此它们可以具有属性) 所以我们将empty 对象的__protp__ 引用到这些方法所在的位置。 (我们将概念上使用的函数视为构造函数,命名为构造函数。
empty.__proto__ = constructor.prototype
    我们必须初始化对象类型值。 在 JS 函数中与对象断开连接。使用点表示法或 bind call apply 之类的方法,我们必须告诉函数对象“函数的 this 上下文是什么”。
let newFunc = constructor.bind(empty)
    现在我们有了一个新函数,它有一个empty 对象作为this 上下文。 执行此功能后。 empty 对象将被填充,如果定义 constructor 函数不返回,则类型对象实例化的结果将是这个 empty 对象(好像这将是该过程的结果)

如您所见,__proto__ 是引用其他对象的对象的属性(在 JS 函数中也是对象)prototype 对象属性由在特定 object type 的实例中使用的属性组成。

您可以从短语functions are objects 中猜到,函数也具有__proto__ 属性,因此它们可以引用其他对象的prototype 属性。这就是prototype inheritance 的实现方式。

【讨论】:

以上是关于__proto__ VS。 JavaScript 中的原型的主要内容,如果未能解决你的问题,请参考以下文章

prototype与__proto__区别

Javascript深入__proto__和prototype的区别和联系

JavaScript 源型链

JavaScript:解释继承、__proto__ 和原型的图表

在 JavaScript 中 prototype 和 __proto__ 有什么区别

javascript原型链