前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试题目之原生实现call、apply、bind

面试题目之原生实现call、apply、bind

作者头像
挥刀北上
发布2019-07-19 15:42:47
7600
发布2019-07-19 15:42:47
举报
文章被收录于专栏:Node.js开发Node.js开发Node.js开发

去年写了篇文章,call、apply和bind的区别,但是随着市场对前端工程师的要求越来越高,我们也要与时俱进,需要深入的了解一下这三个方法的原理。

大家看这篇文章之前如果已经了解如何使用call、apply和bind了,那么请继续往下看,如果不是很清楚,建议先读一下这篇文章call、apply和bind的区别

首先我们来给call下个定义:

call方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

举个例子:

var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call(foo); // 1

从上面代码的执行结果,我们可以看到,call首先改变了this的指向,使函数的this指向了foo,然后使bar函数执行了。

总结如下:1、call改变函数this指向,2、调用函数

思考一下:我们如何实现上面的效果呢?代码改造如下:

//将bar函数挂载到foo对象上,使其成为foo的方法,用foo.bar来调用
var foo = {
    value:1,
    bar:function(){
        console.log(this.value)
    }
}
foo.bar()     //1

仔细观察一下我们做了什么,将bar函数挂载到foo对象上,使其成为foo的方法,用foo.bar来调用。

再来看上面代码的执行结果:打印1,有没有什么启发呢?为了模拟call方法,我们可不可以这样做呢:

a、将函数设为某个对象的属性(或者方法)

b、通过该对象的属性调用该函数

c、将该对象上的这个属性(或者方法)

代码如下:

Function.prototype.myCall = function(context) {
  context = context || window
  //将函数挂载到对象的fn属性上
  context.fn = this
  //处理传入的参数
  const args = [...arguments].slice(1)
  //通过对象的属性调用该方法
  const result = context.fn(...args)
  //删除该属性
  delete context.fn
  return result
}

我们看一下上面的代码:

1、首先我们对参数context做了兼容处理,不传值,context默认值为window。

2、然后我们将函数挂载到context上面,context.fn = this;

3、处理参数,将传入myCall的参数截取,去除第一位,然后转为数组;

4、调用context.fn,此时fn的this指向context;

5、删除对象上的属性 delete context.fn

5、将结果返回。

以此类推,我们顺便实现一下apply,唯一不同的是参数的处理,代码如下:

Function.prototype.myApply = function(context) {

  context = context || window
  context.fn = this
  let result
    // myApply的参数形式为(obj,[arg1,arg2,arg3]);
    // 所以myApply的第二个参数为[arg1,arg2,arg3]
    // 这里我们用扩展运算符来处理一下参数的传入方式
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}

以上便是call和apply的模拟实现,唯一不同的是对参数的处理方式。

接着再来思考一下bind的实现,在模拟bind的实现之前,先看一下bind的使用案例:

var obj = {a:1};
function bar(){
    console.log(this.a);
}
bar.bind(obj)();

我们看到,bind函数虽然也能改变bar函数的this,但是改变后,函数并不会执行,只是返回一个新的函数,想执行就得继续调用,仔细观察第五行代码的写法。

根据上面的使用案例,我们先实现一个简单版本的bind:

Function.prototype.myBind = function(ctx) {
    return () => { // 要用箭头函数,否则 this 指向错误
        return this.call(ctx)
    }
}
var obj = {a:1};
function bar(){
    console.log(this.a);
}
bar.myBind(obj)();

但是这样比较简陋,函数的参数一多就不能处理了,如下面这种情况:

bar.bind(obj, 2)(2)
// or
bar.bind(obj)(2, 2)

为了兼容bind调用时满足参数传递的不同方式,代码修改如下:

Function.prototype.myBind = function(ctx, ...argv1) {
    return (...argv2) => {
        return this.call(ctx, ...argv1, ...argv2)
    }
}
//测试代码
var obj = {a:1};
function bar(b,c){
    console.log(this.a+b+c);
}
bar.myBind(obj)(20,30);
bar.myBind(obj,20,30)();

仔细观察上面的代码,基本上能实现按照不同方式进行传参了,当然此处myBind和原生的bind相比还是有些不足,这些就留给大家去完善了,如果你对本文有什么其他想法或者建议,欢迎留言。

资料引用:

https://segmentfault.com/a/1190000018428876

https://segmentfault.com/a/1190000015438195#articleHeader3

https://github.com/mqyqingfeng/Blog/issues/11

https://juejin.im/book/5bdc715fe51d454e755f75ef/section/5bdd0d8e6fb9a04a044073fe

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 nodejs全栈开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档