手写call、apply和bind方法
- 一、方法介绍
- 1.1 call 方法
- 1.2 apply 方法
- 1.3 bind
- 二、方法的实现
- 2.1 call 方法
- 2.2 apply 方法
- 2.3 bind 方法
一、方法介绍
apply、call和bind都是系统提供给我们的内置方法,每个函数都可以使用这三种方法,是因为apply、call和bind都实现在了Function的原型上(Function.prototype),而他们的作用都是给我们函数调用时显式绑定上this。下面先介绍一下它们的基本用法:
1.1 call 方法
call方法:使用一个指定的 this值和单独给出的一个或多个参数来调用一个函数。
- 使用语法:func.call(thisArg, arg1, arg2, …)
- thisArg:在func函数调用时绑定的this值;
- arg1, arg2, …:指定的参数列表,将作为参数传递给func函数;
- 使用效果:
function foo(x, y ,z) {
console.log(this, x, y, z)
}
const obj = { name: 'curry', age: 30 }
/**
* 1.将obj对象绑定给foo函数的this
* 2.call剩余参数中的a b c分别传递给foo函数对应的三个参数
*/
foo.call(obj, 'a', 'b', 'c')
1.2 apply 方法
apply方法:调用一个具有给定this值的函数,以及以一个**数组(或类数组对象)**的形式提供的参数。
- 使用语法:func.apply(thisArg, [argsArray])
- thisArg:在func函数调用时绑定的this值;
- [argsArray]:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给func函数;
- 使用效果:
function foo(x, y ,z) {
console.log(this, x, y, z)
}
const obj = { name: 'curry', age: 30 }
/**
* 1.将obj对象绑定给foo函数的this
* 2.数组中的1 2 3分别传递给foo函数对应的三个参数
*/
foo.apply(obj, [1, 2, 3])
1.3 bind
bind方法:创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
- 使用语法:func.bind(thisArg[, arg1[, arg2[, …]]])
- thisArg:调用func函数时作为this参数传递给目标函数的值;
- arg1, arg2, …:当目标函数被调用时,被预置入func函数的参数列表中的参数;
- 使用效果:
function foo(...args) {
console.log(this, ...args)
}
const obj = { name: 'curry', age: 30 }
/**
* 1.将obj对象绑定给foo函数的this
* 2.bind剩余参数中的1 2 3分别传递给foo函数中参数
* 3.也可在newFoo调用时传入参数,这时bind传递的参数会与newFoo调用时传递的参数进行合并
*/
const newFoo = foo.bind(obj, 1, 2, 3)
newFoo()
newFoo('a', 'b', 'c')
二、方法的实现
function foo(...arg) {
console.log(this.name, ...arg);
}
function obj() {
this.name = 'obj'
}
2.1 call 方法
Function.prototype.myCall = function(context, ...args) {
// 1. 检查上下文是否为 null 或 undefined,如果是则设置为全局对象(浏览器中为 window)
context = context || window;
// 2. 为上下文对象创建一个唯一属性来保存当前函数,
// 以便在调用完后删除这个属性,避免污染原始对象
const fnKey = Symbol();
context[fnKey] = this;
// 3. 通过上下文对象调用函数并传入参数
const result = context[fnKey](...args);
// 4. 删除添加的属性
delete context[fnKey];
// 返回函数调用结果
return result;
};
// 原始call方法
foo.call(obj, 1, 2, 3)
// 手写call方法
foo.myCall(obj, 1, 2, 3)
输出结果:
2.2 apply 方法
Function.prototype.myApply = function (context, args) {
// 1.检查上下文是否为 null 或 undefined,如果是则设置为全局对象(浏览器中为 window)
context = context || window;
// 2. 为上下文对象创建一个唯一属性来保存当前函数,
// 以便在调用完后删除这个属性,避免污染原始对象
const fnKey = Symbol();
context[fnKey] = this;
let result;
// 3. 检查是否传入了参数
if (args) {
// 使用扩展运算符展开参数数组,并通过上下文对象调用函数
result = context[fnKey](...args);
} else {
// 如果未传入参数则直接通过上下文对象调用函数
result = context[fnKey]();
}
// 4. 删除添加的属性
delete context[fnKey];
// 返回函数调用结果
return result;
};
foo.apply(obj, [1, 2, 3])
foo.myApply(obj, [1, 2, 3])
输出结果:
2.3 bind 方法
Function.prototype.myBind = function(context, ...args) {
const fn = this;
return function(...innerArgs) {
// 使用 apply 方法将传入的上下文和参数传递给函数,并返回调用结果
return fn.apply(context, [...args, ...innerArgs]);
};
};
let fn = foo.bind(obj,1,2,3)
let fn2 = foo.myBind(obj,1,2,3)
fn('a','b','c')
fn2('a','b','c')
输出结果: