JS常用手写题

70 阅读1分钟

千分符

'12345678'// '12,345,678'
str.replace(/\B(?=(\d{3})+$)/g, ',')

分析:

  • \B(\b)匹配到的是一个位置,\B代表非单词边界(\b表示单词边界)单词边界
  • ?=exp表示匹配符合后面正则exp的位置

首先匹配第一个非单词边界(1和2之间),然后?=预测后面的内容为3个连续\d{3}的数字,+做循环,匹配完若为结尾则成功;接着从第二个非单词边界(2和3之间)... 最后查询完符合匹配就是在1和2之间 4和5之间

防抖节流-高频考题

防抖

事件被触发n秒后再执行回调,如果n秒内又被触发则重新计时

比如给输入框在输入的时候要去请求接口,不能说输入一个字就去请求一次,而是等用户全部输入完再去请求,上代码

function debounce(fn,delay){
    let timer = null
    return function(...args){
        timer && clearTimeout(timer)
        timer = setTimeout(()=>{
            fn.apply(this,args)  
        },delay)                
    }
}

节流

规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

function throttle(fn,delay){
    let t1 = 0
    return function(...args){
        let t2 = Date.now()
        if(t2-t1 >= delay){
            fn.apply(this,args)
            t1 = t2
        } 
    }
}

call & apply & bind

call

Function.prototype.call = function(context,...args){
    context = context ? Object(context) : window
    args ||= []
    const sym = Symbol()
    context[sym] = this
    const res = context[sym](...args)
    delete context[sym]
    return res
}

apply

Function.prototype.call = function(context,args){
    context = context ? Object(context) : window
    args ||= []
    const sym = Symbol()
    context[sym] = this
    const res = context[sym](...args)
    delete context[sym]
    return res
}

bind

Function.prototype.bind = function(context,...args){
    context = context ? Object(context) : window
    args ||= []
    const self = this
    
    const resFn = function(...args2){
        context.apply(this instanceof resFn ? this : context,args.concat(args2))
    }
    resFn.prototype = Object.create(self.prototype)
    return resFn
}

具体可以参考我这篇文章

柯里化

如何实现 add(2)(3)(4)=9?

function curry(fn,...args){
    const length = fn.length
    const allArgs = [...args]
    const resFn = (...args2)=>{
        allArgs.push(...args2)
        if(allArgs.length === length){
           return fn(...allArgs) 
        }else{
           return resFn 
        }
    }
    return resFn
}

const add = (a,b,c)=>a+b+c
const addFn = curry(add)
addFn(2)(3)(4)

发布订阅模式

class EventEmitter {
    constructor() {
      // key: 事件名
      // value: callback [] 回调数组
      this.events = {}
    }
    on(name, callback) {
      if (this.events[name]) {
          this.events[name].push(callback)
      } else {
          this.events[name] = [callback]
      }
    }
    off(name, callback) {
      if (!this.events[name]) return;
      if (!callback) {
          // 如果没有callback,就删掉整个事件
          this.events[name] = undefined;
      }
      this.events[name] = this.events[name].filter((item) => item !== callback);
    }
    once(name,callback){
      funtion fn(){
          callback()
          this.off(name,callback)  
      }  
      this.on(name,fn)
    }
    emit(name, ...args) {
      if (!this.events[name]) return
      this.events[name].forEach(cb => cb(...args))
    }
  }

树转列表

const data = [
    {
        id: 1,
        text: '节点1',
        parentId: 0,
        children: [
            {
                id: 2,
                text: '节点1_1',
                parentId: 1
            }
        ]
    }
]
function tree2list(tree){
    const res = []
    function fn(tree){
        tree.forEach(function(item){
            if(item.children){
                fn(item.children)
            }
            res.push(item)
        })
    }
    fn(tree)
    return res
}

列表转树

[
    {
        id: 1,
        text: '节点1',
        parentId: 0
    },
    {
        id: 2,
        text: '节点1_1',
        parentId: 1
    }
    ...
]
function list2tree(list){
    const obj = {}
    const res = []
    arr.forEach(item => {
        obj[item.id] = {
            ...item,
            children:[]
        }
    })
    
    Object.values(obj).forEach(item => {
        const parentId = item.parentId
        if(+parentId === 0){
            res.push(item)
        }else{
            obj[parentId].children.push(item)
        }
    })
    return res
}

JSON2DOM

{
  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 jsoń2dom(obj){
    const dom = document.createElement(obj.tag)
    const attrs = obj.attrs || {}
    const children = obj.children || []
    for(let k in attrs){
      dom.setAttribute(k,attrs[k])
    }

    children.forEach(item => {
      dom.appendChild(json2dom(item))
    })
    return dom
}

DOM2JSON

function dom2json(domTree) {
    const obj = {}
    obj.tag = domTree.tagName
    obj.children = []
    domTree.childNodes.forEach(child=>{
      if(child.nodeType!==1)return
      obj.children.push(dom2json(child))
    })
    return obj

实现有并行限制的 Promise 调度器

class Scheduler {
  constructor(limit) {
    this.limit = limit
    this.tasks = []
  }
  start() {
    for (let i = 0; i < this.limit; i++) {
      this.run()
    }
  }
  add(timer, n) {
    const task = () => {
      return new Promise(resolve => {
        setTimeout(() => {
          console.log(n)
          resolve()
        }, timer)
      })
    }
    this.tasks.push(task)
  }
  async run() {
    if (!this.tasks.length ) return
    const t = this.tasks.shift()
    await t()
    this.run()
  }
}
const scheduler = new Scheduler(2);
const addTask = (...args) => {
  scheduler.add(...args)
};
addTask(500, "5")
addTask(300, "3")
addTask(100, "1")
addTask(400, "4")
scheduler.start()// 3 1 5 4

LazyMan

// lazyMan(“Hank”).eat(“supper”).sleepFirst(5)输出
// //等待5秒
// Wake up after 5
// Hi This is Hank!
// Eat supper
class LazyMan{
    constructor(name){
        this.name = name
        const t = ()=>{
            console.log('this is' + this.name)
            this.run()
        }
        this.task = [t]

        setTimeout(()=>{
            this.run()
        })
    }
    run(){
        const t = this.task.shift()
        t && t()
    }
    eat(food){
        this.task.push(() => {
            console.log(`${this.name}${food}`)
            this.run()
        })
        return this
    }
    sleep(time){
        const t = () => {
            console.log(`sleep等${time}秒`)
            setTimeout(() => {
                this.run()
            }, time * 1000)
        }
        this.task.push(t)
    
        return this
    }
    sleepFirst(time){
        const t = () => {
            console.log(`sleepFirst等${time}秒`)
            setTimeout(() => {
                this.run()
            }, time * 1000)
        }
        this.task.unshift(t)
    
        return this
    }
}
function lazyMan(name){
    return new LazyMan(name)
}

深拷贝

参考我这篇文章

new

function myNew(Fn,...args){
    const obj = Object.create(Fn.prototype)
    const res = Fn.apply(obj,args)
    return typeof res === 'object' ? res : obj
}

参考

寄生组合继承

function inheritPrototype(super,sub){
    sub.prototype = Object.create(super.prototype,{
        constructor:{
            enumerable:false,
            configurable:true,
            writable:true,
            value:sub
        }
    })
}
function Super(){}
function Sub(...args){
    Super.apply(this,args)
}
inheritPrototype(Super,Sub)

参考

Promise

参考