JavaScript中的call,apply,bind学习总结

899 阅读3分钟

JavaScript 中的 call, apply, bind 这三个函数的作用和区别在各大论坛都被讨论了很多次了,但是我一直都还没来得及好好总结,这次正好看到了一个很不错的关于JavaScript 中 apply 、call 的详解,自己就学习并总结了一波~,正好更新一波博客😂

call 和 apply

其实 call 和 apply 的作用很明确,就是要通过对它们传入的参数来确定函数上下文的对象,也就是通过参数来确定函数中的 this 关键字的指向,this 关键字是 JavaScript 中一个很重要、很值得我们去加深学习和理解的东西,具体关于 this 的知识总结,我以前写了一个博客,感兴趣的话请参考浅析JavaScript中的this关键字

call 和 apply 的作用是相同的,它们的唯一区别就是对传入参数的格式要求不同。

  • call()

    call 方法在调用的时候,需要传入一个函数的上下文对象和一个参数列表

    let obj = {
        name: 'nicole'
    }
    let func = function (){
        console.log(this.name)
        console.log(arguments)
    }
    func.call(obj,{age: '20'},{id: '1'}) 
    // 运行结果是打印出 nicole 和 [{age: "20"},{id: '1'}]
    

    函数的上下文被指向了传入的第一个参数obj,如果我们直接调用func(obj,{age: '20'},{id: '1'}),那么函数的 this 则指向了 window (严格模式下为 undefined)。

  • apply()

    apply方法在调用的时候,需要传入一个函数的上下文对象和一个数组

    let obj = {
        name: 'nicole'
    }
    let func = function (){
        console.log(this.name)
        console.log(arguments)
    }
    func.apply(obj,[{age: '20'},{id: '1'}]) 
    // 运行结果是打印出 nicole 和 [{age: "20"},{id: '1'}]
    

    由于callapply方法的作用都是一样的,所以在决定究竟要用哪个的时候,我们可以直接从参数的形式来考虑,如果我们要传入的参数是数组,那我们就直接使用apply就好。

bind

callapply方法不同的是,callapply方法在被调用时就直接执行了当前的函数,而bind直接返回了一个改变了函数上下文之后的新函数,这个新函数与原函数并非是同一个函数,而且与call类似,bind方法接受列表形式的参数

let obj = {
    name: 'nicole'
}
let func1 = function (){
    console.log(this)
}
let func2 = func1.bind(obj,{age: '20'})
func1 === func2 // false
func2() // {name: "nicole"}

我们可以自己实现一个bind函数

Function.prototype.bindTest = function () {
    let self = this;
    let context = Array.prototype.shift.call(arguments);
    let arg = Array.prototype.slice.call(arguments);
    return function () {
        self.apply(context, [...arg, ...arguments]);
    }
}
let obj = {
    name: 'nicole'
}
let func1 = function () {
    console.log(this);
    console.log(arguments);
}
let func2 = func1.bindTest(obj)
func2({age: 11});
// 运行结果 {name: "nicole"} [{age: 11}]

值得注意的是,bind方法返回的函数里面的 this 值不可以再被改变了,也就是说不能再修改函数的上下文,不管再对函数调用call还是apply,都无法再改变函数的上下文。而普通的函数则能借助call还是apply各种肆意的修改函数的上下文。

let obj1 = {
	name: 'nicole'
}
let obj2 = {
	name: 'neil'
}
let func = function (){
	console.log(this)
}
let func1 = func.bind(obj1)
func1.call(obj2) 
// 运行结果是{name: "nicole"},也就是说虽然调用了func1的 call 方法,但是函数上下文依然是obj1

func.call(obj1) // {name: "nicole"}
func.call(obj2) // {name: "neil"}

func.apply(obj1) // {name: "nicole"}
func.apply(obj2) // {name: "neil"}