JSの「this」を把握する(一)


一般的にはfunctionを関数に訳し、methodを方法に訳します.
私たちは一般的に、○○オブジェクトの方法は、「○○オブジェクトの関数」とは言わない.なぜなら、対象に向かって、一つの対象が具体的な例であり、自分なりの方法があるからです.関数は、オブジェクトとは関係ないです.
また、scope(作用領域)およびcontext(コンテキスト)も、迷いやすいところである.参考してください.Javascript ContectとScopeの学習まとめ
推荐阅覧:深入浅出ES 6(1~10)シリーズ
一つの言叶でプログラムを书くことができるということは、その言叶を正しく理解して使うということではない.もちろん、JavaScriptもそうです.JS(JavaScriptの略称)はとても使いやすい言語ですが、中にはたくさんの細かいところがあります.初心者はあまり知らないかもしれません.経験豊富なJSの達人でもたまに穴に落ちます.
多くの経験があるプログラマーはthisに対してJS内部でどのように動作していますか?通俗的に言えば、thisは別名を引用しているだけです.この別名は現在のオブジェクトだけを知っています.これも一番難しいところです.
本論文はあなたの考えを整理し、thisキーワードの内部動作原理を紹介します.
では、thisは何の鬼ですか?
簡単に言えば、thisは特殊な識別子キーであり、各functionにおいて自動的にスコープに基づいて決定され、今回呼び出した「所有者、owner」を指す.しかし、この問題を完全に解決するには、二つの重要な問題に答える必要があります.thisはどのように作成されましたか?
JavaScript関数を呼び出すたびに、どのようなパラメータが入ってきましたか?関数はどのように呼び出されましたか?関数はどこで呼び出されましたか?などの新しいオブジェクトを作成します.このオブジェクトのもう一つの重要な属性はthis参照であり、関数はどのオブジェクトかの方法であり、thisはこのオブジェクトに自動的にバインドされる.
ヒント:詳細はECMAScript言語規範§10.4.3節およびその中の関連リンクを参照してください.
var car = {
  brand: "Nissan",
  getBrand: function(){
    console.log(this.brand);
  }
};

car.getBrand();
// output: Nissan
この例では、this.brandオブジェクトの参照として使用される.したがって、carthis.brandと同等である.car.brandは誰を指していますか?
すべてのfunctionでは、thisの値は、コンテキスト(context、関数が呼び出し時刻における環境)に基づいて決定される.thisのスコープは、関数が定義する位置に関係なく、関数がどこで呼び出されるかによって決まる.
行ごとにJavaScriptコードは実行コンテキストで実行されます.thisが指すオブジェクトは、新しい実行コンテキストに入るたびに固定され、他の異なるコンテキストに遷移するまで変化する.コンテキストの実行(およびthisのバインディング)を決定するには、コールポイント(call-site)、コールポイントすなわち関数がコードで呼び出される位置を見つける必要がある.
次の例を見ましょう.
var brand = 'Nissan';
var myCar = {brand: 'Honda'};

var getBrand = function() {
  console.log(this.brand);
};

myCar.getBrand = getBrand;
myCar.getBrand();
// output: Honda

getBrand();
// output: Nissan
thisおよびmyCar.getBrand()は、同じ関数を指すが、getBrand()は、どのコンテキストで呼び出されるかによって異なるので、異なるthisである.
functionはどのオブジェクトの方法であるかをすでに知っています.関数ではgetBrand()はこのオブジェクトに結び付けられます.
上記のコードでは、第1回の関数呼び出しに対応するものがthisオブジェクトであり、第2回の対応はmyCarである.したがって、異なる文脈は異なる結果を生む.
コンテキストを呼び出す
次に、異なる文脈におけるwindowの指向を見てみよう.
グローバルスコープ(Global Scope)
すべてのJavaScriptが実行されている時には唯一のグローバルオブジェクトがあります.ブラウザでは、グローバルオブジェクトはgetBrand()である.Node.jsの中でwindow.getBrand()です.
グローバル実行コンテキスト(任意の関数以外のコード)では、thisは、厳密なモードであるかどうかにかかわらず、グローバルオブジェクトを指す.
ローカルスコープ(Local Scope)
関数では、windowは、関数がどのように呼び出されるかによって異なります.三つの状況に分けます
1.簡単な関数呼び出し(Simple Function Call)にglobalを使用する.
第一のシナリオは、独立した関数を直接呼び出すことである.
function simpleCall(){
  console.log(this);
}

simpleCall();
// output: the Window object
この場合、this値はコールされていない.コードは厳密なモードで実行されていないので、strict modeはまたオブジェクトでなければならないので、彼の値はデフォルトではグローバルオブジェクトです.
厳密モードであれば、コンテキスト実行時になぜ値が設定されますか?指定されていない場合は、thisが続きます.
function simpleCall(){
  "use strict";
  console.log(this);
}

simpleCall();
// output: undefined
2.オブジェクトの方法(Object’s Method)でthisを使用する.
関数をオブジェクトの属性として保存することで、オブジェクトを介してこの方法を呼び出すことができます.関数をオブジェクトとする方法で呼び出すと、中のthis値が呼び出し方法のオブジェクトとして設定されます.
var message = {
  content: "I'm a JavaScript Ninja!",
  showContent: function() {
    console.log(this.content);
  }
};

