常见js面试题--手写call,apply,bind

手写简易call,apply,bind

面试中可能会让你手写写这几个函数,但是他们的内部实现其实是C++代码,所以我们这里只是用js实现简易版的,不会过多的考虑边界情况,当然,应对面试,肯定是够用了。

一、call函数实现

对于这个我就一步一步的给大家分析(后面的apply和bind就不在一步步地分析了)

1.在Function的原型上挂载我们自己的call函数

Function.prototype.xtCall = function() {
     console.log(111);
}
function foo() {
     console.log('222');
}

foo.xtCall()

当我们执行foo.xtCall()时,只会打印111 说明我们的foo是未被调用的,下一步目标自然是执行foo.xtCall()时,让他调用foo

Function.prototype.xtCall = function() {
    console.log(111);
    const fn = this
    fn()
}
function foo() {
    console.log('222');
}
foo.xtCall()

我们的xtCall函数,在被人调用的时候,明显是隐式调用,如foo.xtCall(),所以里面的this,指向的是调用者,如foo,显示你就会发现我们的111和222都会打印了。

接下来,我们就应想办法改变this的指向了(因为call是有改变this指向的功能的哟)

Function.prototype.xtCall = function(thisArg) {
    
    // 获取需要执行的函数
    const fn = this
    // 调用需要被执行的函数
    thisArg.fn = fn
    thisArg.fn()
    delete thisArg.fn
}
function foo() {
    console.log('222', this);
}
foo.xtCall({name:'xt'})

看thisArg.fn = fn thisArg.fn()如果我们不这样写 我们是直接写fn()那么foo的this,打印的是window,因为fn()是默认绑定,但是我们要做的操作是把我们传入的第一个值作为this,那么我们就可以想到用隐式绑定,覆盖默认绑定,也是就thisArg.fn()。那么delete thisArg.fn是做什么的呢,这是因为我们这里是js不是c++,在执行thisArg.fn = fn,他们给我们传入的对象(thisArg)添加上fn所以我们需要在thisArg.fn()执行后删除它,让他不要多出属性。

其实到这里 我们的对象类型,就已经完成了,我们再来考虑,传入的是number,string这种类型的情况。

这里我们可以先看看系统提供的call方法,是怎么实现的,其实他是把number,string这种类型转成了对象类型。其实我们相信也知道,因为number,string他们是不可以添加属性的,只有转出object类型。

所以我们先实现吧

Function.prototype.xtCall = function(thisArg) {
    
    // 获取需要执行的函数
    const fn = this

    // 当thisArg不是object类型时,强转为object
    thisArg = Object(thisArg) // 如果thisArg本身就是Object他也不会嵌套的,放心用

    // 调用需要被执行的函数
    thisArg.fn = fn
    thisArg.fn()
    delete thisArg.fn
}
function foo() {
    console.log('222', this);
}
foo.xtCall(111)

经过上面的处理,已经意味着thisArg是number,string,boolean,object时我们都处理好了,但是我们知道系统的call,当你传入的是null,nudefined时他的this是window,所以我们继续处理吧

// call的实现
Function.prototype.xtCall = function(thisArg) {
    
    // 获取需要执行的函数
    const fn = this

    // 当thisArg不是object类型时,强转为object 如果你null或者undefined时指向window
    thisArg = (thisArg !== null || thisArg !== undefined) ? Object(thisArg) : window

    // 调用需要被执行的函数
    thisArg.fn = fn
    thisArg.fn()
    delete thisArg.fn
}
function foo() {
    console.log('222', this);
}
foo.xtCall(null)

好了 我们现在对this就处理好了,现在我们就剩最后一步,处理额外参数问题

// call的实现
// call的实现
Function.prototype.xtCall = function(thisArg, ...args) {
    // console.log(args); args是个数组[10,20]
    
    // 获取需要执行的函数
    const fn = this

    // 当thisArg不是object类型时,强转为object 如果你null或者undefined时指向window
    thisArg = thisArg ? Object(thisArg) : window

    // 调用需要被执行的函数
    thisArg.fn = fn
    // ...args 展开 args -》 [10,20] -> 10,20
    const res = thisArg.fn(...args)
    delete thisArg.fn

    // 返回执行结果
    return res
}
function foo(n1, n2) {
    // console.log('222', this, n1, n2);
    return n1 + n2
}
// foo.xtCall({name: 'xt'}, 10, 20)
console.log(foo.xtCall({name: 'xt'}, 10, 20));

这里我们主要就是通过es6中的剩余参数及…展开运算符来处理的参数问题,并且我们也拿到了函数执行返回结果,到此我们的zi定义call就全部实现了。

二、apply函数实现

其实他与call实现都是差不多的,唯一不一样的就是,apply的参数使用数组接收的

Function.prototype.xtApply = function (thisArg, argsArr) {
  const fn = this;
  thisArg = (thisArg === undefined || thisArg === null) ? window : Object(thisArg);
  thisArg.fn = fn
  let res
  if (!argsArr) {
    res = thisArg.fn()
  } else {
    res = thisArg.fn(...argsArr)
  }
  delete thisArg.fn
  return res
}
function foo(n1, n2) {
    // console.log('222', this, n1, n2);
    return n1 + n2
}
// foo.xtApply({name: 'xt'}, [10,20])
console.log(foo.xtApply({name: 'xt'}, [10,20]));

三、bind函数实现

bind返回的是个函数,且他的参数是一个个的传的,且参数可以在bind的时候传 也可以在调其返回函数时传
如:

function foo(n1, n2, n3) {
    console.log(this, n1, n2, n3);
}

// var bar = foo.bind(this, 1, 2, 3)
// bar()

// var bar = foo.bind(this)
// bar(1, 2, 3)

const bar = foo.bind(this, 1)
bar(2, 3)

这三种写法都是一样的,所以这些都是我们要考虑的情况了

Function.prototype.xtBind = function (thisArg, ...args) {
  const fn = this;
  thisArg = (thisArg === undefined || thisArg === null) ? window : Object(thisArg);
  thisArg.fn = fn
  return function (...newArgs) {
    const finalArgs = [...args, ...newArgs]
    const res = thisArg.fn(...finalArgs)
    delete thisArg.fn
    return res
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值