【问题标题】:This keyword in function call [duplicate]函数调用中的这个关键字[重复]
【发布时间】:2019-09-05 04:37:01
【问题描述】:

let user = {
  name: "John",
  sayHello() {
    alert(this.name);
  },
  sayGoodBye() {
    alert("good bye " + this.name);
  }
};

(user.name === "John" ? user.sayHello : user.sayGoodBye)();

或者如果(user.sayHello)(); 不在条件范围内,它怎么会起作用。那就是三元运算符表达式?

【问题讨论】:

  • 无法重现,即使修复了丢失的分号 - this 总是在函数内部引用 window,无论条件如何,结果都不正确
  • 编辑后现在可以使用了。但问题仍然是为什么?
  • this.name 在此示例中将始终引用 window.name
  • 复制代码并将条件更改为false,它可以正常工作。顺便说一句,运行这段代码给出了乱码。
  • 我知道 window 对象,并且在发布问题之前我确实尝试过搜索。问题的重点是为什么这个 (user.name === "John" ? user.sayHello : user.sayGoodBye)();不行,但是 (user.sayGoodBye)();工作吗?

标签: javascript this


【解决方案1】:

你的答案。 this.name 在对象中不起作用,因为 this 将解析范围并在 user 对象之外查找 name 变量。

var user = {
  name: "John",
  sayHello() {
    console.log("Say hello" , this.name);
    alert(user.name);
  },
  sayGoodBye() {
    alert("good bye " + user.name);
  }
};

(user.name === "John" ? user.sayHello : user.sayGoodBye)();

试试这个代码。它工作正常。

【讨论】:

  • 但是如果条件为假,怎么会正常工作呢?我不明白。代码仍然使用 let 而不是 var。
  • 我无法理解您的评论。请您正确解释一下好吗? @kenryuakuma
  • 抱歉错误。我的意思是为什么如果我们只调用 (user.sayHello)();那么它有效吗?但不在三元运算符表达式内?
  • 它在三元运算符中工作。 @kenyauakuma
【解决方案2】:

为什么您的代码不起作用? 因为这将引用窗口对象。如果您将在用户对象上调用该函数,那么函数中的 this 将引用用户对象

试试这个:

let user = {
  name: "John",
  sayHello() {
    console.log("Hello:",this.name);
  },
  sayGoodBye() {
    console.log("good bye " + this.name);
  }
};

(user.name === "John1" ? user.sayHello() : user.sayGoodBye());
(user.name === "John" ? user.sayHello() : user.sayGoodBye());

第二个替代方法可以像这样手动绑定函数:

let user = {
  name: "John",
  sayHello() {
    console.log("Hello:",this.name);
  },
  sayGoodBye() {
    console.log("good bye " + this.name);
  }
};

// Manual this binding
user.sayHello = user.sayHello.bind(user);
user.sayGoodBye = user.sayGoodBye.bind(user);

(user.name === "John1" ? user.sayHello : user.sayGoodBye)();
(user.name === "John" ? user.sayHello : user.sayGoodBye)();

【讨论】:

  • 诀窍是如何使用我发布的代码解析 this 关键字。代码 sn-p 是从一位资深 javascript 开发者写的一篇文章中抄来的。如果条件为假,则代码无需修改即可正常工作。
  • 对我来说,当条件为假时,这不起作用。检查这个repl - repl.it/repls/BlindAnxiousMonad
  • @kenryuakuma 请重新检查结果,上下文始终为window。这篇文章有多久了,或者你能提供一个链接吗?
  • 它在 jsfiddle 中工作。但无论如何,如果我们只输入 (user.sayHello)(); ,为什么它会起作用?这就是为什么我不明白。
  • @ManishMenaria 注意,在第一个 sn-p 中,您以不应该使用的方式使用三元运算符(只是因为副作用)。
【解决方案3】:

Internals:参考类型深入的语言功能

本节涵盖一个高级主题,以更好地理解某些极端情况。

如果您想更快地进行,可以跳过或推迟。

一个复杂的方法调用可能会丢失这个,例如:

let user = {
  name: "John",
  hi() { alert(this.name); },
  bye() { alert("Bye"); }
};

user.hi(); // John (the simple call works)

// now let's call user.hi or user.bye depending on the name
(user.name == "John" ? user.hi : user.bye)(); // Error!

在最后一行有一个条件运算符,可以选择 user.hi 或 user.bye。在这种情况下,结果是 user.hi。

然后用括号()立即调用该方法。但它不能正常工作!

如您所见,调用导致错误,因为调用内部“this”的值未定义。

这可行(对象点法):

user.hi();

这不(评估方法):

(user.name == "John" ? user.hi : user.bye)(); // Error!

为什么?如果我们想了解它发生的原因,让我们深入了解 obj.method() 调用的工作原理。

仔细观察,我们可能会注意到 obj.method() 语句中有两个操作:

First, the dot '.' retrieves the property obj.method.
Then parentheses () execute it.

那么,关于这方面的信息是如何从第一部分传递到第二部分的呢?

如果我们将这些操作放在不同的行上,那么这肯定会丢失:

let user = {
  name: "John",
  hi() { alert(this.name); }
}

// split getting and calling the method in two lines
let hi = user.hi;
hi(); // Error, because this is undefined

这里hi = user.hi将函数放入变量中,然后在最后一行是完全独立的,所以没有这个。

为了使user.hi() 调用有效,JavaScript 使用了一个技巧——点 '.'返回的不是函数,而是特殊引用类型的值。

引用类型是“规范类型”。我们不能显式使用它,但它是由语言内部使用的。

Reference Type的值为三值组合(base、name、strict),其中:

base 是对象。 name 是属性名称。 如果 use strict 生效,则 strict 为真。

属性访问user.hi的结果不是一个函数,而是一个Reference Type的值。对于严格模式下的 user.hi,它是:

// Reference Type value
(user, "hi", true)

在引用类型上调用括号 () 时,它们会收到有关对象及其方法的完整信息,并且可以设置正确的 this(=user 在这种情况下)。

引用类型是一种特殊的“中介”内部类型,目的是从 dot 传递信息。调用括号 ()。

像赋值hi = user.hi 这样的任何其他操作都会将引用类型作为一个整体丢弃,取user.hi(一个函数)的值并将其传递。所以任何进一步的操作都会“失去”这个。

因此,只有在使用点 obj.method() 或方括号 obj'method' 语法直接调用函数时, this 的值才会以正确的方式传递(它们在此处执行相同的操作)。

【讨论】:

  • 我认为您应该修复问题中的误导性陈述(“如果条件为假,则代码正常工作,因为 user.sayGoodBye(); 被调用并传入 i>") 以避免更多的反对票。事实上,这个问题是不可重现的。顺便提一句。结果不是乱码,“sif5”是放置 Stack Snippet 的 window 的名称。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-17
  • 1970-01-01
  • 2011-09-13
  • 2017-09-17
  • 2021-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多