js其余手写题汇总

js其余手写题汇总,第1张

Promise
// 原文链接:https://juejin.cn/post/6856213486633304078 

const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'

function Promise(executor) {
    const self = this
    self.callbacks = []
    self.state = PENDING
    self.data = undefined
    // 执行callbacks里的函数,并保存data,并将当前promise状态改为resolved
    function resolve(val) {
        // Promise的状态只能改变一次
        if (self.state !== PENDING) {
            return
        }
        self.data = val
        self.state = RESOLVED
        if (self.callbacks.length > 0) {
            self.callbacks.forEach(cb => {
                setTimeout(() => {
                    cb.onResolved(self.data)
                });
            })
        }
    }
    function reject(err) {
        if (self.state !== PENDING) {
            return
        }
        self.data = err
        self.state = REJECTED
        if (self.callbacks.length > 0) {
            self.callbacks.forEach(cb => {
                setTimeout(() => {
                    cb.onRejected(self.data)
                });
            })
        }
    }
    // 执行Promise调用传进来的函数
    try {
        executor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}
Promise.prototype.then = function (onResolved, onRejected) {

    const self = this
    // .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。值传透可以理解为,
    // 当传入then的不是函数的时候,这个then是无效的。而实际原理上其实是当then中传入的不算函数,
    // 则这个then返回的promise的data,将会保存上一个的promise.data。这就是发生值穿透的原因。
    // 而且每一个无效的then所返回的promise的状态都为resolved。
    onResolved = typeof onResolved === 'function' ? onResolved : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

    return new Promise((resolve, reject) => {
        // 调用指定回调函数的处理,根据执行结果。改变return的promise状态
        function handle(callback) {
            try {
                const result = callback(self.data)
                if (result instanceof Promise) {
                    // 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
                    result.then(
                        value => { resolve(value) },
                        reason => { reject(reason) }
                    )
                } else {
                    // 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
                    resolve(result)
                }
            } catch (e) {
                //  3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
                reject(e)
            }
        }
        if (self.state === PENDING) {
            self.callbacks.push({
                onResolved: () => handle(onResolved),
                onRejected: () => handle(onRejected)
            })
        } else if (self.state === RESOLVED) {
            setTimeout(() => {
                handle(onResolved)
            });
        } else {
            setTimeout(() => {
                handle(onRejected)
            });
        }
    })
}
Promise.prototype.catch = function (onRejected) {
    return this.then(undefined, onRejected)
}

// 返回一个指定结果的promise对象
// 有这三种情况:和上面then有点像
// Promise.resolve(1)
// Promise.resolve(Promise.resolve(1))
// Promise.resolve(Promise.reject(1))

Promise.resolve = function (value) {
    return new Promise((resolve, reject) => {
        if (value instanceof Promise) {
            value.then(
                val => resolve(val),
                err => reject(err)
            )
        } else {
            resolve(value)
        }
    })
}

// 返回一个指定reason的失败状态的promise对象
Promise.reject = function (value) {
    return new Promise((resolve, reject) => {
        reject(value)
    })
}

// 返回一个promise对象,只有当所有promise都成功时返回的promise状态才成功
Promise.all = function (promises) {
    const values = []
    let resolveCount = 0
    return new Promise((resolve, reject) => {
        // 把不是promise的数字包装成promise
        promises.forEach((p, i) => {
            Promise.resolve(p).then(
                res => {
                    resolveCount++
                    values.push(res)
                    if (resolveCount === promises.length) {
                        resolve(values)
                    }
                },
                // 如果遍历到promise状态为reject就直接返回reject的promise
                err => {
                    reject(err)
                }
            )
        })
    })
}
// 返回一个promise对象,状态由第一个完成的promise决定
Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        promises.forEach((p, i) => {
            Promise.resolve(p).then(
                val => {
                    // 只要有一个成功,返回的promise的状态九尾resolved
                    resolve(val)
                }, err => {//只要有一个失败,return的promise状态就为reject
                    reject(err)
                }
            )
        })
    })
}

