【问题标题】:Why doesn't Javascript bind my dot expression correctly? [duplicate]为什么 Javascript 不能正确绑定我的点表达式? [复制]
【发布时间】:2018-02-01 22:18:46
【问题描述】:

我想知道点抽象方法(例如dog.bark)是在运行时绑定还是在编译时绑定。我的问题涉及以下代码,它会引发错误:

(true ? ''.toLowerCase : ''.toUpperCase)()

但以下不是:

true ? ''.toLowerCase() : ''.toUpperCase()

为什么我的字符串文字 '' 在第一个示例中没有得到解析?

【问题讨论】:

标签: javascript oop


【解决方案1】:
(true ? ''.toLowerCase : ''.toUpperCase)()

相当于:

String.prototype.toLowerCase.call()
// or:
String.prototype.toLowerCase.call(undefined)

然而,

true ? ''.toLowerCase() : ''.toUpperCase()

相当于:

String.prototype.toLowerCase.call('')

在这两种情况下,call 的第一个参数都被转换为一个对象,String.prototype.toLowerCase 中的this 将引用该对象。

undefined不能转成对象,但是空字符串可以:

function logThis () { console.log(this); }

logThis.call('');

SO sn-p 控制台仅显示{},但实际上与您从new String('') 获得的内容相同。阅读 MDN 上的字符串包装器。

【讨论】:

  • 我知道它已经很老了,但是你第一次说“等同于”的地方是完全错误的。在三级表达式中,''.toLowerCase 不会调用该函数。它只会检查String.prototype 中的toLowerCase 是否不虚假。所以应该删除这两个衬垫的第一行......
  • @marekful 不,三元表达式将返回 String.prototype.toLowerCase。最后的括号调用该函数。
  • 是的,这是对它的唯一调用,但您说它转换为对函数的两次调用,String.prototype.toLowerCase.call() 带有空参数。这在两种情况下完全相同。正如您建议的'' 与没有一样,传递的参数没有区别。可能表明将空字符串文字转换为 String 实例吸引了赞成票,这确实很好,但人们只是把目光从其他地方移开,而不是进行批判性评估。
  • 恐怕我没有完全理解你。您是否认为 sn-p 被翻译为两个单独的函数调用?这不是我的意思,显然其他观众也理解。但这有点模棱两可。我希望编辑后的答案更清楚。
【解决方案2】:

因为当您执行(true ? ''.toLowerCase : ''.toUpperCase)() 时,您不会调用绑定到字符串的函数。您只是在没有任何上下文的情况下调用该函数。

考虑以下示例:

var obj = {
    objname: "objname",
    getName: function() {
        return this.objname;
    }
}

当您使用obj.getName() 调用它时,它会正确返回值,但是当您执行以下操作时:

var fn = obj.getName
fn() // returns undefined because `fn` is not bound to `obj`

【讨论】:

    【解决方案3】:

    因为这些方法适用于 this 上下文,而在您的示例中,thisundefined

    使用bind 方法覆盖this 变量的一种方法:

    (true ? ''.toLowerCase : ''.toUpperCase).bind('Hello')();
    

    这将返回hello

    【讨论】:

    • 只要(true ? ''.toLowerCase : ''.toUpperCase).call('Hello') 也可以。
    • @IlmariKaronen,是的,我想通过在此处使用 bind 来明确表示我正在覆盖 this 变量
    【解决方案4】:

    在您的第一个示例中,toLowerCase 函数与其上下文(空字符串对象)分离,然后被调用。由于您没有将该函数重新附加到它具有 undefined 作为其上下文的任何内容。

    存在这种行为是为了通过混入实现代码重用:

    var obj1 = {
       name: "obj1",
       getName: function() { return this.name; }
    }
    
    var obj2 = {
       name: "obj2",
    }
    
    obj2.getName = obj1.getName //now obj2 has the getName method with correct context
    
    console.log(obj2.getName())

    【讨论】:

      【解决方案5】:

      一旦你了解了方法在幕后如何在 javascript 中工作,这实际上非常简单。

      toUpperCase 是一种方法。这是一个对特定对象进行操作的函数……通常通过 this 变量。

      Javascript 是一种原型语言......这意味着附加到对象的函数只是函数并且可以复制。幕后有一些工作可以确保在您调用方法时将this 设置为正确的东西,但这项工作仅在您将其作为方法调用时才会发生......就像obj.method() 形式一样。

      换句话说:''.toUpperCase() 确保在您调用 this 时将其设置为字符串 ''

      当您将其称为toUpperCase() 时,this 未设置为任何特定内容(在这种情况下,不同的环境对this 执行不同的操作)

      你的代码可以改写成这样:

      var function_to_call;
       if (true) {
          function_to_call = ''.toLowerCase;
       } else {
          function_to_call = ''.toUpperCase;
       }
      
       function_to_call();
      

      因为您的函数调用:function_to_call() 不在object.method() 语法中,所以将this 设置为正确对象的事情没有完成,并且您的函数调用在this 未设置为您想要的情况下执行.

      正如其他人指出的那样,您可以使用func.call(thing_to_make_this)func.apply() 明确地将正确的内容附加到此。

      我发现使用.bind() 更有帮助——在我看来,它的使用率极低。 function_name.bind(this_object) 为您提供了一个新函数,该函数将始终将 this 附加到正确的事物上:

      // assuming function_to_call is set
      function_that_works = function_to_call.bind(my_object)
      
      function_that_works(); // equivalent to my_object.function_to_call()
      

      这意味着您可以像普通函数一样传递从bind() 返回的函数,并且它将在您想要的对象上工作。这在回调中特别有用,因为您可以创建一个绑定到创建它的对象的匿名函数:

      // this won't work because when this runs, 'this' doesn't mean what you think
      setTimeout(function() { this.display_message('success'); }, 2000);
      
      // this will work, because we have given setTimeout a pre-bound function.
      setTimeout(function() { this.display_message('success'); }.bind(this), 2000); 
      

      TL;DR:您不能将方法作为函数调用并期望它工作,因为它不知道this 应该是什么。如果要使用该函数,则必须使用.call().apply().bind() 以确保在函数执行时正确设置this

      希望对您有所帮助。

      【讨论】:

      • TL;DR:Javascript 太疯狂了。
      • 由于理智主要基于人群的规范,该陈述的真实性取决于您是在人群中还是在人群之外。我认为 javascript 是非常理智的,实际上非常酷和灵活的方式不是很多其他语言......但我不希望 javascript 是除了 javascript 之外的任何东西。如果您来自具有严格引用的语言和/或面向类的语言,则不能使用相同的准则。它们根本没有意义。这就像坐在喷气式飞机的飞行员座位上,询问换档装置和手套箱在哪里。
      猜你喜欢
      • 1970-01-01
      • 2014-06-26
      • 1970-01-01
      • 2016-07-12
      • 2010-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-21
      相关资源
      最近更新 更多