在 JavaScript 中,bind
、call
和 apply
方法都可以用来改变函数的 this
指向。下面我们将分别实现这些方法的简单版本。
1. 实现 bind
bind
方法创建一个新的函数,在调用时设置 this
值,并返回这个新的函数。
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Not a function');
}
const fn = this;
const args = Array.prototype.slice.call(arguments, 1);
return function bound() {
const boundArgs = Array.prototype.slice.call(arguments);
return fn.apply(context, args.concat(boundArgs));
};
};
2. 实现 call
call
方法立即调用函数,并设置 this
值,同时传递参数列表。
Function.prototype.myCall = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Not a function');
}
context = context || window;
const args = Array.prototype.slice.call(arguments, 1);
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result;
};
3. 实现 apply
apply
方法与 call
类似,但传递的参数是一个数组。
Function.prototype.myApply = function (context, args) {
if (typeof this !== 'function') {
throw new TypeError('Not a function');
}
context = context || window;
args = args || [];
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result;
};
示例代码
下面是一个完整的示例,展示了如何使用这些自定义的方法:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定义 bind, call, apply</title>
</head>
<body>
<script type="text/javascript">
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Not a function');
}
const fn = this;
const args = Array.prototype.slice.call(arguments, 1);
return function bound() {
const boundArgs = Array.prototype.slice.call(arguments);
return fn.apply(context, args.concat(boundArgs));
};
};
Function.prototype.myCall = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Not a function');
}
context = context || window;
const args = Array.prototype.slice.call(arguments, 1);
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result;
};
Function.prototype.myApply = function (context, args) {
if (typeof this !== 'function') {
throw new TypeError('Not a function');
}
context = context || window;
args = args || [];
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result;
};
// 测试对象
const obj = {
name: '牛客网'
};
// 测试函数
function greet(message) {
console.log(`${message}, ${this.name}`);
}
// 使用 myBind
const greetBound = greet.myBind(obj, '欢迎来到');
greetBound(); // 输出: 欢迎来到, 牛客网
// 使用 myCall
greet.myCall(obj, '欢迎来到'); // 输出: 欢迎来到, 牛客网
// 使用 myApply
greet.myApply(obj, ['欢迎来到']); // 输出: 欢迎来到, 牛客网
</script>
</body>
</html>
详细步骤
-
实现
myBind
:- 检查调用者是否为函数。
- 获取上下文
context
和传入的参数args
。 - 返回一个新的函数
bound
,在调用时使用apply
方法设置this
值并传递参数。
-
实现
myCall
:- 检查调用者是否为函数。
- 获取上下文
context
和传入的参数args
。 - 将函数赋值给
context
的一个临时属性fn
,调用该属性并传递参数,然后删除该属性。
-
实现
myApply
:- 检查调用者是否为函数。
- 获取上下文
context
和传入的参数数组args
。 - 将函数赋值给
context
的一个临时属性fn
,调用该属性并传递参数,然后删除该属性。
测试
-
使用
myBind
:- 创建一个绑定了
obj
上下文的新函数greetBound
,并调用它。
- 创建一个绑定了
-
使用
myCall
:- 直接调用
greet
函数,并设置obj
为this
值。
- 直接调用
-
使用
myApply
:- 直接调用
greet
函数,并设置obj
为this
值,参数以数组形式传递。
- 直接调用