// Promise.any 的规则是这样:
// 空数组或者所有 Promise 都是 rejected,则返回状态是 rejected 的新 Promsie,且值为 AggregateError 的错误;
// 只要有一个是 fulfilled 状态的,则返回第一个是 fulfilled 的新实例;
// 其他情况都会返回一个 pending 的新实例;
Promise.any = function (promiseArr) {
    let index = 0
    return new Promise((resolve, reject) => {
        if (promiseArr.length === 0) return
        promiseArr.forEach((p, i) => {
            Promise.resolve(p).then(
                val => {
                    resolve(val)
                }, err => {
                    index++
                    if (index === promiseArr.length) {
                        reject(new AggregateError('All promises were rejected'))
                    }
                })
        })
    })
}
Promise.finally = function(callback) {
  return this.then(res => {
    callback()
    return res
  }, err => {
    callback()
    throw err
  })
}
真实dom虚拟dom互转

虚拟dom转真实dom:

{
  tag: 'DIV',
  attrs:{
  id:'app'
  },
  children: [
    {
      tag: 'SPAN',
      children: [
        { tag: 'A', children: [] }
      ]
    },
    {
      tag: 'SPAN',
      children: [
        { tag: 'A', children: [] },
        { tag: 'A', children: [] }
      ]
    }
  ]
}
把上诉虚拟Dom转化成下方真实Dom
<div id="app">
  <span>
    <a></a>
  </span>
  <span>
    <a></a>
    <a></a>
  </span>
</div>
function render(vnode) {
    if (typeof vnode === 'number') {
        vnode = String(vnode)
    }
    if (typeof vnode === 'string') {
        return document.createTextNode(vnode)
    }
    const dom = document.createElement(vnode.tag)
    if(vnode.attrs){
        Reflect.ownKeys(vnode.attrs).forEach((key)=>{
            const val = vnode.attrs[key]
            dom.setAttribute(key, val)
        })
    }
    vnode.children.forEach((child) => {
        dom.appendChild(render(child))
    })
    return dom
}

真实dom转虚拟dom:

function domToVdom (domTree){
    const vdom = {}
    vdom.tag = domTree.tagName
    vdom.children = []
    const children = Array.prototype.slice.call(domTree.children)
    children.forEach((child)=>{
        vdom.children.push(domToVdom(child))
    })
    return vdom
}
发布订阅模式
class Event {
    constructor() {
        // 储存事件的数据结构
        // 为查找迅速, 使用对象(字典)
        this._cache = {}
    }

    // 绑定
    on(type, callback) {
        // 为了按类查找方便和节省空间
        // 将同一类型事件放到一个数组中
        // 这里的数组是队列, 遵循先进先出
        // 即新绑定的事件先触发
        let fns = (this._cache[type] = this._cache[type] || [])
        if (fns.indexOf(callback) === -1) {
            fns.push(callback)
        }
        return this
    }

    // 触发
    // emit
    trigger(type, data) {
        let fns = this._cache[type]
        if (Array.isArray(fns)) {
            fns.forEach((fn) => {
                fn(data)
            })
        }
        return this
    }

    // 解绑
    off(type, callback) {
        let fns = this._cache[type]
        if (Array.isArray(fns)) {
            if (callback) {
                let index = fns.indexOf(callback)
                if (index !== -1) {
                    fns.splice(index, 1)
                }
            } else {
                // 全部清空
                fns.length = 0
            }
        }
        return this
    }
}
实现数组原型方法

map

Array.prototype.myMap = function(cb, thisArgs){
    if(typeof cb !== 'function'){
        throw new TypeError(cb + 'is not a function~')
    }
    const target = this
    const result = []
    for(let i = 0; i < target.length; i ++){
        const res = cb.call(thisArgs, target[i], i, target)
        result.push(res)
    }
    return result
}

forEach

