「this」キーワードは関数内でどのように機能しますか?


248

JavaScriptで興味深い状況に遭遇しました。オブジェクトリテラル表記を使用して複数のオブジェクトを定義するメソッドを持つクラスがあります。これらのオブジェクト内では、thisポインターが使用されています。プログラムの動作から、thisポインタはメソッドが呼び出されたクラスを参照しており、リテラルによって作成されているオブジェクトを参照していないと推定しました。

これは私には期待できる方法ですが、これは恣意的です。これは定義された動作ですか?ブラウザ間で安全ですか?それが「仕様がそう言っている」ことを超えている理由である根本的な理由はありますか(たとえば、それはいくつかのより広範な設計決定/哲学の結果ですか)?削減されたコード例:

// inside class definition, itself an object literal, we have this function:
onRender: function() {

    this.menuItems = this.menuItems.concat([
        {
            text: 'Group by Module',
            rptletdiv: this
        },
        {
            text: 'Group by Status',
            rptletdiv: this
        }]);
    // etc
}

私もこれを行うと発生します var signup = { onLoadHandler:function(){ console.log(this); return Type.createDelegate(this,this._onLoad); }, _onLoad: function (s, a) { console.log("this",this); }};
Deeptechtons


この投稿をチェックしてください。このキーワードのさまざまな使用法と動作について説明があります。
Love Hasija 2017

このリンクをチェックアウトscotch.io/@alZami/understanding-this-in-javascript
AL-

回答:


558

私の他のポストから共食い、ここにあなたが知っていると思った以上だこの

始める前に、Javascriptについて覚えておくべき最も重要なことと、それが意味をなさない場合に繰り返します。Javascriptにはクラスがありません(ES6 class構文シュガーです)。何かがクラスのように見える場合、それは巧妙なトリックです。JavaScriptにはオブジェクト関数があります。(これは100%正確ではありません。関数は単なるオブジェクトですが、それらを個別のものと考えると役立つ場合があります)

この変数は、関数に装着されています。あなたが関数を起動するたびに、これはあなたが機能を呼び出す方法に応じて、一定の値を与えています。これは、しばしば呼び出しパターンと呼ばれます。

JavaScriptで関数を呼び出す方法は4つあります。あなたはとしての機能を呼び出すことができる方法として、機能として、コンストラクタ、およびと適用されます

方法として

メソッドは、オブジェクトにアタッチされる関数です

var foo = {};
foo.someMethod = function(){
    alert(this);
}

メソッドとして呼び出されると、これは関数/メソッドが含まれるオブジェクトにバインドされます。この例では、これはfooにバインドされます。

機能として

スタンドアロン関数がある場合、this変数は「グローバル」オブジェクトにバインドされます。ほとんどの場合、ブラウザのコンテキストではウィンドウオブジェクトです。

 var foo = function(){
    alert(this);
 }
 foo();

これが原因である可能性がありますが、気分を害しないでください。多くの人々はこれを悪い設計決定と考えています。コールバックはメソッドとしてではなく関数として呼び出されるため、一貫性のない動作のように見えるのはこのためです。

多くの人々は、ええと、このようなことをすることで問題を回避します

var foo = {};
foo.someMethod = function (){
    var that=this;
    function bar(){
        alert(that);
    }
}

あなたは、変数を定義することを指すこれを。クロージャー(それ自体がすべてのトピック)はそれを保持ます。そのため、コールバックとしてbarを呼び出しても、参照は保持されます。

注:use strictモードで関数として使用した場合、thisグローバルにバインドされません。(それはですundefined)。

コンストラクターとして

関数をコンストラクターとして呼び出すこともできます。あなたが使用している命名規則(TestObject)に基づいて、これはあなたがしていることかもしれませんし、あなたをつまずかせているものであります

newキーワードを使用して、コンストラクターとして関数を呼び出します。

function Foo(){
    this.confusing = 'hell yeah';
}
var myObject = new Foo();

コンストラクターとして呼び出されると、新しいオブジェクトが作成され、これがそのオブジェクトにバインドされます。繰り返しますが、内部関数があり、それらがコールバックとして使用されている場合は、それらを関数として呼び出します。これはグローバルオブジェクトにバインドされます。var that = this trick / patternを使用します。

一部の人々は、コンストラクター/新しいキーワードは、クラスに似たものを作成する方法としてJava /伝統的なOOPプログラマーに投げかけられた骨であると考えています。

Applyメソッドを使用

最後に、すべての関数には「適用」という名前のメソッドがあります(そうです、関数はJavaScriptではオブジェクトです)。Applyを使用すると、これの値を決定でき、引数の配列を渡すこともできます。これは役に立たない例です。

function foo(a,b){
    alert(a);
    alert(b);
    alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);

8
注:厳格モードthisundefined、関数呼び出し用です。
Miscreant 2015

1
関数宣言。function myfunction(){}は、「this」がグローバルスコープ(ウィンドウ)である「メソッドとして」の特殊なケースです。
リチャード2015

1
@richard:strictモードを除き、thisスコープとは関係ありません。あなたはグローバルオブジェクトを意味します
TJクラウダー2016

@ alan-storm。「コンストラクタとして」の場合とthis.confusing = 'hell yeah';同じvar confusing = 'hell yeah';?したがって、どちらも許可されmyObject.confusingますか?this内部作業用のプロパティやその他の変数を作成するために使用できるようにするだけでなく、それは素晴らしいことです。
wunth

しかし、もう一度、機能するものは関数の外で実行でき、値はコンストラクターに渡されるとfunction Foo(thought){ this.confusing = thought; }var myObject = new Foo("hell yeah");
思います。

35

関数呼び出し

