关于Function.prototype.bind

作者: 李轻舟 | 来源:发表于2019-03-09 08:44 被阅读5次

    bind()方法会创建一个新函数,称为绑定函数。当调用这个绑定函数时,绑定函数会以创建它时传入bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

    实际使用中我们经常会碰到这样的问题:

    function Person(name){
      this.nickname = name;
      this.distractedGreeting = function() {
        setTimeout(function(){
          console.log("Hello, my name is " + this.nickname);
        }, 500);
      }
    }
    var alice = new Person('Alice');
    alice.distractedGreeting();
    //Hello, my name is undefined
    

    这个时候输出的this.nickname是undefined,原因是this指向是在运行函数时确定的,而不是定义函数时候确定的,再因为setTimeout在全局环境下执行,所以this指向setTimeout的上下文:window

    以前解决这个问题的办法通常是缓存this,例如:

    function Person(name){
      this.nickname = name;
      this.distractedGreeting = function() {
        var self = this; // <-- 注意这一行!
        setTimeout(function(){
          console.log("Hello, my name is " + self.nickname); // <-- 还有这一行!
        }, 500);
      }
    }
    var alice = new Person('Alice');
    alice.distractedGreeting();
     // after 500ms logs "Hello, my name is Alice"
    

    这样就解决了这个问题,非常方便,因为它使得setTimeout函数中可以访问Person的上下文。但是看起来稍微一种蛋蛋的忧伤。
    但是现在有一个更好的办法!您可以使用bind。上面的例子中被更新为:

    function Person(name){
      this.nickname = name;
      this.distractedGreeting = function() {
        setTimeout(function(){
          console.log("Hello, my name is " + this.nickname);
        }.bind(this), 500); // <-- this line!
      }
    }
    var alice = new Person('Alice');
    alice.distractedGreeting();
    // after 500ms logs "Hello, my name is Alice"
    

    bind() 最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的 this 值。
    JavaScript新手经常犯的一个错误是将一个方法从对象中拿出来,然后再调用,希望方法中的 this 是原来的对象。(比如在回调中传入这个方法。)如果不做特殊处理的话,一般会丢失原来的对象。从原来的函数和原来的对象创建一个绑定函数,则能很漂亮地解决这个问题:

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

    浏览器支持:

    Browser Version support
    Chrome 7
    Firefox (Gecko) 4.0 (2)
    Internet Explorer 9
    Opera 11.60
    Safari 5.1.4

    很不幸,Function.prototype.bind 在IE8及以下的版本中不被支持,所以如果你没有一个备用方案的话,可能在运行时会出现问题。bind 函数在 ECMA-262 第五版才被加入;它可能无法在所有浏览器上运行。你可以部份地在脚本开头加入以下代码,就能使它运作,让不支持的浏览器也能使用 bind() 功能。

    幸运的是,MDN为没有自身实现 .bind() 方法的浏览器提供了一个绝对可靠的替代方案:

    if (!Function.prototype.bind) {
      Function.prototype.bind = function (oThis) {
        if (typeof this !== "function") {
        // closest thing possible to the ECMAScript 5 internal IsCallable function
          throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
        }
    
        var aArgs = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP && oThis
          ? this
          : oThis || window,
          aArgs.concat(Array.prototype.slice.call(arguments)));
        };
    
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
        return fBound;
      };
    }
    

    参考阅读:
    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

    http://www.smashingmagazine.com/2014/01/23/understanding-javascript-function-prototype-bind/

    http://krasimirtsonev.com/blog/article/JavaScript-bind-function-setting-a-scope

    语法

    fun.bind(thisArg[, arg1[, arg2[, …]]])

    参数

    thisArg

    当绑定函数被调用时,该参数会作为原函数运行时的 this 指向.当使用new 操作符调用绑定函数时,该参数无效.

    arg1, arg2, …

    当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数.

    描述

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

    相关文章

      网友评论

        本文标题:关于Function.prototype.bind

        本文链接:https://www.haomeiwen.com/subject/gxnrpqtx.html