【问题标题】:Confusion about 'this' and prototyping in JavaScript [duplicate]关于 JavaScript 中的“this”和原型设计的困惑 [重复]
【发布时间】:2018-02-11 06:30:01
【问题描述】:

我对通过原型设计和 JavaScript 中的“this”关键字实现继承有些困惑。

let person = {
  stomach: [],

  eat(food) {
    this.stomach.push(food);
  }
};

let tony = {
  __proto__: person
};

let peter = {
  __proto__: person
};

tony.eat("shawarma");
alert( tony.stomach ); // shawarma


alert( peter.stomach ); // shawarma

在上面的示例中,为什么即使没有推送任何内容,最后一行也会给出答案“shawarma”?

【问题讨论】:

标签: javascript


【解决方案1】:

因为tonypeter 共享 数组,它位于person。只有一个数组,你只是改变它的状态。

在您创建了tonypeter 之后,您的内存中有这个(省略细节):

+−−−−−−−−−−−+ 人−−−−−−−−−−−−−−−−−−−−−+−+−−>| (对象) | / / +−−−−−−−−−−−+ +−−−−−−−−−−−−+ | | |胃|−−−−−>| (数组) | | | +−−−−−−−−−−−+ +−−−−−−−−−−−−−+ | | |长度:0 | | | +−−−−−−−−−−−−−+ +−−−−−−−−−−−−+ | | 托尼---->| (对象) | | | +−−−−−−−−−−−−+ | | | __proto__ |−−+ | +−−−−−−−−−−−−+ | | +−−−−−−−−−−−−+ | 彼得--->| (对象) | | +−−−−−−−−−−−−+ | | __proto__ |−−−−−+ +−−−−−−−−−−−−−+

无论您是通过tony.__proto__.stomach 还是peter.__proto__.stomach(通过原型链)访问该数组,您都只能访问那个数组。当您通过eat 在其上推送"shawarma" 时,该数组的状态将被修改,并且无论您采用哪条路径到达它都是可见的:

+−−−−−−−−−−−+ 人−−−−−−−−−−−−−−−−−−−−−+−+−−>| (对象) | / / +−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−+ | | |胃|−−−−−>| (数组) | | | +−−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−+ | | |长度:1 | | | | 0:“沙瓦玛”| +−−−−−−−−−−−−+ | | +−−−−−−−−−−−−−−−−−+ 托尼---->| (对象) | | | +−−−−−−−−−−−−+ | | | __proto__ |−−+ | +−−−−−−−−−−−−+ | | +−−−−−−−−−−−−+ | 彼得--->| (对象) | | +−−−−−−−−−−−−+ | | __proto__ |−−−−−+ +−−−−−−−−−−−−−+

你可以通过给tonypeter 提供他们的自己的 stomachs 来解决这个问题,并且可能从person 中删除stomach(尽管如果你愿意,你可以离开它直接使用person 以及将其用作原型):

let person = {
  stomach: [], // You may or may not want to remove this, depending
  eat(food) {
    this.stomach.push(food);
  }
};

let tony = {
  __proto__: person,
  stomach: []
};

let peter = {
  __proto__: person,
  stomach: []
};

tony.eat("shawarma");
console.log(tony.stomach);  // shawarma

console.log(peter.stomach); // empty

【讨论】:

  • 我需要变得快很多才能在拥挤的标签中回答:(
  • @suraj:在这种情况下,我们都应该去寻找重复而不是回答;我意识到我很傻,很快就找到了欺骗目标。
【解决方案2】:

因为“stomach”是原型的一个属性,对于 tony 和 peter 对象来说是完全相同的。您需要在构造函数中为每个新创建的对象重新初始化“stomach”属性。

同时尽量避免引用 proto https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

class Person {
    constructor() {
        // created separately for every new object
        this.stomach = [];
    }

    eat(food) {
        this.stomach.push(food);
    }
}

var tony = new Person();
var peter = new Person();

【讨论】:

    【解决方案3】:

    这是因为tonypeter 共享相同的person 原型,其中包含stomach 属性。

    当您触发tony.eat('shawarma') 时,eat 方法正在启动,this 指向person 对象。所以这个人的胃正在改变。

    如果你给petertony他们自己的胃,一切都会很好;)。

    let person = {
      eat(food) {
        this.stomach.push(food);
      }
    };
    
    let tony = {
      stomach: [],
      __proto__: person
    };
    
    let peter = {
      stomach: [],
      __proto__: person
    };
    

    编辑: 刚刚看到@wookieb 的回答——比我的还要好。

    【讨论】:

      【解决方案4】:

      tony.hasOwnProperty("stomach") 将返回 false(因为它不是其他 OO 语言所称的 属性)并且现在是原型的一部分(如 类属性 或 静态成员)。每个使用它的对象都有一个原型。

      因此,任何以person__proto__prototype 的对象都将共享相同的stomach

      你可以在 ES6 中改用这个:

      class Person{
        constructor(){
          this.stomach = [];
        }
      
        eat(food){
          this.stomach.push(food);
        }
      }
      
      const tony = new Person();
      const peter = new Person();
      
      class RandomPelo extends Person{
        constructor(){
          super();
          this.randomness = true;
        }
      
        eat(food){
          super.eat(food);
          this.weird_shared_stomach.push(food);  
        }
      }
      
      const pelo = new RandomPelo();
      

      注意:
      class 关键字只是构造函数概念的语法糖,因此您也可以修改其原型以添加“静态”属性:

      RandomPelo.prototype.weid_shared_stomach = [];

      【讨论】:

        猜你喜欢
        • 2022-01-14
        • 1970-01-01
        • 2014-04-21
        • 2020-12-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-04
        • 1970-01-01
        相关资源
        最近更新 更多