Javascript 中的“this”关键字如何在对象字面量中起作用?

IT技术 javascript
2021-01-11 01:03:43

我已经看到“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个回答

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 元素。或者对于与 DOM 无关的事件,例如setTimeoutXMLHTTPRequestthis指的是全局对象。例如:

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可以将其重新分配给任何东西(谷歌“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 引入了严格模式,它改变了不是作为方法调用或通过 call 或 apply 调用的函数中 this 的含义,因此我们必须添加新规则:

7 - 在严格模式下,this不允许引用全局对象(浏览器中的窗口)。因此,当函数未作为方法调用或未this通过call手动绑定到任何内容时applybindthis变为undefined

"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函数中的 将始终指向父对象,而永远不会指向子对象。这意味着使继承与箭头函数一起工作的唯一方法是覆盖父对象的所有箭头函数。

@SébastienGarcia-Romeo:那么答案就看情况了。仔细阅读上面的答案。在这种情况下,您this如何编写函数或如何声明函数根本无关紧要重要的是你如何调用你的函数。asyncFunction(obj1.obj2.fn)并且this在严格模式下将是全局对象或未定义。asyncFunction(function(){obj1.obj2.fn()})然后this将是obj2asyncFunction(obj1.obj2.fn.bind(mango))然后this将是mango
2021-03-10 01:03:43
修改了答案,忘记了何时this在函数之外使用的情况
2021-03-13 01:03:43
在第 5 项中,你能解释thisin的绑定return "I am a " + this.type;是从哪里产生的吗?
2021-03-14 01:03:43
@lanzz:啊忘了。实际上,即使在 IE 上,它通常也指目标
2021-03-21 01:03:43
thiswindow仅在 IE 浏览器中的事件处理程序中指代在所有其他浏览器中,this指的是事件的目标。
2021-04-08 01:03:43

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

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

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

而对象字面量只是一个表达式;如果this出现,并且它不在文字中包含的函数体内,它只是this在代码中出现该表达式的点处的值

在 Javascript 中,只有函数调用才能建立新的this上下文。当您调用 时foo.bar(),在bar函数内,this将绑定到foo; 当你调用的时候foo(),里面this会绑定到window. 对象字面量构造函数不是方法调用,因此它不会this以任何方式影响它仍然会引用它在对象文字之外引用的任何内容。

this.foo未定义,因为在您的所有示例中,this都指的是全局window对象。此外,即使您尝试了obj1.foo,它仍然会返回 undefined ,因为在评估整个表达式之前尚未创建该属性。试试这个:

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

之所以有效,是因为在您调用 时obj1.bar(),该对象将在那时创建;并且因为您在函数中,该this对象将引用当前对象。