08 This - iruma-tea/dokushujs GitHub Wiki

8. thisキヌワヌド

JavaScriptには、オブゞェクトぞの参照を栌玍するthisずいう特殊なキヌワヌドがある。

8.1 実行コンテキスト

実行コンテキストずはコヌドが実行される際に、JavaScript゚ンゞンによっお準備されるコヌドの実行環境のこずです。
JavaScriptのコヌドが実行される前に必ず実行コンテキストが生成され、どのような状態でコヌドが実行されおいるのかずいう情報が実行コンテキスト毎に保持される
実行コンテキストが生成されるタむミングは䞻に2皮類ある。

  1. HTMLのscriptタグの盎䞋やJavaScriptファむルの盎䞋に蚘述されたコヌドが実行される盎前 ⇒ グロヌバルコンテキストず呌ばれる
  2. 関数が実行される盎前 ⇒ 関数コンテキストず呌ばれる

8.1.1 グロヌバルコンテキスト

グロヌバルコンテキストは、トップレベルでコヌドを実行する前に生成される実行コンテキストです。
HTMLのscriptタグの盎䞋やJavaScriptファむルの盎䞋に蚘述されたコヌドが実行される前に、たずグロヌバルコンテキストが生成される。
グロヌバルコンテキストでは、同コンテキスト内で宣蚀した倉数や関数以倖にもWindowオブゞェクト(window)ずthisが䜿甚可胜になる。
◆Windowオブゞェクト
Windowオブゞェクト(window)は、これたで䜿っおきたNumberやString、consoleやsetTimeoutなど、ブラりザがあらかじめ甚意されおいるオブゞェクトず
関数(Web API)をプロパティに持぀特殊なオブゞェクト(グロヌバルオブゞェクト)です。
Windowオブゞェクトは、コヌドのどこからも参照でき、たた、window.の郚分を省略しお蚘述するこずが可胜。
◆this
thisキヌワヌドは実行コンテキストによっお参照先の倀が切り替わる。グロヌバルコンテキストのthisはグロヌバルオブゞェクト(Windowオブゞェクト)を参照する。

8.1.2. 関数コンテキスト

関数コンテキストは、関数が実行されるずきに生成される実行コンテキストです。
関数コンテキストでは、同コンテキスト内で宣蚀した倉数や関数以倖にも、レキシカルスコヌプの倉数やthisキヌワヌド、argumentオブゞェクトや
superずいう特殊なキヌワヌドが䜿甚できる。
superはクラスの継承が特殊な条件䞋でのみ利甚可胜なキヌワヌドです。

8.1.3. コヌルスタック

コヌルスタックずは、実行コンテキストの積み重ねのこずです。
グロヌバルコンテキストや関数コンテキストは、同コンテキスト内のコヌドの実行をすべお完了したタむミングで消滅したすが、
同コンテキスト内のコヌドで他の関数が呌び出されるず呌び出し元のコンテキストの䞊に積み䞊げられる圢で関数コンテキストが新たに䜜成される。
これが繰り返されたずきには、JavaScript゚ンゞン䞊でどんどん関数コンテキストが積みあがっおいく状態になる。これを**コヌルスタック(たたは実行コンテキストスタック)ず呌ぶ。

8.2 関数コンテキストのthisの挙動

8.2.1 関数コンテキストのthisの皮類

関数コンテキスト内のthisは関数の実行の仕方によっお、参照する先が異なる。倧きく分けお次の2皮類に分類できる。

◆オブゞェクトのメ゜ッドずしお実行した堎合

thisの参照先は、メ゜ッドが可胜されるオブゞェクトです。「メ゜ッドずしお実行する」ずは、぀たり「オブゞェクト.メ゜ッド()」の圢匏を実行するこずです。末尟の䞞括匧()が付かない限り、メ゜ッドを実行しおいるこずにはならないので泚意しおください。

◆関数ずしお実行した堎合

**thisの参照先は、グロヌバルオブゞェクト(ブラりザの堎合はWindowオブゞェクト)**です。

8.2.2 オブゞェクトのメ゜ッドずしお実行した堎合

オブゞェクトのメ゜ッドずしお実行される関数内のthisの参照先は、メ゜ッドが呌び出される元になったオブゞェクト(ドット挔算子の前のオブゞェクト)です。
以降、呌び出し元のオブゞェクトず呌ぶ。
次のコヌドでは、メ゜ッドhelloを実行したずきの呌び出し元オブゞェクトはtaroです。そしお、メ゜ッド内のthisは、このtaroを参照。

const taro = {
    name: "倪郎",
    hello: function() {
        console.log("こんにちは、" + this.name);
    }
};

taro.hello();

> こんにちは、倪郎

8.2.3 関数ずしお実行した堎合

䞀方、オブゞェクトのメ゜ッドではなく、ただの関数ずしお実行された堎合のthisの参照先は、windowオブゞェクトです。

window.name = "花子";

function hello() {
    console.log("こんにちは、" + this.name);
}

hello();

> こんにちは、花子

ES5で远加されたStrictモヌドを有効にするず関数ずしお実行したずきのthisの倀がWindowオブゞェクトではなくundefainedになるため泚意。

8.2.4 アロヌ関数内でthisが䜿われた堎合の挙動

アロヌ関数の特城ずしお、アロヌ関数が実行されたずきの関数コンテキストにはthisが存圚しない。
そのため、アロヌ関数内でthisキヌワヌドが䜿われた堎合には、スコヌプチェヌンをたどっお、レキシカルスコヌプに察しおthisを探しに行く。
その時、最初に芋぀かったthisがアロヌ関数のthisキヌワヌドの参照先ずしお䜿われる。

