理解Javascript Bind()方法

对于初学者来说绑定函数可能是不怎么受关心的函数之一,但是当你将一个方法从对象中拿出来,然后再调用,希望方法中的this还是原来的对象时,如果不做特殊处理,一般会丢失原来的对象。这个时候其实你只需要一个Function.prototype.bind()就能解决。

当你第一次碰到这种情况的时候,你的想法可能是将this赋给一个变量,然后你就可以在你改变内容的时候引用它。大部分人会选择self_this或者是context来作为一个变量名称。这种方法是可行的,但是Function.prototype.bind()是一种更好更直接的方法。

this赋给变量的方法示例

    var myObj = {

        specialFunction: function () {

        },

        anotherSpecialFunction: function () {

        },

        getAsyncData: function (cb) {
            cb();
        },

        render: function () {
            var that = this;
            this.getAsyncData(function () {
                that.specialFunction();
                that.anotherSpecialFunction();
            });
        }
    };
myObj.render();

如果我们render函数没有将this赋值给变量that,而是写成下面这种形式:

    render: function () {
            this.getAsyncData(function () {
                this.specialFunction();
                this.anotherSpecialFunction();
            });
        }

那么就会产生一个错误:

Uncaught TypeError: this.specialFunction is not a function
    at <anonymous>:17:18
    at Object.getAsyncData (<anonymous>:12:9)
    at Object.render (<anonymous>:16:14)
    at <anonymous>:23:7

我们需要在回调函数被调用的时候,确保render方法中引用的对象还是myObj。这个时候使用that变量来暂存这个this可以解决问题,但是采用Function.prototype.bind()可以更简洁一点。

我们来用bind方法重写一下这个示例:

    render: function(){
        this.getAsyncData(function(){
            this.specialFunction();
            this.anotherSpecialFunction();
        }.bind(this));
    }

我们为什么要这么做

我们来看一下MDN文档中bind函数的描述

fun.bind(thisArg[, arg1[, arg2[, ...]]])
参数

thisArg

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

当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法
返回值

返回由指定的this值和初始化参数改造的原函数拷贝

描述

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

我来分析一下示例,在例子中,bind()函数被调用时,创建了一个新函数,然后把thismyObj的内容传递给bind()函数。当回调函数被执行时,bind()函数把这个this提供给原函数。

Function.prototype.bind()的实现

    Fuction.prototype.bind = function(scope){
        var fn = this;
        return function(){
            return fn.apply(scope);
        };
    }

一个简单的使用示例

    var foo = {
        x: 3;
    }

    var bar = function(){
        console.log(this.x);
    }

    bar();//undefined

    var boundFunc = bar.bind(foo);

    boundFunc();//3

第一次bar()执行的时候,console.log(this.x)里面的this指向的是全局作用域
bind方法执行的以后,把this指向了foo而不是全局作用域。

浏览器支持

BroswerVersion Support
Chrome7
Firefox(Gecko)4.0(2)
Internet Explorer9
Opera11.60
Safari5.1.4

虽然Function.prototype.bind不能在IE8及以下的版本使用,但是Mozilla Developer Network提供了另一种可靠方法。

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,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

用法示例

1.创建绑定函数

bind() 最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的 this 值。

this.x = 9; 
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 返回 81

var retrieveX = module.getX;
retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域

// 创建一个新函数,将"this"绑定到module对象
// 新手可能会被全局的x变量和module里的属性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81

2.偏函数

bind()的另一个最简单的用法是使一个函数拥有预设的初始参数。这些参数(如果有的话)作为bind()的第二个参数跟在this(或其他对象)后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。

function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

// Create a function with a preset leading argument
var leadingThirtysevenList = list.bind(undefined, 37);

var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]

3.配合setTimeout

在默认情况下,使用 window.setTimeout() 时,this 关键字会指向 window (或全局)对象。当使用类的方法时,需要 this 引用类的实例,你可能需要显式地把 this 绑定到回调函数以便继续使用实例。

function LateBloomer() {
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
  window.setTimeout(this.declare.bind(this), 1000);
};

LateBloomer.prototype.declare = function() {
  console.log('I am a beautiful flower with ' +
    this.petalCount + ' petals!');
};

var flower = new LateBloomer();
flower.bloom();  // 一秒钟后, 调用'declare'方法

4.作为构造函数使用的绑定函数

这是bind()的超前用法,在生产环境中请谨慎使用
自然而然地,绑定函数适用于用new操作符 new去构造一个由目标函数创建的新的实例。当一个绑定函数是用来构建一个值的,原来提供的 this就会被忽略。然而, 原先提供的那些参数仍然会被前置到构造函数调用的前面。

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function() { 
  return this.x + ',' + this.y; 
};

var p = new Point(1, 2);
p.toString(); // '1,2'

var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);
// 以下这行代码在 polyfill 不支持,
// 在原生的bind方法运行没问题:
//(译注:polyfill的bind方法如果加上把bind的第一个参数,即新绑定的this执行Object()来包装为对象,Object(null)则是{},那么也可以支持)
var YAxisPoint = Point.bind(null, 0/*x*/);

var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'

axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new Point(17, 42) instanceof YAxisPoint; // true

你知道不需要做特别的处理就可以用new操作符 new创建一个绑定函数。必然地,你需要知道不需要做特别处理就可以创建一个可以被直接调用的绑定函数,即使你更希望绑定函数是用new操作符 new 来调用。

// 这个例子可以直接在你的 javascript 控制台运行
// ...接着上面的代码继续(译注:

// 仍然能作为一个普通函数来调用
// (即使通常来说这个不是被期望发生的)
YAxisPoint(13);

emptyObj.x + ',' + emptyObj.y;   //  '0,13'

5.快捷调用

在你想要为一个需要特定的 this 值的函数创建一个捷径(shortcut)的时候,bind() 方法也很好用。

你可以用 Array.prototype.slice 来将一个类似于数组的对象(array-like object)转换成一个真正的数组,就拿它来举例子吧。你可以创建这样一个捷径:

var slice = Array.prototype.slice;

// ...

slice.apply(arguments);

bind() 可以使这个过程变得简单。在下面这段代码里面,sliceFunction.prototypeapply()方法的绑定函数,并且将 Array.prototypeslice() 方法作为 this的值。这意味着我们压根儿用不着上面那个 apply()调用了。

// same as "slice" in the previous example
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.apply.bind(unboundSlice);

// ...

slice(arguments);

引用

原文地址:https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/

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

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值