【问题标题】:Why does `(object.fun = object.fun)() use `window` but (object.fun)() uses `object` for `this`?为什么 `(object.fun = object.fun)() 使用 `window` 而 (object.fun)() 使用 `object` 作为 `this`?
【发布时间】:2016-06-23 13:01:00
【问题描述】:

在《Professional JavaScript for Web Developers》第 3 版一书中说:

var name = "The window";

var object = {
    name: "My object",

    getName: function() {
        return this.name;
    }
}

object.getName();   //"My object"
(object.getName)(); //"My object"
(object.getName = object.getName)(); //"The window"

问题是为什么(object.getName)(); 会打印"My object" 而不是像第三个那样打印"The window"

我认为object.getName 应该返回一个指向函数的指针,就像第三个一样。 this 应指向全局对象,在 Web 浏览器中为 window。因此它应该打印"The window"。然而,object.getName 似乎返回 object.getName 本身。为什么?

【问题讨论】:

  • @PraveenKumar:这不是骗人的。 WenqiMa 在问为什么 3 次调用中的第二次给出“我的对象”而不是像第三次那样给出“窗口”。
  • 感谢@squint 编辑...
  • 赋值运算符(=)的结果就是设定值。该表达式在 将其重新分配给对象后返回函数本身。由于this 值取决于函数的调用方式,因此this 值在这种情况下是指全局对象。
  • 谢谢@squint。这正是我想知道的。
  • @Vohuman - 为什么不将其发布为答案?在一些笨蛋复制你的评论之前快点做。

标签: javascript


【解决方案1】:

赋值运算符(=)的结果就是设定值。考虑这个例子:

> var num;
> num = 3;
< 3 // the returned value of the assignment

表达式在将函数重新分配给对象后返回函数本身。由于this 值取决于函数的调用方式,因此this 值在这种情况下指的是全局对象。在第二个 sn-p ((object.getName)();) 中,该函数作为 object 对象的成员调用,这就是为什么 this 引用 object

【讨论】:

  • 简短而正确。 = 运算符的返回值是未绑定的函数,这导致了这里的混乱。
【解决方案2】:

更令人困惑的部分实际上是关于第三个例子,但让我回答你关于第二个例子的具体问题:

问题是为什么(object.getName)(); 会打印"My object" 而不是像第三个那样打印"The window"

想想 JavaScript 如何解析你的代码片段:

object // This is the reference to the object you've made. No surprises here

object.getName // A reference to the function `getName`, with `this` bound to `object`
(object.getName) // This is the same thing as #2. The parens aren't important here

object.getName(); // Invoking the function, as you normally would
(object.getName)(); // Strange syntax, but just invoking the function all the same

但是当“窗口”被打印出来的时候呢?

这是更令人困惑的部分。

(object.getName = object.getName)(); // Things start to go strange here

这部分是因为赋值 (=) 运算符,也是因为 JavaScript function binding (this keyword) is lost after assignment

当您引用函数对象然后将该引用分配给任何内容时,它会作为“值类型”而不是“引用类型”传递。这会删除 this 指针,除非您手动将其绑定回来。见the documentation for the this keyword, when calling a function as an object method:

当函数作为对象的方法被调用时,其this 被设置为调用该方法的对象。

通过修改您的示例,这意味着什么可能会变得更加清楚:

var name = "The window";

var object = {
    name: "My object",

    getName: function() {
        console.log(this); // See what `this` is bound to
        return this.name;
    }
}

object.getName(); // Prints `Object {name: "My object"}`
(object.getName)(); // Also prints `Object {name: "My object"}`
var func = object.getName;
func(); // Prints `Window`
(object.getName = object.getName)(); // Also prints `Window`

如果您尝试将函数作为回调传递给其他函数,您会注意到相同的行为:

function test(callback) {
    callback();
}

test(object.getName); // Prints `Window`

但是,如果基础对象在您调用它时仍被绑定,那么您将看到正常的对象行为:

function test(obj) {
    obj.getName();
}

test(object); // Prints `Object {name: "My object"}`

其他有趣或有用的行为

执行分配不会破坏原始对象。即使您在其他一些参考上得到了thisobject 仍然完好无损:

(object.getName = object.getName)(); // Prints `Window`
object.getName(); // Prints `Object {name: "My object"}`. It didn't break

如果您将函数引用分配回对象的不同属性,您会看到this 在新属性上被正确分配。这是因为重要的部分是您是否在调用函数时指定了基础对象:

var temp = object.getName;
temp(); // Prints `Window`
object.somethingElse = temp;
object.somethingElse(); // Prints `Object {name: "My object"}`. `this` is correctly bound

如果你从一个对象中提取一个函数并将其粘贴到一个完全不同的对象上,那么它仍然可以工作,但this 将绑定到新对象。

Object prototypes 依靠这种行为来施展魔法。

var basketball = {
    name: "Sports equipment?!",
}

basketball.doStuff = object.getName;
basketball.doStuff(); // Prints `Object {name: "Sports equipment?!"}`

如果由于某种原因您在没有绑定this 的情况下获得了对您的函数的引用,您仍然可以更正它,只要您有对原始对象的引用:

var temp = object.getName;
temp(); // Prints `Window`
var temp2 = temp.bind(object);
temp2(); // Prints `Object {name: "My object"}` because you bound `this`
temp.apply(object); // Also prints `Object {name: "My object"}`

【讨论】:

  • 谢谢@Merlyn。现在我的问题是为什么 object.getName 与 (object.getName) 相同。我认为 (object.getName)() 就像: var gn = object.getName; gn();
  • @WenqiMa 它“有点”像它,但是当 javascript 对象方法位于赋值运算符的右侧时,它们会发生奇怪的事情。只要您执行... = object.getNamethis 就会被剥离。将它放在括号中不会这样做,只是将它放在赋值运算符的右侧。造成这种情况的原因在我中间提到的链接里有详细说明——stackoverflow.com/questions/2701987/…
  • 实际上,它更多的是关于如何调用函数而不是赋值运算符(无论你是在“引用类型”上调用函数,它具有this 或“值类型” ,但不是),但由于语言的设计,赋值运算符是您开始注意到差异的地方。
猜你喜欢
  • 2016-01-27
  • 1970-01-01
  • 2016-04-06
  • 1970-01-01
  • 2023-03-30
  • 2011-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多