手写 call、apply、bind

call

call 的作用就一句话:call 能够显式地绑定函数中的 this 并调用函数。

参数

javascript
1function.call(thisArg, arg1, arg2, ...)

例子

javascript
1function foo(){
2  console.log(this.age)
3}
4const obj={age:'18'}
5foo() // undefined
6foo.call(obj) // 18

上面的例子中,call 显示地将 obj 绑定到 foo 函数中的 this 上,所以能够打印出 obj 的属性 age

了解原理后,我们直接开写

javascript
1Function.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 第二个参数接收一个数组

参数

javascript
1function.apply(thisArg, arrayArg)

我们直接改写就行

javascript
1Function.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

示例

javascript
1this.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 的实现,只是需要返回一个函数
javascript
1Function.prototype.likeBind=function (thisArg,...args1){
2  thisArg.func=this
3  return (...args2)=>{
4    thisArg.func(...args1,...args2)
5    delete thisArg.func
6  }
7}

这样基于柯里化的方式,不但可以在绑定 bind 调用时传递第二个参数,还可以在调用后加参数。

js
1const 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 了吗?
javascript
1Function.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
js
1Function.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!