简体   繁体   English

__proto__属性的MDN说明中的可能错误?

[英]Possible error in MDN explanation of __proto__ property?

So, in working to further solidify my understanding of Object-Oriented JavaScript, I have been voraciously reading, and then testing things that I don't understand. 因此,在进一步巩固我对面向对象JavaScript的理解的过程中,我经常阅读并测试我不理解的东西。 I was reading the Mozilla Developer Network (MDN) article titled "Object.prototype. proto " at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto 我读了Mozilla开发人员网络(MDN)的文章,题为“Object.prototype中的原 ”。在: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

and came across the following explanation: 并遇到以下解释:

For objects created using new fun, where fun is a function defined in a script, this value [ __proto__ ] is the value of fun.prototype at the time new fun is evaluated. 对于使用new fun创建的对象,其中fun是脚本​​中定义的函数,此值[ __proto__ ] 是评估new fun时 fun.prototype的值 (That is, if a new value is assigned to fun.prototype, previously-created fun instances will continue to have the previous value as their [[Prototype]], and subsequent new fun calls will use the newly-assigned value as their [[Prototype]].) (也就是说,如果将新值分配给fun.prototype,则先前创建的fun实例将继续以其[[Prototype]]作为其先前的值,随后的新fun调用将使用新分配的值作为其[ [原型]]。)

Note: MDN is using [[Prototype]] to refer to the "internal" Prototype of an object, which is referenced as __proto__ in JavaScript code. 注意:MDN使用[[Prototype]]引用对象的“内部”原型,在JavaScript代码中将其称为__proto__

So I opened up my Chrome console, and wrote some simple JavaScript as such: 因此,我打开了我的Chrome控制台,并编写了一些简单的JavaScript,例如:

function Person(name, age)
{
    this.name = name?name:"Parent Function";
    this.age = age?age:"Old as Time";
}

var parent = new Person("Ebeneezer", 42);    

//new Person evaluated before strength is added to Person.prototype
var child = new Person("Aluiscious", 12);

console.log(child.strength);

Person.prototype.strength = "your value here";
console.log(child.strength);

var second_child = new Person('Sprout', 5);
console.log(second_child.strength);

After this, if I type in child. 在此之后,如果我输入孩子。 __proto__ and second_child. __proto__和second_child。 __proto__ into the console, I get the same value, which is Person {strength: "your value here"} __proto__进入控制台,我得到相同的值,即Person {强度:“此处的值”}}

According to MDN, shouldn't child. 根据MDN,不应该是孩子。 __proto__ "continue to have the previous value" of Person.prototype as their internal Prototype? __proto__ Person.prototype的 “继续具有先前的值”作为其内部原型?

The MDN docs are talking about completely replacing the prototype, not adding new properties or methods to it (which will be added to all objects sharing that prototype since the internal [[Prototype]] property is shared). MDN文档讨论的是完全替换原型,而不是向其添加新的属性或方法(由于内部[[Prototype]]属性是共享的,因此将被添加到共享该原型的所有对象中)。 Consider this example: 考虑以下示例:

function Person(name, age)
{
    this.name = name?name:"Parent Function";
    this.age = age?age:"Old as Time";
}

Person.prototype.strength = "some strength";
var parent = new Person("Ebeneezer", 42);

console.log(parent.strength); //"some strength"

//Replace `Person.prototype` with a completely new prototype object
Person.prototype = {
    //setting the 'constructor' property correctly when replacing a prototype object
    //is a best practice, but it will work without this too
    constructor: Person
};

console.log(parent.strength); //still "some strength"

var child = new Person("Aluiscious", 12);

//This will be undefined, because the object was created after the prototype was changed
console.log(child.strength);

In the above example, the [[Prototype]] properties of the instances refer to two different prototype objects, since I replaced the prototype using .prototype = before creating the second object. 在上面的示例中,实例的[[Prototype]]属性引用了两个不同的原型对象,因为在创建第二个对象之前,我使用.prototype =替换了原型。

It's important to understand that the internal prototype property is shared among all instances created with the same prototype. 重要的是要了解内部原型属性是在使用同一原型创建的所有实例之间共享的。 That's why in your example, the strength property gets added to both objects - the internal [[Prototype]] property of both objects is still a reference to the same shared prototype object. 这就是为什么在您的示例中,会将strength属性添加到两个对象中的原因-两个对象的内部[[Prototype]]属性仍然是对同一共享原型对象的引用。 It's also important to recognize that object and array properties of the prototype are shared as well. 同样重要的是要认识到原型的对象和数组属性也要共享。 So for example, suppose you added a children array to your Person prototype: 例如,假设您向Person原型添加了一个children数组:

//Don't do this!
Person.prototype.children = [];
var parent1 = new Person("Ebeneezer", 42);
parent1.children.push(new Person("Child A"));

var parent2 = new Person("Noah", 35);
parent2.children.push(new Person("Child B"));

You might expect that this would cause Ebeneezer to have an array containing only Child A, and Noah to have an array containing only Child B, but in fact both parents will now have an array containing BOTH Child A and Child B, because children actually refers to the same array belonging to the internal [[Prototype]] object. 您可能希望这会导致Ebeneezer的数组仅包含子项A,而Noah的数组仅包含子项B,但实际上,父母双方现在都将同时包含子项A和子项B,因为children实际上是指属于内部[[Prototype]]对象的同一数组。

