call、apply 的区别

call:第一个参数是为函数内部指定this指向,后续的参数则是函数执行时所需要的参数,一个一个传递。

apply:第一个参数与call相同,为函数内部this指向,而函数的参数,则以数组的形式传递,作为apply第二参数。

call 的性能更好,不过 lodash 里的源码当参数小于等于 3 时用 call,之后用 apply

call模拟实现

Function.prototype.myCall = function(obj, ...arg) {
    if(typeof this !== 'function'){
        throw new TypeError(this + ' is not a function');
    }
  	const context = obj;
  	const fn = Symbol();
  	context[fn] = this;
  	const ret = context[fn](...arg)
  	delete context[fn]
  	return ret;
}

apply模拟实现

Function.prototype.myApply = function(obj, arg) {
    if(typeof this !== 'function'){
        throw new TypeError(this + ' is not a function');
    }
  	const context = obj;
  	const fn = Symbol();
  	context[fn] = this;
  	const ret = context[fn](...arg)
  	delete context[fn]
  	return ret;
}

为什么call 比apply 快?

这里就要提到他们被调用之后发生了什么。

Function.prototype.apply (thisArg, argArray)

1、如果IsCallable(Function)为false,即Function不可以被调用,则抛出一个TypeError异常。
2、如果argArray为null或未定义,则返回调用function的[[Call]]内部方法的结果,提供thisArg和一个空数组作为参数。
3、如果 Type(argArray)不是Object,则抛出TypeError异常。
4、获取argArray的长度。调用argArray的[[Get]]内部方法,找到属性length。 赋值给len。
5、定义 n 为ToUint32(len)。ToUint32(len)方法:将其参数len转换为范围为0到232-1的232个整数值中的一个。
6、初始化 argList 为一个空列表。
7、初始化 index 为 0。
8、循环迭代取出argArray。重复循环 while(index < n)
a、将下标转换成String类型。初始化 indexName 为 ToString(index).
b、定义 nextArg 为 使用 indexName 作为参数调用argArray的[[Get]]内部方法的结果。
c、将 nextArg 添加到 argList 中,作为最后一个元素。
d、设置 index = index+1
9、返回调用func的[[Call]]内部方法的结果,提供thisArg作为该值,argList作为参数列表。

Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )

1、如果 IsCallable(Function)为false,即Function不可以被调用,则抛出一个TypeError异常。
2、定义argList 为一个空列表。
3、如果使用超过一个参数调用此方法,则以从arg1开始的从左到右的顺序将每个参数附加为argList的最后一个元素
4、返回调用func的[[Call]]内部方法的结果,提供thisArg作为该值,argList作为参数列表。

我们可以看到,明显apply比call的步骤多很多。
由于apply中定义的参数格式(数组),使得被调用之后需要做更多的事,需要将给定的参数格式改变(步骤8)。 同时也有一些对参数的检查(步骤2),在call中却是不必要的。
另外一个很重要的点:在apply中不管有多少个参数,都会执行循环,也就是步骤6-8,在call中也就是对应步骤3 ,是有需要才会被执行。

综上,call 方法比 apply 快的原因是 call 方法的参数格式正是内部方法所需要的格式

apply最后也是转换为call实现的

更新:apply 转化的是内置的 call,并非 Function.prototype.call,感谢楼里同学提醒。apply 最后还是转化成 call 来执行的,call 要更快毫无疑问,详细介绍见 https://tc39.es/ecma262/#sec-function.prototype.apply

bind实现

Function.prototype.bind2 = function(context, ...args){
    var fn = this; 
    return function () { // 这里不能使用箭头函数,不然参数arguments的指向就很尴尬了,指向父函数的参数
        fn.call(context, ...args, ...arguments);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值