JavaScript 中的 call、bind 和 apply 都是函数的方法,它们用于控制函数的执行上下文和参数。
一、函数使用方法介绍
call:
call方法可以调用一个函数,并允许你直接设置函数的this值。- 语法:
func.call(thisArg, arg1, arg2, ...) thisArg是可选的,并用作函数内部的this值。- 其余参数是传递给函数的参数列表。
- 示例:
function Person(name) {
this.name = name;
this.greet = function() {
console.log('Hello, ' + this.name);
};
}
var alice = new Person('Alice');
var greetAlice = alice.greet;
greetAlice.call(alice); // 输出:Hello, Alice;
apply:
apply方法类似于call,但它允许你传递一个数组作为参数。- 语法:
func.apply(thisArg, [argsArray]) thisArg是可选的,并用作函数内部的this值。argsArray是一个数组,其中的元素将作为单独的参数传递给函数。- 示例:
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
var alice = { name: 'Alice' };
var greetAlice = greet;
greetAlice.apply(alice, ['Hello', '!']); // 输出:Hello, Alice!;
bind:
bind方法创建一个新的函数,该函数在调用时将指定的this值和任何参数传递给提供的函数。- 语法:
func.bind(thisArg[, arg1[, arg2[, ...]]]) thisArg是可选的,并用作函数内部的this值。- 后续参数将绑定为提供的函数的参数。如果没有提供后续参数,则绑定为
undefined。 - 示例:
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
var alice = { name: 'Alice' };
var greetAlice = greet.bind(alice, 'Hello');
greetAlice('!'); // 输出:Hello, Alice!;
二、函数使用情景
1: 改变函数上下文 (this 值)
function Person(name) {
this.name = name;
this.greet = function() {
console.log('Hello, ' + this.name);
};
}
var alice = new Person('Alice');
// 使用 call 改变 greet 方法的 this 值
alice.greet.call(alice); // 输出: Hello, Alice
2: 动态地改变函数参数
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
var person = { name: 'Alice' };
// 使用 apply 传递参数列表
greet.apply(person, ['Hello', '!']); // 输出: Hello, Alice!
3: 创建可复用的代码 (回调函数)
function logTime() {
console.log('Current time is: ' + new Date().toLocaleTimeString());
}
// 使用 bind 创建一个新的函数,该函数始终以特定时间格式输出时间
var logTimeFormatted = logTime.bind(null);
logTimeFormatted(); // 输出: Current time is: HH:mm:ss (例如: 14:30:20)
4: 实现函数柯里化
函数柯里化(Currying)是一种将多参数的函数转换成一系列单参数函数的技巧。
在JavaScript中,可以使用bind()方法来实现函数柯里化。通过将函数绑定到特定的上下文和参数,可以创建一个新的函数,该函数将始终使用绑定的上下文和参数来调用原始函数。然后,可以使用这个新的函数来逐步传递参数,从而实现函数柯里化的效果。
下面是一个使用bind()方法实现函数柯里化的示例:
function greet(name, punctuation) {
console.log(name + punctuation);
}
// 使用 bind 方法创建新的函数,将 name 参数绑定为 'Alice'
var greetAlice = greet.bind(null, 'Alice');
// 调用新函数,逐步传递参数
greetAlice('!'); // 输出:Alice!
三、实现call、apply、bind
1.实现call函数
Function.prototype.myCall = function (context) {
//判断调用对象
if (typeof this !== "function") {
console.error("type error");
}
//获得参数
let args = [...arguments].slice(1),
result = null;
//判断context是否传入,如果未传入则设置为window
context = context || window;
//将调用函数设为对象的方法
context.fn = this;
//调用函数
result = context.fn(...args);
//将属性删除
delete context.fn;
return result;
};
function myFunction(a, b) {
console.log(this.name + " " + a + " " + b);
return "运行";
}
var obj = { name: "John" };
myFunction.myCall(obj, 1, 2); // 输出 "John 1 2"
2.实现apply函数
Function.prototype.myApply = function (context) {
//判断调用对象是否为函数
if (typeof this !== "function") {
throw new TypeError("error");
}
let result = null;
//判断context是否存在,如果未传入则为window
context = context || window;
//将函数设为对象的方法
context.fn = this;
//调用方法
if (arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}
//将属性删除
delete context.fn;
return result;
};
var obj = { name: "John" };
myFunction.myApply(obj, [3, 4]); // 输出 "John 3 4"
3.实现bind函数
Function.prototype.myBind = function (context) {
//判断调用对象是否为函数
if (typeof this !== "function") {
throw new TypeError("Error");
}
//获取参数
var args = [...arguments].slice(1),
fn = this;
return function Fn() {
console.log("this instanceof Fn==", this instanceof Fn);
//根据调用方式,传入不同绑定值
//this instanceof Fn 的判断是为了确定当前上下文是否为 Fn 类型的实例。如果是,那么原函数的上下文将被保留;如果不是,那么上下文将被设置为传入的 context。
// 这个判断非常重要,因为在 JavaScript 中,函数的上下文可以在运行时动态地改变。如果不进行这个判断,那么即使传入了新的上下文,由于 JavaScript 的动态性,this 也可能不会如预期地被绑定到传入的上下文上。
return fn.apply(
this instanceof Fn ? this : context,
args.concat(...arguments)
);
};
};
function myFunction(age) {
console.log(this.name, age);
}
var obj = {
name: "Mike",
};
var myBoundFunction = myFunction.myBind(obj, 222);
myBoundFunction();