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);

为什么前两次尝试失败,但后两次成功?如果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 元素。or 对于与 DOM 不相关的事件,例如setTimeoutor XMLHTTPRequestthis指的是全局对象。例如:

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()orapply()方法调用函数时,this可以将其重新分配给任何东西(谷歌“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"

Function.bind()现代 javascript 实现中,我们现在有另一个规则:

6 - 函数也可以使用该方法显式绑定this到对象。bind()bind方法返回this绑定到传递给的参数的函数的新实例bind。例如:

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 引入了严格模式,它改变了函数中 this 的含义,这些函数不是作为方法调用或通过 call 或 apply 调用的,因此我们必须添加一个新规则:

7 - 在严格模式下,this不允许引用全局对象(浏览器中的窗口)。因此,当一个函数没有作为方法调用或没有通过orthis手动绑定到任何东西时,然后变为:callapplybindthisundefined

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

ECMAscript 6 引入了箭头函数。箭头函数通过提前绑定来改变 this 的行为方式。

8 - 在箭头函数中,this在函数声明时绑定。所以this在下面的代码中:

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

就好像函数被声明为如下代码:

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

Note that since the this in arrow functions are bound at the time the function is declared you can't use arrow functions if you want to use inheritance. That's because the this in the function will always point to the parent object and will never point to the child object. That means that the only way to make inheritance work with arrow function is to override all arrow functions from the parent object.

于 2012-11-18T15:40:11.093 回答
14

我认为您可能遗漏了函数和对象字面量之间的关键区别:

在调用函数之前,不会评估函数的主体。

这意味着 的值this取决于函数的调用方式如果它作为对象上的方法调用(例如someObj.someFunc()),this则将指向函数体内的该对象。如果它被称为独立函数 ( someFunc())。他们身体中的代码将继承this调用者环境中的任何内容。但无论哪种方式,定义this函数时的值都无关紧要。

而对象字面量只是一个表达式;如果this出现,并且它不在文字中包含的函数体内,它只是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.foo未定义,因为在您的所有示例中,this都指的是全局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 回答