今天来实现JavaScript的bind函数。 首先看MDN的bind函数描述:
从上面可以看出来,var A = B.bind(this)函数其实干了这几件事情:
- 返回一个函数,且这个函数后面运行时的this就是bind(this)传入的this
- 接收参数,这些参数(如果有的话)作为bind()的第二个参数跟在this(或其他对象)后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面
- 使用new操作bind函数返回的函数时,之前传入的this会被忽略,也就是说new的优先级高于bind
第一步
首先实现第一步:
Function.prototype.Zbind = function (othis) { var originFunc = this; return function () { originFunc.apply(othis); } } var obj = { } function createAgumon() { this.name = "agumon"; } var createAgumonBind = createAgumon.Zbind(obj); createAgumonBind(); obj;// {name: "agumon"} 复制代码
第二步
第二步考虑传参的问题,首先看看原生的bind函数是如何传参的:
var obj = { } function createAgumon(gender, age) { this.name = "agumon"; this.gender = gender; this.age = age; } var createAgumonBind = createAgumon.bind(obj, 'female'); createAgumonBind(22); 复制代码
可以看出来在bind函数中能先传部分参数,运行bind返回的函数时可以再传入部分参数。 自己实现:
Function.prototype.Zbind = function (othis) { var originFunc = this; var partArgs = [].slice.call(arguments, 1); var func = function() {}; var boundFunc = function () { var finalArgs = partArgs.concat([].slice.call(arguments)); return originFunc.apply(othis, finalArgs); } return boundFunc; } var obj = { } function createAgumon(gender, age) { this.name = "agumon"; this.gender = gender; this.age = age; } var createAgumonBind = createAgumon.Zbind(obj, 'female'); createAgumonBind(22); obj;// {name: "agumon", gender: "female", age: 22} 复制代码
第三步
使用new来调用bind返回的函数时,会忽略bind传入的thisnew操作和普通的函数调用有哪些区别?粗略的来讲,例如new F()这样的调用,有以下几个步骤:
1. 新建一个对象,var o = new Object()
1. 设置原型链,o.proto = F.prototype
1. 把F函数体内的this绑定为o,并且执行F函数的代码
1. 判断F的返回类型: 如果是值类型,则返回o 如果是引用类型,则返回该引用类型对象
开始实现:
Function.prototype.Zbind = function (othis) { var originFunc = this; var partArgs = [].slice.call(arguments, 1); var func = function() {}; var boundFunc = function () { var finalArgs = partArgs.concat([].slice.call(arguments)); return originFunc.apply(this instanceof boundFunc ? this : othis, finalArgs); } return boundFunc; } var obj = {} function createAgumon(gender, age) { this.name = "agumon"; this.gender = gender; this.age = age; } var createAgumonBind = createAgumon.Zbind(obj, 'female'); var newObj = new createAgumonBind(22); obj // {} newObj // {name: "agumon", gender: "female", age: 22} 复制代码
关键的地方在于这里:this instanceof boundFunc ? this : othis,如果是new操作的话,此时this的__proto__已经指向了boundFunc,所以使用instanceof可以检测出是否在使用new操作
小细节
原型丢失
刚刚实现的Zbind方法有个小问题:
Function.prototype.Zbind = function (othis) { var originFunc = this; var partArgs = [].slice.call(arguments, 1); var func = function() {}; var boundFunc = function () { var finalArgs = partArgs.concat([].slice.call(arguments)); return originFunc.apply(this instanceof boundFunc ? this : othis, finalArgs); } return boundFunc; } var obj = { } function createAgumon(gender, age) { this.name = "agumon"; this.gender = gender; this.age = age; } createAgumon.prototype.college = 'THU' var createAgumonBind = createAgumon.Zbind(obj, 'female'); var newObj = new createAgumonBind(22); console.log(newObj.college)// undefined 复制代码
可以看出来原型链丢失了,newObj.college得是'THU'才行
修改:
Function.prototype.Zbind = function (othis) { var originFunc = this; var partArgs = [].slice.call(arguments, 1); var func = function() {}; var boundFunc = function () { var finalArgs = partArgs.concat([].slice.call(arguments)); return originFunc.apply(this instanceof boundFunc ? this : othis, finalArgs); } func.prototype = originFunc.prototype; boundFunc.prototype = new func(); return boundFunc; } var obj = { } function createAgumon(gender, age) { this.name = "agumon"; this.gender = gender; this.age = age; } createAgumon.prototype.college = 'THU' var createAgumonBind = createAgumon.Zbind(obj, 'female'); var newObj = new createAgumonBind(22); console.log(newObj.college)// 'THU' 复制代码
为什么要使用func.prototype = originFunc.prototype;boundFunc.prototype = new func();,而不是直接用**boundFunc.prototype = originFunc.prototype;**是因为这样写的话,修改boundFunc.prototype会影响到原函数的prototype。
that'all
参考资料: