2016-07-27 17 views
4

次のスニペットで何が起こっているのか、バインディングを使用する方法(ウォーク・フレンズをコンストラクタの外にある方法で修正する方法)について理解していますが、なぜこのようなことが起こりますか?クラスのスコープを独自のメソッドにバインドしなければならないのは直感的ではないようです。ES6クラス内のスコープ

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.'

答えて

6

何が起こっている古いスタイルで存在しないと同じように、ES2015(「ES6」)クラスとインスタンスの「方法」の間には本質的な接続がないことですコンストラクタ関数friend.walkはそのまま生のメソッド参照を返します。あなた自身でなければ、friendにバインドするものは何もありません。言い換えれば、friend.walk === Person.prototype.walktrueです。たとえば、直感に反したあなたの理解は正しいです(範囲についてではなく、むしろthisの値)。 :-)

新しいclassは、ほぼ完全に構文的な砂糖です(ただし、砂糖の良さは分かります)。いずれかが結合、

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); // ** 
    } 
} 

または作成あなたのPersonクラスは、ほぼ正確にこのES5コードに相当します矢印の関数として、コンストラクタ内walk、代わりのプロトタイプに:

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

私は弱いマップで完全に遅くなりました: https://jsfiddle.net/cswl/3cqjjfvc/1/ – cswl

+0

@cswl:LOL!しかし、それは 'Person'インスタンスが関連する' friend'をメモリ内に保持することを妨げるものではないことに注意してください。 WeakMapとWeakSetの**キー**はオブジェクトではなく弱く保持されていることに注意してください。だから私が間違っている場合は私を修正しますが、実際にはここでWeakMapを使用しても、バインディングだけでは役に立ちません。 –

+1

@ T.J.Crowder美しく語られた答えをありがとう。それは非常に明確に表現されていました。 ベスト、 –

1

の唯一の4ユースケースがあります。 JSのあなたは良い読み取りのためにthisをチェックしたいかもしれません。この特定のケースでは、friend引数によって渡されたオブジェクトのwalk関数を参照する指示this.walkFriend = friend.walk;があります。 これは新しい関数定義でもなく、それが属するオブジェクトに属していませんです。これは、friendと呼ばれるオブジェクトに存在する関数へのリフェラルです。しかし、あなたがそれを呼び出すと、friend.walk関数内のthisが呼び出されたオブジェクトになります。したがってnameというオブジェクトのプロパティは、thisによって参照されます。

これを解決するにはいくつかの方法があります。 1つはあなたが推測するようバインドです。 this.walkFriend = friend.walk.bind(friend);のようにすることもできますし、this.walkFriend = _ => friend.walk();のように自分自身のスコープ内から呼び出すこともできます(クラスの矢印を使用すると、あなたもうまくいくはずです)

関連する問題