面试常撕API:call、apply、bind
前记
大家想必在那么多次的面试中总有这个见怪不怪的问题:“知道改变this指向的三个api吗?实现原理三个任选一个写出来吧”,这篇文章就是针对我在面试中碰到的这个问题进行一些整理和学习记录。
相同点和不同点
相同点
- 三个都是用于改变this指向;
- 接收的第一个参数都是this要指向的对象;
- 都可以利用后续参数传参;
不同点
- call 和 bind 传参相同,多个参数依次传入的;
- apply 只有两个参数,第二个参数为数组;
- call 和 apply 都是对函数进行直接调用,而 bind 方法不会立即调用函数,而是返回一个修改 this 后的函数;
手撕call
- 第一参数接收 this 对象
- 改变this指向:将函数作为this对象的方法
- 展开语法,支持传入和调用参数列表
- 调用并删除方法,返回结果
Function.prototype.myCall = function(ctx,...args){
if(!ctx) ctx = Object.create(null)
ctx.fn = this
const res = ctx.fn(...args)
delete ctx.fn
return res
}
调用 fn.myCall 时会将 fn 中的this指向修改为传入的第一个参数ctx;将后面的参数传入给fn,并立即执行函数fn。
手撕apply
- 第一参数接收 this 对象
- 改变this指向:将函数作为传入 this 对象的方法
- 第二个参数默认数组
- 展开语法,支持调用参数列表
- 调用并删除方法,返回结果
Function.prototype.myApply = function(ctx,args = []){
if(!ctx) ctx = Object.create(null)
ctx.fn = this
const res = ctx.fn(...args)
delete ctx.fn
return res
}
fn.myApply 的作用和myCall相同:修改this指向,并立即执行fn。区别在于传参形式不同,apply接收两个参数,第一个参数是要指向的this对象,第二个参数是一个数组,数组里面的元素会被展开传入fn,作为fn的参数。
手撕bind
- 第一个参数接收this对象
- 返回函数,根据使用方式
Function.prototype.myBind = function(ctx,...args){
const fn = this
return function F(...args2){
return this instanceof F ? new Fn(...args,...args2):fn.apply(ctx,args.concat(args))
}
}
fn.Mybind 的作用是只修改this指向,但不会立即执行fn;会返回一个修改了this指向后的fn。需要调用才会执行。myBind 的传参和call相同。