面试手写题

70 阅读3分钟

节流与防抖

//防抖函数:一段时间内,事件触发只执行最后一次,例如回城技能 B
function debounce(fn, wait) {
    let timer = null
    return function() {
        let context = this
        // 如果事件被再次触发,直接清除定时器
        if(timer) {
            clearTimeout(timer)
            timer = null
        }
        
        timer = setTimeout(()=>{
            fn.apply(context, arguments)
        },wait)
    }
}

    // 节流函数:事件按照一定时间的间隔执行。类似技能cd
    function throttle(fn,wait) {
    let flag = null
    return function () {
        if(flag) return
        flag = setTimeout(()=>{
            fn.apply(this,arguments)
        },wait)
    }
    
}

手写深拷贝

const isObj = (val) =>{
      return typeof val === 'object' && val !== null
    }

    function deepClone (obj) {
      // 通过instanceof 判断是否为数组,不是则是对象
      const newObj = obj instanceof Array ? [] : {};
      // 拷贝
      for (const key in obj) {
        if (Object.hasOwnProperty.call(object, key)) {
          const element = obj[key];
          newObj[key] = isObj(element) ? deepClone(element) : element;
        }
      }

      return newObj;
    }

手写数组拍平方法

// 1.es6语法-flat
    let arr = [1,2,[3,4,[5,6]]] // ====> [1,2,3,4,5,6]
    let a1 = arr.flat(Infinity)
    console.log('flat拍平数组===》》',a1);

    // 2.序列化后正则
    let str = JSON.stringify(arr).replace(/(\[|\])/g, '')
    str = '[' + str + ']'
    console.log(str);

    // 3.递归处理
    function flat(arr) {
      let result = []
      for (const item of arr) {
        item instanceof Array ? result = result.concat(flat(item)) : result.push(item)
      }
      return result
    }
    let a3 = flat(arr)
    console.log('aeee3333333',a3);

手写call,apply,bind

Function.prototype.myCall= function(context, ...args) {
      context = context || window

    
      const key = Symbol()

      context[key] = this

      let result = context[key](...args)

      delete context[key]

      return result
    }

    Function.prototype.myApply = function (context, args=[]) {
      context = context || window

      const key = Symbol()

      context[key] = this

      let result = context[key](...args)

      delete context[key]

      return result


    }
    Function.prototype.myBind = function(context, ...args1) {
      const self = this
      return function(...args2) {
        return self.apply(context, [...args1, ...args2])
      }
    }

手写instanceof

function myInstanceof(left,right) {
      if(typeof left !=='object' || left === null) return false
      let proto = Object.getPrototypeOf(left)
      let prototype = right.prototype
      while(true) {
        if(!proto) {
          return false
        }
        if(proto==prototype) {
          return true
        }
        proto = Object.getPrototypeOf(proto)
      }

    }

手写new

function myNew(constructor, ...args) {
      // 创建一个新的空对象
      const newObj = {}
      // 将新对象的原型链接到构造函数的原型对象
      Object.setPrototypeOf(newObj, constructor.prototype);
      // newObj.__proto__ = constructor.prototype
      // 将构造函数的作用域赋值给新对象,并执行构造函数
      const result = constructor.apply(newObj, args);
      // 如果对象有一个显式的返回一个对象,则返回该对象否则返回新对象
      return typeof result === 'Object' && result !== null ? result : newObj;
    }

