JavaScript中,模擬 call 的底層實現
面試題
function fn1 () {
console.log(1)
}
function fn2 () {
console.log(2)
}
// call 的作用
// 調用 call fn1函數,並改變fn1內部的this,將 this 指向 fn2
fn1.call(fn2) // 1
fn1.call.call(fn2) // 2
模擬 call 的實現
沒有傳參
// 讓所有的函數對象,都具有 mycall 方法
// context.fn1()
Function.prototype.mycall = function mycall(context, ...args) {
// context = context || window
// ??
// 只有 context 的值是 null 或者 undefined 的時候,纔會返回 window
context = context ?? window
// 此處的 this 指向的是 fn1 函數
// 臨時把 this 存儲進 context.fn 中
context.fn = this
const result = context.fn(...args)
delete context.fn
return result
}
執行過程
// fn1.mycall.mycall(fn2),執行過程
Function.prototype.mycall = function mycall(context, ...args) {
// context ---> fn2
context = context ?? window
// context.fn ---> this ---> mycall
// fn2.mycall
context.fn = this
// 暫停,等待執行 fn2.mycall (沒有參數)
const result = context.fn(...args)
delete context.fn
return result
}
// 調用 fn2.mycalll (沒傳參數)
Function.prototype.mycall = function mycall(context, ...args) {
// context ---> Window
context = context ?? window
// context.fn ---> this ---> fn2
// window.fn2
context.fn = this
// window.fn2 (沒有參數)
const result = context.fn(...args)
delete context.fn
return result
}
傳入參數
// context.fn1()
Function.prototype.mycall = function mycall(context, ...args) {
// 此處要去處理 context 是原始值的問題
switch (typeof context) {
case 'number':
context = new Number(context)
break
case 'string':
context = new String(context)
break
case 'boolean':
context = new Boolean(context)
break
default:
// context = context || window
context = context ?? window
}
// 此處的 this 指向的是 fn1 函數
// 臨時把 this 存儲進 context.fn 中
context.fn = this
const result = context.fn(...args)
delete context.fn
return result
}
const obj = {
name: 'zs' }
// fn1.mycall(obj)
fn1.mycall.mycall(fn2, "1", 2)
}