跟我一起来刷题(1)——实现call,apply,bind

188 阅读2分钟
为了更好的拥抱变化,保持自己在市场的一个竞争力,每天都应该刷点题来考察自己的一个综合能力。
我认为刷题是作为一个自驱技术人员每天必备要做的事情,和吃喝拉撒一样。

call,apply,bind

const person = {
    name: 'Bob',
    sayName: function() {
        console.log(this.name,...arguments);
    }
};

const person2 = {
    name: 'Mary'
}

1、call,apply的区别是什么

这两个都用于改变函数作用域,第一个参数都是用于改变函数体的this指向,不同在第二个参数:call传入的参数是一个一个的;apply,传入的参数是一个数组;

person.sayName.call(person2, 'eat','sleep');
person.sayName.apply(person2, ['eat','sleep']);

2、call,apply的哪个性能好一些

如上面第一点区别可以看到,call传入的参数是格式更符合函数的解析;apply传入的参数还需要进一步解析;所以call的性能更好一些。

  • 在loadsh这些库中,都推荐使用call;
  • es6支持解构数组,...arguments;所以用call也能代替apply

3、 手写call,apply,bind;

call

person.sayName.call(person2, 'eat','sleep');

原理:用call函数改变this的指向,其实就是person2中是否有sayName,如果没有sayName,就手动绑定一个sayName的属性,去调用;person2.sayName();

Function.prototype.myCall = function(context) {
    context = context || window;
    // this就是person.sayName,拿到person.sayName函数;
    context.fn = this;
    // 拿到剩余参数,如:'eat','sleep';
    const args = [...arguments].slice(1);
    context.fn(...args);
    delete context.fn
}
person.sayName.myCall(person2,'eat','sleep'); 
// Mary ,eat, sleep

Apply

apply和call差不多实现,只要注意第二个参数是数组传入

Function.prototype.myApply = function(context) {
    context = context || window;
    context.fn = this;
    const args = [...arguments].slice(1);
    context.fn(args);
    delete context.fn
}

Bind

bind是返回改变作用域后函数的handle,所以return的是一个函数,但是要注意一个args.concat(newArgs)两次参数的一个拼装处理,底层还是调用了apply的实现

Function.prototype.myBind = function(context) {
    const _self = this;
    const args = [...arguments].slice(1);
    return function() {
        const newArgs = [...arguments];
        return _self.apply(context, args.concat(newArgs))
    }

}