JavaScript-bind

前言:

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。(MDN)

特点:

  • 返回一个新函数
  • 可传入参数

简言之,bind是用来改变函数的this对象的指向的。

返回函数的模拟实现:

var foo = {
    name: 'natsu'
};

function bar() {
	return this.name;
}

var bindFoo = bar.bind(foo);

console.log(bindFoo()); // natsu

模拟实现:

// 第一版
Function.prototype.bind = function (context) {
    var self = this;
    return function () {
        return self.apply(context);
    }

}

传参的模拟实现:

执行 bind 返回的函数的时候,可不可以传参呢?

var foo = {
    value: 1
};

function bar(name, age) {
    console.log(this.value, name, age);
}

var bindFoo = bar.bind(foo, 'natsu', '20');
bindFoo();

当bind()需要传入参数时,我们可以通过arguments 进行处理:

//	第二版
Function.prototype.bind = function(context) {
	var self = this;
	// 获取bind2函数从第二个参数到最后一个参数
	var args = Array.prototype.slice.call(arguments, 1);
	return function() {
		// 这个时候的arguments是指bind返回的函数传入的参数
		var bindArgs = Array.prototype.slice.call(arguments);
		self.apply(context, args.concat(bindArgs ));
	};
};

构造函数效果的模拟实现

bind 还有一个特点,就是:

一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。也就是说当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效。

例子:

var value = 2;

var foo = {
    value: 1
};

function bar(name, age) {
    this.habit = 'learing';
    console.log(this.value, name, age);
}

bar.prototype.friend = 'cc';

var bindFoo = bar.bind(foo, 'natsu');

var obj = new bindFoo('20');
// undefined	natsu	20

console.log(obj.habit, obj.friend);
// learing	cc

注意:尽管在全局和 foo 中都声明了 value 值,最后依然返回了 undefined,说明绑定的 this 失效了,如果大家了解 new 的模拟实现,就会知道这个时候的 this 已经指向了 obj。

Function.prototype.bind = function() {
	var self = this;
	// 获取bind2函数从第二个参数到最后一个参数
	var args = Array.prototype.slice.call(arguments, 1);
	var	fBound = function() {
		// 这个时候的arguments是指bind返回的函数传入的参数
		var bindArgs = Array.prototype.slice.call(arguments);
		 // 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
		 // 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
		 return self.apply(this instanceof fBound ? this : context, args.concat(bindArgs));
	};
	// 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值
    fBound.prototype = this.prototype;
    return fBound;
};

构造函数效果的优化实现:

我们直接将 fBound.prototype = this.prototype,我们直接修改 fBound.prototype 的时候,也会直接修改绑定函数的 prototype。

Function.prototype.bind = function() {
	var self = this;
	// 获取bind2函数从第二个参数到最后一个参数
	var args = Array.prototype.slice.call(arguments, 1);
	var fNOP = function () {};
	
	var	fBound = function() {
		// 这个时候的arguments是指bind返回的函数传入的参数
		var bindArgs = Array.prototype.slice.call(arguments);
		 // 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
		 // 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
		 return self.apply(this instanceof fBound ? this : context, args.concat(bindArgs));
	};
	// 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值
   	fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
};

问题:

  1. apply 这段代码跟 MDN 上的稍有不同

在 MDN 中文版讲 bind 的模拟实现时,apply 这里的代码是:

self.apply(this instanceof self ? this : context || this, args.concat(bindArgs))

多了一个关于 context 是否存在的判断,然而这个是错误的!

var value = 2;
var foo = {
    value: 1,
    bar: bar.bind(null)
};

function bar() {
    console.log(this.value);
}

foo.bar() // 2

以上代码正常情况下会打印 2,如果换成了 context || this,这段代码就会打印 1!

  1. 调用 bind 的不是函数咋办?
if (typeof this !== "function") {
  throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
  1. 兼容
Function.prototype.bind = Function.prototype.bind || function () {
    ……
};

总结:

Function.prototype.bind = function (context) {

    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);

    var fNOP = function () {};

    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }

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

参考:
MDN

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值