手写promise

 // 3个状态
    const PENDING = 'PENDING'; // 默认状态
    const FULTILLED = 'FULTILLED'; // 完成
    const REJECTED = 'REJECTED' // 失败

    class Promise {
      constructor(executor) {
        // 默认状态为PENDING
        this.status = PENDING;
        // 存放成功状态的值,默认值为undefined
        this.value = undefined
        // 存放失败状态的值,默认值为undefined
        this.reason = undefined

        this.onResolvedCallbacks = [] // 存放成功的回调
        this.onRejectedCallbacks = [] // 存放失败的回调

        // 调用此方法即为成功
        let resolve = (value) => {
          // 只有当状态为PENDING时才更新状态,防止executor执行两次 resolve和reject方法
          if(this.status === PENDING) {
            this.status = FULTILLED
            this.value = value
            // 依次执行对应的函数
            this.onResolvedCallbacks.forEach(fn => fn())
          }

        }

         // 调用此方法即为失败
         let reject = (reason) => {
          // 只有当状态为PENDING时才更新状态,防止executor执行两次 resolve和reject方法
          if(this.status === PENDING) {
            this.status = REJECTED
            this.reason = reason
            // 依次执行对应的函数
            this.onRejectedCallbacks.forEach(fn => fn())
          }

        }

        try {
          executor(resolve, reject)
        } catch (error) {
          // 异常时执行
          reject(error)
        }

      }
      
      then(onFulfilled, onRejected) {
        if(this.status === FULTILLED) {
          onFulfilled(this.value)
        }

        if(this.status === REJECTED) {
          onRejected(this.reason)
        }

        if(this.status === PENDING) {
          this.onRejectedCallbacks.push(() => {
            onFulfilled(this.value)
          })

          this.onRejectedCallbacks.push(() => {
            onRejected(this.reason)
          })
        }
      }
    }
    static all(promises) {
        let res = []
        let count = 0
        //5.1
        return new myPromise((resolve, reject) => {
          //5.2
          if (!Array.isArray(promises)) {
            return reject(new TypeError('Argument is not iterable'))
          }
          //5.3
          if (!promises.length) {
            return resolve(res)
          }
          //5.5
          promises.forEach((item, index) => {
            //myPromise.resolve能同时处理px实例/thenable/基本类型数据
            myPromise.resolve(item).then(val => {
              count++
              res[index] = val
              count === promises.length && resolve(res)
              // 5.4
            }, reject)
          })
        })
     }


    const promise = new Promise((resolve, reject) => {
      resolve('成功')
    }).then((data)=> {
      console.log('success', data);
    },
    (err) => {
      console.log('error', err);
    }
    )

数组转树形结构


let list = [
    { "id": 12, "parent_id": 1, "name": "朝阳区" },
    { "id": 241, "parent_id": 24, "name": "田林街道" },
    { "id": 31, "parent_id": 3, "name": "广州市" },
    { "id": 13, "parent_id": 1, "name": "昌平区" },
    { "id": 2421, "parent_id": 242, "name": "上海科技绿洲" },
    { "id": 21, "parent_id": 2, "name": "静安区" },
    { "id": 242, "parent_id": 24, "name": "漕河泾街道" },
    { "id": 22, "parent_id": 2, "name": "黄浦区" },
    { "id": 11, "parent_id": 1, "name": "顺义区" },
    { "id": 2, "parent_id": 0, "name": "上海市" },
    { "id": 24, "parent_id": 2, "name": "徐汇区" },
    { "id": 1, "parent_id": 0, "name": "北京市" },
    { "id": 2422, "parent_id": 242, "name": "漕河泾开发区" },
    { "id": 32, "parent_id": 3, "name": "深圳市" },
    { "id": 33, "parent_id": 3, "name": "东莞市" },
    { "id": 3, "parent_id": 0, "name": "广东省" }
  ]

function treeing(arr) {
    let tree = []
    const map = {}

    for (const item of arr) {
      const id = item.id
      const pid = item.parent_id
      if (!map[id]) {
      map[id] = {
        children: [],
      }
    }

    map[id] = {
      ...item,
      children: map[id]['children']
    }
      const treeItem = map[id]
      if(pid == 0) {
        tree.push(treeItem)
      }else {
        if(!map[pid]) {
          map[pid] = {
            children:[]
          }
        }
        map[pid].children.push(treeItem)
      }
      
    }
    return tree
  }