【问题标题】:Why "this" is undefined in function called via logical operator?为什么“this”在通过逻辑运算符调用的函数中未定义?
【发布时间】:2019-11-05 21:27:19
【问题描述】:

以下代码 (codepen) 不起作用。

"use strict"; //without strict "this" works, but it refers to window object

let person = {
  name : "Shimon",
  logName : function(){
    //console.log("test") //this works
    console.log(this.name); //doesn't work
  }
};

//person.logName(); //works
(false || person.logName)(); //doesn't work. Uncaught TypeError: Cannot read property 'name' of undefined

我想知道为什么。
当我打电话给(false || person.logName)(); 时,我想打电话给person.logName(),它确实打电话了。
那么为什么我不能在这个方法中使用this 呢?

【问题讨论】:

  • 而不是记录这个名字。您实际上可以退货。之前的 es2015 标准可能存在问题,您将其称为 self.所以生成 es2015 方法代替 person = { name:"Simon", logname(){return this.name}}
  • @bhspencer 这似乎是一个非常全球性的问题。我在那里找不到一点。如果您能指定更精确的方式,我将不胜感激。

标签: javascript


【解决方案1】:

JavaScript 引擎确定 this 引用的 4 条规则。

  1. New Bound:当前函数是否使用 new 关键字调用
  2. 显式绑定:函数是使用Function#call 还是Function#apply 调用的。
  3. 隐式绑定:函数是否直接在其拥有的对象上?
  4. 默认:如果是严格模式undefined,否则global

在您的情况下,person.logName() 属于第三类,因此 this 引用设置为 person 的值。
但在另一种情况下,(false || person.logName)() 行等价于var x = false || person.logName; x();。那是因为您使用表达式来获取函数,因此上下文丢失了。
所以你要么需要使用person.logName()要么(false || person.logName).call(person)

这是一个更复杂的例子:

var actionMap = {
  0: function() {
    console.log(this === actionMap ? "actionMap" : "global");
  },
  1: function() {
    console.log(this === actionMap ? "actionMap" : "global");
  },
};

function other() {
  console.log(this === actionMap ? "actionMap" : "global");
}

actionMap[0](); // works
(actionMap[0])(); // works, brackets get ignored
(actionMap[0] || actionMap[1])(); // context is lost in the expression
(actionMap[2] || actionMap[1])(); // context is lost in the expression
(actionMap[2] || actionMap[1]).call(actionMap); // works, context is called explicitly

var boundOther = other.bind(actionMap);
(boundOther || actionMap[1])(); // function is bound

正如您在此示例中所见,即使两个函数都属于同一个对象,上下文也会丢失。因为被调用的函数是由表达式派生的,并且不会立即在其拥有的对象上调用。

【讨论】:

  • 很好的解释。谢谢。
【解决方案2】:

这就是 Javascript 语义的行为方式。除非您从附加到 this 的对象中调用该方法,否则无法在函数范围内解析。

你的例子:

(false || person.logName)();

可以改写为:

var func = person.logName;
func();

在这里您可以直观地看到logName()person 分离的中间步骤。这具有删除 this 上下文的副作用。


有一些方法可以对“分离”函数强制执行 this 上下文。其中之一是Function.prototype.bind(),它可以将this 绑定到任何函数。

(false || person.logName.bind(person))();

这里创建了一个新函数,将person 绑定为this 以实现所需的行为。或者,您可以使用Function.prototype.call() 来避免创建新函数。

(false || person.logName).call(person);

这适用于您设计的用例,但它可能不适合您的实际用例,因为当您“分离”的函数不是来自同一个对象时,您需要将不同的对象作为 this 参数传递.

【讨论】:

    【解决方案3】:

    this 适用于实例化的类/函数。在您的情况下,您必须将对象视为静态对象:

    let person = {
      name : "Shimon",
      logName : function(){
        console.log(person.name);
      }
    };
    

    【讨论】:

      【解决方案4】:

      如果您想要这种行为,您可以使用类或原型继承

      class Person {
        constructor(name) {
          this.name = name;
        }
      
        logName() {
          console.log(this.name);
        }
      }
      

      function person(name) {
        this.name = name;
      }
      
      person.prototype.logName = function() {
        console.log(this.name);
      }
      
      let shimon = new Person("Shimon");
      shimon.logName()  // >> Shimon
      

      【讨论】:

        猜你喜欢
        • 2011-02-15
        • 1970-01-01
        • 2018-11-17
        • 2020-12-23
        • 1970-01-01
        • 1970-01-01
        • 2016-12-02
        • 1970-01-01
        • 2010-10-29
        相关资源
        最近更新 更多