__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]]
(在所有对象中)的引用对象?
[[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 中的原型的主要内容,如果未能解决你的问题,请参考以下文章
Javascript深入__proto__和prototype的区别和联系
JavaScript:解释继承、__proto__ 和原型的图表