认识Function.prototype.bind
本文由 小茗同学 发表于 2016-09-22 浏览(661)
最后修改 2018-05-02 标签:function prototype bind

先来看个例子

假设现有一个方法test,它接受一个参数str,现在我有3个现成的参数,我需要根据它们生成3个定制的无参方法:

function test(str){console.log(str);}
var params = ['aaa', 'bbb', 'ccc'];
var functions = [];
for(var i=0; i<params.length; i++)
{
	functions[i] = (function(str){return function(){test(str)};})(params[i]);
}
for(var i=0; i<functions.length; i++) functions[i](); // 测试

这个例子没啥实际意义,可以看看这个有点意义的例子:http://blog.liuxianan.com/async-function-transport-value.html

上面的例子作用无非就是给一个指定的方法绑定参数

bind方法介绍

语法:

function.bind(新函数上下文, 参数1, 参数2, 参数n...);

作用:
创建一个新函数,当这个新函数被调用时,它的this值是传递给bind()的第一个参数, 它的参数是bind()的剩余其他参数和其原本的参数的合并.

示例:

function testBind(p1, p2, p3, p4)
{
	console.log(this, p1, p2, p3, p4);
}
var newFn = testBind.bind(window, '我是第1个参数', '我是第2个参数');
newFn();
newFn('我是第3个参数', '我是第4个参数');

现在,我们可以直接使用Function.prototype.bind来更简单地实现最前面的例子:

function test(str){console.log(str);}
var params = ['aaa', 'bbb', 'ccc'];
var functions = [];
for(var i=0; i<params.length; i++)
{
	functions[i] = test.bind(this, params[i]);
}
for(var i=0; i<functions.length; i++) functions[i](); // 测试

兼容性与Polyfill

此方法总体来说兼容性比较好,在 ECMA-262 第五版被加入。

根据自己的理解简单实现一个bind方法:

Function.prototype.mybind = function(thisArg) {
	// 注意arguments是一个伪数组
	var fn = this, bindArgs = [].slice.call(arguments, 1);
	return function() {
		return fn.apply(thisArg, bindArgs.concat([].slice.call(arguments)));
	};
};
function test(a, b, c){console.log(this, a, b, c);}
test.mybind(this, 1)(2, 3); // 测试

下面是火狐MDN上实现的Polyfill

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

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

	return fBound;
  };
}

需要注意的是,上述算法和浏览器的实现算法还是有一些差别的,尽管可能还有其他不同之处,却绝大部分情况下没必要去管。

参考资料

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind