これがどこにあるかに基づいてこれを理解するために、状況は大まかに3つのタイプに分けることができます。
1。関数では:これは通常、暗黙的なパラメーターです。
2。関数の外側(上部範囲内):ブラウザでは、これはグローバルオブジェクトを指します。 node.jsでは、モジュールのエクスポートを指します。
3。eval()に渡された文字列:eval()が直接呼び出された場合、これは現在のオブジェクトを指します。 eval()が間接的に呼ばれる場合、これはグローバルオブジェクトを指します。
これらのカテゴリに対して対応するテストを実施しました。
1。これは関数です
関数は基本的にJSの呼び出されたすべての構造を表すことができるため、これはこれが使用される最も一般的なシナリオであり、関数を次の3つの役割に細分化できます。
実際の機能
コンストラクタ
方法
1.1これは実際の関数で
実際の機能では、これの値は、それが配置されているコンテキストに依存するパターンです。
Sloppy Mode:これは、グローバルオブジェクト(ブラウザのウィンドウ)を指します。
コードコピーは次のとおりです。
function sloppyfunc(){
console.log(this ===ウィンドウ); // 真実
}
sloppyfunc();
厳密なモード:この値は未定義です。
コードコピーは次のとおりです。
関数strictfunc(){
「Strictを使用」;
console.log(this === undefined); // 真実
}
strictfunc();
これは関数の暗黙的なパラメーターであるため、その値は常に同じです。ただし、値を表示するためにcall()またはapply()メソッドを使用して、この値を定義できます。
コードコピーは次のとおりです。
function func(arg1、arg2){
console.log(this); // 1
console.log(arg1); // 2
console.log(arg2); // 3
}
func.call(1、2、3); //(this、arg1、arg2)
func.Apply(1、[2、3]); //(これ、arraywithargs)
1.2これはコンストラクターで
newを介してコンストラクターとして関数を使用できます。新しい操作は新しいオブジェクトを作成し、このオブジェクトをこれを介してコンストラクターに渡します。
コードコピーは次のとおりです。
varはこれを保存しました。
function const(){
savedthis = this;
}
var inst = new Const();
console.log(savedthis === inst); // 真実
JSでの新しい操作の実装の原則は、次のコードに大まかに示されています(より正確な実装については、この実装もより複雑です):
コードコピーは次のとおりです。
function newoperator(connt、arraywithargs){
var thisvalue = object.create(constr.prototype);
constr.apply(thisvalue、arraywithargs);
この値を返します。
}
1.3これはメソッドで
方法では、これの使用は、従来のオブジェクト指向言語でより多くなる傾向があります。これによって指摘されたレシーバー、つまりこの方法を含むオブジェクトです。
コードコピーは次のとおりです。
var obj = {
方法:function(){
console.log(this === obj); // 真実
}
}
obj.method();
2。これは範囲内です
ブラウザでは、スコープはグローバルスコープであり、これはこのグローバルオブジェクト(ウィンドウなど)を指します。
コードコピーは次のとおりです。
<スクリプト>
console.log(this ===ウィンドウ); // 真実
</script>
node.jsでは、通常、モジュールで関数を実行します。したがって、トップレベルの範囲は非常に特別なモジュール範囲です。
コードコピーは次のとおりです。
// `blobal`(not` window`)グローバルオブジェクトを参照してください。
console.log(math === global.math); // 真実
// `this`はグローバルオブジェクトを参照しません。
console.log(this!== global); // 真実
// `this`はモジュールのエクスポートを指します。
console.log(this === module.exports); // 真実
3。これはeval()で
eval()は、(call()などの他の手段で、関数名「eval '」を呼び出すことにより)または直接呼び出すことができます。詳細については、こちらをご覧ください。
コードコピーは次のとおりです。
//実際の関数
function sloppyfunc(){
console.log(eval( 'this')=== window); // 真実
}
sloppyfunc();
関数strictfunc(){
「Strictを使用」;
console.log(eval( 'this')=== undefined); // 真実
}
strictfunc();
//コンストラクター
varはこれを保存しました。
function const(){
savedthis = eval( 'this');
}
var inst = new Const();
console.log(savedthis === inst); // 真実
//メソッド
var obj = {
方法:function(){
console.log(eval( 'this')=== obj); // 真実
}
}
obj.method();
4。これに関連するトラップ
これに関連する3つのトラップについては、以下に紹介する必要があります。次の例では、Strictモードを使用するとコードのセキュリティが向上する可能性があることに注意してください。実際の機能では、この価値は未定義であるため、何かがうまくいかないときに警告が表示されます。
4.1新しい使用を忘れた
コンストラクターを呼び出すために新しい使用を使用していない場合、実際に実際の関数を使用しています。したがって、これはあなたが期待する価値ではありません。ずさんなモードでは、これはウィンドウを指し、グローバル変数を作成します。
コードコピーは次のとおりです。
関数ポイント(x、y){
this.x = x;
this.y = y;
}
var p = point(7、5); //新品を忘れます!
console.log(p === undefined); // 真実
//グローバル変数が作成されました:
console.log(x); // 7
console.log(y); // 5
ただし、Strictモードを使用している場合でも、警告が表示されます(this === undefined):
コードコピーは次のとおりです。
関数ポイント(x、y){
「Strictを使用」;
this.x = x;
this.y = y;
}
var p = point(7、5);
// typeRror:未定義のプロパティ「x」を設定できません
4.2不適切な使用方法
メソッドの値を直接取得する場合(呼び出すのではなく)、このメソッドを関数として使用しています。パラメーターとしてメソッドを関数または呼び出しメソッドに渡す場合、これを行う可能性が最も高くなります。これは、Settimeout()および登録イベントハンドラーの場合です。 CallIT()メソッドを使用して、このシナリオをシミュレートします。
コードコピーは次のとおりです。
/** settimeout()およびsetimmediate()に似ています*/
関数callit(func){
func();
}
スロッピーモードの関数としてメソッドを呼び出すと、 *これはグローバルオブジェクトを指しているため、作成されたものはグローバル変数になります。
コードコピーは次のとおりです。
var counter = {
カウント:0、
// Sloppy-Modeメソッド
inc:function(){
this.count ++;
}
}
callit(counter.inc);
//動作しませんでした:
console.log(counter.count); // 0
//代わりに、グローバル変数が作成されました
//(nanは未定義に++を適用する結果です):
console.log(count); // nan
これを厳格なモードで行うと、これは未定義であり、希望する結果は得られませんが、少なくとも警告が表示されます。
コードコピーは次のとおりです。
var counter = {
カウント:0、
// Strict-Modeメソッド
inc:function(){
「Strictを使用」;
this.count ++;
}
}
callit(counter.inc);
// typeRror:未定義のプロパティ「カウント」を読むことができません
console.log(counter.count);
期待される結果を取得するには、bind()を使用できます。
コードコピーは次のとおりです。
var counter = {
カウント:0、
inc:function(){
this.count ++;
}
}
callit(counter.inc.bind(counter));
//それはうまくいきました!
console.log(counter.count); // 1
bind()は、この値を常にカウンターに設定できる別の関数を作成します。
4.3これを隠します
メソッドで関数を使用する場合、その関数には独自の機能が無視されることがよくあります。これはメソッドとは異なるため、これら2つを一緒に混ぜることはできません。詳細については、次のコードを参照してください。
コードコピーは次のとおりです。
var obj = {
名前:「ジェーン」、
友達:['Tarzan'、 'Cheeta']、
ループ:function(){
「Strictを使用」;
this.friends.foreach(
function(friend){
console.log(this.name+'knows'+friend);
}
);
}
};
obj.loop();
// typeRror:未定義のプロパティ「名前」を読むことができません
この関数の値が未定義であり、これはメソッドループ()ではこれとは異なるため、上記の例の関数のnameは使用できません。以下は、この問題を解決するための3つのアイデアです。
1。これ=これは、これを変数に割り当てて、これが明示的に明示されるように(それを除いて、自己はこれを保存するために使用される非常に一般的な変数名でもあります)、その変数を使用します。
コードコピーは次のとおりです。
ループ:function(){
「Strictを使用」;
var that = this;
this.friends.foreach(function(friend){
console.log(that.name+'nowledge'+friend);
});
}
2。BIND()。 bind()を使用して関数を作成します。この関数には常に渡す値が含まれています(次の例では、この方法):
コードコピーは次のとおりです。
ループ:function(){
「Strictを使用」;
this.friends.foreach(function(friend){
console.log(this.name+'knows'+friend);
} .bind(this));
}
3。foreachの2番目のパラメーターを使用します。 foreachの2番目のパラメーターは、コールバック関数に渡され、コールバック関数のこれとして使用されます。
コードコピーは次のとおりです。
ループ:function(){
「Strictを使用」;
this.friends.foreach(function(friend){
console.log(this.name+'knows'+friend);
}、 これ);
}
5。ベストプラクティス
理論的には、実際の関数はこれ自体に属していないと思います。上記のソリューションは、このアイデアにも基づいています。 ECMAScript 6は、矢印関数を使用してこの効果を実現します。これは、独自の機能を持たない関数です。このような関数では、暗黙の存在があるかどうかを心配することなく、これを自由に使用できます。
コードコピーは次のとおりです。
ループ:function(){
「Strictを使用」;
// foreach()のパラメーターは矢印関数です
this.friends.foreach(friend => {
// `this`はループの` this`です
console.log(this.name+'knows'+friend);
});
}
これを実際の関数の追加パラメーターと見なすAPIの一部が好きではありません。
コードコピーは次のとおりです。
前に(function(){
this.addmatchers({
tobeinrange:function(start、end){
...
}
});
});
暗黙のパラメーターを明示的に渡すように書くと、コードは理解する方がよく見えます。これは、矢印関数の要件と一致しています。
コードコピーは次のとおりです。
前に(api => {
api.addmatchers({
tobeinrange(start、end){
...
}
});
});