javascript - 设置继承自prototype的属性值,为什么prototype值没有改变?

标签 javascript

我在 Chrome 中做了一些测试。 很简单,我改变继承的属性值。 我假设该集合将发生在原型(prototype)对象上,但原型(prototype)值不会改变。

实际上,在子对象上创建了一个新属性。 这是预期的吗?

> function Shape() {};
> var base = new function() { this.mValue = 10; };
> Shape.prototype = base;
> var s = new Shape();
> s.mValue = 20;
> base;

< Object {mValue: 10}

最佳答案

Set the property value inherited from prototype, why the prototype value does not change?

因为当您分配给 s.mValue 时,它会在 s 上创建一个属性,该属性会遮蔽原型(prototype)上的属性。

属性访问的工作原理如下:

  • 如果您获取属性的值,它首先查看对象本身以查看是否具有该值。如果是,则返回该对象的值。如果没有,它会查找对象的原型(prototype),如果原型(prototype)有,则返回原型(prototype)的值;如果没有,则转到原型(prototype)的原型(prototype),依此类推。

  • 如果您设置属性的值,它会将其设置在对象本身上,而不是沿着原型(prototype)链向上。

显然这是简单的版本。 :-)

如果您是从基于类的语言接触到它,这可能会有点令人惊讶,但是一旦您理解了它,它就非常简单。

为了清楚起见,让我们添加一些 ASCII 艺术。在此代码之后:

function Shape() {};
var base = new function() { this.mValue = 10; };
Shape.prototype = base;

...我们的内存中有这样的内容:

        +−−−−−−−−−−−−−−−−−−+                  
Shape−−>|     function     |                  
        +−−−−−−−−−−−−−−−−−−+
        | [[Prototype]]    |−−−−−> Function.prototype
        | prototype        |−+  
        +−−−−−−−−−−−−−−−−−−+ |    +−−−−−−−−−−−−−−−+                    
                             +−−−>|    object     |                    
                             |    +−−−−−−−−−−−−−−−+                    
base−−−−−−−−−−−−−−−−−−−−−−−−−+    | [[Prototype]] |−−−> Object.prototype
                                  | mValue: 10    |                    
                                  +−−−−−−−−−−−−−−−+                    

(If prototype vs. [[Prototype]] is unfamiliar to you, see the explanation at ¹ at the end of the answer.)

Then we do:

var s = new Shape();

并得到:

        +−−−−−−−−−−−−−−−−−−+                  
Shape−−>|     function     |                  
        +−−−−−−−−−−−−−−−−−−+
        | [[Prototype]]    |−−−−−> Function.prototype
        | prototype        |−+  
        +−−−−−−−−−−−−−−−−−−+ |    +−−−−−−−−−−−−−−−+                    
                             +−+−>|    object     |                    
                             | |  +−−−−−−−−−−−−−−−+                    
base−−−−−−−−−−−−−−−−−−−−−−−−−+ |  | [[Prototype]] |−−−> Object.prototype
                               |  | mValue: 10    |                    
        +−−−−−−−−−−−−−−−−−−+   |  +−−−−−−−−−−−−−−−+                    
s−−−−−−>|      object      |   |        
        +−−−−−−−−−−−−−−−−−−+   |
        | [[Prototype]]    |−−−+
        +−−−−−−−−−−−−−−−−−−+

At this point, if we did console.log(s.mValue), the JavaScript engine would look at s and, not finding a property with the name mValue, it would follow the __proto__ link to get the prototype, and look there. Finding it there, it gets the value from there.

Then we do:

s.mValue = 20;

并得到:

        +−−−−−−−−−−−−−−−−−−+                  
Shape−−>|     function     |                  
        +−−−−−−−−−−−−−−−−−−+
        | [[Prototype]]    |−−−−−> Function.prototype
        | prototype        |−+  
        +−−−−−−−−−−−−−−−−−−+ |    +−−−−−−−−−−−−−−−+                    
                             +−+−>|    object     |                    
                             | |  +−−−−−−−−−−−−−−−+                    
base−−−−−−−−−−−−−−−−−−−−−−−−−+ |  | [[Prototype]] |−−−> Object.prototype
                               |  | mValue: 10    |                    
        +−−−−−−−−−−−−−−−−−−+   |  +−−−−−−−−−−−−−−−+                    
s−−−−−−>|      object      |   |        
        +−−−−−−−−−−−−−−−−−−+   |
        | [[Prototype]]    |−−−+
        | mValue: 20       |
        +−−−−−−−−−−−−−−−−−−+

At this point, if we did console.log(s.mValue), the JavaScript engine would look at s, find the property, and get its value from s.


Side note: var base = new function() { this.mValue = 10; }; is a really long way to write var base = { mValue: 10 }. ;-) And it very very very slightly slows down access to the properties inherited from Object.prototype, as it inserts an extra unnecessary prototype in the chain. Also, you don't need to create a new object for Shape to assign to instances; it already has one.

So that code can be simplified to:

function Shape() {} // No need for ; after function declarations, they're not statements
Shape.prototype.mValue = 10;
var s = new Shape();
s.mValue = 20;
Shape.prototype;

(从 2023 年更新答案开始,我们可能不会使用 var。)

只是FWIW。


1 关于原型(prototype)[[Prototype]]:

  • prototype 是某些函数具有的属性,它指向要分配为通过该函数通过 new 创建的对象的原型(prototype)的对象。示例:Date.prototype 指向用作通过 new Date 创建的日期实例原型(prototype)的对象。
  • [[Prototype]] 是对象的内部字段,指向对象的原型(prototype)。示例:如果您执行了 const d = new Date(),则 d'[[Prototype]] 字段指向 Date。原型(prototype)[[Prototype]] 字段无法在代码中直接访问。您可以使用 Object.getPrototypeOf(d) 来访问它,或者(不推荐)使用 Object.prototype 中定义的名为 __proto__ 的访问器属性来访问它>。 (但是不要在新代码中使用 __proto__ 。它只是为了向后兼容为 JavaScript 引擎编写的代码,该引擎在标准化之前添加了该功能,因此没有必要,请注意,不继承的对象来自 Object.prototype 没有它。)

关于javascript - 设置继承自prototype的属性值,为什么prototype值没有改变?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29790292/

相关文章:

JavaScript 解析引用中的变量

javascript - 使用 React Hooks 卸载组件时如何访问状态?

javascript - 如何通过js获取div信息?

javascript - 如何获取我的 JavaScript 数组的所有子字符串(连续子序列)?

javascript - 正则表达式获取字符之间的所有字符

javascript - 如何从选择菜单中删除选项?

javascript - 我适合哪个JavaScript框架?

javascript - Vue.js 3.0 : Read and set object properties from method

javascript - Backbone.iobind 应该是node包还是bower包?

javascript - ExtJS - 从不同文件访问变量