That's why I consider it a best practice to always declare data properties in the constructor, and only methods on the prototype. 这就是为什么我认为最好的做法是始终在构造函数中声明数据属性,而仅在原型上声明方法。 For example: 例如:

function Person(name, age)
{
    this.name = name?name:"Parent Function";
    this.age = age?age:"Old as Time";
    this.children = [];
}

//it's fine to declare methods on the prototype - in fact it's good, because it saves
//memory, whereas if you defined them in the constructor there would be a separate copy
//of the method for each instance
Person.prototype.addChild = function(child) {
    if (!child instanceof Person) {
        throw new Error("child must be a Person object");
    }
    //Note: in a real system you would probably also want to check that the passed child
    //object isn't already in the array
    this.children.push(child);
}

Note: The modification vs. replacement concept applies to prototype properties in addition to the prototypes themselves. 注意:修改与替换概念不仅适用于原型本身,还适用于原型属性。 If you set a property directly on an object, it will be used instead of the property on the prototype. 如果直接在对象上设置属性,则将使用它代替原型上的属性。 So if I were to change my above example to this: 因此,如果我将上面的示例更改为:

Person.prototype.children = [];
var parent1 = new Person("Ebeneezer", 42);
parent1.children.push(new Person("Child A"));

var parent2 = new Person("Noah", 35);
parent2.children = [];
//now `parent2` has its own `children` array, and Javascript will use that
//instead of the `children` property on the prototype.
parent2.children.push(new Person("Child B"));

...then the two parents would have separate children arrays, but of course I'm mentioning this just for illustrative purposes, and you should declare array or object properties in the constructor as I showed above. ...然后两个父对象将有单独的children数组,但是我仅出于说明目的提及它,您应该在构造函数中声明数组或对象属性,如我上面所示。 In this example, the children array for parent1 is still referring to the children property on the prototype, so if you were to create a new Person object then it would still share children with Ebeneezer: 在此示例中, parent1children数组仍然引用原型上的children属性,因此,如果要创建一个新的Person对象,则它将仍与Ebeneezer共享children对象:

var parent3 = new Person("Eve");
console.log(parent3.children); //array containing Child A

This article may also be helpful for understanding this: http://www.bennadel.com/blog/1566-using-super-constructors-is-critical-in-prototypal-inheritance-in-javascript.htm 本文对于理解这一点也可能会有所帮助: http : //www.bennadel.com/blog/1566-using-super-constructors-is-critical-in-prototypal-inheritance-in-javascript.htm

Just adding an answer because this behavior does not only apply to prototype and it should be made clear what the difference between de referencing and mutating is. 仅仅添加一个答案是因为这种行为不仅适用于原型,还应明确指出取消引用和变异之间的区别是什么。

I think the correct terms are de reference versus mutate. 我认为正确的术语是参考和变异。 You're mutating: 您正在变异:

var org = {};
var copy1 = org;//copy1 is a reference to org
var copy2 = org;//copy2 is a reference to org
org.mutate=1;
console.log(copy1===org);//true
console.log(copy1===copy2);//true
console.log(copy2===org);//true
//basically copy1, copy2 and org all point to the same object
//so they all have a member called mutate with a value of 1
//because there is only one object with 3 variables referencing it.

Here is what MDN is talking about (de reference): 这是MDN在谈论的内容(参考):

var org = {orgVal:22};
var copy1 = org;//copy1 is a reference to org
var copy2 = org;//copy2 is a reference to org
//de reference copy1 and copy2
org={mutate:1};
console.log(copy1===org);//false
console.log(copy1===copy2);//true
console.log(copy2===org);//false
console.log(copy1.orgVal);//=22
//since copy1 and copy2 both still reference the same object
//  mutating copy1 will affect copy2
copy1.orgVal='changed';
console.log(copy2.orgVal);//='changed'

De referencing a constructor's prototype after crating a lot of instances has a negative affect on performance ( see here ). 在创建许多实例之后取消引用构造函数的原型会对性能产生负面影响( 请参阅此处 )。 Which is why you usually don't de reference constructor.protoype after creating instances. 这就是为什么您通常在创建实例后不取消引用builder.protoype的原因。

Mutating prototype members or prototype can have unexpected results as shown here (under More about prototype). 突变原型成员或原型可具有意想不到的效果,如图这里 (下更多关于原型)。 It can be useful as well as long as you know why you're doing it and what is actually happening. 只要您知道自己为什么这样做以及实际发生的事情,它就会很有用。

Matt has mentioned this in his answer but distinguishes between data and behavior where it should be between shared and instance specific. 马特(Matt)在回答中提到了这一点,但区分了数据和行为,而数据和行为则应在共享和特定于实例之间进行。 There can be shared data that is deliberately modified on instances although using a static member would usually be better (Person.static=...). 可以在实例上故意修改共享数据,尽管通常使用静态成员会更好(Person.static = ...)。 Even in cases where you use a factory pattern and can't hard code the constructor name you could use someInstance.constructor.static (assuming you didn't wreck prototype.constructor when setting the prototype). 即使在使用工厂模式且无法对构造函数名称进行硬编码的情况下,也可以使用someInstance.constructor.static (假设设置原型时不会破坏prototype.constructor)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM