javascript 设置继承自prototype的属性值,为什么prototype值不变?

lqfhib0f  于 2023-01-29  发布在  Java
关注(0)|答案(1)|浏览(142)

我在Chrome中做了一些测试。很简单,我改变了继承的属性值。我假设设置会发生在原型对象上,但是原型值没有改变。
实际上在子对象上创建了一个新属性。这是预期的吗?

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

< Object {mValue: 10}
qni6mghb

qni6mghb1#

设置继承自prototype的属性值,为什么prototype值不变?
因为当你赋值给s.mValue时,它会在s上创建一个 * new * 属性,这个属性隐藏了原型上的属性。
属性访问的工作方式如下:

  • 如果你正在获取属性的值,它首先查看对象本身是否有,如果有,它返回对象的值,如果没有,它查看对象的原型,如果原型有,它返回原型的值;如果没有,它将转到原型的原型,依此类推。
  • 如果你正在 * 设置 * 属性的值,它会在对象本身上设置它,它不会在原型链中向上移动。

显然这是简单的版本。:-)
如果您是从基于类的语言开始使用它,这可能会有点令人惊讶,但是一旦您理解了它,它就真的很简单了。
为了清楚起见,让我们使用一些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    |                    
                                  +−−−−−−−−−−−−−−−+
  • (如果您不熟悉prototype[[Prototype]],请参见答案末尾¹处的解释。)*

然后我们做:

var s = new Shape();

并得到:

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

此时,如果我们使用console.log(s.mValue),JavaScript引擎将查看s,如果没有找到名为mValue的属性,它将沿着__proto__链接获取原型,并查看那里,如果在那里找到它,它将从那里获取值。
然后我们做:

s.mValue = 20;

并得到:

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

此时,如果我们执行console.log(s.mValue),JavaScript引擎将查看s,找到属性,并从s获取其值。
旁注:var base = new function() { this.mValue = 10; };比写var base = { mValue: 10 }真的长。- )而且它 * 非常非常轻微地 * 减慢了对从Object.prototype继承的属性的访问,因为它在链中插入了一个额外的不必要的原型,而且,你不需要为Shape创建一个新的对象来分配给示例;它已经有了一个。
因此代码可以简化为:

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;

(And在2023年更新答案时,我们可能不会使用var。)
只是FWIW。
¹关于prototype[[Prototype]]

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

相关问题