我们都知道,call(),apply(),bind()都可以改变函数内部的this指向,那他们到底是如何实现的呢,下面我们用js来模拟实现,为什么说是模拟实现呢?因为我们的js引擎,主流的v8引擎源代码是通过c++编写的,所以我们只能说是模拟实现,但是实现的功能都是一样的,但是一些edge case可能会考虑的不是很充分。
call实现
Function.prototype.hqcall = function(thisArg, ...args) { // 剩余参数
const fn = this // 因为再调用函数的时候采用的是隐式调用,所以this会指向调用这个函数(hqcall)的函数(foo)
// 判断传进来的值,如果是null或undefined 则指向window
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
// 再内部可以直接使用call修改this指向
// fn.call(thisArg)
thisArg.fn = fn // 可以先将这个函数添加到对象中
const result = thisArg.fn(...args) // 展开运算符 // 然后通过对象的方式进行调用 (隐式绑定
delete thisArg.fn
return result
}
function foo(num1, num2) {
console.log(this)
return num1 + num2
}
const res = foo.hqcall({name: 'lhq'}, 20, 30)
console.log(res)
// foo.hqcall(123)
// foo.hqcall(null)
// foo.hqcall(undefined)
// foo.call(123)
/**
* Obejct()
* 这个方法会将传入的值转成对应的对象
*/
// console.log(Object('test'))
apply实现
// 手动实现apply
Function.prototype.hqapply = function(thisArg, arr) {
// 1.获取到要执行的函数
const fn = this
// 2.处理绑定的thisArg参数
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
// 3.将要执行的函数添加到对象中
thisArg.fn = fn
// 4.执行函数,传入参数并拿到返回值
// let result = null
// if (!arr) { // 没有传递参数
// result = thisArg.fn()
// } else { // 有传递参数
// result = thisArg.fn(...arr)
// }
// arr = arr ? arr : []
arr = arr || []
const result = thisArg.fn(...arr)
// 5.删除对象中保存的方法
delete thisArg.fn
// 6.返回最终的结果
return result
}
function foo(num1, num2) {
console.log('foo被执行了')
console.log(this)
return num1 + num2
}
function bar() {
console.log('bar执行了')
}
const res = foo.hqapply({name: 'hq'}, [20, 60])
bar.hqapply({name: 'hq'})
bind实现
Function.prototype.hqbind = function(thisArg, ...argArray) {
// 1.获取到要执行的函数
const fn = this
// 2.处理绑定的thisArg参数
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
return function proxyFn(...args) {
// 3.将函数放到thisArg中进行使用
thisArg.fn = fn
// 4.对两个传递进来的参数合并
const finalAgs = [...argArray, ...args]
const result = thisArg.fn(...finalAgs)
delete thisArg.fn
// 返回结果
return result
}
}
function foo() {
console.log('执行了foo')
}
const bar = foo.hqbind({name: 'lhq'})
bar()
foo.bind()
function num(num1, num2, num3, num4) {
console.log(num1, num2, num3, num4)
}
const res = num.bind({}, 10, 20)
res(30, 40)
/**
* bind 传递参数的时候可以分开传递,在bind调用的时候传递,再返回的函数调用时继续传递参数
*
*/