js の (function(){return this})() はスコープに関わらずグローバルなthisを返すのでやばい!

js の (function(){return this})() はスコープに関わらずグローバルthisを返すのでやばい.

実例

({scope1(){
  ({scope2(){
    let global = (function(){return this})();
    console.log(global); // 一番外側のthisを取る(scope1ではない,strictモードでなければwindowと同義)
    console.log(this); // ここのthisは scope2 に束縛されている
  }}).scope2();
  console.log(this); // ここのthisは scope1 に束縛されている
}}).scope1();

this以外は意図通り動く

let hoge = 0;
({scope1(){
  let hoge = 1;
  ({scope2(){
    let hoge = 2;
    let local = (function(){return hoge})();
    console.log(local); // (意図通り)このスコープのhoge(=2)をとる
    console.log(hoge); // このスコープのhoge(=2)をとる
  }}).scope2();
  console.log(hoge); // このスコープのhoge(=1)をとる
}}).scope1();

解決策

Function.prototype.bind(this)でthisを束縛する

({scope1(){
  ({scope2(){
    let global = (function(){return this}).bind(this)();
    console.log(global); // thisをbindできている
    console.log(this); // ここのthisは scope2 に束縛されている
  }}).scope2();
  console.log(this); // ここのthisは scope1 に束縛されている
}}).scope1();

調べ方を変えて return this bind で調べると結構ヒットしますね…
アロー関数とかclassとか出た後でthisで悩まずに済む時代になってからjs書き始めたので全然知らなかった…

なんか書いてた

What does “return this” do within a javascript function?
にちょっと書いてました.

this refers to the current context, and changes meaning depending on the manner in which you're invoking a function.
With function invocation, this refers to the global object, even if the function is being invoked from a method, and the function belongs to the same class as the method invoking it. Douglas Crockford has described this as "mistake in the design of the language" [Crockford 28]

Douglas Crockford 氏も言語設計のミスだと言ってるのは面白いですね.

まとめ

とりあえずデフォルトでスコープガン無視はやばい!
やはりアロー関数で全て制御できるようになるべきだった…
そういえばjsfuckでもreturn thiswindowを取得するのはやってましたね.(スコープに関わらずなのは知らなかったけど…)

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.