为什么Javascript没有正确绑定我的点表达式?

时间:2017-08-24 06:48:16

标签: javascript oop

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

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

但以下情况并非如此:

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

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

5 个答案:

答案 0 :(得分:21)

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

相当于:

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

然而,

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

相当于:

String.prototype.toLowerCase.call('')

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

undefined无法转换为对象,但空字符串可以:

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

logThis.call('');

SO片段控制台仅显示{},但实际上与new String('')相同。阅读MDN上的字符串包装器。

答案 1 :(得分:8)

由于这些方法适用于this上下文,因此在您的示例中,this 未定义

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

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

这将返回hello

答案 2 :(得分:7)

一旦你了解了方法在幕后的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设置为正确的对象的事情没有完成,并且您的函数调用以{{1执行没有设置你想要的东西。

正如其他人所指出的那样,您可以使用thisfunc.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()

TL; DR:您不能将方法作为函数调用并期望它可以工作,因为它不知道// 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); 应该是什么。如果您想使用该功能,则必须使用this.call().apply()来确保在函数执行时正确设置.bind()

希望有所帮助。

答案 3 :(得分:4)

因为当你执行(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`

答案 4 :(得分:0)

在您的第一个示例中,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())