Array.prototype.myForEach = function (cb, thisArgs) {
    if(typeof cb !== 'function'){
        throw new TypeError(cb + 'is not a function~')
    }
    const target = this
    for(let i = 0; i < target.length; i ++){
        cb.call(thisArgs, target[i], i, target)
    }
}

filter

Array.prototype.myFilter = function(cb, thisArgs){
    if(typeof cb !== 'function'){
        throw new TypeError(cb + 'is not a function~')
    }
    const target = this
    const result = []
    for(let i = 0; i < target.length; i ++){
        const res = cb.call(thisArgs, target[i], i, target)
        if(res) result.push(target[i])
    }
    return result
}

reduce

Array.prototype.myReduce = function (cb, initialVal){
    if(typeof cb !== 'function'){
        throw new TypeError(cb + 'is not a function~')
    }
    let target = this, result 
    if(initialVal){
        result = initialVal
    }else{
        if(target.length > 0){
            result = target[0]
        }else{
            throw new TypeError( 'Reduce of empty array with no initial value' );
        }
    }
    for(let i = 1; i < target.length; i ++){
        result = cb(result, target[i], i, target)
    }
    return result
}
Object.is

Object.is不会转换被比较的两个值的类型,这点和=更为相似,他们之间也存在一些区别。 1. NaN在=中是不相等的,而在Object.is中是相等的 2. +0和-0在===中是相等的,而在Object.is中是不相等的

Object.prototype.myIs = function (x, y){
    if(x === y){
        // x和y相等那就只需要处理 +0和-0的情况
        return x !== 0 || 1 / x === 1 / y
    }
    // 不相等就只需要处理NaN的情况
    return x !== x && y !== y
}
分片思想解决大数据量渲染问题

原理就是利用 window.requestAnimationFrame在每一帧插入一定的数量,然后递归执行

let total = 10000
const once = 20
const ul = document.createElement('ul')
document.body.appendChild(ul)
function loop(currTotal) {
    window.requestAnimationFrame(() => {
        const pageCount = Math.min(total, once)
        for (let i = 0; i < pageCount; i++) {
            const li = document.createElement('li')
            li.innerText = '还有' + (currTotal - i)
            ul.appendChild(li)
        }
        if(currTotal <= 0){
            return 
        }
        loop(currTotal - once)
    })
}
loop(total)
判断对象是否有环
const isCycle = (obj) => {
    const arr = [obj]
    let flag = false

    function cycle(o) {
        const keys = Object.keys(o)
        // 要遍历key不要遍历value,否则会出现不可遍历的情况
        for (const key of keys) {
            const temp = o[key]
            if (typeof temp === 'object' && temp !== null) {
                if (arr.indexOf(temp) >= 0) {
                    flag = true
                    return
                }
                arr.push(temp)
                cycle(temp)
            }
        }
    }

    cycle(obj)
    return flag
}
Promisify

以前我一直不知道promiseify怎么写,因为如果传入一个异步函数的话你是不知道异步函数啥时候
执行完毕的,也就不好进行promise的resolve,但是看了es6-promiseify的源码之后知道了解决方法:
因为node中的异步任务执行完毕都会执行传入的回调函数来返回错误和结果,所以我们可以自行传入
我们定义的回调函数,然后判断是否有错误执行reject,有结果执行resolve,这样也就知道了异步
函数的调用结束时机

const fs = require('fs')
// 源码
function promiseify(cb) {
    function fn(...args) {
        return new Promise((resolve, reject) => {
            args.push(function (err, ret) {
                if (err) {
                    return reject(err)
                }
                return resolve(ret)
            })
            cb.apply(this, args)
        })
    }
    return fn
}
// 使用
const pfs = promiseify(fs.readFile)
pfs('./add.js', 'utf-8').then(ret => {
    console.log(ret)
    console.log('async')
}).catch((err) => {
    console.log(err)
})
console.log('sync')

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1324845.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存