ヒント
まず第一に、私はこの記事が退屈であり、JSではこれに過ぎないことを知っています。そして、この部分を書いている何千もの記事があります。
しかし、私はまだJSでこれに関する記事を書きたいと思っています。これは要約と見なすことができます。 (神々は私の他の記事を読むことができます)
JSでは、このコンテキストは常に予測不可能であり、しばしばバグは常に混乱しています。実際、さまざまな状況下で実行する方法を明確に区別する限り、それは問題ありません。
グローバル実行
まず、これがグローバル環境にあるものを見てみましょう。
初め。ブラウザ:
console.log(this); // window {speechsynthesis:speechsynthesis、caches:cachestorage、localstorage:storage、sessionstorage:storage、webkitstorageinfo:deprecatedstorageinfo…}}ウィンドウオブジェクトが印刷されていることがわかります。
2番。ノード:
console.log(this); // global
グローバルオブジェクトが印刷されていることがわかります。
概要:グローバル範囲では、現在のグローバルオブジェクト(ブラウザのウィンドウ、ノードのグローバル)を実行します。
関数で実行します
純粋な関数呼び出し
これは、関数を使用する最も一般的な方法です。
function test(){console.log(this);}; test(); // window {speechsynthesis:speechsynthesis、caches:cachestorage、localstorage:storage、sessionstorage:storage、webkitstorageinfo:deprecatedstorageinfo…}関数が直接呼び出されると、グローバルコールに属し、この時点でこれがグローバルオブジェクトを指していることがわかります。
Strict Mode「Strictを使用」;
純粋な関数呼び出しが厳密なモードで実行される場合、これはグローバルを指すのではなく、未定義です。これは、JSのいくつかの非老朽化した動作を排除することです。
'strict'; function test(){console.log(this);}; test(); // undefinedもちろん、それを即時の実行機能に置く方が良いでしょう。世界的な状況を汚染することを避けます。
(function(){"strict"; console.log(this);})(); // undefinedメソッド呼び出しとしてオブジェクト
関数がオブジェクトのメソッドと呼ばれる場合:
var obj = {name: 'qiutc'、foo:function(){console.log(this.name); }} obj.foo(); // 'qiutc'この時点で、これは現在のオブジェクトを指します。
もちろん、私たちはこれを行うことができます:
function test(){console.log(this.name);} var obj = {name: 'qiutc'、foo:test} obj.foo(); // 'qiutc' 'また、JSではすべてがオブジェクトであり、関数もオブジェクトであるため、変更されていません。テストの場合、これは単なる関数名であり、この関数を指す関数への参照です。 foo =テストの場合、fooはこの関数も指しています。
オブジェクトのメソッドを変数に割り当ててから、この変数を直接呼び出すとどうなりますか。
var obj = {name: 'qiutc'、foo:function(){console.log(this); }} var test = obj.foo; test(); // windowこれは現時点でグローバルな世界を実行していることがわかります。 test = obj.fooを配置すると、テストは関数への参照を直接指します。現時点では、実際にはOBJオブジェクトとは何の関係もないため、通常の関数として直接呼ばれるため、これはグローバルオブジェクトを指しています。
いくつかの落とし穴
コールバック関数のいくつかの落とし穴にしばしば遭遇します。
var obj = {name: 'qiutc'、foo:function(){console.log(this); }、foo2:function(){console.log(this); setimeout(this.foo、1000); }} obj.foo2();このコードを実行した後、印刷物は互いに異なることがわかります。
初めてこれをFOO2に直接印刷することで、ここでOBJオブジェクトを指して、疑いの余地はありません。
ただし、This.fooはsetimeoutで実行され、グローバルオブジェクトに向かっています。ここでは機能方法として使用されていませんか?これはしばしば多くの初心者を困惑させます。
実際、SettimeOutは単なる関数であり、関数にはパラメーターが必要になる場合があります。これをsettimeout関数にパラメーターとして渡します。楽しいパラメーターが必要なように。パラメーターを渡すとき、実際にそのような操作fun = this.fooを行いました。ないことを見て、私たちはthis.fooの参照を直接楽しむことを指摘します。実行すると、Fun()が実際に実行されるため、OBJとは何の関係もありません。これは通常の関数として直接呼ばれるため、これはグローバルオブジェクトを指します。
この問題は、多くの非同期コールバック関数で一般的に発生します。
解決する
この問題を解決するために、閉鎖機能を使用して対処できます。
var obj = {name: 'qiutc'、foo:function(){console.log(this); }、foo2:function(){console.log(this); var _this = this; setimeout(function(){console.log(this); // window console.log(_this); // object {name: "qiutc"}}、1000); }} obj.foo2();これを直接使用することはまだウィンドウであることがわかります。 Foo2でこれがOBJを指しているため、最初に変数_を使用して保存し、次にコールバック関数で_を使用して現在のオブジェクトを指すことができます。
SetimeOutの別のピット
前述のように、コールバック関数がバインディングスコープなしで直接実行される場合、このポイントはグローバルオブジェクト(ウィンドウ)に固定されています。ただし、SettimeOutのコールバック関数は、厳密なモードで異なることを示しています。
'strict'; function foo(){console.log(this);} setimeout(foo、1); // window論理的に言えば、厳密なモードを追加しましたが、Foo Callはこれを指定していないため、未定義である必要がありますが、ここにはまだグローバルオブジェクトがあります。厳密なモードが失敗したからでしょうか?
いいえ、厳密なモードでさえ、SettimeOutメソッドが着信関数を呼び出す場合、関数がこれを指定しない場合、それは暗黙的な操作を行います - foo()の代わりにfoo.apply(ウィンドウ)を呼び出すのと同等のグローバルコンテキストを自動的に挿入します。
もちろん、関数を渡すときに既にこれを指定している場合、setimeout(foo.bind(obj)、1);など、グローバルオブジェクトに注入されません。
コンストラクターとして使用します
JSでは、クラスを実装するには、いくつかのコンストラクターを定義する必要があります。コンストラクターを呼び出すときは、新しいキーワードを追加する必要があります。
function person(name){this.name = name; console.log(this);} var p = new person( 'qiutc'); // person {name: "qiutc"}コンストラクターと呼ばれると、このコンストラクターが呼び出されたときにインスタンス化されたオブジェクトを指すことがわかります。
もちろん、コンストラクターは実際には関数です。通常の関数として実行すると、これはグローバルに実行されます。
function person(name){this.name = name; console.log(this);} var p = person( 'qiutc'); // window違いは、関数(新しい)を呼び出す方法です。
矢印関数
新しいES6仕様では、矢印関数が追加されています。通常の機能と最も異なることは、このポインティングです。閉鎖を使用してこのポインティングの問題を解決したことを覚えていますか?矢印関数を使用すると、より完全に解決できます。
var obj = {name: 'qiutc'、foo:function(){console.log(this); }、foo2:function(){console.log(this); setimeout(()=> {console.log(this); // object {name: "qiutc"}}、1000); }} obj.foo2();ご覧のとおり、SettimeOutによって実行された関数では、ウィンドウに印刷されるはずですが、これはここでOBJを指し示しています。その理由は、SettimeOutに渡された関数(パラメーター)が矢印関数であるためです。
関数本文のこのオブジェクトは、使用されるオブジェクトではなく、定義されたオブジェクトです。
例に基づいて、この文を理解しましょう。
OBJ.FOO2()が実行されると、電流はOBJを指します。 SettimeOutを実行するとき、最初に匿名の矢印関数を定義します。キーポイントはここにあります。この矢印関数を定義するときにこれが実行される矢印関数のオブジェクトは、この矢印関数を定義するときにスコープでこれに指し示されます。つまり、これはobj.foo2、つまりobjです。したがって、矢印関数を実行するとき、obj.foo2-> objのこの - > obj;
簡単に言えば、これは矢印関数では、それを定義するときの範囲内でのみこれに関連しており、それがどこでどのように呼ばれるかとは何の関係もありません。同時に、このポインティングは不変です。
呼び出し、適用、縛ります
JSでは、関数もオブジェクトであり、いくつかの方法もあります。ここでは、3つの方法を紹介します。このポインターを関数に変更できます。
電話
fun.call(thisarg [、arg1 [、arg2 [、...]]]))
すぐに機能を実行します。最初のパラメーターは、実行関数のこのコンテキストを指定し、その後のパラメーターは実行関数に渡す必要があるパラメーターです。
適用する
fun.apply(thisarg [、[arg1、arg2、...]]))
すぐに機能を実行します。最初のパラメーターは、実行関数のこのコンテキストを指定し、2番目のパラメーターはアレイです。これは実行関数に渡されるパラメーターです(呼び出しとの差)。
バインド
var foo = fun.bind(thisarg [、arg1 [、arg2 [、...]]]);
関数は実行されませんが、新しい関数を返します。この新しい関数は、このコンテキストを指定し、その後のパラメーターは、関数を実行するために渡す必要があるパラメーターです。
これらの3つの関数は実際には似ています。全体的な目的は、関数(これ)のコンテキストを指定することです。コール関数を例として見てみましょう。
通常の関数にこれを指定します
var obj = {name: 'qiutc'}; function foo(){console.log(this);} foo.call(obj); // object {name: "qiutc"}foo.call(obj)を実行すると、関数のこれがOBJオブジェクトを指していることがわかります。
オブジェクト内のメソッドにこれを指定します
var obj = {name: 'qiutc'、foo:function(){console.log(this); }} var obj2 = {name: 'tcqiu222222'}; obj.foo.call(obj2); // object {name: "tcqiu222222"}関数を実行すると、これがOBJ2を指していることがわかります。これは成功しています。
コンストラクターにこれを指定します
function person(name){this.name = name; console.log(this);} var obj = {name: 'qiutc22222222'}; var p = new person.call(obj、 'qiutc'); // constructer:person.callはコンストラクターではありません(…)ここでエラーが報告されました。なぜなら、私たちは人ではなく新しい人に行ったため、ここでの関数はコンストラクターではありません。
バインドして試してみてください:
function person(name){this.name = name; console.log(this);} var obj = {name: 'qiutc22222222'}; var person2 = person.bind(obj); var p = new person2( 'qiutc'); // person {name: "qiutc"} console.log(obj);印刷されているのは、OBJとは関係のない人によってインスタンス化されたオブジェクトであり、OBJは変更されておらず、有効にすることなくこのコンテキストを人に指定することを示しています。
したがって、BINDを使用してコンストラクターにこれを指定すると結論付けることができます。新しいコンストラクターの場合、これはBIND関数によって指定されません。
もちろん、Bindはこれを指定するだけでなく、パスパラメーターも指定できます。この操作を試してみましょう:
function person(name){this.name = name; console.log(this);} var obj = {name: 'qiutc22222222'}; var person2 = person.bind(obj、 'qiutc111111'); var p = new person2( 'qiutc'); // person {name: "qiutc11111"}}}ご覧のとおり、これを指定することは機能しませんが、着信パラメーターは依然として機能します。
矢印関数にこれを指定します
グローバルの下で矢印関数を定義しましょう。これにより、この矢印関数のこれは、必然的にグローバルオブジェクトを指します。コールメソッドを使用してこれが変更された場合はどうなりますか:
var afoo =(a)=> {console.log(a); console.log(this);} afoo(1); // 1 // windowvar obj = {name: 'qiutc'}; afoo.call(obj、2); // 2 // windowご覧のとおり、ここでこれを指しているコールの操作は成功していなかったため、矢印関数でこれがすでに決定されているときにすでに決定されていると結論付けることができます(これを定義する範囲で実行します)。 (呼び出し、適用、バインド)などの操作は、これを変更することはできません。
矢印関数は良好で、これは変わらないことを忘れないでください。
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。