在 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}`);}// 使用 myBindconst greetBound = greet.myBind(obj, '欢迎来到');greetBound(); // 输出: 欢迎来到, 牛客网// 使用 myCallgreet.myCall(obj, '欢迎来到'); // 输出: 欢迎来到, 牛客网// 使用 myApplygreet.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
值,参数以数组形式传递。
- 直接调用