【问题标题】:javascript: this != window, properties still undefinedjavascript: this != window, 属性仍未定义
【发布时间】:2021-08-04 00:11:01
【问题描述】:

我正在做一个关于 Javascript 的 Salesforce Trailhead,并提出了一个关于这个范围的问题。

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

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

那么为什么 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"
*/

返回:

在线:https://codepen.io/lsag/pen/gOWjyEj

【问题讨论】:

  • 因为您使用的是对象原语而不是类实例?如果你需要一个行为 OOP(ish) 的this,然后编写一个类,创建它的一个实例,你就完成了。否则,JS 的作用域规则很特别,你应该deeply familiarise yourself with how this scoping works
  • 每个function() {} 都有自己的this,因为您在forEach 中的功能与showMessage function 不同,它们都有自己不同的this。在你的 forEach 回调中记录this,你会看到window

标签: javascript scope this


【解决方案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();

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

【讨论】:

    【解决方案2】:

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

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

    您可以看到 this 引用了对象 message,因为它是方法 showMessage 的上下文。

    在下面的例子中,你可以看到this被引用到了窗口,因为它在另一个没有任何先前引用的函数中,所以它从窗口中获取this

    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();

    【讨论】:

    • 这里有很多需要调查的地方。谢谢!!
    【解决方案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();
    

    因为如果你想坚持对象字面量,你最好理解how this works in JavaScript at a deep level

    还要注意所有字符串都使用反引号:不要在现代 JS 中使用 '",使用 template string 符号,直到你知道什么时候不应该使用它们(这几乎永远不会)。

    【讨论】:

      【解决方案4】:

      乍一看,我的脑袋说“哦,这是因为它将范围绑定到新函数”,但当我想得更多时,这没有任何意义。

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

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

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

      无论是这种情况还是发生了其他更奇怪的事情,例如 javascript 如果它被分配给一个对象,则忽略函数范围,但如果它是另一个函数中的函数,则实现函数范围。第一个选项对我来说听起来更合理。

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

      【讨论】:

        猜你喜欢
        • 2019-03-18
        • 2017-08-20
        • 2018-01-31
        • 1970-01-01
        • 2019-01-21
        • 2016-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多