Javascript相关API手写

50 阅读1分钟

1、手写Promise

1.1 Promise构造函数

function Promise(executor) {
    if (typeof excutor !== 'function') {
        throw new TypeError(`promise value ${executor} is not function`)
    }
    
    this.state = 'pending' // 'pending', 'fulfilled', 'rejected'
    this.resolverList = []
    this.rejecterList = []
    this.finallyList = []
    this.data = null
    
    const self = this
    // 提供resolve钩子函数
    function resolve(data) {
        // ???
        if (data instanceof Promise) {
            return data.then(self.resolve, self.reject)
        }
        // setTimeout模拟异步,
        setTimeout(function() {
            // 保证状态的唯一性
            if (self.state === 'pending') {
                self.state = 'fulfilled'
                self.data = data
                while(self.resolverList.length) {
                    const resolver = self.resolverList.shift()
                    resolver(data)
                }
                // finally钩子流程结束后被触发
                while(self.finallyList.length) {
                    const finallyFn = self.finallyList.shift()
                    finallyFn(data)
                }
            }
        })
    }
    
    function reject(reason) {
        setTimeout(function(){
            if (self.state === 'pending') {
                self.state = 'rejected'
                self.data = reason
                while(self.rejecterList.length) {
                    const rejecter = self.rejecterList.shift()
                    rejecter(reason)
                }
                while(self.finallyList.length) {
                    const finallyFn = self.finallyList.shift()
                    finallyFn(data)
                }
            }
        })
    }
    
    // 回调函数立即执行,并提供resolve,reject钩子函数
    try {
        excutor(resolve, reject)
    } catch(err) {
        reject(err)
    }
}

1.2 关键的then函数

  1. 返回promise1对象
  2. 如果then钩子执行结果返回的是promise2,则promise2的钩子触发时会执行promise1的钩子
  3. 如果执行结果不是promies实例,则结果作为promise1的返回值
Promise.prototype.then = function(onResolve, onReject) {
    // 判断钩子,并补全
    onResolve = typeof onResolve === 'function' ? onResolve : function(data) { return data }
    onReject = typeof onReject === 'function' ? onReject : function(reason) { return reason }
    const self = this
    // 当then函数被调用时,可能promise可能存在三种状态
    if (self.state === 'fulfilled') {
        // 返回promise
        return new Promise((resolve, reject) => {
            try {
                // 获取执行结果
                const result = onResolve(self.data)
                // 判断结果是否是promise实例
                if (result instanceof Promise) {
                    // 当result有结果时,promise的钩子就会被触发,就可以通过.then链式访问结果
                    result.then(resolve, reject)
                } else {
                    // result作为返回值
                    resolve(result)
                }
            } catch(err) {
                reject(err)
            }
        })
    }
    if (self.state === 'rejected') {
        // 返回promise
        return new Promise((resolve, reject) => {
            try {
                const result = onReject(self.data)
                if (result instanceof Promise) {
                    // 当result有结果时,promise的钩子就会被触发,就可以通过.then链式访问结果
                    result.then(resolve, reject)
                } else {
                    resolve(result)
                }
            } catch(err) {
                reject(err)
            }
        })
    }
    if (self.state === 'pending') {
        // 返回promise
        return new Promise((resolve, reject) => {
            self.resolverList.push(function() {
                try {
                    // 获取执行结果
                    const result = onResolve(self.data)
                    // 判断结果是否是promise实例
                    if (result instanceof Promise) {
                        // 当result有结果时,promise的钩子就会被触发,就可以通过.then链式访问结果
                        result.then(resolve, reject)
                    } else {
                        resolve(result)
                    }
                } catch(err) {
                    reject(err)
                }
            })
            self.rejecterList.push(function() {
                try {
                    const result = onReject(self.data)
                    if (result instanceof Promise) {
                        // 当result有结果时,promise的钩子就会被触发,就可以通过.then链式访问结果
                        result.then(resolve, reject)
                    } else {
                        resolve(result)
                    }
                } catch(err) {
                    reject(err)
                }
            })
        })
    }
}

1.3 catch函数

  1. 返回promise对象
  2. 函数的返回值作为promise的结果
Promise.prototype.catch = function(reject) {
    // 借用then来实现
    return this.then(null, reject)
}

1.4 finally函数