message.showContent();   
// output: I'm a JavaScript Ninja!
thisundefinedオブジェクトの一つの方法であるので、thisthisに相当する.
3.コンストラクタFunctionでshowContent()を使用する
私たちはmessageオペレータを通じて関数を呼び出すことができます.この場合,この関数は構造関数(すなわち対象工場)になる.上述の単純関数呼び出しと方法呼び出しとは異なり、構造関数呼び出しはthis.contentの値として新しいオブジェクトに入り、新しい構造のこのオブジェクトを結果として暗黙的に返します.
関数がコンストラクタとして使用されると(message.contentキーを介して)、そのthis値は新しく作成されたそのオブジェクトに結合される.newキーワードが使用されていない場合、彼はただの普通の関数であり、thisnewオブジェクトを指す.
function Message(content){
  this.content = content;
  this.showContent = function(){
    console.log(this.content);
  };
}

var message = new Message("I'm JavaScript Ninja!");

message.showContent();
// output: I'm JavaScript Ninja!
上記の例では、thisという構造関数がある.newオペレータを使用して新たなオブジェクトを作成しました.名前はthisです.また、新しいオブジェクトのwindow属性として、構造関数の文字列にも伝達されます.最後の行のコードを通して、この文字列は成功的に印刷された.Message()は新たに作成されたオブジェクトを指すので、構造関数自体ではない.newをどのように正しく使うか?
本節では、messageの挙動を決定するいくつかの内部メカニズムを学ぶ.
JavaScriptでは、すべての関数が対象ですので、関数にも独自の方法があります.すべての関数には二つの方法があります.この2つの方法によって関数のコンテキストを変更することができ、いつでも有効であり、contentの値を明示的に設定するために使用されます.this方法は、2つのパラメータを受信する.1つ目はthisに設定されるオブジェクトであり、2番目のパラメータはオプションであり、パラメータに入るならば、thisの2番目のパラメータとしてパッケージ化すれば良い.this方法とapply()は、基本的に同じであり、後のパラメータは配列ではなく、1つずつ分散して後に付加される.
次は練習します.
function warrior(speed, strength){
  console.log(
    "Warrior: " + this.kind +
    ", weapon: " + this.weapon +
    ", speed: " + speed +
    ", strength: " + strength
  );
}

var warrior1 = {
  kind: "ninja",
  weapon: "shuriken"
};

var warrior2 = {
  kind: "samurai",
  weapon: "katana"
};

warrior.call(warrior1, 9, 5);
// output: Warrior: ninja, weapon: shuriken, speed: 9, strength: 5
warrior.apply(warrior2, [6, 10]);
// output: Warrior: samurai, weapon: katana, speed: 6, strength: 10
ここでは、工場関数thisがあります.異なる戦士オブジェクトを導入することによって、異なるタイプのwarrior(戦士)を作成します.では、工場関数内でapply()は、call()および/またはapply()を介して私たちが入ってきたオブジェクトを指す.
最初の関数呼び出しでは、warrior()方法を用いてthiscall()オブジェクトに設定し、必要な他のパラメータを入力し、パラメータ間をカンマで区切った.第二の関数呼び出しでは、ほとんど同じです.apply()オブジェクトが入ってきただけで、必要なパラメータを一つの配列にパッケージします.call()およびthisに加えて、ECMAScript 5は、関数または方法を呼び出したときにwarrior1方法によってwarrior2オブジェクトをバインドすることもできる.次の例を見ましょう.
function warrior(kind){
  console.log(
    "Warrior: " + kind +
    ". Favorite weapon: " + this.weapon +
    ". Main mission: " + this.mission
  );
}

var attributes = {
  weapon: "shuriken",
  mission: "espionage"
};

var ninja = warrior.bind(attributes, "ninja");

ninja();
// output: Warrior: ninja. Favorite weapon: shuriken. Main mission: espionage
この例では、call()方法の使用方法はまだ同様であるが、apply()は、新しい関数(方法体と作用領域はbindと同じ)を作成し、従来のthis関数を変更していない.新しい関数の機能は古いものと同じで、bind()オブジェクトにのみ結合されています.
warrior.bind()方法とwarrior()warrior()との違いは、attributesの後には関数が実行されず、他の関数に伝達され、ある適切なタイミングで再起動されることである.】
締め括りをつける
So,bindに関する知識はほとんどこれらです.初心者としては、これらを身につけるだけで十分です.正しく使うことができます.自分に自信を持ってください.もちろん、使用中に問題があるかもしれません.次の記事を読んで、JSの「call」を把握してください.
おすすめの読書
阮一峰先生の:ECMAScript 6入門
CNBlog:Javascript ContectとScopeの学習まとめ
infoq:深入浅出ES 6(1~10)シリーズ
babeljs-ES 6:https://babeljs.io/repl/
JSの「apply」を把握する(一)
JSの「bind()」を把握する(二)
関連リンク
原文のリンク:Revealing the Inner Working s of JavaScript’s“this”Keyword
日付:2015年5月1日
翻訳日:2015年09月17日
作者:鉄錨http://blog.csdn.net/renfufei