これを位置に応じて理解すると、状況は大きく次の 3 つのタイプに分類できます。
1. 関数内: これは通常、暗黙的なパラメーターです。
2. 関数の外側 (最上位スコープ内): ブラウザーでは、これはグローバル オブジェクトを指しますが、Node.js では、モジュールのエクスポートを指します。
3. eval() に渡される文字列: eval() が直接呼び出された場合、これは現在のオブジェクトを参照し、eval() が間接的に呼び出された場合、これはグローバル オブジェクトを参照します。
これらのカテゴリに対応するテストを実施しました:
1. 関数内のこれ
関数は基本的に JS 内のすべての呼び出し可能な構造を表すことができるため、これを使用する最も一般的なシナリオであり、関数は次の 3 つの役割に分けることができます。
実関数
コンストラクター
メソッド
1.1 実関数の this
実際の関数では、this の値は、それが見つかったコンテキストに依存するパターンです。
ずさんなモード: これはグローバル オブジェクト (ブラウザーのウィンドウ) を指します。
function sinnerFunc() {
コンソール.log( this === window); // true
}
sloppyFunc();
厳密モード: この値は未定義です。
function strictFunc() {
' strict を使用します' ;
console.log(this === unknown) // true
}
strictFunc();
これは関数の暗黙的なパラメーターであるため、その値は常に同じです。ただし、call() または apply() メソッドを使用して、この値を明示的に定義できます。
function func(arg1, arg2) {
console .log(this); // 1
console.log(arg1); // 3
}
func.call(1) , 2, 3); // (this, arg1, arg2)
func.apply(1, [2, 3]); // (this, arrayWithArgs)
コンストラクター内の
1.2 this
new を使用すると、関数をコンストラクターとして使用できます。新しい操作は新しいオブジェクトを作成し、このオブジェクトを this を通じてコンストラクターに渡します。
var SavedThis;
function Constr( ) {
SavedThis = this;
}
var inst = new Constr();
console.log(savedThis === inst); // true
JS での新しい操作の実装原理は、大まかに次のコードに示されているとおりです (より正確な実装については、ここを参照してください。この実装もより複雑です):
function newOperator(Constr, arrayWithArgs) {
var thisValue = Object.create(Constr.prototype);
Constr.apply(thisValue, arrayWithArgs);
return thisValue;
}
1.3 this メソッド内
メソッド内での this の使用は、従来のオブジェクト指向言語により傾向があります。this が指す受信側は、このメソッドを含むオブジェクトです。
var obj = {
メソッド: function () {
console.log(this === obj) // true
}
}
obj.method();
2. これはスコープ内です
ブラウザでは、スコープはグローバル スコープであり、これは (ウィンドウと同様に) グローバル オブジェクトを指します。
<script><div class="codebody" id="code60724"> コンソール。 log(this === window); // true<br></script>
Node.js では、通常、モジュール内の関数を実行します。したがって、トップレベルのスコープは非常に特殊なモジュール スコープです:
// `global` (`window ではありません) `) はグローバル オブジェクトを参照します:
console.log(Math === global.Math); // true
// `this` はグローバル オブジェクトを参照しません:
console .log( this !== global); // true
// `this` はモジュールのエクスポートを参照します:
console.log(this === module.exports) // true
3. これを eval() で実行します
eval() は、直接 (関数名 'eval' を呼び出すことによって) または間接的に (call() などの他の手段によって呼び出す) 呼び出すことができます。詳細については、こちらを参照してください。
// 実関数
function sinnerFunc () {
console.log(eval('this') === window); // true
}
sloppyFunc();
function strictFunc() {
'厳密な使用 ';
console.log(eval('this') === unknown); // true
}
strictFunc();
// コンストラクター
var selectedThis;
function Constr() {
SavedThis = eval('this');
}
var inst = new Constr();
console.log(savedThis === inst ); // true
// Methods
var obj = {
メソッド: function () {
console.log(eval('this') === obj); / true
}
}
obj.method();
4. これに関連するトラップ
これに関連して、以下に紹介する 3 つの罠に注意する必要があります。次の例では、Strict モードを使用するとコードのセキュリティが向上することに注意してください。実際の関数では、この値は未定義であるため、何か問題が発生すると警告が表示されます。
4.1 新しい
を使うのを忘れた
コンストラクターの呼び出しに new を使用していない場合は、実際には実際の関数を使用していることになります。したがって、これは期待した値にはなりません。 Sloppy モードでは、これは window を指し、グローバル変数を作成します:
function Point(x, y) {
this .x = x;
this.y = y;
}
var p = Point(7, 5) // 忘れていました new!
console.log(p == = 未定義) ; // true
// グローバル変数が作成されました:
console.log(y); // 5
ただし、厳密モードを使用している場合は、引き続き警告 (this===未定義) が表示されます:
function Point(x, y) {
' use strict';
this.x = x;
this.y = y;
}
var p = Point(7, 5);
// TypeError: Cannot未定義のプロパティ「x」を設定します
4.2 メソッドの不適切な使用
メソッドの値を直接取得する (呼び出すのではなく) 場合、そのメソッドは関数として使用されます。おそらく、メソッドをパラメータとして関数または呼び出しメソッドに渡したいときにこれを行うことになるでしょう。これは、setTimeout() と登録されたイベント ハンドラーの場合に当てはまります。 callIt() メソッドを使用して、このシナリオをシミュレートします:
/**setTimeout() および setImmediate() に似ています*/
関数 callIt(func) {
func();
}
Sloppy モードでメソッドを関数として呼び出すと、*this* はグローバル オブジェクトを指すため、後続の作成はすべてグローバル変数になります。
コードをコピー コードは次のとおりです:
var counter = {
count: 0,
// スロッピーモードメソッド
inc: function () {
this.count ;
}
}
callIt(counter.inc);
// 動作しませんでした:
console.log(counter.count); // 0
// 代わりに、グローバル変数が作成されました
// (NaN は未定義に適用した結果です):
console.log(count) // NaN
;
これを Strict モードで実行した場合、これは未定義であり、依然として望ましい結果は得られませんが、少なくとも次の警告が表示されます。
var counter = {
count: 0,
// Strict モードのメソッド
inc: function () {
'use strict';
this.count ;
}
}
callIt(counter. inc);
// TypeError: 未定義の
console.log(counter.count);
のプロパティ 'count' を読み取れません
期待どおりの結果を得るには、bind() を使用できます。
var counter = {
count: 0,
inc: function () {
this.count ;
}
}
callIt(counter.inc.bind(counter));
// うまくいきました!
コンソール .log(counter.count) // 1
bind() は、常に this の値を counter に設定する別の関数を作成します。
4.3 これを非表示にします
メソッド内で関数を使用するとき、その関数に独自の this があることを忘れがちです。これはメソッドとは異なるため、この 2 つを混合することはできません。詳細については、次のコードを参照してください:
var obj = {
name: 'Jane' ,
friends: [ 'Tarzan', 'Cheeta' ],
ループ: function () {
'use strict';
this.friends.forEach(
function ( friends) {
console.log(this.name ' know ' friends);
のプロパティ 'name' を読み取れません。
上記の例では、メソッドloop()のthisとは異なり、関数内のthisの値が未定義であるため、関数内のthis.nameは使用できません。この問題を解決するための 3 つのアイデアを以下に示します。
1. that=this、これを変数に代入して、これが明示的に表示されるようにします (それに加えて、self はこれを格納するために使用される非常に一般的な変数名でもあります)、その変数を使用します:
コードをコピー
this.friends.forEach(function (friend) {
console.log(that.name ' know ' friends);
});
}
2.バインド()。 bind() を使用して、this に渡したい値 (以下の例ではメソッドの this) が常に含まれる関数を作成します。
コードをコピー
}.bind(this));
}
3. forEach の 2 番目のパラメータを使用します。 forEach の 2 番目のパラメーターはコールバック関数に渡され、コールバック関数の this として使用されます。
loop: function () {
'厳密な使用 ';
this.friends.forEach(function (friend) {
console.log(this.name ' は友人を知っています);
}, this);
}
5. ベストプラクティス
理論的には、実際の関数には独自の this はないと思います。上記の解決策もこの考えに基づいています。 ECMAScript 6 は、この効果を実現するためにアロー関数を使用します。アロー関数は、独自の this を持たない関数です。このような関数では、暗黙的に存在するかどうかを気にする必要がなく、気軽に使用できます。
loop: function () {
'use strict ';
// forEach() のパラメータはアロー関数です
this.friends.forEach(friend => {
// `this` はループの `this`
console.log (この名前は友人を知っています);
});
}
一部の API がこれを実際の関数の追加パラメータとして扱うのが好きではありません:
beforeEach(function () {
this.addMatchers ({
toBeInRange: function (start, end) {
...
} });
暗黙的なパラメーターを明示的に記述して渡すと、コードが理解しやすくなり、これはアロー関数の要件と一致します。
beforeEach(api => { api。