js手撕this绑定--模拟call函数的实现
第一印--模拟call函数的实现:
第一步我们向所有函数父类的原型上也就是window的Function属性原型上添加模仿的call函数
//Function原型上添加模仿的call函数 Function.prototype.imicall=function (){ console.log("模拟call:"); //这里面的this就是sum这个函数 let fn=this fn() } function sum(){ console.log("i am sum:",this); } sum.imicall()
第二步尝试用隐式绑定把函数引用的this绑定到指定传入的对象上(简单实现)
Function.prototype.imicall=function (flag){ console.log("模拟call:"); let fn=this flag.fn=fn flag.fn() delete flag.fn } function sum(){ console.log("i am sum:",this); } var obj={name:'xwl'} sum.imicall(obj)
第三步考虑边界(绑定对象的选值情况)--进一步完善this绑定
首先要知道通过call函数绑定的this类型最后都为object
function test(){ console.log(typeof this); console.log(this); console.log("i am test"); } test.call(123)
接下来考虑下null和undefined如何处理
我们先来看下结果
可以看到都是window,至于转换成对象,这个简单,直接用Object包裹就可以了
所以思路如下:如果flag有值且不为空那么就用Object包裹,否则就赋为window
Function.prototype.imicall=function (flag){ console.log("模拟call:"); if(flag!==0){ flag=flag?Object(flag):window }else{ flag=Object(flag) } let fn=this flag.fn=fn flag.fn() delete flag.fn } function sum(){ console.log("i am sum:",this); } var obj={name:'xwl'} sum.imicall(obj)
接下来我们来测试下好使不
Function.prototype.imicall=function (flag){ console.log("模拟call:"); if(flag!==0){ flag=flag?Object(flag):window }else{ flag=Object(flag) } let fn=this flag.fn=fn flag.fn() delete flag.fn } function sum(){ console.log("i am sum:",this); } var obj={name:'xwl'} sum.imicall(obj) sum.imicall(null) sum.imicall(undefined) sum.imicall()
第四步解决参数传递问题
首先想到的是我们能不能用函数自带的保存参数的arguments来做
Function.prototype.imicall=function (flag){ console.log("模拟call:"); console.log(arguments); if(flag!==0){ flag=flag?Object(flag):window }else{ flag=Object(flag) } let fn=this flag.fn=fn flag.fn() delete flag.fn } function sum(num1,num2){ console.log("i am sum:",this); console.log(num1+num2); } var obj={name:'xwl'} sum.imicall(obj,1,2)
所以我们把下标从1开始的元素推到新的数组里,再将数组展开放到执行函数参数中
Function.prototype.imicall=function (flag){ console.log("模拟call:"); let args=[] for(var i=1;i<arguments.length;i++){ args.push(arguments[i]) } if(flag!==0){ flag=flag?Object(flag):window }else{ flag=Object(flag) } let fn=this flag.fn=fn flag.fn(...args) delete flag.fn } function sum(num1,num2){ console.log("i am sum:",this); console.log(num1+num2); } var obj={name:'xwl'} sum.imicall(obj,1,2)
感觉有点麻烦对吗?
那就用es6,需要注意的是如果我们向flag后面添加个新的参数,并对其展开,那么它会是什么?
欸嘿,它直接就是数组,那就好办了,底下直接展开不就完事了!
第五步别家函数都可以显示返回值,你不也得搞一个!
Function.prototype.imicall=function (flag,...args){ console.log("模拟call:"); if(flag!==0){ flag=flag?Object(flag):window }else{ flag=Object(flag) } let fn=this flag.fn=fn var result=flag.fn(...args) console.log(result); delete flag.fn return result } function sum(num1,num2){ console.log("i am sum:",this); // console.log(num1+num2); return num1+num2 } var obj={name:'xwl'} var res=sum.imicall(obj,1,2) console.log(res);
最后一步完整版
Function.prototype.imicall=function (flag,...args){ console.log("模拟call:"); if(flag!==0){ flag=flag?Object(flag):window }else{ flag=Object(flag) } let fn=this flag.fn=fn var result=flag.fn(...args) delete flag.fn return result } function sum(num1,num2){ console.log("i am sum:",this); return num1+num2 } var obj={name:'xwl'} var res=sum.imicall(obj,1,2)