ポインティングルール
スコープの静的な性質とは異なり、this の値は動的であり、コードが実際に実行されたときにのみ決定されます。これを指すためのルールは後述します。
デフォルトのバインド
// アプリケーション:デフォルトでは
// ポインティングルール:非厳密モードはグローバルオブジェクトを指し、厳密モードはundefinedを指す。
var a = "a";
function foo() {
console.log(this.a);
}
foo(); // "a"((を実装する。)
暗黙のバインド
// ケース:コンテキストオブジェクトを使って関数を呼び出す場合
// ポインティングルール:コールチェーンの最後のレベル、つまり関数の直接の呼び出し元を指す。
var a = "a",
obj1 = { a: "a1", foo },
obj2 = { a: "a2", obj1, foo };
function foo() {
console.log(this.a);
}
obj1.foo(); // 'a1'
obj2.foo(); // 'a2'
obj2.obj1.foo(); // 'a1'
// 以下の例では、barはobj1を指しているが.obj2.foo,
// しかし、実際にバーが呼び出されるときには、コンテキスト・オブジェクトは伴わない。,
// を実装しているので、暗黙のロスが発生し、デフォルトのバインドが適用される。
var bar = obj2.obj1.foo;
bar(); // 'a'((を実装する。)
明示的バインディング
// ケース:call、bind、bindによって手動でthisを変更した。
// ポインティングルール:call、apply、bindの最初のパラメーターを指す。
var a = "global_a",
b = "global_b",
obj = {
a: "obj_a",
b: "obj_b",
};
function foo(a, b) {
console.log(this.a, this.b, a, b);
}
foo(a, b); // undefined, undefined, global_a, global_b
foo.call(obj, a, b); // obj_a, obj_b, global_a, global_b
foo.apply(obj, [a, b]); // obj_a, obj_b, global_a, global_b
const fooBind = foo.bind(obj);
fooBind(a, b); // obj_a, obj_b, global_a, global_b
new
// new "とコンストラクターを使ってインスタンスを生成する。
// ポインティング・ルール:コンストラクタの thisは、生成されたインスタンスを指す。
function Foo(a) {
this.a = a;
}
var obj = new Foo("a"),
console.log(obj.a); // 'a'
アロー関数
// 応用:アロー関数を使う場合
// ポインティング・ルール:アロー関数の外側のレイヤーでthisを指す。
// 注:アロー関数はそれ自身の thisを持たないので、それらを指す thisはcall、apply、bindなどで変更することはできない。
var a = "a",
obj = {
a: "obj",
foo: function () {
console.log(this.a);
(() => {
console.log(this.a);
})();
},
bar: () => {
console.log(this.a);
},
};
obj.foo(); // 'obj', 'obj'
obj.bar(); // 'a'((を実装する。)
への参照を変更します。
メソッドと原則
Function.prototype.call/apply/bind
this""ポインタを明示的に変更することは可能です。これは、ターゲット・オブジェクトを関数呼び出しのコンテキスト・オブジェクトにすることで、thisが暗黙的にターゲット・オブジェクトにバインドされるようにします。
// は配列.prototype.forEach ,
// forEachが呼ばれたときのコンテキスト・オブジェクトはarrなので、thisはarrを指す。
const arr = ["h", "e", "l", "l", "o"];
arr.forEach();
// 配列から文字列へのForEachメソッドを実装する。.prototype
// このように、Stringは独自のforEachメソッドを持つ。
String.prototype.forEach = Array.prototype.forEach
// この場合、forEachの呼び出しのコンテキストはstrなので、thisはstrを指している。
const str = String("hello"),
str.forEach()
callの実装
// @params { Any } obj: thisバインド先のオブジェクト。
// @params { Any } ...args:
Function.prototype.myCall = function (obj, ...args) {
// コンテキスト・オブジェクトがない場合、アプリケーションはデフォルトでバインドする。
obj = obj || window;
// objが基本型の場合は、まずそれをオブジェクトにラップする。
obj = Object(obj);
// funcプロパティとobjプロパティの名前の衝突を避ける。
const func = Symbol();
// オブジェクトに this、つまり関数を与える。
obj[func] = this;
// コンテキスト・オブジェクトをobjとし、thisをobjにバインドする。
const result = obj[func](...args);
// を削除するのを忘れないこと。[func]このプロパティは
delete obj[func];
// 結果を返す
return result;
};
const str = "hello";
Array.prototype.forEach.myCall(str, function (val) {
console.log(val);
});
アプリケーション
// @params { Any } obj: thisバインド先のオブジェクト。
// @params { Array } args:
Function.prototype.myApply = function (obj, args) {
obj = obj || window;
obj = Object(obj);
const func = Symbol();
obj[func] = this;
const result = obj[func](...args);
delete obj[func];
return result;
};
const str = "hello";
Array.prototype.forEach.myApply(str, [
function (val) {
console.log(val);
},
]);
バインド
// @params { Any } obj: thisバインド先のオブジェクト。
// @params { Any } ...args:
Function.prototype.myBind = function (obj, ...args) {
obj = obj || window;
obj = Object(obj);
const func = Symbol();
obj[func] = this;
// 関数キュレーションのサポート
return function (...newArgs) {
obj[func](...args, ...newArgs);
};
};
const str = "hello";
const strForEach = Array.prototype.forEach.myBind(str);
strForEach(function (val) {
console.log(val);
});