【问题标题】:Scope within ES6 ClassesES6 类中的作用域
【发布时间】:2016-07-27 12:35:01
【问题描述】:

我了解下面的 sn-p 中发生了什么,以及如何修复(使用绑定或通过使 walkfriend 成为构造函数之外的方法),但为什么会发生这种情况?将一个类的作用域绑定到它自己的方法对我来说似乎违反直觉。

class Person {
    constructor(name, friend) {
        this._name = name;
        if(friend) {
          this.walkFriend = friend.walk;
        }
    }
  
    get name() {
        return this._name.toUpperCase();
    }
  
    walk() {
        console.log(this.name + ' is walking.');
    }
}
         
let bob = new Person('Bob');
let bill = new Person('Bill', bob);

console.log(bob.name); // BOB
console.log(bill.name); // BILL
bill.walk() // Bill is walking.
bill.walkFriend(); // We expect 'BOB is walking', but we get 'BILL is walking.'

【问题讨论】:

    标签: javascript class ecmascript-6


    【解决方案1】:

    发生的情况是 ES2015(“ES6”)类中的“方法”与实例之间没有内在联系,就像旧式构造函数没有一样。¹friend.walk 只返回一个原始方法参考,没有任何东西可以将它绑定到friend,除非你自己这样做。换句话说,friend.walk === Person.prototype.walk 是true。例如,您的反直觉理解是正确的(除了它与范围无关,而是this 的值)。 :-)

    请记住,新的class 几乎完全只是语法糖(但是,你知道,是一种好的糖)。你的 Person 类几乎完全等同于这个 ES5 代码:

    var Person = function Person(name, friend) {
        this._name = name;
        if(friend) {
            this.walkFriend = friend.walk;
        }
    };
    
    Object.defineProperty(Person.prototype, "name", {
        get: function() {
            return this._name.toUpperCase();
        },
        configurable: true
    });
    
    Object.defineProperty(Person.prototype, "walk", {
        value: function() {
            console.log(this.name + ' is walking.');
        },
        writable: true,
        configurable: true
    });
    

    你说过你知道如何解决它,实际上你的两个解决方案都可以,无论是绑定:

    constructor(name, friend) {
        this._name = name;
        if(friend) {
            this.walkFriend = friend.walk.bind(frield);   // **
        }
    }
    

    或在构造函数中创建walk 作为箭头函数,而不是在原型上:

    constructor(name, friend) {
        this._name = name;
        this.walk = () => {                           // **
            console.log(this.name + ' is walking.');  // **
        };                                            // **
        if(friend) {
            this.walkFriend = friend.walk;
        }
    }
    

    ¹ 方法与定义它的类的 原型 之间存在 内在联系,如果您在方法中使用 super 关键字,则会使用这种联系.链接方法的 [[HomeObject]] 字段的规范调用(但您无法在代码中访问它,如果您不在方法中使用 super,它可以被 JavaScript 引擎优化掉)。

    【讨论】:

    • 我用 Wea​​kMap 完全延迟了:| jsfiddle.net/cswl/3cqjjfvc/1
    • @cswl:哈哈!但请注意,这不会阻止 Person 实例将其关联的 friend 保留在内存中。请记住,只有WeakMap 和WeakSet 中的键 被弱保存,而不是对象。如果我错了,请纠正我,但实际上在这里使用 WeakMap 并不能真正帮助仅仅绑定。
    • @T.J.Crowder 谢谢你措辞优美的回答。它表达得非常清楚。最好的,
    【解决方案2】:

    this 在 JS 中只有 4 个用例。您可能想查看this 以获得良好的阅读。在这种特殊情况下,您有一条指令this.walkFriend = friend.walk;,它引用friend 参数传入的对象中的walk 函数。 它既不是新的函数定义,也不属于它所在的对象。它只是对存在于被称为friend 的对象中的函数的引用。但是,当您调用它时,friend.walk 函数中的this 将成为调用它的对象。因此,您将获得 this 引用的对象的 name 属性。

    有几种方法可以解决此问题。你猜的一个是绑定。您可以将其设置为 this.walkFriend = friend.walk.bind(friend); 或其他方式,从它自己的范围内调用它,例如 this.walkFriend = _ => friend.walk();(我想既然您使用类箭头也应该适合您。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-10
      • 1970-01-01
      • 2016-12-19
      • 1970-01-01
      • 2016-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多