面试题答案
一键面试1. bind、call 与 apply 方法的底层实现原理
改变函数执行上下文
- call:
func.call(thisArg, arg1, arg2, ...)
方法使用一个指定的this
值和单独给出的一个或多个参数来调用一个函数。它会立即执行函数,并将thisArg
绑定为函数执行时的this
上下文。 - apply:
func.apply(thisArg, [arg1, arg2, ...])
与call
类似,区别在于apply
接受一个参数数组。同样会立即执行函数,并把thisArg
作为函数执行时的this
上下文。 - bind:
func.bind(thisArg, arg1, arg2, ...)
创建一个新的函数,当这个新函数被调用时,this
值被指定为thisArg
,其余参数按照顺序提供给新函数。新函数返回后并不会立即执行,而是等待被调用时才执行,并且在新函数执行时,this
上下文被绑定为bind
方法传入的thisArg
。
处理参数
- call:参数是逐个列出的,
call
方法将这些参数依次传递给被调用的函数。 - apply:接受一个数组作为参数列表,将数组中的元素依次传递给被调用的函数。
- bind:可以在
bind
时传入部分参数,这些参数会被“固化”在新函数中。当新函数被调用时,再传入剩余的参数,新函数的参数是bind
时传入的参数与调用新函数时传入的参数的组合。
2. 性能优化建议及理由
减少不必要的绑定
在性能敏感的大型项目中,频繁使用 bind
、call
和 apply
会带来一定的性能开销。如果一个函数的 this
上下文在整个项目生命周期内基本固定,那么可以在初始化时就使用 bind
绑定好 this
上下文,而不是每次调用时都使用 call
或 apply
来临时改变上下文。这样可以减少每次调用时重新绑定上下文的开销。
复用绑定后的函数
如果需要多次以相同的 this
上下文和部分相同参数调用一个函数,建议使用 bind
创建一个新函数并复用它,而不是每次都使用 call
或 apply
。因为 bind
创建新函数的开销只在创建时发生一次,而 call
和 apply
每次调用都需要重新处理上下文和参数。
缓存 bind
结果
对于一些频繁使用且 this
上下文和参数固定的场景,可以将 bind
的结果进行缓存。这样避免了重复创建相同绑定的函数,提高性能。
3. 优化后的代码示例
假设我们有一个对象 obj
和一个函数 func
,func
需要在 obj
的上下文中执行:
// 原始代码
const obj = {
value: 42
};
function func(a, b) {
return this.value + a + b;
}
// 频繁使用 call
const result1 = func.call(obj, 1, 2);
// 频繁使用 apply
const result2 = func.apply(obj, [1, 2]);
// 优化后的代码 - 使用 bind 并缓存
const boundFunc = func.bind(obj);
const result3 = boundFunc(1, 2);
// 进一步优化,如果部分参数也固定
const fixedArgsFunc = func.bind(obj, 1);
const result4 = fixedArgsFunc(2);
在上述优化后的代码中,通过 bind
创建了绑定上下文的函数,并进行了复用,减少了 call
和 apply
频繁调用带来的性能开销。如果有部分固定参数,也可以在 bind
时一并传入,进一步优化性能。