Promise.prototype.finally = function(callback) {
    this.finallyList.push(onFinally)
}

1.5 resolve函数

  1. 如果参数是promsie实例,则直接返回该实例
Promise.resolve = function(data) {
    if (data instanceof Promise) {
        return data
    }
    return new Promise((resolve, reject) => {
        resolve(data)
    })
}

1.6 reject函数

Promise.reject = function(reason) {
    return new Promise((resolve, reject) => {
        reject(reason)
    })
}

1.7 all函数

  1. 返回promise对象
  2. 所有状态都为fulfilled时才返回fulfilled,且结果按顺序排列
  3. 有一个rejected,则返回rejected
Promise.all = function(promiseList) {
    if (!Array.isArray(promiseList)) {
        throw new TypeError(`${promiseList} is not iterable`)
    }
    return new Promise((resolve,reject) => {
        let idx = 0
        let resolverList = Array(promiseList.length)
        for (let index = 0; index < promiseList.length; index++) {
            const p = promiseList[index];
            p.then(res => {
                idx += 1
                // 按顺序排列结果
                resolverList[index] = res
            }, reject).finally(() => {
                // 都fulfilled,才resolve
                if (idx === promiseList.length) {
                    resolve(resolverList)
                }
            })
        }
    })
}

1.8 any函数

  1. 返回promise对象
  2. 所有状态都为rejected时才返回rejected,且结果按顺序排列
  3. 有一个fulfilled,则返回fulfilled
Promise.any = function(promiseList) {
    if (!Array.isArray(promiseList)) {
        throw new TypeError(`${promiseList} is not iterable`)
    }
    return new Promise((resolve,reject) => {
        let idx = 0
        let rejecterList = Array(promiseList.length)
        for (let index = 0; index < promiseList.length; index++) {
            const p = promiseList[index];
            p.then(resolve, rej => {
                idx += 1
                // 按顺序排列结果
                rejecterList[index] = rej
            }).finally(() => {
                // 都rejected,才reject
                if (idx === promiseList.length) {
                    reject(rejecterList)
                }
            })
        }
    })
}

1.9 race函数

  1. 返回promise对象
  2. 谁先出结果,就返回谁的结果
Promise.race = function(promiseList) {
    if (!Array.isArray(promiseList)) {
        throw new TypeError(`${promiseList} is not iterable`)
    }
    return new Promise((resolve, reject) => {
        while (promiseList.length) {
            const p = promiseList.shift()
            p.then(resolve, reject)
        }
    })
}

1.10 allSettled函数

  1. 返回promsie对象
  2. 返回状态永远是fulfilled
  3. 按顺序返回所有的结果
Promise.allSettled = function(promiseList) {
    if (!Array.isArray(promiseList)) {
        throw new TypeError(`${promiseList} is not iterable`)
    }
    let idx = 0
    let result = Array(promiseList.length)
    return new Promise((resolve) => {
        for (let index = 0; index < promiseList.length; index++) {
            const p = promiseList[index];
            p.then(res => {
                result[index] = { status: 'fulfilled', value: res}
            }, rej => {
                result[index] = { status: 'rejected', value: rej}
            }).finally(() => {
                idx+=1
                if (idx === promiseList.length) {
                    resolve(result)
                }
            })
        }
    })
}

1.11 加强版then函数

Promise.prototype.then = function(onResolve, onReject){
    // 判断钩子,并补全
    onResolve = typeof onResolve === 'function' ? onResolve : function(data) { return data }
    onReject = typeof onReject === 'function' ? onReject : function(reason) { return reason }
    const self = this
    let promise2 = null
    // 当then函数被调用时,可能promise可能存在三种状态
    if (self.state === 'fulfilled') {
        // 返回promise
        return promise2 = new Promise((resolve, reject) => {
            try {
                // 获取执行结果
                const result = onResolve(self.data)
                resolvePromise(promise2, result, resolve, reject)
            } catch(err) {
                reject(err)
            }
        })
    }
    if (self.state === 'rejected') {
        // 返回promise
        return promise2 = new Promise((resolve, reject) => {
            try {
                const result = onReject(self.data)
                resolvePromise(promise2, result, resolve, reject)
            } catch(err) {
                reject(err)
            }
        })
    }
    if (self.state === 'pending') {
        // 返回promise
        return promise2 = new Promise((resolve, reject) => {
            self.resolverList.push(function() {
                try {
                    // 获取执行结果
                    const result = onResolve(self.data)
                    resolvePromise(promise2, result, resolve, reject)
                } catch(err) {
                    reject(err)
                }
            })
            self.rejecterList.push(function() {
                try {
                    const result = onReject(self.data)
                    resolvePromise(promise2, result, resolve, reject)
                } catch(err) {
                    reject(err)
                }
            })
        })
    }
}

