以下成功打印'foo'。
var obj = {
name: 'foo',
printName: function printName() {
console.log(this.name);
}
};
var printButton= document.getElementById('printIt');
printButton.addEventListener('click', function(){
obj.printName();
});
但是,以下内容不会:
printButton.addEventListener('click', obj.printName() );
我知道解决方案...只需使用bind
以便我们引用obj
对象。即:
printButton.addEventListener('click', obj.printName.bind(obj) );
那为什么我们不需要在第一个示例中使用bind
。我不知道为什么在匿名函数中包装obj.printName()
函数调用会导致console.log
正确引用和打印this
,但是当直接在click
之后调用时,您需要使用bind
好吧,我评论了一些关于这个问题的好信息,所以我不妨回答一下!
功能一流
好吧,让我们从JavaScript的一些基础知识开始,这些基础知识与其他一些编程语言非常不同:在javascript中,函数是一等公民- 这只是一种奇特的说法,您可以将函数保存到变量中,并且可以将函数传递到其他函数中。
const myFunction = function () { return 'whoa a function'; }
array.map(function () { return x + 1; });
由于这个奇妙的功能,表达式之间存在很大差异:
表达式 1
obj.printName
和
表达式 2
obj.printName();
在表达式 1 中:未调用函数,因此表达式的值为function
在表达式 2 中:正在调用函数,因此表达式的值就是函数返回的值。在您的情况下,这undefined
addEventListener
该方法addEventListener
接受两个参数:
- 事件类型的
string
- 将在事件触发时运行的
function
。
下车,那是什么意思?
当您致电时
// doesn't work
printButton.addEventListener('click', obj.printName() );
您不是将类型function
的值传递给addEventListener
方法,而是传递undefined
。
// works
printButton.addEventListener('click', obj.printName.bind(obj) );
然后工作(出于一个原因),因为第二个参数实际上是类型function
.
bind
做什么?为什么它返回一个函数?
现在我们需要讨论bind
实际上做了什么。它与指针*this
有关。
*指针,我的意思是指向某个对象的引用标识符
bind
是存在于每个函数对象上的方法,它只是将所需对象的this
指针绑定到函数
最好通过一个例子来说明这一点:
假设您有一个类Fruit
,它有一个方法printName
。现在我们知道您可以将方法保存到变量中,让我们尝试一下。在下面的示例中,我们分配了两件事:
- 使用
bind
boundMethod
unboundMethod
没有使用bind
class Fruit {
constructor() {
this.name = 'apple';
}
printName() {
console.log(this.name);
}
}
const myFruit = new Fruit();
// take the method `printName`
const boundMethod = myFruit.printName.bind(myFruit);
const unboundMethod = myFruit.printName;
boundMethod(); // works
unboundMethod(); // doesn't work
那么当你不打电话给bind
时会发生什么?为什么这行不通?
如果在这种情况下不调用 bind,则可以认为存储在标识符unboundMethod
中的函数的值为:
// doens't work
const unboundMethod = function() {
console.log(this.name);
}
其中函数的内容与Fruit
类中printName
的方法的内容相同。你明白为什么这是一个问题吗?
因为this
指针仍然存在,但它要引用的对象不再在范围内。当您尝试调用unboundMethod
时,您会收到一个错误,因为它无法在this
中找到name
。
那么当你使用bind
时会发生什么?
松散地bind
可以被认为是用你传递给bind
的对象替换函数的this
值。
因此,如果我将:myFruit.printName.bind(myFruit)
分配给boundMethod
,那么您可以像这样考虑分配:
// works
const boundMethod = function() {
console.log(myFruit.name);
}
其中this
替换为myFruit
底线/TL;博士
何时在事件处理程序中使用
bind
当您想用另一个对象/指针替换函数内的this
es 时,您需要使用Function.prototype.bind
。如果你的函数从不使用this
,那么你不需要使用bind
。
那么为什么我们不需要在第一个例子中使用绑定呢?
如果你不需要"取方法"(即取function
类型的值),那么你也不需要使用bind
另一种说法是:如果你直接从对象调用该方法,你不需要bind
同一个对象。
在包装函数中,您直接调用对象的方法(如表达式 2 中所示)。因为您在调用该方法时没有"采用方法"(我们在 Fruit 示例中将方法"采用"到变量中),因此无需使用bind
。
printButton.addEventListener('click', function(){
// directly invoke the function
// no method "taking" here
obj.printName();
});
希望这对:D有所帮助
注意:由于要传递函数,因此您需要在obj.printName()
中调用不带括号的printButton.addEventListener('click', obj.printName() );
。
答案在于 Javascript 中绑定this
的方式。在 JS 中,调用函数的方式决定了this
的绑定方式。因此,当您提供如下所示的回调函数时:
printButton.addEventListener('click', function(){
obj.printName();
});
注意,printName 是通过dot
表示法调用的。当this
绑定到点之前的对象时,这称为隐式绑定规则,在本例中为obj
。显然,在这种情况下,您将获得预期的输出。
但是,当您这样称呼它时:
printButton.addEventListener('click', obj.printName );
请注意,您传递的只是 obj 内部函数的address
。因此,在这种情况下,有关obj的信息将丢失。换句话说,回调函数的代码没有可用于设置this
的有关 obj 的信息。它所拥有的只是要调用的函数的地址。
希望这有帮助!
编辑: 看看这个我称之为模仿本机绑定bind2
crude
实现。这只是为了说明本机bind
函数如何返回新函数。
Function.prototype.bind2 = function (context) {
var callBackFunction = this;//Store the function to call later
return function () { //return a new function
callBackFunction.call(context);//Later when called, apply
//context, this is `obj` passed
//in bind2()
}
};
function hello() {
alert(this.name);
}
obj = {
name:'ABC'
};
var f = hello.bind2(obj);
f();
注意:函数 f() 在这里是如何hard bound
的。 f() 与obj
进行了硬绑定this
。您现在无法将this
更改为obj
以外的其他。这是绑定的另一件事,可能会帮助您了解。