ES6课程范围

时间:2016-07-27 12:35:01

标签: javascript class ecmascript-6

我理解下面代码片段中发生了什么,以及如何修复(使用绑定或通过让walk friend成为构造函数之外的方法),但为什么会发生这种情况?对于我来说,将类的范围绑定到自己的方法似乎是违反直觉的。

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(); // expect 'BOB us walking', but get 'BILL us walking.'

2 个答案:

答案 0 :(得分:7)

正在发生的事情是,ES2015(“ES6”)类中的“方法”与实例之间没有内在联系,就像没有旧式构造函数一样.¹friend.walk只是返回一个原始方法引用,除了你自己这样做之外,它没有任何关系将它绑定到friend。换句话说,friend.walk === Person.prototype.walktrue。例如,你的反直觉理解是正确的(除了它不是范围,而是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引擎对其进行优化)。

答案 1 :(得分:1)

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

有几种方法可以解决这个问题。一个你猜的是绑定。您可以像this.walkFriend = friend.walk.bind(friend);或其他人一样调用它,从this.walkFriend = _ => friend.walk();范围内调用它(我认为,因为你使用类箭头也应该对你很好。)