Javascript中的“this”关键字如何在对象字面值内运行?
我见过“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
不是绑定到当前的对象字面量,它是什么绑定?
Javascript是一个晚期绑定语言。 事实上,这是非常晚的约束力。 这不仅在编译期间没有被绑定,而且在运行时也没有被绑定(就像大多数其他的后期绑定语言一样)。 在JavaScript中, this
是在通话时绑定的。
绑定规则与大多数其他OO语言完全不同,这就是为什么它混淆了很多不熟悉javascript的人。
基本上,在代码中如何以及在何处使用它并不影响this
行为方式(不pipe它是独立函数还是对象文字等都没有关系),这决定了函数的价值。
规则是:
1 – 当一个函数被调用为构造函数时,会创build一个新的对象并将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元素。 或者,对于不像DOM或XMLHTTPRequest
this
与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()
方法call()
函数时,可以将其重新分配给任何东西(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 – 函数也可以使用bind()
方法显式地将其绑定到一个对象。 bind
方法返回一个新的函数实例,绑定到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
函数没有通过call
或者apply
或bind
手动bind
那么this
就变成了undefined
:
"use strict"; function foo () { return this; } foo(); // returns undefined instead of the global object
更多更新:
ECMAscript 6引入了箭头函数。 箭头函数通过早期绑定来改变这种行为。
8 – 在箭头函数中, this
是在声明函数时绑定的。 所以在下面的代码中:
var x = () => {return this};
就像这个函数声明如下代码一样:
var x = function () {return this}.bind(this);
请注意,由于箭头函数中的this
在声明函数时被绑定,因此如果要使用inheritance,则不能使用箭头函数 。 这是因为函数中的this
将始终指向父对象,并且永远不会指向子对象。 这意味着使用箭头函数inheritance工作的唯一方法是覆盖父对象的所有箭头函数。
在Javascript中,只有函数调用build立一个新的上下文。 当你调用foo.bar()
,在bar
函数内, this
会被绑定到foo
; 当你调用foo()
,它会被绑定到window
。 一个对象的文字构造函数不是一个方法调用,所以不会以任何方式影响this
; 它仍然会引用它在对象字面之外引用的内容。
我想你可能会错过函数和对象文字之间的关键区别:
在函数被调用之前,函数的主体不被评估。
对象文本的主体立即被评估。
当你定义一个函数时, this
函数不会绑定到与你定义的函数相关的任何东西上。 但是在函数被调用的时候 ,它被绑定到函数被调用的对象上。
this.foo
是未定义的,因为在所有的例子中, this
是指全局window
对象。 此外,即使您尝试使用obj1.foo
,它仍然会返回未定义的,因为该属性尚未创build,直到评估完整个expression式。 试试这个:
var obj1 = { foo: new Date(), bar: function() { return new MyDate( this.foo ); // will work } };
它的工作原理是当你调用obj1.bar()
,对象将被创build, 因为你在一个函数中, this
对象将引用当前对象。