在JavaScript中,所有的函数再被调用的时候都会默认传入两个参数,一个是this,还有一个是arguments。在默认情况下this都是指当前的调用函数的对象。但是有时候我们需要改变this的指向,也就是说使函数可以被其他对象来调用,那么我们应该怎样做呢?这时候我们就可以使用call,apply和bind方法了。
this指向 = 谁调用,指向谁(这是错误的!!!)
this永远指向最后一个调用它的那个对象(正解)
如何解决this指向问题?
1.使用ES6中箭头函数
2.函数内部使用_this = this
3.使用apply,call,bind方法
4.new实例化一个对象
一:call,apply和bind方法的来历
在js中所有的函数都是Function的实例,而且对于Function来说,它的原型即Function.prototype中含有很多东西,其中call,apply和bind方法就是Function原型中的方法,所以根据原型的规则,所有的函数都可以使用原型中属性和方法,所以来说,对于所有的函数都可以使用call,apply和bind方法。
简单一句话:call,apply和bind都是Function原型中的方法,而所有的函数都是Function的实例。
我认为它们的作用可以用一句话来描述:就是改变this的指向。对于这句话的解释,我们可以结合代码来理解。
二:call函数
首先我们放个截图看看官方对call的解释:
从图示上我们可以看到,方法解释为:调用对象的方法,将另一个对象替换为当前对象。
两个参数,thisArg:要用作当前对象的对象。argArray:传递给方法的参数列表。
怎么理解呢,下面放个代码图方便直观解释下:
在上面的截图中我们可以明确的看到,在调用test()方法后传入参数后,this的指向为全局对象。
为方便区分,我们再用call函数改变this的指向。
在这张图上,我们在调用test方法时,使用call函数,在call函数里传递了person对象及属性birthday,我们可以看到再次打印出来的this已经变成了person对象。
综上,可以得出结论:call函数传递两个参数(一个用来改变this指向的对象(必传),一个用于使用的参数列表(选传))。
三:apply函数
接下来我们看看apply函数。
apply() 方法调用一个函数,其具有一个指定的this值,以及作为一个数组(或者类似数组的对象)提供的参数。
官方对apply的解释是:对于给定的函数,创建一个与原始函数具有相同主体的绑定函数。绑定函数的this对象与指定对象相关联,并具有指定的初始参数。
两个参数:
thisArg:在fun函数运行时指定的this值。指定this的值并不一定是函数执行时真正的this值,如果是原始值的this会指向该原始值的自动包装对象。
argArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给fun函数。参数为null或者undefined,则表示不需要传入任何参数。
我们可以看到,在apply中传入person对象和参数数组(这里我们直接使用Object.values()将person对象的属性值直接转化为数组了)。打印出来的this指定依旧被改变为person对象了。
四:bind函数
最后我们看看bind函数。
bind() 方法对于给定的函数,创建一个与原始函数具有相同主体的绑定函数。绑定函数的this对象与指定对象相关联,并具有指定的初始参数。
thisArg:this关键字可以在新函数中引用的对象。
argArray:传递给新函数的参数列表。
可以看到,bind创建了一个源函数相同的函数,且拥有this对象及参数列表。
call,apply,bind都是js里改变this指向的函数,不同的是,call只接受一个参数,apply接收一个数组,bind创建一个同源函数。
五:call的应用
比如判断数据类型:
console.log(Object.prototype.toString.call("qq")) // [Object String] 返回值都是字符串类型
console.log(Object.prototype.toString.call(12)) // [object Number]
console.log(Object.prototype.toString.call(false)) // [object Boolean]
console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
console.log(Object.prototype.toString.call(null)) // [object Null]
console.log(Object.prototype.toString.call(function(){})) // [object Function]
console.log(Object.prototype.toString.call([])) // [object Array]
console.log(Object.prototype.toString.call({})) // [object Object]
然后进行封装
很简单的实现了类型判断。