call、apply和bind
在 JavaScript 中,call、apply 和 bind 是函数对象的三个方法,它们都用于显式地设置函数调用时的 this 值(即改变函数内部的上下文)。虽然功能相似,但它们在调用方式和参数传递上有所不同。
1.call
语法:
func.call(thisArg, arg1, arg2, ...)
特点:
- 立即执行函数。
- 参数以逗号分隔的形式传入。
手写示例:
Function.prototype.Call = function(context,...args){
// 1. 类型检查
if (typeof this !== 'function') {
throw new TypeError(this + ' is not a function');
}
// 2. 处理 null/undefined
if (context == null) {
context = globalThis;
} else {
// 3. 将原始值包装为对象
context = Object(context);
}
// 4.创建一个symbol,目的是避免键名fn和context上的键名重复
const fn = Symbol()
// 5.把函数(this)暂时存放到context上,这是的this是调用call的函数
context[fn] = this
// 6.执行函数
const result = context[fn](...args)
// 7.删除掉临时存放的函数
delete context[fn]
return result
}
2.apply
语法:
func.apply(thisArg, [argsArray])
特点:
- 立即执行函数。
- 第二个参数是一个数组(或类数组),用于传递参数。
手写示例:
Function.prototype.Applay = function(context,args){
// 1. 类型检查
if (typeof this !== 'function') {
throw new TypeError(this + ' is not a function');
}
// 2. 处理 null/undefined
if (context == null) {
context = globalThis;
} else {
// 3. 将原始值包装为对象
context = Object(context);
}
// 4.创建一个symbol,目的是避免键名fn和context上的键名重复
const fn = Symbol()
// 5.把函数(this)暂时存放到context上,这是的this是调用call的函数
context[fn] = this
// 6.执行函数
const result = context[fn](...args)
// 7.删除掉fn
delete context[fn]
return result
}
💡 小技巧:当你有一组参数存在数组中,又想传给函数,用
apply很方便。不过 ES6 后也可以用扩展运算符配合call:greet.call(person, ...['Hi', '.'])。
3. bind
语法:
const newFunc = func.bind(thisArg, arg1, arg2, ...);
特点:
- 不立即执行,而是返回一个新函数,该函数的
this被永久绑定到指定对象。 - 可以预设部分参数(柯里化/偏函数应用)。
手写示例:
Function.prototype.Bind = function(context,...contextArgs){
// 1. 类型检查
if (typeof this !== 'function') {
throw new TypeError(this + ' is not a function');
}
// 2.存放调用Bind方法的函数
const fn = this;
// 3. 处理 null/undefined
if (context == null) {
context = globalThis;
} else {
// 4. 将原始值包装为对象
context = Object(context);
}
const boundFunction = function(...args){
// 5.判断Bind后的函数
// 如果new了,this就是它自己,如果是普通函数调用就是context
const thisArg = this instanceof fn ? this : context;
return fn.apply(thisArg, contextArgs.concat(args));
}
// 6.判断fn上有原型对象,那么Bind后的函数,也应该继承它
if(fn.prototype){
boundFunction.prototype = Object.create(fn.prototype)
}
return boundFunction;
}
⚠️ 注意:
bind返回的新函数,即使再用call或apply改变this,也不会生效(因为this已被硬绑定)。
对比总结
| 方法 | 是否立即执行 | 参数形式 | 返回值 | 主要用途 |
|---|---|---|---|---|
| call | ✅ 是 | 逐个参数 | 函数执行结果 | 临时改变 this 并调用 |
| apply | ✅ 是 | 参数数组 | 函数执行结果 | 参数为数组时改变 this 调用 |
| bind | ❌ 否 | 逐个参数(可预设) | 新函数 | 创建 this 固定的新函数 |
实际应用场景
- 借用方法:比如用
Array.prototype.slice.call(arguments)把类数组转为真数组。 - 事件处理:在回调中保持正确的
this(如btn.addEventListener('click', handler.bind(this)))。 - 函数柯里化:通过
bind预设部分参数,生成更具体的函数。
思考部分(ps:没事找事部分):
问题1:bind方法实现要考虑继承原型链,为什么call和apply不用考虑
问题2:为什么使用bind方法后的函数,再使用call和apply,他的this不会再改变