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 48. Rotate Image
    leetcode 203. Remove Linked List Elements 、83. Remove Duplicates from Sorted List 、82. Remove Duplicates from Sorted List II(剑指offer57 删除链表中重复的结点) 、26/80. Remove Duplicates from Sorted ArrayI、II
    leetcode 263. Ugly Number 、264. Ugly Number II 、313. Super Ugly Number 、204. Count Primes
    leetcode 58. Length of Last Word
    安卓操作的一些问题解决
    leetcode 378. Kth Smallest Element in a Sorted Matrix
    android studio Gradle Build速度加快方法
    禁用gridview,listview回弹或下拉悬停
    Android Studio找不到FragmentActivity类
    安卓获取ListView、GridView等滚动的距离(高度)
  • 原文地址:https://www.cnblogs.com/goloving/p/8542781.html
Copyright © 2011-2022 走看看