javascript:这个 != 窗口,属性仍未定义

javascript: this != window, properties still undefined

提问人:Luis Aguiar 提问时间:8/4/2021 更新时间:8/4/2021 访问量:53

问:

我正在做一个关于Javascript的Salesforce跟踪,出现了一个关于这个范围的问题。

https://trailhead.salesforce.com/content/learn/modules/modern-javascript-development/understand-javascript-functions?trail_id=learn-to-work-with-javascript

我以为this.hello在函数中是未定义的,因为这将指向窗口,其中.name down不存在。但是 window == 这是 false,该对象的日志显示它访问了属性。

那么,为什么 this.name 是未定义的呢?

let message = {
  hello: "Hello",
  names: ["Sue", "Joe"],
  showMessage: function () {
    console.log("this, message");
    console.log(window == this);
    console.log(this);
    this.names.forEach(function (name) {
      console.log(this.hello + " " + name);
    });
  }
};
message.showMessage();

/*
false
Object
    hello: "Hello"
    names: (2) ["Sue", "Joe"]
Sue
"undefined Sue"
"undefined Joe"
*/

返回:

enter image description here

出版日期: https://codepen.io/lsag/pen/gOWjyEj

JavaScript 作用域

评论

0赞 Mike 'Pomax' Kamermans 8/4/2021
因为您使用的是对象原语而不是类实例?如果你需要一个行为 OOP(ish),那么写一个类,创建一个实例,你就完成了。否则,JS 的范围规则非常特殊,您应该深入了解范围的工作原理this
1赞 Nick Parsons 8/4/2021
每个都有自己的函数,因为你在你的函数和showMessage是不同的,它们都有自己不同的。登录 forEach 回调,你会看到function() {}thisforEachfunctionthisthiswindow

答:

1赞 see sharper 8/4/2021 #1

如果在 forEach 中使用箭头函数,将获得预期结果:

let message = {
  hello: "Hello",
  names: ["Sue", "Joe"],
  showMessage: function () {
    console.log("this, message");
    console.log(window == this);
    console.log(this);
    this.names.forEach((name) => {
      console.log(this.hello + " " + name);
    });
  }
};
message.showMessage();

这是因为箭头函数没有自己的绑定,而是从周围的作用域获取它。您的函数未绑定到任何对象,因此它采用 .thiswindowthis

1赞 AlexSp3 8/4/2021 #2

这是因为关键字获取了您调用的函数的上下文。所以它不等于窗口。this

let message = {
  hello: "Hello",
  names: ["Sue", "Joe"],
  showMessage: function () {
    console.log(this);
  }
};

message.showMessage();

您可以看到,这是引用对象的,因为它是方法的上下文。messageshowMessage

在下面的示例中,您可以看到它被引用到窗口,因为它位于另一个函数中,没有任何先前的引用,因此它从窗口获取:thisthis

let message = {
  hello: "Hello",
  names: ["Sue", "Joe"],
  showMessage: function () {
    this.names.forEach(function (name) {
      console.log(this === window);  // referred to the window
    });
  }
};

message.showMessage();

为了避免这种情况,只需使用箭头函数

let message = {
      hello: "Hello",
      names: ["Sue", "Joe"],
      showMessage: function () {
        this.names.forEach((name) => {  // arrow function
          console.log(this.hello + " " + name);  // referred to the object
        });
      }
    };

    message.showMessage();

评论

0赞 Luis Aguiar 8/5/2021
这里有很多要调查的地方。谢谢!!
1赞 Mike 'Pomax' Kamermans 8/4/2021 #3

让我们回过头来:除非你已经是 JS 专家,否则如果你想要基于面向对象编程的一般知识,以你期望的方式工作的“对象方法和属性”,请不要使用对象文字。

使用类:

class Message {
  constructor(hello = ``, names = []) {
    this.hello = hello;
    this.names = names.slice();
  }

  showMessage() {
    this.names.forEach(name => console.log(`${this.hello} ${name}`));
  }
}

let message = new Message(`hello`, [`Sue`, `Joe`]);
message.showMessage();

因为如果你想坚持对象文字,你最好在深层次上理解它在 JavaScript 中是如何工作的

另请注意,所有字符串都使用反引号:不要使用或在现代 JS 中使用模板字符串表示法,直到您知道何时不应该使用它们(这几乎永远不会)。'"

0赞 user16435030 8/4/2021 #4

乍一看,我的脑袋说“哦,这是因为它将范围绑定到新功能”,但当我仔细想想时,这就没有意义了。

console.log(window == this);
console.log(this);

“this”绑定到“message”的对象范围,而不是函数。因此,声明对该范围没有影响。您还可以在其中看到,其中显示了正在记录的整个对象。showMessage: function ()console.log(this)

那么下一个问题就可以了,但是为什么下一个功能,突然实际上改变了作用域。我认为这是因为它与函数无关,很可能是因为使用 forEach 创建了一个新范围而不是函数。因此,在该循环中使用的 this 绑定到循环,而不是函数。function (name)

无论是那个还是其他正在发生的事情,都更奇怪,比如 javascript 如果函数作用域被分配给一个对象,则忽略函数作用域,但如果它是另一个函数中的函数,则实现函数作用域。第一种选择对我来说听起来更合理。

但我只是一个投机的白痴。