js手写 call、apply、bind 函数
了解 call、apply、bind 函数
call、apply和bind三个函数都可以用来改变JavaScript函数内部的this指向,这是它们共同的特点,只不过在用法上略微不同。过多的话也不多说直接上案例吧
call
call是Function对象自带的一个方法,它可以用来调用函数并且改变函数内部的this指向。call函数的第一个参数就是要指定的this指向,后面的参数则是传入函数的参数列表。
function sayHello() {
console.log(this); // { name: 'John' }
console.log(`Hello, ${this.name}!`);
}
let person = { name: 'John' };
sayHello.call(person); // 输出:Hello, John!
在这个例子中,我们使用了call函数将sayHello函数内部的this指向了person对象,从而输出了“Hello, John!”这句话。
apply
apply与call很相似,区别在于传入的参数列表是一个数组,而不是一系列单独的参数。这使得apply更适合用于传入动态数量的参数。
function sum(a, b, c) {
console.log(this); // { name: 'zjl' }
return a + b + c;
}
const obj = {
name: 'zjl'
}
let nums = [1, 2, 3];
let result = sum.apply(obj, nums);
console.log(result); // 输出:6
在这个例子中,我们使用了apply函数将sum函数内部的this指向了obj对象,并且传入了一个数组nums作为参数列表,最终计算出了1+2+3=6。
bind
bind函数也可以用来改变函数内部的this指向,但是它与call和apply的区别在于,bind函数会返回一个新函数,而不是立即调用原函数并改变this指向。
let person = { name: 'John' };
function sum(num1, num2) {
console.log(this); // { name: 'John' }
return num1 + num2
}
let res = sum.bind(person, 1);
console.log(res(2)); // 3
bind函数是这三个函数当中较为特别的,它可以实现柯里化操作,我们会发现在我们这个例子中 sum 函数接受两个 number 参数,第一次我们通过 bind 函数调用的时候只传递了一个参数,第二次我们调用通过调用 bind 函数返回的函数又传递了一个参数,输出的结果就是先后传递number参数的和,这不就是js中的柯里化操作么
关于这三个函数的介绍和应用就到这里了,接下来就让我来带大家实现用js手写这个三个函数
手写 call 函数
步骤:
- 在Function原型上添加一个mycall方法,该方法接收两个参数:thisArg和args,其中thisArg表示需要改变的this指向,args表示传递给函数的参数。
- 如果传入的thisArg为null或undefined,则将其转换为全局对象window。
- 创建一个Symbol作为属性名,避免与对象原有属性名冲突。
- 将当前函数作为thisArg对象的属性。
- 将fn属性设置为不可枚举。
- 调用thisArg对象的属性,即当前函数,并传入参数args。
- 删除thisArg对象的属性。
- 返回函数调用结果。
Function.prototype.mycall = function (thisArg, ...args) {
// 如果传入的thisArg为null或undefined,则将其转换为全局对象window
thisArg = thisArg !== null && thisArg !== undefined ? Object(thisArg) : window
// 创建一个Symbol作为属性名,避免与对象原有属性名冲突
const fn = Symbol('fn')
// 将当前函数作为thisArg对象的属性
thisArg[fn] = this
// 将fn属性设置为不可枚举
Object.defineProperty(thisArg, fn, {
enumerable: false
})
// 调用thisArg对象的属性,即当前函数,并传入参数args
const res = thisArg[fn](...args)
// 删除thisArg对象的属性
delete thisArg[fn]
// 返回函数调用结果
return res
}
function test(num1, num2) {
console.log(this); // { name: 'zjl' }
console.log(num1 + num2); // 3
}
const obj = {
name: 'zjl'
}
// 调用test函数,并将obj作为thisArg,1和2作为参数
test.mycall(obj, 1, 2)
手写 apply 函数
手写 apply 函数跟手写 call 函数很像,不同之处就是 apply 函数传入的参数列表是一个数组,而不是一系列单独的参数。废话不多说,直接上代码:
Function.prototype.myapply = function (thisArg, argsArr) {
// 如果传入的thisArg为null或undefined,则将其转换为全局对象window
thisArg = thisArg !== null && thisArg !== undefined ? Object(thisArg) : window
// 创建一个Symbol作为属性名,避免与对象原有属性名冲突
const fn = Symbol('fn')
// 将当前函数作为thisArg对象的属性
thisArg[fn] = this
// 将fn属性设置为不可枚举
Object.defineProperty(thisArg, fn, {
enumerable: false
})
// 调用thisArg对象的属性,即当前函数,并传入参数args
const res = thisArg[fn](...argsArr)
// 删除thisArg对象的属性
delete thisArg[fn]
// 返回函数调用结果
return res
}
function sum(num1, num2) { // 定义一个示例函数
console.log(this); // { name: 'jay' }
return num1 + num2; // 返回两个参数的和
}
const obj = { name: "jay" }; // 定义一个对象作为thisArg
const res = sum.myapply(obj, [2, 3]) // 使用obj作为this和[2, 3]作为参数调用sum函数
console.log(res); // 5
手写 bind 函数
步骤:
- 在Function原型上添加一个mybind方法,该方法接收两个参数:thisArg和args,其中thisArg表示需要改变的this指向,args表示传递给函数的参数。
- 如果传入的thisArg为null或undefined,则将其转换为全局对象window。
- 将 this 赋值给 _this。
- 返回一个新的函数 proxyFn,接收 proxyArgs 作为参数。
- 在 proxyFn 函数内部创建一个Symbol作为属性名,避免与对象原有属性名冲突。
- 将原函数作为thisArg对象的属性。
- 将fn属性设置为不可枚举。
- 调用thisArg对象的属性,即原函数,并传入参数args和proxyArgs。
- 删除thisArg对象的属性。
- 返回 proxyFn 函数调用结果。
Function.prototype.mybind = function(thisArg, ...args) {
// 如果传入的thisArg为null或undefined,则将其转换为全局对象window
thisArg = thisArg !== null && thisArg !== undefined ? Object(thisArg) : window
const _this = this
// 返回一个新的函数,接收proxyArgs作为参数
function proxyFn(...proxyArgs) {
// 创建一个Symbol作为属性名,避免与对象原有属性名冲突
const fn = Symbol('fn')
// 将原函数赋值给thisArg的fn属性
thisArg[fn] = _this
// 将fn属性设置为不可枚举
Object.defineProperty(thisArg, fn, {
enumerable: false
})
// 合并两个剩余参数
const argsArr = [...args, ...proxyArgs]
// 调用原函数并返回结果
const res = thisArg[fn](...argsArr)
// 删除fn属性
delete thisArg[fn]
// 返回函数调用结果
return res
}
return proxyFn
}
function sum(num1, num2) {
console.log(this); // { name: 'jay' }
return num1 + num2
}
const obj = { name: 'jay' }
// 使用mybind方法将sum函数绑定到obj对象上,并传入参数1
const proxyFn = sum.mybind(obj, 1)
// 调用proxyFn函数并传入参数2
console.log(proxyFn(2)); // 3
总结
至此我们就已经完成了如何用js手写 call、apply、bind 函数,其实不难发现整个代码实现其实不难,无非就是思路问题,主要看自己是否敢去尝试,共勉!
转载自:https://juejin.cn/post/7227453567685427260