function resolvePromise(promise, result, resolve, reject) {
    if (promise === result) {
        // 避免循环引用
        throw new TypeError('Chaining cycle detected for promise!')
    }
    // 如果result为Promise
    if (result instanceof Promise) {
        if (result.state === 'pending') {
            result.then(function(res){
                // 需要判断res的类型,可能会存在循环引用,没想道对应的场景
                resolvePromise(promise, res, resolve, reject)
            }, reject)
        } else {
            result.then(resolve, reject)
        }
        return
    }
    let called = false
    if (result !== null && (typeof result === 'object' || typeof x ==== 'function')) {
        try {
            // result可能是第三方自己封装的promise对象
            let then = result.then
            iftypeof then === 'function') {
                then.call(result, function(res) {
                    if (called) return
                    called = true
                    resolvePromise(promise, res, resolve, reject)
                }, function(rej) {
                    if (called) return
                    called = true
                    reject(rej)
                })
            } else {
                resolve(result)
            }
        } catch(err) {
            if (called) return 
            called = true
            reject(err)
        }
    }
}

2、手写new、bind、call、apply

2.1 new

  1. 创建一个新对象
  2. 新对象的原型链继承构造函数的原型(继承构造函数原型)
  3. 通过call函数执行构造函数中的代码(为新对象添加构造函数的属性,继承自有属性)
  4. 构造函数执行结果是引用类型或者函数则直接该结果,否则返回新对象
function _new(ctor, ...args) {
    if (typeof ctor !== 'function') {
        throw new TypeError(`${ctor} is not a constructor`)
    }
    const o = {}
    o.__proto__ = Object.create(ctro.prototype)
    const res = ctor.call(o, ...args)
    let isObject = typeof res === 'object' && res !== null;
    let isFunction = typeof res === 'function';
    return isObject || isFunction ? res : obj;
}

2.2 call函数

Function.prototype.call = function(context, args) {
    const fn = this;
    context = context ?? window;
    context.fn = fn;
    const res = context.fn(...args)
    delete context.fn
    return res
    
}

2.3 apply函数

Function.prototype.apply = function(context, args) {
    const fn = this
    context = context ?? window
    context.fn = fn
    const res = context.fn(args)
    delete context.fn
    return res
}

2.4 bind函数

  1. 返回一个新函数
  2. 新函数继承原函数的原型
  3. 当新函数被当作构造函数执行时,不绑定新this
// 代码参考core.js
function isCallable(target) {
    if (typeof target !== 'function') {
        return false
    }
    if (typeof target.call !== 'function') {
        return false
    }
    return true
}
const factories = {}
// 工厂函数,用来创建实例
function construct(C, argsLength, args) {
    if (!factories.hasOwnProperty(argsLength)) {
        let list = []
        for (let i = 0; i < argsLength; i++) {
            list[i] = `a[${i}]`;
        }
        factories[argsLength] = Function('C,a', `return new C(${list})`);
    }
    return factories[argsLength](C, args)
}

Function.prototype.bind = function(that, ...partArgs){
    let F = this
    if (!isCallable(F)) {
        // 用来规避 Function.prototype.bind.apply(xxx, args)
        throw new TypeError(`${F} is not function or not has call method`)
    }
    const Prototype = F.prototype
    const boundFunction = function bound() {
        const args = Array.prototype.push.call(partArgs, arguments)
        // 满足条件意味着当前操作为new实例,这时绑定上下文功能自动失效,而是返回原函数的实例
        return this instanceof boundFunction ? construct(F, args.length, args) : F.apply(that, args)
    }
    if (typeof Prototype === 'object') {
        // 继承原型
        boundFunction.prototype = Prototype
    }
    return boundFunction
}

3、手写数组API

3.1 push方法

