• 安全-js的bind方法


    目录

    bind

    bind()方法有一些使用注意点

    (1)每一次返回一个新函数

    (2)结合回调函数使用

    (3)结合call()方法使用


    bind

    bind()方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。

    1. var d = new Date();
    2. d.getTime() // 1481869925657
    3. var print = d.getTime;
    4. print() // Uncaught TypeError: this is not a Date object.

    上面代码中,我们将d.getTime()方法赋给变量print,然后调用print()就报错了。这是因为getTime()方法内部的this,绑定Date对象的实例,赋给变量print以后,内部的this已经不指向Date对象的实例了。

    bind()方法可以解决这个问题。

    1. var print = d.getTime.bind(d);
    2. print() // 1481869925657

    上面代码中,bind()方法将getTime()方法内部的this绑定到d对象,这时就可以安全地将这个方法赋值给其他变量了。

    bind方法的参数就是所要绑定this的对象,下面是一个更清晰的例子。

    1. var counter = {
    2. count: 0,
    3. inc: function () {
    4. this.count++;
    5. }
    6. };
    7. var func = counter.inc.bind(counter);
    8. func();
    9. counter.count // 1

    上面代码中,counter.inc()方法被赋值给变量func。这时必须用bind()方法将inc()内部的this,绑定到counter,否则就会出错。

    this绑定到其他对象也是可以的。

    1. var counter = {
    2. count: 0,
    3. inc: function () {
    4. this.count++;
    5. }
    6. };
    7. var obj = {
    8. count: 100
    9. };
    10. var func = counter.inc.bind(obj);
    11. func();
    12. obj.count // 101

    上面代码中,bind()方法将inc()方法内部的this,绑定到obj对象。结果调用func函数以后,递增的就是obj内部的count属性。

    bind()还可以接受更多的参数,将这些参数绑定原函数的参数。

    1. var add = function (x, y) {
    2. return x * this.m + y * this.n;
    3. }
    4. var obj = {
    5. m: 2,
    6. n: 2
    7. };
    8. var newAdd = add.bind(obj, 5);
    9. newAdd(5) // 20

    上面代码中,bind()方法除了绑定this对象,还将add()函数的第一个参数x绑定成5,然后返回一个新函数newAdd(),这个函数只要再接受一个参数y就能运行了。

    如果bind()方法的第一个参数是nullundefined,等于将this绑定到全局对象,函数运行时this指向顶层对象(浏览器为window)。

    1. function add(x, y) {
    2. return x + y;
    3. }
    4. var plus5 = add.bind(null, 5);
    5. plus5(10) // 15

    上面代码中,函数add()内部并没有this,使用bind()方法的主要目的是绑定参数x,以后每次运行新函数plus5(),就只需要提供另一个参数y就够了。而且因为add()内部没有this,所以bind()的第一个参数是null,不过这里如果是其他对象,也没有影响。

    bind()方法有一些使用注意点

    (1)每一次返回一个新函数

    bind()方法每运行一次,就返回一个新函数,这会产生一些问题。比如,监听事件的时候,不能写成下面这样。

    element.addEventListener('click', o.m.bind(o));

    上面代码中,click事件绑定bind()方法生成的一个匿名函数。这样会导致无法取消绑定,所以下面的代码是无效的。

    element.removeEventListener('click', o.m.bind(o));

    正确的方法是写成下面这样:

    1. var listener = o.m.bind(o);
    2. element.addEventListener('click', listener);
    3. // ...
    4. element.removeEventListener('click', listener);

    (2)结合回调函数使用

    回调函数是 JavaScript 最常用的模式之一,但是一个常见的错误是,将包含this的方法直接当作回调函数。解决方法就是使用bind()方法,将counter.inc()绑定counter

    1. var counter = {
    2. count: 0,
    3. inc: function () {
    4. 'use strict';
    5. this.count++;
    6. }
    7. };
    8. function callIt(callback) {
    9. callback();
    10. }
    11. callIt(counter.inc.bind(counter));
    12. counter.count // 1

    上面代码中,callIt()方法会调用回调函数。这时如果直接把counter.inc传入,调用时counter.inc()内部的this就会指向全局对象。使用bind()方法将counter.inc绑定counter以后,就不会有这个问题,this总是指向counter

    还有一种情况比较隐蔽,就是某些数组方法可以接受一个函数当作参数。这些函数内部的this指向,很可能也会出错。

    1. var obj = {
    2. name: '张三',
    3. times: [1, 2, 3],
    4. print: function () {
    5. this.times.forEach(function (n) {
    6. console.log(this.name);
    7. });
    8. }
    9. };
    10. obj.print()
    11. // 没有任何输出

    上面代码中,obj.print内部this.timesthis是指向obj的,这个没有问题。但是,forEach()方法的回调函数内部的this.name却是指向全局对象,导致没有办法取到值。稍微改动一下,就可以看得更清楚。

    1. obj.print = function () {
    2. this.times.forEach(function (n) {
    3. console.log(this === window);
    4. });
    5. };
    6. obj.print()
    7. // true
    8. // true
    9. // true

    解决这个问题,也是通过bind()方法绑定this

    1. obj.print = function () {
    2. this.times.forEach(function (n) {
    3. console.log(this.name);
    4. }.bind(this));
    5. };
    6. obj.print()
    7. // 张三
    8. // 张三
    9. // 张三

    (3)结合call()方法使用

    利用bind()方法,可以改写一些 JavaScript 原生方法的使用形式,以数组的slice()方法为例。

    1. [1, 2, 3].slice(0, 1) // [1]
    2. // 等同于
    3. Array.prototype.slice.call([1, 2, 3], 0, 1) // [1]

    上面的代码中,数组的slice方法从[1, 2, 3]里面,按照指定的开始位置和结束位置,切分出另一个数组。这样做的本质是在[1, 2, 3]上面调用Array.prototype.slice()方法,因此可以用call方法表达这个过程,得到同样的结果。

    call()方法实质上是调用Function.prototype.call()方法,因此上面的表达式可以用bind()方法改写。

    1. var slice = Function.prototype.call.bind(Array.prototype.slice);
    2. slice([1, 2, 3], 0, 1) // [1]

    上面代码的含义就是,将Array.prototype.slice变成Function.prototype.call方法所在的对象,调用时就变成了Array.prototype.slice.call。类似的写法还可以用于其他数组方法。

    1. var push = Function.prototype.call.bind(Array.prototype.push);
    2. var pop = Function.prototype.call.bind(Array.prototype.pop);
    3. var a = [1 ,2 ,3];
    4. push(a, 4)
    5. a // [1, 2, 3, 4]
    6. pop(a)
    7. a // [1, 2, 3]

    如果再进一步,将Function.prototype.call方法绑定到Function.prototype.bind对象,就意味着bind的调用形式也可以被改写。

    1. function f() {
    2. console.log(this.v);
    3. }
    4. var o = { v: 123 };
    5. var bind = Function.prototype.call.bind(Function.prototype.bind);
    6. bind(f, o)() // 123

    上面代码的含义就是,将Function.prototype.bind方法绑定在Function.prototype.call上面,所以bind方法就可以直接使用,不需要在函数实例上使用。

  • 相关阅读:
    return 函数
    方法参数传递时的值传递和引用传递
    使用 TinyEngine 低代码引擎实现三方物料集成
    金九银十准备季:Java IO流面试题(含答案)
    leetcode4 寻找两个正序数组的中位数
    【MYSQL】约束
    理想汽车 x JuiceFS:从 Hadoop 到云原生的演进与思考
    redis之常见的集合操作有哪些?
    修改 Ubuntu .cache 和 pip cache 默认路径
    基于 SPI 的增强式插件框架设计
  • 原文地址:https://blog.csdn.net/m0_63069714/article/details/127990149