我见过How does "this" keyword work within a function?,但我没有看到它回答以下内容。
鉴于此代码:
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
未绑定到当前对象文字, 绑定到哪个?
答案 0 :(得分:92)
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
方法返回函数的新实例,其中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引入了严格模式,该模式在未被调用为方法或通过调用或应用调用的函数中更改此含义,因此我们必须添加新规则:
7 - 在严格模式下,不允许this
引用全局对象(浏览器中的窗口)。因此,当函数未被调用为方法或this
未通过call
或apply
或bind
手动绑定任何内容时,this
变为{{} 1}}:
undefined
更多更新:
ECMAscript 6引入了箭头功能。箭头函数通过提前绑定来改变其行为方式。
8 - 在箭头函数中,"use strict";
function foo () {
return this;
}
foo(); // returns undefined instead of the global object
在声明函数时被绑定。所以this
在以下代码中:
this
表现得好像该函数声明如下代码:
var x = () => {return this};
请注意,由于箭头函数中的var x = function () {return this}.bind(this);
在声明函数时被绑定,因此如果要使用继承,则不能使用箭头函数。这是因为函数中的this
将始终指向父对象,并且永远不会指向子对象。这意味着使用箭头函数进行继承的唯一方法是覆盖父对象的所有箭头函数。
答案 1 :(得分:8)
我认为你可能错过了函数和对象文字之间的关键区别:
在调用函数之前,不会计算函数体。
立即评估对象文字的正文。
定义函数时,this
不会绑定到与您定义的函数相关的任何内容。但是当函数被称为时,它被绑定到作为方法调用函数的对象。
答案 2 :(得分:7)
在Javascript中,只有函数调用才能建立新的this
上下文。当您致电foo.bar()
时,在bar
函数中,this
将绑定到foo
;当您致电foo()
时,this
内的window
将被绑定到this
。对象文字构造函数不是方法调用,因此它不会以任何方式影响{{1}};它仍将引用它在对象文字之外引用的任何内容。
答案 3 :(得分: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
对象将引用当前对象。