关于bind的理解

MDN上的解释

bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当新函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

看如下的一个示例

var obj = {
    age: 10,
    getAge: function () {
        return this.age;
    }
}

console.log(obj.getAge());  //10

var getAge = obj.getAge;
console.log(getAge()); //undefined

第一次打印出了10,但是第二次却是undefined,这是由于第二次的上下文没有被保存的原因,所以this指向了window对象,然而window上没有定义age属性,所以返回了undefined

但是,如果使用bind就可以轻松解决上下文保存的问题。

var getAge = obj.getAge.bind(obj);
console.log(getAge()); //10

不仅如此,bind还可以传递多个参数,如果传递了2个或2个以上的参数,那么bind会将第一个参数之后的值传递到绑定的函数参数中去。

var obj = {
    age: 10,
    getAge: function () {
        console.log(Array.prototype.slice.call(arguments)); //[10, 11, 12]
        return this.age;
    }
}

var getAge = obj.getAge.bind(obj, 10, 11 ,12);

可以看到bind中的值全部传递给了绑定函数。那么如果getAge中也传递参数呢?

getAge(100); //[10, 11, 12, 100]

会将在函数中传递的参数向后移位。那么,这有什么用处呢?

有时候,某些函数的前几个参数已经内定了预设值,后面不用再改变了,就可以使用bind传递,之后即使函数传参也不会对其造成影响。

最常见的场景是使用setTimeoutsetInterval的时候,内部的函数this总是指向window,那么此时就可以使用bind来解决这个问题。

当然,使用bind还可以玩一些新花样,如下的这个例子是经典的闭包问题。

<ul>
    <li>list1</li>
    <li>list2</li>
    <li>list3</li>
    <li>list4</li>
    <li>list5</li>
</ul>
<script>
    var list = document.getElementsByTagName('li');
    for (var i = 0; i < list.length; i++) {
        list[i].onclick = function (i) {
            console.log(i); //全部都会输出5
        };
    }
</script>

最常见的解决办法是使用闭包的方式。

for (var i = 0; i < list.length; i++) {
    (function (i) {
        list[i].onclick = function () {
            console.log(i);
        };
    }(i));
}

那么使用bind也可以解决这个问题

for (var i = 0; i < list.length; i++) {
    list[i].onclick = function (i) {
        console.log(i);
    }.bind(list[i], i);
}

再举个例子,如果说我们要添加事件到多个节点,for 循环当然没有任何问题,我们还可以 “剽窃” forEach 方法:

var unboundForEach = Array.prototype.forEach,
    forEach = Function.prototype.call.bind(unboundForEach);

forEach(document.querySelectorAll('input[type="button"]'), function (el) {
    el.addEventListener('click', fn);
});

如果每隔1秒在控制台打印1-5怎么做呢?

for(var i = 1; i <= 5; i++) {
  setTimeout(console.log.bind(console, i), i * 1000);
}

可以看到,bind的玩法有很多,更多等着你去发现。

如果您觉得本文对您有用,欢迎捐赠或留言~
微信支付
支付宝

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注