【问题标题】:Why passing function reference is failing while using function declaration is not?为什么在使用函数声明时传递函数引用失败?
【发布时间】:2018-01-31 22:57:32
【问题描述】:

以下代码失败:

array.map(String.prototype.toLowerCase)

抛出Uncaught TypeError: String.prototype.toLowerCase called on null or undefined。确实,this 没有设置,我知道了。

但奇怪的是,下面的代码没有失败地返回一个空字符串数组:

array.map((s) => String.prototype.toLowerCase(s))

知道为什么吗? 请注意,我知道这不是拥有小写字符串数组的方式。我只是想知道为什么这两种方法的行为不同。

也就是说.map(String.prototype.toLowerCase).map((s) => String.prototype.toLowerCase(s))有什么区别?我认为是相同的,但显然,它的行为不同。请注意,这里的String.prototype.toLowerCase 可以替换为任何内容。

【问题讨论】:

    标签: javascript arrays function functional-programming


    【解决方案1】:

    .map(String.prototype.toLowerCase).map((s) => String.prototype.toLowerCase(s))

    对于第一种情况,当您在对象 (String.prototype.toLowerCase) 之外获取函数时,您会丢失上下文,因此您的上下文为空,map 尝试调用 null or undefined。使用第一个解决方案您无法获得所需的结果,因为您需要将上下文传递给toLowerCase 函数,它必须是数组的每个项目,但您没有得到表示数组的每一项。

    对于第二种情况,您需要通过call 函数将itemcontext 传递给String.prototype.toLowerCase

    var array = ['ASD', 'BSD'];
    
    var lowered = array.map(item => String.prototype.toLowerCase.call(item));
    
    console.log(lowered);

    【讨论】:

    • 这也不起作用,也不能真正回答问题。换句话说,.map(String.prototype.toLowerCase).map((s) => String.prototype.toLowerCase(s)) 有什么区别?我认为是相同的,但显然,它的行为不同。
    • 我认为这并不完全正确,但它帮助我找到了答案,所以谢谢 ;) 基本上,IMO,第一个将此设置为未定义,而第二个将此设置为字符串。原型。请参阅我自己的答案。
    【解决方案2】:

    第二种方法array.map((s) => String.prototype.toLowerCase(s)) 不会抛出错误,因为toLowerCase 没有脱离上下文,即该方法仍然将String.prototype 作为其接收者。

    String.prototype.toLowerCase(s)) 返回一个空字符串,因为参数s 被丢弃。 toLowerCase 改为从其接收对象中获取值。接收对象为String.protoype。要获得实际的字符串,必须将原型转换为字符串。 String.prototype.toString 方法会发生这种情况,该方法的计算结果为 ""。因此String.prototype.toLowerCase(s)) 的计算结果为""

    您可以通过更改toString 方法来验证此行为:

    String.prototype.toString = () => "FOO";
    console.log(String.prototype.toLowerCase()); // "foo"

    【讨论】:

      【解决方案3】:

      .map(String.prototype.toLowerCase) and .map((s) => String.prototype.toLowerCase(s)) 的区别在于.map((s) => String.prototype.toLowerCase(s)) 采用了一个匿名函数的箭头函数。根据箭头函数的定义 "箭头函数表达式的语法比函数表达式短,并且不绑定自己的 this、arguments、super 或 new.target。" 箭头函数不会创建自己的 this,而是使用封闭执行上下文的 this 值。 使用 .map(String.prototype.toLowerCase) 将不起作用,因为您没有将任何执行上下文传递给它,但它正在寻找要执行的上下文。 例如在下面的代码中

      function Person(){
        this.age = 0;
      
        setInterval(() => {
          this.age++; // |this| properly refers to the person object
        }, 1000);
      }
      
      var p = new Person();
      

      请检查此链接 https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions

      【讨论】:

      • 感谢您的回答,但我认为您对“使用封闭执行上下文的 this 值”有误。基本上,IMO,第一个将此设置为未定义,而第二个将此设置为 String.prototype。请参阅我自己的答案。
      • 你是对的,但我的观点与我在下面提供的示例代码有关,其中this 指的是Person 类对象而不是全局窗口对象
      【解决方案4】:

      我想我终于明白了:

      • 第一个将this 设置为undefined,相当于:
      .map((s) => String.prototype.toLowerCase.call(undefined, s))
      
      • 而第二个将this 设置为String.prototype,相当于:
      .map((s) => String.prototype.toLowerCase.call(String.prototype, s))
      

      现在,问题是,为什么 String.prototype.toLowerCase() 返回一个空字符串...但这值得另一个问题 :)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-04-08
        • 2011-06-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-19
        • 2015-06-27
        • 2020-09-10
        相关资源
        最近更新 更多