Array.prototype.push = function(...args) {
    const o = Object(this)
    const len = this.length >>> 0
    const argLen = args.length >>> 0
    // 2^53 - 1 代表的是js的最大安全数,但是从浏览器里实验,数组的最大长度不能超过2^32 - 1,所以这里的逻辑与数组最大长度的实际限制有出入
    if (len + argLen > 2 ** 53 - 1) {
        throw new TypeError("The number of array is over the max value")
    }
    for(let i = 0; i < argLen; i++) {
        o[len + 1] = args[i]
    }
    const newLen = len + argLen
    o.length = newLen
    return newLen
}

3.2 pop方法

Array.prototype.pop = function() {
    const o = Object(this)
    const len = this.length >>> 0
    if (len === 0) {
        return undefined
    } else {
        const newLen = len - 1
        const element = o[newLen]
        delete o[newLen]
        o.length = newLen
        return element
    }
}

3.3 map方法

Array.prototype.map = function(callback, thisObject) {
    const o = Object(this)
    const len = this.length >>> 0
    if (typeof callback !== 'function') {
        throw new TypeError(callback + 'is not a function')
    }
    const A = Array(len)
    let k = 0
    while (k < len) {
        if (k in o) {
            const val = callback.call(thisObject, o[k], k, o)
            A[k] = val
        }
        k ++
    }
    return A
}

3.4 reduce方法

Array.prototype.reduce = function(callback, initialValue){
    const o = Object(this)
    const len = this.length >>> 0
    if (typeof callback !== 'function') {
        throw new TypeError(callback + 'is not a function')
    }
    if (len === 0 && !initialValue) {
        throw new TypeError('Reduce of empty array with no initial value')
    }
    let k = 0;
    var accumulator;
    if (initialValue) {
        accumulator = initialValue
    } else {
        let kPresent = false;
        while (!kPresent && k < len) {
            if (k in o) {
                accumulator = o[k]
                kPresent = true
            }
            k ++
        }
        if (!kPresent) {
            throw new TypeError('Reduce of empty array with no initial value')
        }
    }
    while(k < len) {
        if (k in o) {
            accumulator = callback(accumulator, o[k], k, o)
        }
        k++
    }
    return accumulator
}

4、手写EventEmitter

4.1 EventEmitter构造函数

function EventEmitter() {
    this._event = {}
}

function isValidListener(listener) {
    if (typeof listener === 'function') {
        return true
    } else if (typeof listener === 'object') {
        return isValidListener(listener.listener)
    }
    return false
}

function indexOf(array, item) {
    let result = -1
    item = typeof item === 'object' ? item.listener : item
    for (let i = 0; i < array.length; i++) {
        if (array[i].listener === item) {
        result = i
        break
    }
    return result
}

4.2 on方法

EventEmitter.prototype.on = function(eventName, listener) {
    if (eventName && listener) {
        const listenerList = this._event[eventName] ?? []
        if (!isValidListener(listener)) {
            throw new TypeError(`${listener} is not a function`)
        }
        if (indexOf(listenerList, listener) === -1) {
            const listenerIsWrapped = typeof listener === 'object'
            listenerList.push(listenerIsWrapped ? listener : {
                listener,
                once: false
            })
        }
        this._event[eventName] = listenerList
    }
}

4.3 emit方法

EventEmitter.prototype.emit = function(eventName, ...args) {
    if (eventName) {
        const listenerList = this._event[eventName]
        if (listenerList?.length) {
            for (let i = 0; i < listenerList.length; i++) {
                const listener = listenerList[i]
                if (listener) {
                    listener.listener.call(this, ...args)
                    if (listener.once) {
                        this.off(eventName, listener.listener)
                    }
                }
            }
        }
    }
}

4.4 off方法

EventEmitter.prototype.off = function(eventName, listener) {
    if (eventName && listener) {
        const listenerList = this._event[eventName]
        if (listenerList?.length) {
            const idx = indexOf(listenerList, listener)
            if (idx !== -1) {
                listenerList.splice(idx, 1)
            }
        }
    }
}

4.5 once方法

EventEmitter.prototype.once = function(eventName, listener) {
    this.on(eventName, {listener, once: true})
}

4.6 offAll方法

EventEmitter.prototype.offAll = function(eventName) {
    if (eventName) {
        this._event[eventName] = []
    } else {
        this._event = {}
    }
}