このキーワード
意味
「this」キーワードは非常に重要な構文ポイントです。その意味を理解しなければ、ほとんどの開発作業は完了できないと言っても過言ではありません。
前の章で述べたように、「this」をコンストラクターで使用してインスタンス オブジェクトを表すことができます。さらに、「this」は他の状況でも使用できます。しかし、どのような場合であっても、「this」には常にオブジェクトを返すという共通点があります。
簡単に言えば、「this」はプロパティまたはメソッドが「現在」存在するオブジェクトです。
このプロパティ
上記のコードでは、「this」は「property」属性が現在存在するオブジェクトを表します。
ここでは実際的な例を示します。
var 人 = {
名前:「チャン・サン」、
説明: function () {
'名前:' + this.name を返します。
}
};
person.describe()
// "名前: 張三"
上記のコードでは、this.name
は、name
属性が配置されているオブジェクトを表します。 「this.name」は「describe」メソッドで呼び出され、「describe」メソッドが配置されている現在のオブジェクトは「person」であるため、「this」は「person」を指し、「this.name」は「person」です。 .name`。
オブジェクトのプロパティは別のオブジェクトに割り当てることができるため、プロパティが配置されている現在のオブジェクトは変更可能です。つまり、「this」へのポインタは変更可能です。
var A = {
名前:「チャン・サン」、
説明: function () {
'名前:' + this.name を返します。
}
};
var B = {
名前:「李思」
};
B.describe = A.describe;
B.describe()
// "名前: リー・シー"
上記のコードでは、「A.describe」属性が「B」に割り当てられているため、「B.describe」は、「describe」メソッドが配置されている現在のオブジェクトが「B」であることを意味するため、「this.name」は「B.name」に。
この例を少しリファクタリングすると、「this」への動的なポインタがより明確にわかります。
関数 f() {
'名前:' + this.name を返します。
}
var A = {
名前:「チャン・サン」、
説明: f
};
var B = {
名前:「李思」、
説明: f
};
A.describe() // "名前: 張三"
B.describe() // "名前: Li Si"
上記のコードでは、関数 f
の中で this
キーワードが使用されていますが、f
が配置されているオブジェクトに応じて、this
のポインタも異なります。
関数が別の変数に代入されるたびに、「this」のポインタが変化します。
var A = {
名前:「チャン・サン」、
説明: function () {
'名前:' + this.name を返します。
}
};
var name = '李思';
var f = A.describe;
f() // "名前:李思"
上記のコードでは、A.describe
が変数 f
に割り当てられ、内部の this
は f
が実行されているオブジェクト (この場合は最上位オブジェクト) を指します。
Web プログラミングの別の例を見てみましょう。
<input type="text" name="age" size=3 onChange="validate(this, 18, 99);">
<スクリプト>
関数 validate(obj, lowval, hival){
if ((obj.value < lowval) || (obj.value > hival))
console.log('無効な値!');
}
</script>
上記のコードはテキスト入力ボックスです。ユーザーが値を入力すると、その値が指定された範囲内にあるかどうかを確認するために onChange
コールバック関数が呼び出されます。ブラウザは現在のオブジェクトをコールバック関数に渡すため、「this」は現在のオブジェクト (つまりテキスト ボックス) を渡すことを意味し、ユーザーの入力値は「this.value」から読み取ることができます。
要約すると、JavaScript 言語ではすべてがオブジェクトであり、実行環境もオブジェクトであるため、関数は特定のオブジェクト内で実行され、「this」は関数が実行されるオブジェクト (環境) です。これでユーザーが混乱することはありませんが、JavaScript は実行環境の動的な切り替えをサポートしています。つまり、「this」のポイントは動的であり、それがどのオブジェクトをポイントしているかを事前に決定する方法がありません。これが初心者を混乱させる原因です。一番。
エッセンス
JavaScript 言語がこのような設計になっている理由は、メモリ内のデータ構造に関係しています。
var obj = { foo: 5 };
上記のコードは、オブジェクトを変数 obj
に割り当てます。 JavaScript エンジンはまずメモリ内にオブジェクト { foo: 5 }
を生成し、次にこのオブジェクトのメモリ アドレスを変数 obj
に割り当てます。つまり、変数「obj」はアドレス(参照)である。後で obj.foo
を読み取りたい場合、エンジンはまず obj
からメモリ アドレスを取得し、次にそのアドレスから元のオブジェクトを読み取り、その foo
属性を返します。
元のオブジェクトは辞書構造に保存され、各属性名は属性説明オブジェクトに対応します。たとえば、上記の例の foo
属性は、実際には次の形式で保存されます。
{
フー: {
[[値]]: 5
[[書き込み可能]]: true
[[列挙可能]]: true
[[構成可能]]: true
}
}
foo
属性の値は、プロパティ記述オブジェクトの value
属性に格納されることに注意してください。
この構造は非常に明確ですが、問題は属性の値が関数である可能性があることです。
var obj = { foo: function () {} };
このとき、エンジンは関数をメモリに個別に保存し、その関数のアドレスを foo
属性の value
属性に割り当てます。
{
フー: {
[[値]]: 関数のアドレス
...
}
}
関数は単一の値であるため、異なる環境 (コンテキスト) で実行できます。
var f = function () {};
var obj = { f: f };
//単独で実行
f()
// obj環境の実行
obj.f()
JavaScript を使用すると、関数本体内で現在の環境の他の変数を参照できます。
var f = 関数 () {
コンソール.ログ(x);
};
上記のコードでは、変数 x
が関数本体で使用されています。この変数はランタイム環境によって提供されます。
ここで問題が発生します。関数はさまざまな実行環境で実行できるため、関数本体内に現在の実行環境 (コンテキスト) を取得するメカニズムが必要です。したがって、「this」が表示され、その設計目的は、関数本体内の関数の現在の実行環境を参照することです。
var f = 関数 () {
console.log(this.x);
}
上記のコードでは、関数本体の this.x
は、現在の実行環境の x
を参照します。
var f = 関数 () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f、
×:2、
};
//単独で実行
f() // 1
// obj環境の実行
obj.f() // 2
上記のコードでは、関数「f」はグローバル環境で実行され、「this.x」はグローバル環境の「x」を指します。「obj」環境で実行される場合、「this.x」は「obj」を指します。 .x`。
使用シーン
「this」は主に以下のような利用シーンがあります。
(1)地球環境
グローバル環境は「this」を使用します。これは、トップレベルのオブジェクト「window」を参照します。
this === ウィンドウ // true
関数 f() {
console.log(この === ウィンドウ);
}
f() // true
上記のコードは、グローバル環境で実行される限り、それが関数内にあるかどうかに関係なく、「this」はトップレベルのオブジェクト「window」を参照することを示しています。
(2)コンストラクター
コンストラクター内の「this」はインスタンス オブジェクトを参照します。
var Obj = 関数 (p) {
this.p = p;
};
上記のコードはコンストラクター Obj
を定義しています。 this
はインスタンス オブジェクトを指しているため、コンストラクター内で this.p
を定義することは、インスタンス オブジェクトが p
属性を持つことを定義することと同じです。
var o = new Obj('Hello World!');
o.p // 「ハローワールド!」
(3)オブジェクトメソッド
オブジェクトのメソッドに this が含まれている場合、this のポイントはメソッドが実行されているオブジェクトです。このメソッドを別のオブジェクトに割り当てると、「this」のポインタが変更されます。
ただし、このルールを理解するのは簡単ではありません。以下のコードを見てください。
var obj ={
foo: 関数 () {
コンソール.ログ(これ);
}
};
obj.foo() // obj
上記のコードでは、「obj.foo」メソッドが実行されると、その内部の「this」は「obj」を指します。
ただし、次の使用法では、「this」のポイントが変わります。
// 状況 1
(obj.foo = obj.foo)() // ウィンドウ
// ケース 2
(false || obj.foo)() // ウィンドウ
// 状況 3
(1, obj.foo)() // ウィンドウ
上記のコードでは、「obj.foo」が値です。この値が実際に呼び出されるとき、実行環境はもはや obj
ではなく、グローバル環境になるため、 this
はもはや obj
を指しません。
JavaScript エンジンの内部では、「obj」と「obj.foo」がアドレス 1 とアドレス 2 と呼ばれる 2 つのメモリ アドレスに格納されていることがわかります。このように obj.foo() を呼び出すと、アドレス 1 からアドレス 2 が呼び出されるので、アドレス 2 の実行環境はアドレス 1 となり、this は obj を指します。ただし、上記 3 つの場合は、アドレス 2 を直接取り出して呼び出しています。この場合、実行環境はグローバル環境なので、this はグローバル環境を指します。上記の 3 つの状況は、次のコードと同等です。
// 状況 1
(obj.foo = 関数 () {
コンソール.ログ(これ);
})()
// と同等
(関数 () {
コンソール.ログ(これ);
})()
// ケース 2
(false || 関数 () {
コンソール.ログ(これ);
})()
// 状況 3
(1, 関数 () {
コンソール.ログ(これ);
})()
「this」が配置されているメソッドがオブジェクトの最初の層にない場合、「this」は現在の層のオブジェクトのみを指し、上位層は継承しません。
var a = {
p:「こんにちは」
b: {
m: 関数() {
console.log(this.p);
}
}
};
a.b.m() // 未定義
上記のコードでは、「a.b.m」メソッドは「a」オブジェクトの第 2 層にあり、メソッド内の「this」は「a」ではなく「a.b」を指します。これは、次のコードが実際に実行されるためです。
var b = {
m: 関数() {
console.log(this.p);
}
};
var a = {
p:「こんにちは」
b:b
};
(a.b).m() // b.m() と同等
期待通りの効果を得たい場合は、このように書くしかありません。
var a = {
b: {
m: 関数() {
console.log(this.p);
}、
p:「こんにちは」
}
};
この時点で、ネストされたオブジェクト内のメソッドが変数に割り当てられている場合、「this」は引き続きグローバル オブジェクトを指します。
var a = {
b: {
m: 関数() {
console.log(this.p);
}、
p:「こんにちは」
}
};
var hello = a.b.m;
hello() // 未定義
上記のコードでは、「m」は多層オブジェクト内のメソッドです。簡単にするために、これを「hello」変数に割り当てます。その結果、呼び出されたとき、「this」はトップレベルのオブジェクトを指します。この問題を回避するには、m
が配置されているオブジェクトのみを hello
に代入し、呼び出されたときに this
のポインタが変更されないようにすることができます。
var hello = a.b;
hello.m() // こんにちは
使用上の注意
これを複数層にすることは避けてください
「this」のポインタは未定義であるため、関数内に複数のレベルの「this」を含めないでください。
変数 o = {
f1: 関数 () {
コンソール.ログ(これ);
var f2 = 関数 () {
コンソール.ログ(これ);
}();
}
}
of.f1()
// 物体
// ウィンドウ
上記のコードには 2 つの「this」層が含まれています。実行後、最初の層はオブジェクト「o」を指し、2 番目の層はグローバル オブジェクトを指します。これは、次のコードが実際に実行されるためです。
var temp = function () {
コンソール.ログ(これ);
};
変数 o = {
f1: 関数 () {
コンソール.ログ(これ);
var f2 = temp();
}
}
1 つの解決策は、代わりに外側の層「this」を指す 2 番目の層の変数を使用することです。
変数 o = {
f1: 関数() {
コンソール.ログ(これ);
var that = this;
var f2 = 関数() {
console.log(それ);
}();
}
}
of.f1()
// 物体
// 物体
上記のコードは、変数 that
を定義し、外層の this
を固定的に指し、内層で that
を使用すると、this
が指す変更は発生しません。
実際、変数を使用して this の値を固定し、内部関数がこの変数を呼び出すのは非常に一般的な方法です。必ずマスターしてください。
JavaScript には、この問題を回避するための厳密モードが用意されています。厳密モードでは、関数内の「this」がトップレベルのオブジェクトを指している場合、エラーが報告されます。
変数カウンター = {
カウント: 0
};
counter.inc = 関数 () {
'厳密を使用';
this.count++
};
var f = カウンタ株式会社;
f()
// TypeError: 未定義のプロパティ 'count' を読み取れません
上記のコードでは、inc
メソッドは use strict'
宣言によって strict モードを採用しています。このとき、内部の this
がトップレベルのオブジェクトを指すと、エラーが報告されます。
配列処理メソッドではこれを回避してください
配列の map
メソッドと foreach
メソッドを使用すると、関数をパラメータとして指定できます。 「this」はこの関数内で使用しないでください。
変数 o = {
v: 「こんにちは」、
p: [ 'a1', 'a2' ],
f: 関数 f() {
this.p.forEach(関数 (項目) {
console.log(this.v + ' ' + item);
});
}
}
の()
// 未定義 a1
// 未定義の a2
上記のコードでは、「foreach」メソッドのコールバック関数内の「this」が実際には「window」オブジェクトを指しているため、「o.v」の値を取得できません。理由は前の段落の多層の this と同じです。つまり、内側の層 this は外側を指さず、最上位のオブジェクトを指しています。
この問題を解決する 1 つの方法は、前述したように、中間変数を使用して「this」を修正することです。
変数 o = {
v: 「こんにちは」、
p: [ 'a1', 'a2' ],
f: 関数 f() {
var that = this;
this.p.forEach(関数 (アイテム) {
console.log(that.v+' '+item);
});
}
}
の()
// こんにちは、a1
// こんにちは、a2
もう 1 つの方法は、実行環境を修正するために foreach
メソッドの 2 番目のパラメータとして this
を使用することです。
変数 o = {
v: 「こんにちは」、
p: [ 'a1', 'a2' ],
f: 関数 f() {
this.p.forEach(関数 (項目) {
console.log(this.v + ' ' + item);
}、 これ);
}
}
の()
// こんにちは、a1
// こんにちは、a2
コールバック関数ではこれを回避してください
コールバック関数内の「this」はポインタを変更することが多いため、使用しないことをお勧めします。
var o = 新しいオブジェクト();
of.f = 関数 () {
console.log(this === o);
}
// jQueryの書き方
$('#button').on('click', of.f);
上記のコードでは、ボタンをクリックすると、コンソールに「false」が表示されます。その理由は、「f」メソッドがボタン オブジェクトのコンテキストで呼び出されるため、「this」はもはや「o」オブジェクトではなく、ボタンの DOM オブジェクトを指しているためです。この微妙な違いはプログラミングにおいて見落とされやすく、検出が困難なエラーにつながります。
この問題を解決するには、次のメソッドのいくつかを使用して「this」をバインドします。つまり、「this」が特定のオブジェクトを固定的に指すようにして、不確実性を軽減できます。
これをバインドするメソッド
「this」の動的な切り替えは JavaScript に大きな柔軟性をもたらしますが、同時にプログラミングを難しく曖昧にします。場合によっては、予期しない状況を避けるために「これ」を修正する必要があります。 JavaScript には、this のポインタを切り替えたり固定したりするための call
、apply
、bind
の 3 つのメソッドが用意されています。
Function.prototype.call()
関数インスタンスの call
メソッドは、関数内の this
のポインタ (つまり、関数が実行されるスコープ) を指定し、指定されたスコープで関数を呼び出すことができます。
var obj = {};
var f = 関数 () {
これを返します。
};
f() === ウィンドウ // true
f.call(obj) === obj // true
上記のコードでは、グローバル環境が関数 f
を実行すると、this
はグローバル環境を指します (ブラウザは window
オブジェクトです)。call
メソッドは this
を指定して this
のポイントを変更できます。 thisを使用してオブジェクト
objをポイントし、オブジェクト
objのスコープ内で関数
f` を実行します。
call
メソッドのパラメータはオブジェクトである必要があります。パラメータが空、「null」、および「未定義」の場合、デフォルトでグローバル オブジェクトが渡されます。
var n = 123;
var obj = { n: 456 };
関数 a() {
console.log(this.n);
}
a.call() // 123
a.call(null) // 123
a.call(未定義) // 123
a.call(ウィンドウ) // 123
a.call(obj) // 456
上記のコードでは、「a」関数の「this」キーワードがグローバル オブジェクトを指している場合、戻り結果は「123」になります。 call
メソッドを使用して this
キーワードを obj
オブジェクトに指定すると、返される結果は 456
になります。ご覧のとおり、「call」メソッドにパラメータがない場合、またはパラメータが「null」または「unknown」の場合、それはグローバル オブジェクトを指すのと同じです。
call
メソッドのパラメータがプリミティブ値の場合、元の値は対応するラッパー オブジェクトに自動的に変換されてから call
メソッドに渡されます。
var f = 関数 () {
これを返します。
};
f.call(5)
//数値 {[[PrimitiveValue]]: 5}
上記のコードでは、call
のパラメータは 5
ですが、これはオブジェクトではありません。これは自動的にラッピング オブジェクト (Number
のインスタンス) に変換され、f
内の this
にバインドされます。
call
メソッドは複数のパラメータを受け入れることもできます。
func.call(thisValue, arg1, arg2, ...)
call の最初のパラメータは this が指すオブジェクトで、以降のパラメータは関数を呼び出すときに必要なパラメータです。
関数 add(a, b) {
a + b を返します。
}
add.call(this, 1, 2) // 3
上記のコードでは、call
メソッドは、関数 add
内の this
が現在の環境 (オブジェクト) にバインドされていることを指定しており、パラメーターは 1
と 2
であるため、関数 add
は次のようになります。実行後に「3」を取得します。
「call」メソッドの応用例の 1 つは、オブジェクトのネイティブ メソッドを呼び出すことです。
var obj = {};
obj.hasOwnProperty('toString') // false
// 継承された hasOwnProperty メソッドをオーバーライドします
obj.hasOwnProperty = function () {
true を返します。
};
obj.hasOwnProperty('toString') // true
Object.prototype.hasOwnProperty.call(obj, 'toString') // false
上記コードにおいて、「hasOwnProperty」は「obj」オブジェクトから継承されたメソッドです。このメソッドをオーバーライドすると正しい結果が得られません。 call
メソッドは、obj
に同じ名前のメソッドが存在するかどうかに関係なく、実行のために hasOwnProperty
メソッドの元の定義を obj
オブジェクトに配置することで、この問題を解決できます。結果には影響しません。
Function.prototype.apply()
「apply」メソッドは「call」メソッドと似ており、「this」ポインタを変更してから関数を呼び出します。唯一の違いは、関数の実行時に配列をパラメーターとして受け取ることです。その形式は次のとおりです。
func.apply(thisValue, [arg1, arg2, ...])
apply
メソッドの最初のパラメータは、this
が指すオブジェクトでもあり、null
または unknown
に設定すると、グローバル オブジェクトを指定するのと同じになります。 2 番目のパラメーターは配列であり、配列のすべてのメンバーがパラメーターとして元の関数に渡されます。元の関数のパラメータは、call メソッドでは 1 つずつ追加する必要がありますが、apply メソッドでは配列形式で追加する必要があります。
関数 f(x, y){
コンソール.log(x + y);
}
f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) // 2
上記のコードでは、「f」関数は最初は 2 つのパラメータを受け入れていましたが、「apply」メソッドを使用した後は、配列をパラメータとして受け入れることができます。
これを利用して、いくつかの興味深いアプリケーションを作成できます。
(1) 配列の最大の要素を見つける
JavaScript には、配列の最大の要素を検索する関数がありません。 apply
メソッドを Math.max
メソッドと組み合わせて使用すると、配列の最大の要素を返すことができます。
var a = [10, 2, 4, 15, 9];
Math.max.apply(null, a) // 15
(2) 配列の空の要素を「未定義」に変更します
「apply」メソッドを通じて、「Array」コンストラクターを使用して、配列の空の要素を「未定義」に変換します。
Array.apply(null, ['a', ,'b'])
// [ 'a'、未定義、 'b' ]
空の要素と unknown
の違いは、配列の forEach
メソッドは空の要素をスキップしますが、unknown
はスキップしないことです。したがって、内部要素をトラバースすると、異なる結果が得られます。
var a = ['a', , 'b'];
関数 print(i) {
コンソール.ログ(i);
}
a.forEach(印刷)
//
//b
Array.apply(null, a).forEach(print)
//
// 未定義
//b
(3) 配列状のオブジェクトを変換
さらに、配列オブジェクトの slice
メソッドを使用すると、配列状のオブジェクト (arguments
オブジェクトなど) を実数の配列に変換できます。
Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0:1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, 未定義]
Array.prototype.slice.apply({length: 1}) // [未定義]
上記のコードの「apply」メソッドのパラメータはすべてオブジェクトですが、戻り結果はすべて配列であり、オブジェクトを配列に変換する目的を果たします。上記のコードからわかるように、このメソッドが機能する前提は、処理されるオブジェクトに「length」属性と対応する数値キーが必要であることです。
(4) オブジェクトバインディングコールバック関数
ボタン クリック イベントの前の例は、次のように書き換えることができます。
var obj = 新しいオブジェクト();
var func = function () {
console.log(this === obj);
}
var ハンドラー = 関数 (){
func.apply(obj);
// または f.call(obj);
};
// jQueryの書き方
$('#button').on('click', ハンドラー);
上記のコードでは、ボタンをクリックすると、コンソールに「true」が表示されます。 apply()
メソッド (または call()
メソッド) は、関数が実行されるオブジェクトをバインドするだけでなく、関数を即座に実行するため、関数本体にバインディング文を記述する必要があります。より簡潔に記述する方法は、以下で紹介する bind()
メソッドを使用することです。
Function.prototype.bind()
bind()
メソッドは、関数本体の this
をオブジェクトにバインドし、新しい関数を返すために使用されます。
var d = 新しい日付();
d.getTime() // 1481869925657
var print = d.getTime;
print() // キャッチされない TypeError: これは Date オブジェクトではありません。
上記のコードでは、d.getTime()
メソッドを変数 print
に代入し、その後 print()
を呼び出すと、エラーが報告されます。これは、getTime()
メソッド内の this
が Date
オブジェクトのインスタンスにバインドされ、それを変数 print
に代入した後、内部の this
がそのインスタンスを指さなくなるためです。 「日付」オブジェクト。
bind()
メソッドはこの問題を解決できます。
var print = d.getTime.bind(d);
print() // 1481869925657
上記のコードでは、bind()
メソッドが getTime()
メソッド内の this
を d
オブジェクトにバインドしています。この時点で、このメソッドは他の変数に安全に割り当てることができます。
「bind」メソッドのパラメータは「this」にバインドされるオブジェクトです。より明確な例を次に示します。
変数カウンター = {
カウント: 0、
inc: 関数 () {
this.count++;
}
};
var func = counter.inc.bind(counter);
関数();
counter.count // 1
上記のコードでは、counter.inc()
メソッドが変数 func
に割り当てられています。このとき、bind()
メソッドを使用して、inc()
内の this
を counter
にバインドする必要があります。そうしないとエラーが発生します。
「this」を他のオブジェクトにバインドすることも可能です。
変数カウンター = {
カウント: 0、
inc: 関数 () {
this.count++;
}
};
var obj = {
カウント: 100
};
var func = counter.inc.bind(obj);
関数();
obj.count // 101
上記のコードでは、bind()
メソッドが、inc()
メソッド内の this
を obj
オブジェクトにバインドします。その結果、func
関数を呼び出した後、obj
内の count
属性がインクリメントされます。
bind()
はさらに多くのパラメータを受け入れ、これらのパラメータを元の関数のパラメータにバインドすることもできます。
var add = 関数 (x, y) {
x * this.m + y * this.n を返します。
}
var obj = {
m:2、
n:2
};
var newAdd = add.bind(obj, 5);
newAdd(5) // 20
上記のコードでは、this
オブジェクトをバインドすることに加えて、bind()
メソッドは add()
関数の最初のパラメータ x
を 5
にバインドし、新しい関数 を返します。 newAdd()
の場合、この関数は実行するためにもう 1 つのパラメーター y
を受け入れるだけで済みます。
bind()
メソッドの最初のパラメータが null
または unknown
の場合、関数の実行時に this
をグローバル オブジェクトにバインドすることと同じになります。 (ブラウザの「ウィンドウ」)。
関数 add(x, y) {
x + y を返します。
}
var plus5 = add.bind(null, 5);
プラス5(10) // 15
上記のコードでは、関数 add()
内に this
はありません。bind()
メソッドを使用する主な目的は、将来、関数を実行するたびにパラメータ x
をバインドすることです。新しい関数 plus5()
に必要なのは、別のパラメータ y
を指定するだけです。また、add()
内には this
がないため、bind()
の最初のパラメータは null
になりますが、それが他のオブジェクトの場合は効果がありません。
bind()
メソッドには使用上の注意がいくつかあります。
(1) 毎回新しい関数を返します
bind()
メソッドが実行されるたびに新しい関数が返されるため、問題が発生する可能性があります。たとえば、イベントをリッスンする場合、次のように記述することはできません。
element.addEventListener('click', o.m.bind(o));
上記のコードでは、「click」イベントは、「bind()」メソッドによって生成された匿名関数にバインドされています。これによりバインドを解除できなくなるため、次のコードは無効です。
element.removeEventListener('click', o.m.bind(o));
正しい方法は次のように書くことです。
varlistener = o.m.bind(o);
element.addEventListener('クリック', リスナー);
// ...
element.removeEventListener('click', リスナー);
(2) コールバック関数と組み合わせて使用します
コールバック関数は JavaScript で最も一般的に使用されるパターンの 1 つですが、よくある間違いは、「this」を含むメソッドを直接コールバック関数として扱うことです。解決策は、「bind()」メソッドを使用して「counter.inc()」を「counter」にバインドすることです。
変数カウンター = {
カウント: 0、
inc: 関数 () {
'厳密を使用';
this.count++;
}
};
関数 callIt(コールバック) {
折り返し電話();
}
callIt(counter.inc.bind(counter));
counter.count // 1
上記のコードでは、callIt()
メソッドがコールバック関数を呼び出します。このとき、counter.incを直接渡すと、呼び出されたときにcounter.inc()内のthisがグローバルオブジェクトを指すことになります。 bind()
メソッドを使用して counter.inc
を counter
にバインドした後は、そのような問題は発生せず、this
は常に counter
を指すようになります。
さらに微妙な状況がもう 1 つあります。つまり、一部の配列メソッドは関数をパラメーターとして受け入れることができます。これらの関数内の「this」ポイントも間違っている可能性があります。
var obj = {
名前:「チャン・サン」、
回: [1、2、3]、
印刷: 関数 () {
this.times.forEach(関数 (n) {
console.log(この名前);
});
}
};
obj.print()
// 出力なし
上記のコードでは、「obj.print」内の「this.times」の「this」が「obj」を指していますが、これは問題ありません。ただし、「forEach()」メソッドのコールバック関数内の「this.name」はグローバルオブジェクトを指しているため、値を取得する方法はありません。少し変えると、よりはっきりと見えるようになります。
obj.print = function () {
this.times.forEach(関数 (n) {
console.log(この === ウィンドウ);
});
};
obj.print()
// 真実
// 真実
// 真実
この問題を解決するために、bind()
メソッドを通じて this
をバインドします。
obj.print = function () {
this.times.forEach(関数 (n) {
console.log(この名前);
}.bind(this));
};
obj.print()
// 張三
// 張三
// 張三
(3) call()
メソッドと組み合わせて使用されます
bind()
メソッドを使用すると、配列の slice()
メソッドを例として、一部の JavaScript ネイティブ メソッドの使用形式を書き直すことができます。
[1, 2, 3].slice(0, 1) // [1]
// と同等
Array.prototype.slice.call([1, 2, 3], 0, 1) // [1]
上記のコードでは、配列の slice
メソッドが、指定された開始位置と終了位置に従って [1, 2, 3]
から別の配列を分割します。この本質は、[1, 2, 3]
に対して Array.prototype.slice()
メソッドを呼び出すことです。そのため、call
メソッドを使用してこのプロセスを表現し、同じ結果を得ることができます。
call()
メソッドは基本的に Function.prototype.call()
メソッドを呼び出すため、上記の式は bind()
メソッドを使用して書き換えることができます。
var スライス = Function.prototype.call.bind(Array.prototype.slice);
スライス([1, 2, 3], 0, 1) // [1]
上記のコードの意味は、Array.prototype.slice
を Function.prototype.call
メソッドが配置されているオブジェクトに変換し、呼び出されると Array.prototype.slice.call
になります。他の配列メソッドにも同様の記述を使用できます。
var Push = Function.prototype.call.bind(Array.prototype.push);
var Pop = Function.prototype.call.bind(Array.prototype.pop);
var a = [1,2,3];
プッシュ(a, 4)
a // [1、2、3、4]
ポップ(a)
a // [1、2、3]
さらに一歩進んで、Function.prototype.call
メソッドを Function.prototype.bind
オブジェクトにバインドすると、bind
の呼び出し形式も書き換えられることになります。
関数 f() {
console.log(this.v);
}
var o = { v: 123 };
varbind = Function.prototype.call.bind(Function.prototype.bind);
バインド(f, o)() // 123
上記のコードの意味は、Function.prototype.bind
メソッドを Function.prototype.call
にバインドすることです。そのため、bind
メソッドは関数インスタンスで使用せずに直接使用できます。
参考リンク
- Jonathan Creamer、JavaScript における「this」問題の回避
- Erik Kronberg、JavaScript でバインド、呼び出し、適用
- Axel Rauschmayer、JavaScript のこれ: 仕組み、つまずく可能性があるところ
作者: wangdoc
アドレス: https://wangdoc.com/
ライセンス: クリエイティブ・コモンズ 3.0