[javascript核心-14] 手撕call-apply-bind实现

76 阅读2分钟

本文github地址:JavaScript_Everything 大前端知识体系与面试宝典,从前端到后端,全栈工程师,成为六边形战士


实现call

函数作为对象调用call时,函数call内部的this便指向了该函数。

Function.prototype.myCall = function(target){
    let fn = this
    target.fn = fn
    target.fn()
}

const obj = {
    age : 'flten'
}

function fn(){
    console.log(this.age)
}

fn.myCall(obj)

主要的实现:

Function.prototype.myCall = function(target, ...args){
    let fn = this
    if(Object.is(target, null) || Object.is(target, undefined)) target = globalThis
    if(typeof target !== 'object'){
        target = Object(target)
    }
    target.fn = fn
    let result = target.fn(...args)
    delete target.fn
    return result
}

const obj = {
    age : 'flten'
}

function fn(job){
    console.log(this)
    console.log(this.age)
    console.log(job)
    console.log('-------------')
}

fn.myCall(obj, 'programmer')
fn.myCall('flten')
fn.myCall(undefined)
fn.myCall(0)

function add(num1,num2,num3){
    return num1+num2+num3
}
console.log(add.myCall(null,1,2,3))

存在的问题:

函数副作用:

如果对象存在和函数名同名的属性,就破坏了对象。

解决:使用时间戳作为属性名

Function.prototype.myCall = function(target, ...args){
    let fn = Date.now()
    let self = this
    if(Object.is(target, null) || Object.is(target, undefined)) target = globalThis
    if(typeof target !== 'object'){
        target = Object(target)
    }
    target[fn] = self
    let result = target[fn](...args)
    delete target.fn
    return result
}

判断当前是否处于全局严格模式下:

"use strict"

var fn = (function(){
    return this === undefined
}())

function test(){
    console.log(fn) // true
}

test()

判断局部严格模式:

// "use strict"

var fn = (function(){
    return this === undefined
}())

function test(){
    "use strict"
    console.log(fn) 
}

console.log(test.toString().includes("use strict")) // true

实现apply

call的区别在于传入的参数是一个数组

Function.prototype.myApply = function(target, args){
    let fn = this
    if(Object.is(target, null) || Object.is(target, undefined)) target = globalThis
    if(typeof target !== 'object'){
        target = Object(target)
    }
    target.fn = fn
    if(Object.is(args, undefined)) args = []
    let result = target.fn(...args)
    delete target.fn
    return result
}

function add(...arr){
    return arr.reduce((prev, result)=> prev + result, 0)
}
console.log(add.myApply(null,[1,2,3])) // 6
console.log(add.myApply(null))

bind的实现

Function.prototype.myBind = function(target, ...args){
    let fn = Date.now()
    let self = this
    if(Object.is(target, undefined) || Object.is(target, null)) target = globalThis
    target = Object(target)
    
    return function(){
        target[fn] = self
        let result = target[fn](...args)
        delete target[fn]
        return result
    }
}

function logNumber(num1, num2, num3){
    return num1+num2+num3
}

const newLogNumber = logNumber.myBind(null, 1,2,3)
console.log(newLogNumber()) // 6

加入柯里化支持,不一次性传入的全部参数

Function.prototype.myBind = function(target, ...args){
    let fn = Date.now()
    let self = this
    if(Object.is(target, undefined) || Object.is(target, null)) target = globalThis
    target = Object(target)
    
    return function(...newArgs){
        target[fn] = self
        let result = target[fn](...args, ...newArgs)
        delete target[fn]
        return result
    }
}

function logNumber(num1, num2, num3){
    return num1+num2+num3
}

const newLogNumber = logNumber.myBind(null, 1,2)
console.log(newLogNumber(3)) // 6

本文github地址:JavaScript_Everything 大前端知识体系与面试宝典,从前端到后端,全栈工程师,成为六边形战士