252

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
}
4

6 に答える 6

564

私の別の投稿から共食いされた、これについてあなたが今まで知りたかった以上のものがあります.

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

this変数は関数に付けられます。関数を呼び出すたびに、関数の呼び出し方法に応じて、これに特定の値が与えられます。これは、多くの場合、呼び出しパターンと呼ばれます。

JavaScript で関数を呼び出す方法は 4 つあります。関数は、メソッド関数コンストラクターとして、およびapplyを使用して呼び出すことができます。

メソッドとして

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

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

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

関数として

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

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

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

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

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

thisを指す変数定義します。Closure (独自のトピック) はそれを維持するため、bar をコールバックとして呼び出しても参照は保持されます。

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

コンストラクターとして

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

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

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

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

一部の人々は、constructor/new キーワードは、クラスに似たものを作成する方法として、Java/従来の OOP プログラマーに投げ込まれた骨であると考えています。

Apply メソッドを使用

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

function foo(a,b){
    alert(a);
    alert(b);
    alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);
于 2008-09-25T15:52:39.677 に答える
35

関数呼び出し

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

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

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

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

whereAmI = "window";

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

...括弧付き - - はorfoo()と同等で、実質的にorと同じです。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の引数は引数として関数呼び出しに渡されますが、への追加の引数は 1 つだけ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
};

...オブジェクトを介して Function への参照にアクセスし、括弧を付けて呼び出すことは、 orobj.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たときに何らかの方法でその情報を提供する必要があります。これは、 orおよびクロージャーbazの最初の引数が作用する場所です。callapply

スコープチェーン

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参照を保持しているため、のスコープは「消えません」。bindbind

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

>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"
于 2008-09-25T22:24:16.897 に答える
9

これは定義された動作ですか? クロスブラウザセーフですか?

はい。はい。

どうしてこうなったのか、何か根拠があるのでしょうか...

の意味thisは非常に簡単に推測できます。

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

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

于 2008-09-25T15:38:25.577 に答える
4

ECMAScriptに関する素敵なチュートリアルを見つけました。

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

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

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

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

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

関数コンテキストの場合、関数呼び出しごとにこの値が異なる場合があります

Javascript-the-coreおよびChapter-3-thisを参照

于 2014-03-13T12:40:55.007 に答える
4

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

適切な説明については、Douglas Crockford による「JavaScript: The Good Parts」を参照してください。

于 2008-09-25T15:43:41.120 に答える