「这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战」
Javascript call,apply,bind的区别和简单模拟实现
call,apply,bind最主要的作用,是改变this的指向。在平常的业务中可能用得不是特别多,一般可能是在写一些基础类,工具函数,公用库方法等的时候会用到。啊对了,还有面试的时候哈哈哈。这篇文章主要是来梳理一下三者的特性和用法。
一、共同点和区别
1. call 和 apply 的共同点
call和apply的共同点是,都能够改变函数执行时的执行上下文(即this的指向),而且是立即执行的。
2. call 和 apply 的区别
call和apply的区别主要是体现在语法中的参数传递上。我们可以先把传入调用函数的参数称为调用参数。
call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。
换句话说,call() 中调用参数是依次传入的,而 apply() 中调用参数是作为一个整体(数组或类数组)传入的。
具体的我们不妨顺便复习一下call和apply的语法。
call 的语法
func.call(thisArg, arg1, arg2, ...)
注意点:
- call 的调用者必须是函数 Function。
- call 的第一个参数,是一个对象。 Function 的调用者,将会指向这个对象。如果这个函数处于非严格模式下,指定为
null或undefined(不传) 时会自动替换为指向全局对象,原始值会被包装。 - 第二个参数开始,可以接收任意个参数。每个参数会映射到相应位置的 Function 的参数上。
apply 的语法
func.apply(thisArg , [ argsArray])
注意点:
- apply 的调用者必须是函数 Function,并且只接收两个参数。
- apply 的第一个参数规则和 call 一样。
- 第二个参数,必须是数组或者类数组,它们会被转换成类数组,传入 Function 中,并且会被映射到 Function 对应的参数上。
3. (call,apply)和 bind 的区别
先看MDN上关于bind的介绍
bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
function.bind(thisArg[, arg1[, arg2[, ...]]])
可以发现
- bind() 会创建并返回一个新函数,它不是立即调用的。
- bind() 的第一个参数规则和 call 一样。
- bind() 的传参方式和 call() 类似。
三、简单模拟实现
1. call
//传入一个this 绑定上所有的属性
Function.prototype.MyCall=function(context){
if(typeof this !=='function'){
throw new TypeError('error')
}
context=context||window;
context.fn=this;
const args=[...arguments].slice(1);//除去要绑定的对象,剩下参数应该绑定进去
const result=context.fn(...args);
delete context.fn;
return result;
}
2. apply
// apply
Function.prototype.Myapply()=function(context){
if (typeof this !=='function'){
throw TypeError('Error');
}
context=context||window;
context.fn=this;
let result;
//判断是否存在数组参数,毕竟是可选参数
if(arguments[1]){
result=context.fn(...arguments[1]);
}else{
result=context.fn();
}
delete context.fn;
return result;
}
3. bind
// bind
Function.prototype.myBind=function(context){
if(typeof this !=='function'){
throw TypeError ('Error');
}
const _this=this;
const args=[...arguments].slice(1);
return function F(){
//如果采用的是new方法的话,就不动this
if(this instanceof F){
//链式调用要把新旧参数加上去哦
return new _this(...args,...arguments);
}else{
return _this.apply(context,args.concat([...arguments]));
}
}
}
- thisArg 调用绑定函数时作为this参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。
- 当使用bind在setTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object。
- 如果bind函数的参数列表为空,执行作用域的this将被视为新函数的thisArg。
- arg1, arg2, ... 当目标函数被调用时,预先添加到绑定函数的参数列表中的参数。
注意点:
- 因为返回的是一个函数,所以要考虑new的情况
- 由于链式调用,还要小心只有第一次调用bind传入的this才会生效(才是被绑定的对象)。