最近在工作中突然想到bind函数返回的新函数为什么不能再修改this?于是详细去了解下三个方法的区别,原理。
1、call、apply、bind区别
call方法中接收的是一个参数列表,第一个参数指向this,参数列表在函数执行时都会作为函数形参传入函数。
fn.call(this, arg1, arg2, ...);
apply方法与call方法是一样的、实现方式也基本一样、只是接收的是一个参数数组
fn.call(this, arg1, arg2, ...);
bind与call、apply的区别,call与apply在改变this的时,就会立马执行,而bind绑定this后并不会立马执行,而是返回一个改变了this指向的新函数。
let obj = {}
let fn = function(){
return this === obj
}
let fn1 = fn.bind(obj);
console.log(fn()) // false
console.log(fn1()) // true
2、call、apply、bind方法的实现。
call实现
function myCall(context){
const arg = [...arguments].slice(1) // 获取形参
const _this = this // 保存方法的原this
context = context ? Object(context) : window // object转换防止原始类型
context.fn = _this // 原this赋值fn
const result = context.fn(...arg)
delete context.fn // 执行完成 删除新增属性
return result // 将结果返回
}
apply实现方式与call基本相同
function myApply(context){
const arg = arguments[1]
if(!(arg instanceof Array)){ // 第二个参数不为数组抛出错误
throw new Error('CreateListFromArrayLike called on non-object')
}
const target = this
context = context ? Object(context) : window
context.fn = target
const result = context.fn(...arg)
delete context.fn
return result
}
bind实现
function myBind(context){
const bindArg = [...arguments].slice(1) // 获取形参
const _this = this // 保存方法的原this
return function(){
_this.myApply(context,[...arguments].concat(bindArg ))
}
}
3、bind函数返回的方法为什么不能再修改this?
const obj = {};
const fn = function() {
console.log('test bind');
};
console.log({ fn: fn.bind(obj) });
尝试打印bind返回的新函数fn,可以看到它并不是一个普通的function,而是一个bound function,简称绑定函数:
它的TargetFunction指向了bind前的函数,BoundThis就是绑定的this指向,BoundArgs便是传入的其它参数了。
我们可以得出一个结论,当执行绑定函数时,this指向与形参在bind方法执行时已经确定了,无法改变。
换个思路理解,当执行fn1时,本质上等于window.fn1(),如果this还能被改变,那this岂不是得指向window,那bind方法就没太大意义了。
4、应用
我们都知道Math.max()方法能取到一组数字中的最大值,比如:
Math.max(1, 10); //10
Math.min(1, 10); //1
那我们如何利用此方法求数组中最大最小呢,这里就可以利用apply的特性了:
Math.max.apply(null, [1, 2, 10]); //10
Math.min.apply(null, [1, 2, 10]); //1
在非严格模式下,当我们让this指向null,或者undefined时,this本质上会指向window,不过这里的null就是一个摆设,我们真正想处理的其实是后面的数组。
还记得万能的属性判断方法吗?Object.prototype.toString()结合call方法:
let a = [];
let b = function () {};
Object.prototype.toString.call(a) === "[object Array]" //true
Object.prototype.toString.call(b) === "[object Function]" //true