阅读 375

深入理解JS中call,apply及bind的用法

apply

语法:func.apply(thisArg, [argsArray])

参数

  • thisArg:调用functhis值,如果处于非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象
  • argsArray:调用func的传参(参数需要放到一个数组中)

核心概念(借用)

举个栗子

有一天你一时兴起想做个红烧肉,但万事具备只欠口锅,但是买一个又不太划算,可下一次做可能得到猴年马月。但此时你发现与你合租的小姐姐刚好有这么一口锅,于是你便借了过来做起了红烧肉。

这就是apply的用法,thisArg表示你自己,func则表示那口锅,apply表示借用。

方法优点:靠改变this指向,对代码进行复用。减少重复代码,节约内存。

上代码:

function cooking(food) {
    console.log(`我做了一顿${food}`)
}

function my(...args) {
    cooking.apply(this, args)
}
my('红烧肉')                    // 我做了一顿红烧肉
复制代码

从上面的例子中可以看到,my中没有定义任何方法,但是通过apply以后,my便借用了cooking的方法。

用法总结

  • array将一个数组push到另一个数组中
let arr1=[1,2,3]
let arr2=[4,5]
Array.prototype.push.apply(arr1,arr2)   
console.log(arr1)                           // [1,2,3,4,5]
复制代码

如果直接使用arr1.push(arr2)得到的结果将会是一个数组作为整体传入。得不到我们想要的效果。而直接使用concat的话,它返回的是一个新的数组,而不会对arr1进行修改。

  • 求数组中最大值
let number=[1,2,4,56,6]
let max=Math.max.apply(null,number)
console.log(max)
复制代码

因为Math.max接收的参数为离散数,直接传入数组无法的到想要的结果。这种调用方式相当于Math.max(1,2,4,56,6)

  • 继承
function Person(name){
    this.name=name
    this.say=function(){
        console.log('my name is ' + name)
    }
}
function My(name,age){
    Person.apply(this,[name])
    this.age=age
    this.getAge=function(){
        console.log(age)
    }
}
let my=new My('hkj',22)
console.log(my.name)
my.say()
console.log(my.age)
my.getAge()
复制代码

通过new生成的实例会继承构造函数中的属性和方法,而My中定义的只有agegetAge方法,并未定义namesay方法。但是生成的实例依然可以调用,这就是apply的借用导致的。虽然My未定义此方法但是借了个Person里的name属性和say方法来用。换成术语就是,My继承了Personthis绑定的属性和方法。

类似用法还有很多,就不在此赘述了。只需要牢记借用这个概念就能记住它的用法了。


call

语法:function.call(thisArg, arg1, arg2, ...)

参数

  • thisArg:调用functhis值,如果处于非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象
  • argsArray:调用func的传参(参数需要一个一个传)

用法

apply一样,就是传参不一样,apply传的是数组,而call需要离散化参数传入。用个小技巧记忆:apply需要传array(数组)。call反向记忆就行了。

Object.prototype.toString判断数据类型

类型判断一直是个比较棘手的问题,而Object的toString方法则是最完美的解决办法。默认情况下调用返回值为[object Object]。后面的Object就是返回的类型

var toString = Object.prototype.toString;
toString.call(new Date);            // [object Date]
toString.call(new String);          // [object String]
toString.call(Math);                // [object Math]
toString.call(undefined);           // [object Undefined]
toString.call(null);                // [object Null]
复制代码

可以看出第二个值表示的就是正确返回类型。如果直接调用的话,有些对象重写了toString方法将会得不到我们想要的效果。

比如:

let arr=[1]
let date=new Date()
let fun = function () {}
console.log(date.toString())            // Thu Dec 05 2019 16:46:51 GMT+0800 (中国标准时间)
console.log(arr.toString())             // 1
console.log(fun)                        // ƒ () {}
console.log(Object.prototype.toString.call(arr))        // [object Array]
console.log(Object.prototype.toString.call(date))       // [object Date]
console.log(Object.prototype.toString.call(fun))        // [object Function]
复制代码

这种调用方式会直接调用Object原生的toString方法,避免了调用重写后方法导致结果不正确的问题。

bind

下次写

小结

作为js中最重要的知识点之一,因为其抽象的特性,开始学起来确实有些困难,大部分时候都是知其然不知其所以然。但是它们的用法是很多后面需要学习的知识点的基石。只有牢牢掌握了它们的用法以后的学习才会更轻松。如果对继承用法感兴趣的同学可以阅读我的下一篇文章:深入理解JS原型,原型链,继承及new的实现原理

写作不易,各位小哥哥小姐姐觉得我的博客对你有帮助的话,请给我点个赞哟!