(我知道this的问题,但是答案并不能完全告诉我我需要知道的内容。)
我遇到过这样的情况,我需要在JavaScript中的函数上使用.bind()
才能将this
或本地/类变量传递给函数。但是,我仍然不太清楚何时需要它。
确切地知道何时this
或局部/类变量在函数中可用或不可用的标准是什么?您对此有何看法?
例如:
function() { }
时是什么?class
成员函数,class
getter / setter函数或老式的prototype.function
“成员函数”(作为类的函数)时?for
或forEach
循环中,还是在它们的任何变体中?Array.prototype.forEach.call()
或[].forEach.call()
?我问的主要原因是要意识到潜在的陷阱,避免不得不依靠反复试验。
答案 0 :(得分:2)
Mozilla开发人员网络在this上有一些不错的文档,说明了不同的情况:
bind
查看链接以了解this
在不同上下文中的工作原理,因此应在何时使用bind
强制为函数绑定不同的this
上下文。
通常,bind
用于转移功能的“所有权”。具体来说,根据我的经验,在创建类以强制将对象方法绑定到所讨论的对象之前,已使用过该方法。对于使用箭头功能的情况也很有用,因为箭头功能具有不同的上下文。
答案 1 :(得分:1)
在以下情况下,您需要使用bind
(或类似方法)
function
关键字)函数或方法(在class
或对象常量中),并且this
或将其设置为不正确的值的方式调用原因是,对于传统的函数或方法,this
的值由调用方设置,而不是函数本身的一部分。 (详细信息here和here。)
例如,考虑:
const obj = {
method() {
console.log(this === obj);
}
};
现在,当我们执行obj.method()
时,我们正在使用语法(调用属性访问器操作的结果)来指定this
的含义,因此:
obj.method();
// => true
但是假设我们这样做:
const m = obj.method;
现在,仅调用m()
会将this
设置为默认的this
(严格模式下的undefined
,松散模式下的全局对象):
m();
// => false
我们可以通过this
(及其堂兄call
)为通话明确设置apply
的另一种方法:
m.call(obj);
// => true
一些调用回调的函数使您可以指定要使用的this
。 forEach
做为回调之后的参数:
[1].forEach(m, obj);
// ^ ^^^---- the value to use as `this` in callback
// \-------- the callback to call
// => true
这是其中的一个生动例子:
const obj = {
method() {
console.log(this === obj);
}
};
obj.method();
// => true, `this` was set to `obj` because you did the call on the
// result of a property accessor
const m = obj.method;
m();
// => false, `this` was the default `this` used when `this` isn't
// specified explicitly via syntax or `call`
m.call(obj);
// => true, `this` was explicitly set via `call`
[1].forEach(m, obj);
// => true, `this` was explicitly set via `forEach`'s `thisArg` argument
因此,只要您拥有函数(例如forEach
的回调或事件处理程序),就需要bind
或类似的机制来确保正确的this
使用。
对于其他某些类型的函数,仅是传统的function
关键字)和方法(例如上述obj.method
),情况并非如此。箭头函数关闭 this
,而不使用调用方提供的箭头函数,绑定函数(使用bind
的结果)已绑定this
因此将忽略调用方提供的任何this
。
答案 2 :(得分:0)
向T.J. Crowder和Zapparatus致谢,以提供答案。这4个答案/文章也很有帮助:1 2 3 4
但是,它们要么不完全完整和/或very之以鼻。因此,我决定将我的所有发现以及代码示例组合成一个答案。
确定函数中是否可以使用this
或局部/类变量时,需要考虑以下因素:
注意:还存在严格模式(产生undefined
而不是window
对象)和箭头函数(不会从包含范围更改this
)
以下是明确的规则:
this
是全局对象,在浏览器世界中是window
。this
仍将是window
,不会改变。class
或函数类(new function() { }
)内的成员函数内,函数类的原型(funcClass.prototype.func = function() { }
)内,由相邻成员函数调用的函数内, this
,或者在映射到对象({ key: function() { } }
)或存储在数组([ function() { } ]
)中的函数内部,如果直接用类/对象/数组调用该函数作为呼叫链中的直接前辈(class.func()
,this.func()
,obj.func()
或arr[0]()
),this
是指类/对象/ array实例。.forEach(function() { })
或设置为处理事件)),this
返回到window
或绑定到调用者可能绑定到的任何对象(例如,事件可能会将其绑定到触发对象实例)。
class
的成员函数中(并且只有class
,而不是函数类),如果一个函数内的内容失去其this
上下文(例如通过成为闭包的内部函数),它将变为undefined
,而不是window
... 这里是JSFiddle,其中包含大量代码示例:
outputBox = document.getElementById("outputBox");
function print(printMe = "") {
outputBox.innerHTML += printMe;
}
function printLine(printMe = "") {
outputBox.innerHTML += printMe + "<br/>";
}
var someVar = "someVar";
function func(who) {
printLine("Outer func (" + who + "): " + this);
var self = this;
(function() {
printLine("Inner func (" + who + "): " + this);
printLine("Inner func (" + who + ") self: " + self);
})();
}
func("global");
printLine();
func.call(someVar, "someVar");
printLine();
function funcTwo(who) {
printLine("Outer funcTwo (" + who + "): " + this);
var self = this;
return function funcThree() {
printLine("Inner funcThree (" + who + "): " + this);
printLine("Inner funcThree (" + who + ") self: " + self);
};
}
funcTwo("global")();
printLine();
f = funcTwo("global f");
f();
printLine();
funcTwo.call(someVar, "someVar")();
printLine();
object = {
func: function(who) {
printLine("Object outer (" + who + "): " + this);
var self = this;
(function() {
printLine("Object inner (" + who + "): " + this);
printLine("Object inner (" + who + ") self: " + self);
})();
}
}
object.func("good");
printLine();
bad = object.func;
bad("bad");
printLine();
function funcClass(who) {
printLine("funcClass (" + who + "): " + this);
}
funcClass.prototype.func = function() {
printLine("funcClass.prototype.func: " + this);
self = this;
(function() {
printLine("funcClass.func inner: " + this);
printLine("funcClass.func inner self: " + self);
})();
}
fc = funcClass("bad");
printLine();
fc = new funcClass("good");
fc.func("good");
printLine();
class classClass {
constructor() {
printLine("classClass constructor: " + this);
}
func() {
printLine("classClass.func: " + this);
self = this;
(function() {
printLine("classClass.func inner: " + this);
printLine("classClass.func inner self: " + self);
})();
}
funcTwo() {
this.func();
}
}
cc = new classClass();
cc.func();
printLine();
printLine("Calling funcTwo:");
cc.funcTwo();
printLine();
[0].forEach(function(e) {
printLine("[0].forEach: " + this);
printLine("[0].forEach someVar: " + someVar);
});
[0].forEach(function(e) {
printLine("[0].forEach with [0]: " + this);
}, [0]);
printLine();
arr = [
function(who) {
printLine("Array (" + who + "): " + this);
},
1,
10,
100
];
arr[0]("good");
arrFunc = arr[0];
arrFunc("bad");
printLine();
var button = document.getElementById("button");
button.onclick = function() {
printLine("button: " + this);
}
button.click();
button.onclick = func;
button.click();
setTimeout(function() {
printLine();
printLine("setTimeout: " + this);
printLine("setTimeout someVar: " + someVar);
}, 0);
setTimeout(fc.func, 0);
setTimeout(cc.func, 0);
<input id="button" type="button" value="button"/>
<br/><br/>
<div id="outputBox" />
结论:是的,这很简单。