call(), apply()和bind() 转载

创建于:
最近更新:

在JavaScript中,call(), apply(), 和 bind() 是三个强大的函数方法,它们主要用于调整函数调用时this关键字的指向。虽然它们的目的相似,但在实际应用中各有其独特之处。本文将探讨这些方法的使用场景、区别以及它们的一些巧妙用法。

call()

call() 方法接受一个参数列表,第一个参数用于指定函数执行时this的值,后续参数则按顺序传递给函数。

示例:

var year = 2023;
function getDate(month, day) {
  return this.year + '-' + month + '-' + day;
}

let obj = {year: 2024};
getDate.call(obj, 7, 11); // 输出: "2024-7-11"

apply() 方法

call()相似,apply()也用于设置函数执行时this的值,但它的参数以数组形式传递。

示例:

getDate.apply(obj, [7, 12]); // 输出: "2024-7-12"

适用场景:

当参数数量不确定或参数来自数组时,使用apply()更为方便。

巧妙用法:

结合内置函数如Math.max()Array.prototype.push(),使用apply()可以处理动态参数列表。

bind()

bind() 方法与call()和apply()不同,它不立即执行函数,而是返回一个新的函数,这个函数在调用时会将this绑定到指定的对象。

示例:

var obj = {year: 2024};
var boundGetDate = getDate.bind(obj);
boundGetDate(7, 13); // 输出: "2024-7-13"

适用场景:

当你需要创建一个带有预设this值的可复用函数时,使用bind()

bind()的限制:

bind()无法改变构造函数的this指向。当使用new操作符调用bind()返回的函数时,this将指向新创建的对象。但是这也让bind可以在动态传参时起到关键作用。

call(), apply()和bind()手写的实现

我们可以手动实现这些方法的基本逻辑,以加深理解。

call()和apply()的手写实现:

Function.prototype.myCall = function (context) {
   let obj = context || window
   obj.fn = this
   let args = []
   for (let i = 1, len = arguments.length; i < len; i++) {
       args.push(arguments[i])
   }
   const result = obj.fn(...args)
   return result
}

// test
function add(c, d) {
    return this.a + this.b + c + d;
}
const obj = { a: 1, b: 2 };
console.log(add.myCall(obj, 3, 4)); // 10  
console.log(add.myCall({ a: 3, b: 9 }, 3, 4)); // 19
console.log(add.myCall({ a: 3, b: 9 }, { xx: 1 }, 4)); // 12[object Object]4 


Function.prototype.myApply = function (object, arr) {
    let obj = object || window
    obj.fn = this
    let result
    if (!arr) {
        return obj.fn()
    }
    if (!(arr instanceof Array)) {
        throw new Error('params must be array')
    }
    result = obj.fn(...arr)
    delete obj.fn
    return result
}

// test
function add(c, d) {
    return this.a + this.b + c + d;
}
const obj = { a: 1, b: 2 };
console.log(add.myApply(obj)); // NaN
console.log(add.myApply(obj, [3, 4])); // 10 
console.log(add.myApply(obj, [1, 'abc', '2'])); // 4abc

bind()的手写实现:

Function.prototype.myBind = function(context, ...args) {
  let fn = this;
  let bound = function(...args2) {
    return fn.apply(context, args.concat(args2));
  };
  return bound;
};

总结

  1. call()apply()bind(),都可以用来改变this的指向。
  2. 它们三个的第一个参数都是this要指向的对象。
  3. 它们三个都能传参数,call()bind()传的是参数列表,apply()传的是数组。
  4. call()apply()是直接调用,bind()返回的是函数,可以在后面需要时再调用,所以也可以在调用的时候再传入参数。