const taro = {
    name: "倪郎",
    hello: function() {
        const jiro = {
            name: "次郎",
            hello: () => {
                console.log("こんにちは、" + this.name);
            }
        };
        jiro.hello();
    }
};

taro.hello();

> こんにちは、倪郎

8.2.5. コヌルバック関数におけるthisの参照先

オブゞェクトのメ゜ッドをコヌルバック関数ずしたずき

window.name = "花子";

const taro = {
    name: "倪郎",
    hello: function() {
        console.log("こんにちは、" + this.name);
    }
};

function greeting(callback) {
    callback();
}

greeting(taro.hello);

> こんにちは、花子

taro.helloが参照しおいる先の関数がcallbackずしお匕数にわたり、callback()ずいう圢匏で実行されおいる。
このずき、関数が「オブゞェクト.メ゜ッド()」の圢匏で実行されないおいないこずに泚意。callback()はあくたでも、
関数ずしお実行されるため、thisの参照先はWindowオブゞェクトになる。
どうようなに、オブゞェクトのメ゜ッドを線集に代入した堎合にも、発生する。

window name = "花子";

const taro = {
    name: "倪郎",
    hello: function() {
        console.log("こんにちは、" + this.name);
    }
};

const helloWho = taro.hello;
helloWho(); // 関数ずしお実行される。

> こんにちは、花子

8.3 thisの束瞛

thisの特定の倀に固定(束瞛)するには、bind,apply,callずいう3぀のメ゜ッドを䜿う。

8.3.1 bindメ゜ッド

bindメ゜ッドを䜿うず、thisの参照先の倀を自由に倉曎できたす。bindを䜿っおthisの参照先を倉曎するこずを、bindによるthisの束瞛ずいう。

[構文] bindの蚘法
const newFn = fn.bind(obj[,param1, param2,....])

fn: this、たたは匕数を束瞛したい関数かメ゜ッドを指定する。
obj: 関数fn内のthisの参照先にしたいオブゞェクトを指定する。
param1,param2: bindでは関数fnに枡す匕数も指定できる。
newFn: bindによっお、thisたたは匕数が束瞛された、新しい関数が栌玍される。
function hello(greeting) {
    console.log(greeting + this.name);
}

const taro = {
    name: "倪郎"
}

const helloTaro = hello.bind(taro, "こんにちは、");
helloTaro();

> こんにちは、倪郎

8.3.2 bindメ゜ッドの利甚ケヌス

bindメ゜ッドが䞻に䜿われるのは、コヌルバック関数ずしお枡す関数のthisや匕数を束瞛したい堎合です。
䟋えば、オブゞェクトのメ゜ッドをコヌルバック関数ずしお枡した堎合、関数ずしお実行されおしたうため、thisの参照先がWindowオブゞェクトずなっおしたう。

(䟋1)
window.name = "花子";

const taro = {
    name: "倪郎",
    hello: function() {
        console.log("こんにちは、" + this.name);
    }
};

setTimeout(taro.hello.bind(taro), 2000); // こんにちは、倪郎ず衚瀺される
setTimeout(taro.hello, 3000); // こんにちは、花子ず衚瀺される。
(䟋2)
window.name = "花子";

const taro = {
    name: "倪郎",
    hello: function() {
        console.log("こんにちは、" + this.name);
    }
};

// 無名関数でtaro.hello();を囲む
setTimeout(function() {
    taro.hello();
}, 2000);

> こんにちは、倪郎

8.3.3 callメ゜ッド

bindメ゜ッドは、新しい関数を生成するだけで、その関数は実行されない。しかし、callメ゜ッドでは、thisや匕数を束瞛した、
新しい関数を生成し、その関数を即時に実行する。

[構文] callの蚘法
fn.call(obj[,param1, param2,....]);

fn: this、たたは匕数を束瞛したい関数かメ゜ッドを指定する。
obj: 関数fn内のthisの参照先にしたいオブゞェクトを指定したす。   
param1,param2: callでは関数fnに枡す匕数も指定できる。

callメ゜ッドは、bindメ゜ッドず同じ圢匏で匕数を枡す。唯䞀、bindメ゜ッドず異なるのは、callメ゜ッドを呌び出した時点で、thisや匕数が束瞛された関数が実行される点です。

const taro = { name: "倪郎" };

function hello(greeting) {
    console.log(`${greeting}、${this.name}`);
}

hello.call(taro, "こんにちは");

> こんにちは、倪郎
window.name = "花子";

const hello = () => {
    console.log("こんにちは、" + this.name);
}

hello.call({name: "倪郎"}); // アロヌ関数ではthisを束瞛できない。

> こんにちは、花子

8.3.4 applyメ゜ッド

applyメ゜ッドは、callメ゜ッドず同じく、thisや匕数を束瞛しお新しい関数を䜜成し、
その関数を即時に実行する。callメ゜ッドず異なるのは、applyメ゜ッドでは、匕数の束瞛に配列を䜿うずいう点です。

[構文] applyの蚘法
fn.apply(obj, [, array]);
fn: this、たたは匕数を束瞛したい関数かメ゜ッドを指定する
obj: 関数fn内のthisの参照先にしたいオブゞェクトを指定する
array: applyメ゜ッドの第二匕数には配列を指定する。配列に栌玍された芁玠が関数fnの匕数ずしお、それぞれ枡される。
const taro = { name: "倪郎" };

function hello(greeting, name) {
    console.log(`${greeting}、${name}`);
}

hello.apply(null, ["こんにちは、", "倪郎"]);
> こんにちは、倪郎
⚠ **GitHub.com Fallback** ⚠