関数はオブジェクトの一種です。

すべてのFunctionオブジェクトには、呼び出されたFunctionオブジェクトを実行するcallメソッドとapplyメソッドがあります

呼び出されると、これらのメソッドの最初の引数thisは、関数の実行中にキーワードによって参照されるオブジェクトを指定します。nullまたはundefined、またはの場合、グローバルオブジェクトwindowはに使用されthisます。

したがって、関数を呼び出す...

whereAmI = "window";

function foo()
{
    return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}

...括弧付き- foo()-と同等であるfoo.call(undefined)か、foo.apply(undefined)である、効果的に同じfoo.call(window)又はfoo.apply(window)

>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"

への追加の引数callは、関数呼び出しへの引数として渡されますが、への単一の追加applyの引数は、関数呼び出しの引数を配列のようなオブジェクトとして指定できます。

したがって、またはfoo(1, 2, 3)と同等です。foo.call(null, 1, 2, 3)foo.apply(null, [1, 2, 3])

>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"

関数がオブジェクトのプロパティである場合...

var obj =
{
    whereAmI: "obj",
    foo: foo
};

...オブジェクトを介して関数への参照にアクセスし、括弧でそれを呼び出す- obj.foo()-と同等ですfoo.call(obj)foo.apply(obj)

ただし、オブジェクトのプロパティとして保持されている関数は、それらのオブジェクトに「バインド」されていません。obj上記の定義からわかるように、関数は単なるオブジェクトのタイプなので、参照することができます(したがって、関数呼び出しへの参照によって渡すことも、関数呼び出しからの参照によって返すこともできます)。関数への参照が渡されると、それがどこから渡されたのかについての追加情報は伝達されません。そのため、次のことが起こります。

>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"

関数リファレンスbazへの呼び出しは、呼び出しのコンテキストを提供しないため、と実質的に同じなbaz.call(undefined)ので、this最終的にを参照しますwindowbazそれがに属していることを知りたいobj場合bazは、呼び出されたときに何らかの方法でその情報を提供する必要があります。これは、callor applyとクロージャの最初の引数が機能する場所です。

スコープチェーン

function bind(func, context)
{
    return function()
    {
        func.apply(context, arguments);
    };
}

関数が実行されると、新しいスコープが作成され、それを囲むスコープへの参照が含まれます。匿名の関数が上記の例で作成されたとき、それはそれが作成されたスコープへの参照を持っています、それはbindのスコープです。これは「クロージャー」として知られています。

[global scope (window)] - whereAmI, foo, obj, baz
    |
    [bind scope] - func, context
        |
        [anonymous scope]

変数にアクセスしようとすると、この「スコープチェーン」をたどって、指定された名前の変数が見つかります。現在のスコープに変数が含まれていない場合は、チェーン内の次のスコープを調べます。グローバルスコープ。無名関数が返されてbind実行が終了しても、無名関数はまだbindのスコープへの参照を持っているため、bindのスコープは「消えません」。

上記のすべてを考慮すると、次の例でスコープがどのように機能するか、および特定の値で「事前結合」の周りに関数を渡す手法thisが呼び出されたときに機能する理由を理解できるはずです。

>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"

「関数への参照が渡されると、それがどこから渡されたかについての追加情報は一緒に運ばれません @insinに感謝します。
Alex Marandon

9

これは定義された動作ですか?ブラウザ間で安全ですか?

はい。はい。

それがそうである理由である根本的な理由はありますか...

の意味thisを推測するのは非常に簡単です。

  1. 場合はthis、コンストラクタ関数の内部で使用され、関数がで呼び出されたnewキーワード、this作成されるオブジェクトを参照します。thisパブリックメソッドでもオブジェクトを意味し続けます。
  2. thisネストれた保護れた関数を含む他の場所で使用される場合、グローバルスコープを参照します(ブラウザーの場合はウィンドウオブジェクトです)。

2番目のケースは明らかに設計上の欠陥ですが、クロージャーを使用することで簡単に回避できます。


4

この場合、内部thisthis外部関数の変数ではなくグローバルオブジェクトにバインドされます。言語の設計方法です。

詳しい説明については、ダグラス・クロックフォードの「JavaScript:良い部品」を参照してください。


4

ECMAScriptに関する素晴らしいチュートリアルを見つけました

this値は、実行コンテキストに関連する特別なオブジェクトです。したがって、コンテキストオブジェクト(つまり、実行コンテキストがアクティブ化されているオブジェクト)として名前を付けることができます。

コンテキストのこの値として、任意のオブジェクトを使用できます。

この値は、実行コンテキストのプロパティですが、変数オブジェクトのプロパティではありません。

変数とは逆に、この値は識別子解決プロセスに関与しないため、この機能は非常に重要です。つまり、コードでこれにアクセスする場合、その値は実行コンテキストから直接取得され、スコープチェーンのルックアップはありません。この値は、コンテキストに入るときに一度だけ決定されます。

グローバルコンテキストでは、this値はグローバルオブジェクト自体です(つまり、この値は変数オブジェクトと同じです)。

関数コンテキストの場合、すべての単一の関数呼び出しのこの値は異なる場合があります

参考Javascriptを-コアおよび章-3-この


" グローバルコンテキストでは、this値はグローバルオブジェクト自体です(つまり、この値は変数オブジェクトと同じです) "。グローバルオブジェクトは、(ES4)「変数オブジェクト」とES5環境レコードがあるとして、グローバル実行コンテキストの一部です。しかし、それらはグローバルオブジェクトとは異なるエンティティです(たとえば、環境レコードは直接参照できません。仕様では禁止されていますが、グローバルオブジェクトは参照できます)。
RobG 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.