手写 call、apply、bind
call
call 的作用就一句话:call 能够显式地绑定函数中的 this 并调用函数。
参数
javascript1function.call(thisArg, arg1, arg2, ...)
例子
javascript1function foo(){ 2 console.log(this.age) 3} 4const obj={age:'18'} 5foo() // undefined 6foo.call(obj) // 18
上面的例子中,call 显示地将 obj 绑定到 foo 函数中的 this 上,所以能够打印出 obj 的属性 age
了解原理后,我们直接开写
javascript1Function.prototype.likeCall=function (thisArg,...args){ 2 thisArg.func=this //这里的 this 是 foo.call 中的 foo 函数,谁调用call,this 就是谁 3 const result=thisArg.func(...args) 4 delete thisArg.func //记得删除 5 return result 6}
有了 ES6 的语法加持,我们很快就可以实现。
apply
apply 跟 call 的唯一区别就是 apply 第二个参数接收一个数组
参数
javascript1function.apply(thisArg, arrayArg)
我们直接改写就行
javascript1Function.prototype.likeApply=function (thisArg,args){ 2 thisArg.func=this //这里的 this 是 foo.apply 中的 foo 函数,谁调用apply,this 就是谁 3 const result=thisArg.func(...args) 4 delete thisArg.func //记得删除 5 return result 6}
bind
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。——MDN
示例
javascript1this.x = 9; // 在浏览器中,this 指向全局的 "window" 对象 2var module = { 3 x: 81, 4 getX: function() { return this.x; } 5}; 6 7module.getX(); // 81 8 9var retrieveX = module.getX; 10retrieveX(); 11// 返回 9 - 因为函数是在全局作用域中调用的 12 13// 创建一个新函数,把 'this' 绑定到 module 对象 14// 新手可能会将全局变量 x 与 module 的属性 x 混淆 15var boundGetX = retrieveX.bind(module); 16boundGetX(); // 81
- 第一种方法 诸如 call 的实现,只是需要返回一个函数
javascript1Function.prototype.likeBind=function (thisArg,...args1){ 2 thisArg.func=this 3 return (...args2)=>{ 4 thisArg.func(...args1,...args2) 5 delete thisArg.func 6 } 7}
这样基于柯里化的方式,不但可以在绑定 bind 调用时传递第二个参数,还可以在调用后加参数。
js1const boy = { name: 'qiuyanxi' } 2function foo(args) { 3 console.log(this.name) 4 console.log(args) 5} 6foo.likeBind(boy, '123')() 7foo.likeBind(boy)('123') 8// 'qiuyanxi' 9// '123'
只是这个方法缺点也很显而易见,当没调用 bind 方法返回的函数时,func 存在于传递的 thisArg 中。
- 第二种方法 我们使用 bind 的时候都是采用 function.bind(obj)的方式,这样就能够让 obj 变成 fn 函数中的 this,所以我们可以把 obj 当成 call 参数直接传给 fn,不就可以实现 bind 了吗?
javascript1Function.prototype.likeBind=function (asThis,...args){ 2 const fn=this //这里是把取到调用bind的fn 3 return (...args1)=>{ 4 return fn.likeCall(asThis,...args,...args1) 5 } 6}
- 第三种方法 第三种方法采用 es5 来写,实际原理是一样的,这里顺便套用一下之前写好的 likeCall 和 likeApply
js1Function.prototype.likeBind = function () { 2 var fn = this 3 var args = Array.prototype.slice.likeCall(arguments) //由于 arguments 没有 Array 的原型,所以用这种方式调用 slice 方法 4 var asThis = args[0] //获取到要绑定的this 5 var args1 = args.slice(1) //获取到参数1 6 return function () { 7 var args2 = Array.prototype.slice.likeCall(arguments) //获取到参数2 8 return fn.likeApply(asThis, args1.concat(args2)) 9 } 10}
完成了~
enjoy!