This question already has an answer here:
这个问题在这里已有答案:
- Cases where 'this' is the global Object in Javascript 4 answers
'this'是Javascript 4答案中的全局对象的情况
I am wondering whether dot-abstraction methods (e.g. dog.bark
) bind at runtime or at compile-time. My question concerns the following code, which throws an error:
我想知道点抽象方法(例如dog.bark)是在运行时还是在编译时绑定。我的问题涉及以下代码,它会引发错误:
(true ? ''.toLowerCase : ''.toUpperCase)()
But the following does not:
但以下不是:
true ? ''.toLowerCase() : ''.toUpperCase()
Why is my string literal ''
not getting resolved in the first example?
为什么我的字符串文字''在第一个例子中没有得到解决?
5 个解决方案
#1
7
This is actually quite simple once you get how methods work in javascript behind the scenes.
一旦你了解方法如何在幕后的javascript中工作,这实际上非常简单。
toUpperCase
is a method. This is a function that operates on a specific object... usually via the this
variable.
toUpperCase是一种方法。这是一个对特定对象进行操作的函数...通常通过this变量。
Javascript is a prototypal language... meaning that the functions attached to objects are just functions and can be copied. There is some work behind the scenes that makes sure this
is set to the right thing when you call a method, but this work only happens when you call it as a method... as in the obj.method()
form.
Javascript是一种原型语言...意味着附加到对象的函数只是函数,可以复制。在幕后有一些工作可以确保在调用方法时将其设置为正确的,但只有当您将其作为方法调用时才会发生这种情况......就像在obj.method()表单中一样。
In other words: ''.toUpperCase()
makes sure that this
is set to the string ''
when you call it.
换句话说:''。toUpperCase()确保在调用它时将其设置为字符串''。
When you call it as toUpperCase()
this
is not set to anything in particular (different environments do different things with this
in this case)
当你把它称为toUpperCase()时,这并没有特别设置(在这种情况下,不同的环境会做同样的事情)
What your code does could be rewritten as this:
您的代码可以重写为:
var function_to_call;
if (true) {
function_to_call = ''.toLowerCase;
} else {
function_to_call = ''.toUpperCase;
}
function_to_call();
Because your function call: function_to_call()
is not in the object.method()
syntax, the thing that sets this
to the correct object is not done, and your function call executes with this
not set to what you want.
因为你的函数调用:function_to_call()不在object.method()语法中,所以没有把它设置为正确的对象,你的函数调用执行时没有设置你想要的。
As other people have pointed out, you can use func.call(thing_to_make_this)
or func.apply()
to attach the correct thing to this explicitly.
正如其他人指出的那样,你可以使用func.call(thing_to_make_this)或func.apply()来明确地附加正确的东西。
I find it much more helpful to use .bind()
- which is extremely under-used in my opinion. function_name.bind(this_object)
gives you a new function that will always have this
attached to the correct thing:
我觉得使用.bind()更有帮助 - 在我看来这是非常少用的。 function_name.bind(this_object)为您提供了一个新函数,它始终将此函数附加到正确的东西:
// 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()
and this means you can pass around the function you get back from bind()
as you would a normal function, and it will work on the object you want. This is especially useful in callbacks, as you can create an anonymous function that is bound to the object it was created in:
这意味着您可以像传统函数一样传递从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: You can't call a method as a function and expect it to work, because it doesn't know what this
should be. If you want to use that function, you have to use .call()
, .apply()
or .bind()
to make sure this
is set correctly by the time the function executes.
TL; DR:您不能将方法作为函数调用并期望它可以工作,因为它不知道这应该是什么。如果要使用该函数,则必须使用.call(),. apply()或.bind()来确保在函数执行时正确设置。
Hope that helps.
希望有所帮助。
#2
21
(true ? ''.toLowerCase : ''.toUpperCase)()
is equivalent to:
相当于:
String.prototype.toLowerCase.call()
// or:
String.prototype.toLowerCase.call(undefined)
However,
true ? ''.toLowerCase() : ''.toUpperCase()
is equivalent to:
相当于:
String.prototype.toLowerCase.call('')
In both cases, the first argument to call
is converted to an object, which the this
in String.prototype.toLowerCase
will reference to.
在这两种情况下,调用的第一个参数都将转换为一个对象,String.prototype.toLowerCase中的对象将引用该对象。
undefined
can't be converted to an object, but the empty string can:
undefined不能转换为对象,但空字符串可以:
function logThis () { console.log(this); }
logThis.call('');
The SO snippet console only shows {}
, but it's actually the same thing that you get from new String('')
. Read about the string wrapper on MDN.
SO片段控制台只显示{},但实际上它与新String('')相同。阅读MDN上的字符串包装器。
#3
8
Because these methods apply on the this
context, and in your example the this
is undefined
因为这些方法适用于此上下文,并且在您的示例中,这是未定义的
One way to override this variable by using bind
method:
使用bind方法覆盖此变量的一种方法:
(true ? ''.toLowerCase : ''.toUpperCase).bind('Hello')();
this will return hello
这将返回你好
#4
4
Because when you do (true ? ''.toLowerCase : ''.toUpperCase)()
you are not calling the function that is bound to a string. You are simply calling the function without any context.
因为当你这样做时(true?''。toLowerCase:''。toUpperCase)()你没有调用绑定到字符串的函数。你只是在没有任何上下文的情况下调用函数。
Consider the following example:
请考虑以下示例:
var obj = {
objname: "objname",
getName: function() {
return this.objname;
}
}
When you call it with obj.getName()
, it correctly returns the value, but when you do something like this:
当您使用obj.getName()调用它时,它会正确返回值,但是当您执行以下操作时:
var fn = obj.getName
fn() // returns undefined because `fn` is not bound to `obj`
#5
0
In your first example the toLowerCase
function is detached from its context (the empty string object) and then it's invoked. Since you don't reattach the function to anything it has undefined
as its context.
在第一个示例中,toLowerCase函数与其上下文(空字符串对象)分离,然后调用它。由于您没有将函数重新附加到任何未定义为其上下文的函数。
This behavior exist to enable code reuse through mix-ins:
存在此行为以通过混合启用代码重用:
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())
#1
7
This is actually quite simple once you get how methods work in javascript behind the scenes.
一旦你了解方法如何在幕后的javascript中工作,这实际上非常简单。
toUpperCase
is a method. This is a function that operates on a specific object... usually via the this
variable.
toUpperCase是一种方法。这是一个对特定对象进行操作的函数...通常通过this变量。
Javascript is a prototypal language... meaning that the functions attached to objects are just functions and can be copied. There is some work behind the scenes that makes sure this
is set to the right thing when you call a method, but this work only happens when you call it as a method... as in the obj.method()
form.
Javascript是一种原型语言...意味着附加到对象的函数只是函数,可以复制。在幕后有一些工作可以确保在调用方法时将其设置为正确的,但只有当您将其作为方法调用时才会发生这种情况......就像在obj.method()表单中一样。
In other words: ''.toUpperCase()
makes sure that this
is set to the string ''
when you call it.
换句话说:''。toUpperCase()确保在调用它时将其设置为字符串''。
When you call it as toUpperCase()
this
is not set to anything in particular (different environments do different things with this
in this case)
当你把它称为toUpperCase()时,这并没有特别设置(在这种情况下,不同的环境会做同样的事情)
What your code does could be rewritten as this:
您的代码可以重写为:
var function_to_call;
if (true) {
function_to_call = ''.toLowerCase;
} else {
function_to_call = ''.toUpperCase;
}
function_to_call();
Because your function call: function_to_call()
is not in the object.method()
syntax, the thing that sets this
to the correct object is not done, and your function call executes with this
not set to what you want.
因为你的函数调用:function_to_call()不在object.method()语法中,所以没有把它设置为正确的对象,你的函数调用执行时没有设置你想要的。
As other people have pointed out, you can use func.call(thing_to_make_this)
or func.apply()
to attach the correct thing to this explicitly.
正如其他人指出的那样,你可以使用func.call(thing_to_make_this)或func.apply()来明确地附加正确的东西。
I find it much more helpful to use .bind()
- which is extremely under-used in my opinion. function_name.bind(this_object)
gives you a new function that will always have this
attached to the correct thing:
我觉得使用.bind()更有帮助 - 在我看来这是非常少用的。 function_name.bind(this_object)为您提供了一个新函数,它始终将此函数附加到正确的东西:
// 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()
and this means you can pass around the function you get back from bind()
as you would a normal function, and it will work on the object you want. This is especially useful in callbacks, as you can create an anonymous function that is bound to the object it was created in:
这意味着您可以像传统函数一样传递从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: You can't call a method as a function and expect it to work, because it doesn't know what this
should be. If you want to use that function, you have to use .call()
, .apply()
or .bind()
to make sure this
is set correctly by the time the function executes.
TL; DR:您不能将方法作为函数调用并期望它可以工作,因为它不知道这应该是什么。如果要使用该函数,则必须使用.call(),. apply()或.bind()来确保在函数执行时正确设置。
Hope that helps.
希望有所帮助。
#2
21
(true ? ''.toLowerCase : ''.toUpperCase)()
is equivalent to:
相当于:
String.prototype.toLowerCase.call()
// or:
String.prototype.toLowerCase.call(undefined)
However,
true ? ''.toLowerCase() : ''.toUpperCase()
is equivalent to:
相当于:
String.prototype.toLowerCase.call('')
In both cases, the first argument to call
is converted to an object, which the this
in String.prototype.toLowerCase
will reference to.
在这两种情况下,调用的第一个参数都将转换为一个对象,String.prototype.toLowerCase中的对象将引用该对象。
undefined
can't be converted to an object, but the empty string can:
undefined不能转换为对象,但空字符串可以:
function logThis () { console.log(this); }
logThis.call('');
The SO snippet console only shows {}
, but it's actually the same thing that you get from new String('')
. Read about the string wrapper on MDN.
SO片段控制台只显示{},但实际上它与新String('')相同。阅读MDN上的字符串包装器。
#3
8
Because these methods apply on the this
context, and in your example the this
is undefined
因为这些方法适用于此上下文,并且在您的示例中,这是未定义的
One way to override this variable by using bind
method:
使用bind方法覆盖此变量的一种方法:
(true ? ''.toLowerCase : ''.toUpperCase).bind('Hello')();
this will return hello
这将返回你好
#4
4
Because when you do (true ? ''.toLowerCase : ''.toUpperCase)()
you are not calling the function that is bound to a string. You are simply calling the function without any context.
因为当你这样做时(true?''。toLowerCase:''。toUpperCase)()你没有调用绑定到字符串的函数。你只是在没有任何上下文的情况下调用函数。
Consider the following example:
请考虑以下示例:
var obj = {
objname: "objname",
getName: function() {
return this.objname;
}
}
When you call it with obj.getName()
, it correctly returns the value, but when you do something like this:
当您使用obj.getName()调用它时,它会正确返回值,但是当您执行以下操作时:
var fn = obj.getName
fn() // returns undefined because `fn` is not bound to `obj`
#5
0
In your first example the toLowerCase
function is detached from its context (the empty string object) and then it's invoked. Since you don't reattach the function to anything it has undefined
as its context.
在第一个示例中,toLowerCase函数与其上下文(空字符串对象)分离,然后调用它。由于您没有将函数重新附加到任何未定义为其上下文的函数。
This behavior exist to enable code reuse through mix-ins:
存在此行为以通过混合启用代码重用:
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())