我想知道点抽象方法(例如dog.bark
)是在运行时还是在编译时绑定。我的问题涉及以下代码,它会引发错误:
(true ? ''.toLowerCase : ''.toUpperCase)()
但以下情况并非如此:
true ? ''.toLowerCase() : ''.toUpperCase()
为什么我的字符串文字''
在第一个示例中没有得到解决?
答案 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执行没有设置你想要的东西。
正如其他人所指出的那样,您可以使用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()
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())