zoukankan      html  css  js  c++  java
  • JavaScript中的bind方法及其常见应用

    一、bind()方法的实现

      在JavaScript中,方法往往涉及到上下文,也就是this,因此往往不能直接引用。就拿最常见的console.log("info…")来说,避免书写冗长的console,直接用log("info…")代替,不假思索的会想到如下语法:

    var log = console.log;
    log("info");

      很遗憾,运行报错:TypeError: Illegal invocation。

      原因很清楚:对于console.log("info…")而言,log方法在console对象上调用,因此log方法中的this指向console对象,而我们用log变量指向console.log方法,然后直接调用log方法,此时log方法的this指向的是window对象,上下文不一致,当然会报错了。

      此时我们可以用bind方法解决这个问题。bind方法允许手动传入一个this,作为当前方法的上下文,然后返回持有上下文的方法。例如:

    var write = document.write.bind(document);
    write("hello");

      这样就不会报错了。但是,bind方法并不支持ie 8以及更低版本的浏览器,我们完全可以自己实现一个,很简单。

    Function.prototype.bind = Function.prototype.bind || function(context){ 
        var _this = this;   
        return function(){ 
            _this.apply(context, arguments); 
        }; 
    };

      核心就是通过apply方法实现,闭包的经典应用。_this指向当前方法,context指向当前方法的上下文,二者均通过闭包访问。

      bind所做的就是自动封装函数在函数自己的闭包中,这样我们可以捆绑上下文(this关键字)和一系列参数到原来的函数。你最终得到的是另一个函数指针。

    function add(a,b){
        return a + b;
    }
    var newFoo = add.bind(this,3,4);

      请注意,我们不仅捆绑this到newFoo()函数,而且我们也捆绑了两个参数。所以,当我们调用newFoo()的时候,返回值将是7。但是,如果我们在调用之前newFoo更改的参数的话,会发生什么?

      如果我们使用变量绑定参数到newFoo(),然后在调用newFoo()前改变变量,你觉得值会变为什么呢?

    function add(a,b){
        return a + b;
    }
    var a = 3;
    var b = 4;
    var newFoo = add.bind(this,a, b);
    a = 6;
    b = 7;
    console.log(newFoo());

      返回值仍然是7,因为bind()绑定的是参数的值,而不是实际变量的值。这是好消息,我们可以在代码中利用这个巨大的优势。

    二、bind()的应用:

    1、绑定函数的this值

      bind()最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的this值。常见的错误就像上面的例子一样,将方法从对象中拿出来,然后调用,并且希望this指向原来的对象。如果不做特殊处理,一般会丢失原来的对象。使用bind()方法能够很漂亮的解决这个问题:

    this.num = 9; 
    var mymodule = {
      num: 81,
      getNum: function() { return this.num; }
    };
    
    module.getNum(); // 81
    
    var getNum = module.getNum;
    getNum(); // 9, 因为在这个例子中,"this"指向全局对象
    
    // 创建一个'this'绑定到module的函数
    var boundGetNum = getNum.bind(module);
    boundGetNum(); // 81

      改变对象方法里this的值

      改变事件处理函数里的this值,因为在事件处理函数中的this指向的是dom元素,在某些情况下我们需要改变这个this值

    2、偏函数实现

      截取一段关于偏函数的定义:

    Partial application can be described as taking a function that accepts some number of arguments, 
    binding values to one or more of those arguments,

    and returning a new function that only accepts the remaining, un-bound arguments.

      这是一个很好的特性,使用bind()我们可以设定函数的预定义参数,然后调用的时候传入其他参数即可。

    //使用bind,我们就可以像这样写代码实现Currying:
    function add(a,b,c) {
      return a+b+c;
    }
    var addAgain = add.bind(this, 1, 2);
    var result = addAgain(3);
    function list() {
      return Array.prototype.slice.call(arguments);
    }
    
    var list1 = list(1, 2, 3); // [1, 2, 3]
    
    // 预定义参数37
    var leadingThirtysevenList = list.bind(undefined, 37);
    
    var list2 = leadingThirtysevenList(); // [37]
    var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]

    3、在定时器中使用,比如和setTimeout一起使用

      一般情况下setTimeout()的this指向window或global对象。当使用类的方法时需要this指向类实例,就可以使用bind()将this绑定到回调函数来管理实例。

    4、bind用于事件处理程序

      当一个事件处理程序被调用时,它访问的上下文会生成事件,而不是在创建事件处理程序的对象中。通过使用bind,可以肯定的是,函数会被访问正确的上下文。

    三、多次绑定bind方法

      如果使用bind()方法多次绑定,最终得到的this会是哪个绑定的呢?
    function say() {  
        alert(this.x);  
    };  
    var a = say.bind({x: 1});  
    var b = a.bind({x: 2});  
    b(); // 这里会输出1还是2呢?

      那么我们不妨分析一下:

    //say函数使用bind方法,穿进去了一个对象,相当于
    var a = function() {  
        return say.apply({x: 1});  
    };  
    
    //如果我们对得到的函数a再进行绑定,则相当于
    var b = function() {  
        return a.apply({x: 2});  
    };  
    即
    var b = function() {  
        return function() {  
            return say.apply({x: 1});  
        }.apply({x: 2});  
    };  
      这样虽然我们改变了函数a里this的值,但是最后函数say里的this的值还是由第一次绑定时的参数决定,而与函数a中的this值无关。

    1、多次绑定的结果

      所以无论使用bind绑定多少次,最终原函数的this值是由第一次绑定传的参数决定的

     2、多次绑定参数的顺序

    function say() {  
        alert(this.x);  
    };  
    var a = say.bind({x: 1},1,2,3);  
    var b = a.bind({x: 2},4,5,6);  
    a(7,8,9);  
    b(7,8,9);   
    // 此时原函数say参数的顺序的怎样的呢?  
    // 是[4,5,6,1,2,3,7,8,9]还是[1,2,3,4,5,6,7,8,9] 

      首先对say使用bind方法,会改变函数say的this值,和“内置”参数。所以 a(7,8,9) 的参数组成是:内置的参数 + 调用时传入的参数 = 最终函数,即[1,2,3]+ [7,8,9] = [1,2,3,7,8,9]

      而对函数a使用bind方法,只会改变函数a的this值,和往函数a里“内置”参数。所以 b(7,8,9) 的参数组成是:[1,2,3](在函数say内置的参数) + [4,5,6](在函数a内置的参数) + [7,8,9] = [1,2,3,4,5,6,7,8,9]
      总结:对哪个函数使用bind()方法即改变这个函数的this值,和内置其参数,或者说像克里化一样理解,先预置好参数
    var a = say.bind({x:1},1,2,3); // 是改变函数say的this值,和在函数say上预置参数1,2,3  
    var b = a.bind({x: 2}, 4,5,6); // 是改变函数a的this,和在函数a上预置预置参数4,5,6 

     

  • 相关阅读:
    leetcode 之Jump Game
    leetcode 之 Symmetric Tree
    leetcode 之 House Robber
    设计模式之建造者模式
    centos7 yum tab 补全
    设计模式之适配器模式
    设计模式之状态模式
    设计模式之外观模式
    设计模式之模板方法模式
    对以<uses-permission android:maxSdkVersion="xx" /> 中的说明
  • 原文地址:https://www.cnblogs.com/goloving/p/8542781.html
Copyright © 2011-2022 走看看