59

「this」キーワードは関数内でどのように機能しますか? 、しかし、それが次のように答えているとは思いません。

このコードを考えると:

var MyDate = function(date) {
    this.date = date;
};

var obj1 = {
    foo: new Date(),
    bar: new MyDate(this.foo)  //  this.foo is undefined
};

var obj2 = {};
obj2.foo = new Date();
obj2.bar = new MyDate(this.foo);  //  this.foo is undefined

var obj3 = {
    foo: new Date(),
    bar: new MyDate(obj3.foo)
};

var obj4 = {};
obj4.foo = new Date();
obj4.bar = new MyDate(obj4.foo);

最初の 2 回は失敗するのに、最後の 2 回は成功するのはなぜですか? thisが現在のオブジェクト リテラルにバインドされていない場合、何バインドされていますか?

4

4 に答える 4

128

Javascript は遅延バインディング言語です。実際、バインディングは非常に遅いです。thisコンパイル時にバインドされないだけでなく、実行時にもバインドされません (他のほとんどの遅延バインディング言語が行うように)。JavaScript では、this呼び出し時にバインドされます。

バインディング ルールは他のほとんどの OO 言語とはかなり異なるため、javascript に慣れていない多くの人々を混乱させるようです。

this基本的に、コード内で使用する方法と場所は、 のthis動作に影響しません (スタンドアロン関数、オブジェクト リテラルなどであるかどうかは関係ありません)。値を決定するのthisは、関数の呼び出し方法です。

ルールは次のとおりです。

1 - 関数がコンストラクターとして呼び出されると、新しいオブジェクトが作成され、thisそのオブジェクトにバインドされます。例えば:

function Foo () {
    this.bar = 1; // when called with the new keyword
                  // this refers to the object just created
}
new Foo().bar;

2 - オブジェクト メソッドとして呼び出されたthis場合、メソッドが属するオブジェクトを参照します。基本的に、最後のドットの前の名前。例えば:

foo.bar = 1;
foo.baz = function () {
    alert(this.bar); // this refers to foo when called as foo.baz()
}
foo.baz();

3 - 関数の外で使用される場合、または関数がメソッドとして呼び出されない場合this、グローバル オブジェクトを参照します。JavaScript の仕様では、存在するということ以外にグローバル オブジェクトに名前を付けていませんが、ブラウザでは伝統的に と呼ばれていwindowます。例えば:

bar = 1;
alert(this.bar); // this refers to the global object
foo = {
    bar: this.bar // also global object
}
function foofoo () {
    alert(this.bar); // also refers to the global object
}
foofoo();

4 - イベント ハンドラー (onclick など) ではthis、イベントをトリガーした DOM 要素を参照します。setTimeoutまたはXMLHTTPRequest、やなどの DOM に関連付けられていないイベントの場合thisは、グローバル オブジェクトを参照します。例えば:

foo.bar = 1;
foo.baz = function () {
    alert(this.bar); // this would normally be foo but if this
                     // function is assigned to an event it would
                     // point to the element that triggered the event
}
somediv.bar = 2;
somediv.onclick = foo.baz; // clicking on somedive alerts 2 instead of 1

5 - 最後に、call()またはapply()メソッドのいずれかを使用して関数が呼び出されるとthis、何にでも再割り当てできます (google "mdn function.prototype.call")。このように、JavaScript の任意のオブジェクトは、別のオブジェクトのメソッドを借用/盗むことができます。例えば:

cat = {
    type: "cat",
    explain: function () {
        return "I am a " + this.type;
    }
}
dog = {
    type: "dog"
}
cat.explain.call(dog); // returns "I am a dog"

最新の JavaScript 実装でFunction.bind()は、別のルールがあります。

6 - メソッドを使用して、関数をthisオブジェクトに明示的にバインドすることもできますbind()。メソッドは、に渡された引数にバインドされている関数のbind新しいインスタンスを返します。例えば:thisbind

function explain () {
    return "I am a " + this.type;
}
dog = {
    type: "dog"
}
var dog_explain = explain.bind(dog);
dog_explain(); // returns "I am a dog"

ECMAscript 5 では、メソッドとして呼び出されないか、call または apply で呼び出されない関数で this の意味を変更する厳密モードが導入されたため、新しいルールを追加する必要があります。

7 - 厳密モードの場合this、グローバル オブジェクト (ブラウザーのウィンドウ) を参照できません。したがって、関数がメソッドとして呼び出されていない場合、またはorthisを介して手動で何かにバインドされていない場合は、次のようになります。callapplybindthisundefined

"use strict";
function foo () {
    return this;
}
foo(); // returns undefined instead of the global object

ECMAscript 6 でアロー関数が導入されました。アロー関数は、早期にバインドすることで、これがどのように動作するかを変更します。

8 - アロー関数でthisは、関数が宣言された時点でバインドされます。したがってthis、次のコードでは:

var x = () => {return this};

関数が次のコードのように宣言されているかのように動作します。

var x = function () {return this}.bind(this);

thisin アロー関数は関数が宣言された時点でバインドされるため、継承を使用する場合はアロー関数を使用できないことに注意してください。これthisは、関数内の が常に親オブジェクトを指し、子オブジェクトを指すことはないためです。つまり、矢印関数で継承を機能させる唯一の方法は、親オブジェクトのすべての矢印関数をオーバーライドすることです。

于 2012-11-18T15:40:11.093 に答える
14

関数とオブジェクト リテラルの主な違いを見落としている可能性があると思います。

関数が呼び出されるまで、関数の本体は評価されません。

つまり、 の値は、関数の呼び出し方法thisに依存します。オブジェクトのメソッドとして呼び出された場合 (例: )、関数本体内のそのオブジェクトを指します。スタンドアロン関数 ( ) として呼び出された場合。それらの本体のコードは、呼び出し元の環境にあるものをすべて継承します。しかし、いずれにせよ、関数が定義された時点での値はまったく問題になりません。someObj.someFunc()thissomeFunc()thisthis

一方、オブジェクト リテラルは単なる式です。ifthisが表示され、リテラル内に含まれる関数本体内にない場合this、その式が表示されるコード内のポイントの値になります。

于 2012-11-18T15:05:49.927 に答える
9

Javascript では、関数呼び出しのみが新しいthisコンテキストを確立します。を呼び出すとfoo.bar()bar関数内で;thisにバインドされます。fooを呼び出すとfoo()、その内部でthisにバインドされwindowます。オブジェクト リテラル コンストラクターはメソッド呼び出しではないため、影響はありませんthis。オブジェクトリテラルの外で参照していたものはすべて参照します。

于 2012-11-18T15:05:29.613 に答える
7

this.foothisすべての例でグローバルwindowオブジェクトを参照しているため、未定義です。また、 を試してもobj1.foo、式全体が評価されるまでプロパティが作成されないため、undefinedが返されます。代わりにこれを試してください:

var obj1 = {
    foo: new Date(),
    bar: function() {
        return new MyDate( this.foo ); // will work
    }
};

を呼び出すobj1.bar()までに、オブジェクトはそれまでに作成されているため、機能します。関数内にいるため、thisオブジェクトは現在のオブジェクトを参照します。

于 2012-11-18T15:05:56.060 に答える