【20230511】面试题每日打卡-手写题(一)

71 阅读2分钟

函数的链式调用

题目:实现一个Robot函数,能以如下方式调用Robot("Mike").eat("lunch").wait(3000).eat("dinner"),使其输出结果为:

HelloI'M Mike
eat lunch
// 等待3s
eat dinner

答案:

function Robot(name) {
    console.log(`helo i'm ${name}`);
    let lock = false
    let waitingTaskQueue = []
    const _eat = food => console.log(`eat ${food}`);
    const self = {
        eat: (food) => {
            if(lock) {
                waitingTaskQueue.push(() => {
                    _eat(food)
                })
            } else  _eat(food);
            return self
        },
        wait: (timer) => {
            lock = true
            setTimeout(() => {
                lock = false
                while(waitingTaskQueue.length) waitingTaskQueue.shift()()
            },timer)
            return self
        }
    }
    return self
}

Robot("Mike").eat("lunch").wait(3000).eat("dinner")

数组随机取值

题目:实现一个函数,传入一个整数数组,要求每次调用返回的值都是在整数数组中的随机一个值,而且每次调用返回的结果和上一次不一样。

答案:

function getRandomItem(arr) {
  let lastIndex;
  return function() {
    let index;
    do {
      index = Math.floor(Math.random() * arr.length);
    } while (index === lastIndex);
    lastIndex = index;
    return arr[index];
  }
}

Request封装

题目:将回调函数风格的请求代码进行Promise风格封装,且增加设置超时时间和超时错误重传功能。

请求代码如下:

request("xxx/xxx",(res,err) => { // ... })

答案:

function requestWithTimeout(url, timeout = 5000, retryCount = 3) {
  let retry = retryCount;
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      if (retry > 0) {
        retry--;
        requestWithTimeout(url, timeout, retry)
          .then(resolve)
          .catch(reject);
      } else {
        reject(new Error("请求超时"));
      }
    }, timeout);
    request(url, (res, err) => {
      clearTimeout(timer);
      if (err) {
        reject(err);
      } else {
        resolve(res);
      }
    });
  });
}

补充代码,使结果符合预期

补充代码,使得打印结果为world、male和world2,待补充代码如下:

function Clazz() {}
Claszz.prototype.setAttr = function(key,val) {
	this[key]  = value
}
//开始

//结束
var  clazz1 = new Clazz()
var clazz2 = new Clazz()
clazz1.setAttr("name","world")
clazz1.setAttr("sex","male")
clazz1.name = "hello"
clazz1.sex = "female"
clazz2.setAttr("name","world2")
clazz2.name = "world222"
console.log(clazz1.name,clazz1.sex)
console.log(clazz1.name)

答案:

function Clazz() {}
Clazz.prototype.setAttr = function(key,val) {
	this[key]  = val
}

//开始
Clazz.prototype.setAttr = function(key,val) {
    // 更新属性值并打印
    this[key]  = val
    // 重写属性的setter,防止直接修改属性值
    Object.defineProperty(this, key, {
        set: function() {},
        get: function() {
            return val;
        }
    });
}
//结束

var clazz1 = new Clazz()
var clazz2 = new Clazz()
clazz1.setAttr("name","world")
clazz1.setAttr("sex","male")
clazz1.name = "hello"
clazz1.sex = "female"
clazz2.setAttr("name","world2")
clazz2.name = "world222"
console.log(clazz1.name,clazz1.sex)
console.log(clazz2.name)

发布订阅的简易实现

class EventEmitter {
    constructor() {
        this.events = {}
    }

    subscribe(eventName, callback) {
        if (!this.events[eventName]) {
            this.events[eventName] = []
        }
        this.events[eventName].push(callback)
    }

    publish(eventName, ...args) {
        if (!this.events[eventName]) {
            return
        }
        this.events[eventName].forEach(callback => {
            callback(...args)
        })
    }
}

function observe(obj) {
    Object.keys(obj).forEach(key => {
        let internalValue = obj[key]
        Object.defineProperty(obj, key, {
            get() {
                console.log(`读取${key}: ${internalValue}`)
                return internalValue
            },
            set(newValue) {
                console.log(`设置${key}: ${newValue}`)
                internalValue = newValue
                eventEmitter.publish(key, newValue)
            }
        })
    })
}

const eventEmitter = new EventEmitter()

const person = {
    name: 'Alice',
    age: 30
}

observe(person)

eventEmitter.subscribe('name', (newValue) => {
    console.log(`订阅者收到name的更新:${newValue}`)
})

eventEmitter.subscribe('age', (newValue) => {
    console.log(`订阅者收到age的更新:${newValue}`)
})

person.name = 'Bob' // 输出:设置name: Bob 和 订阅者收到name的更新:Bob
person.age = 31     // 输出:设置age: 31 和 订阅者收到age的更新:31

解决循环引用问题的深拷贝实现(仅支持数组和对象)

function deepClone(obj, map = new Map()) {
  if (typeof obj !== 'object' || obj === null) return obj;
  if (map.has(obj)) return map.get(obj);
  let result;
  if (Array.isArray(obj)) {
    result = [];
    map.set(obj, result);
    for (let i = 0; i < obj.length; i++) {
      result[i] = deepClone(obj[i], map);
    }
  } else {
    result = {};
    map.set(obj, result);
    for (let key in obj) {
      result[key] = deepClone(obj[key], map);
    }
  }
  return result;
}

lodash chunk方法模拟实现

const chunk = (arr,size = 1) => arr.reduce((pre,cur) => {
  if(pre.length === 0) return [[cur]]
  if(pre[pre.length - 1].length < size) {
    pre[pre.length - 1].push(cur)
    return pre
  } else {
    return [...pre,[cur]]
  }
},[])

大数相加

function add(numStr1 = "",numStr2 = "") {
    let maxLength = Math.max(numStr1.length,numStr2.length)
    while(numStr1.length < maxLength) numStr1 = "0" + numStr1
    while(numStr2.length < maxLength) numStr2= "0" + numStr2
    let res = ""
    let flag = false
    for(let i = maxLength - 1; i >= 0; i--) {
        let each = (numStr1[i] - 0) + (numStr2[i] - 0) + (flag ? 1 : 0)
        if(each >= 10) {
            res = (each - 10) + res
            flag = true
        } else {
            res = each + res
            flag = false
        }
    }
    if(flag) res = '1' + res
    return res
}

对象数组转树结构

用例:

let arr = [
    { id: 1, name: '部门1', pid: 0 },
    { id: 2, name: '部门2', pid: 1 },
    { id: 3, name: '部门3', pid: 1 },
    { id: 4, name: '部门4', pid: 3 },
    { id: 5, name: '部门5', pid: 4 },
]

答案:

const arrToTree = (arr) => {
    const map = new Map()
    arr.forEach(ele => {
        if (!map.get(ele.pid)) map.set(ele.pid, [ele])
        else map.set(ele.pid, [...map.get(ele.pid), ele])
    });
    const rootArr = map.get(0)
    function toTree(root) {
        if (!map.get(root.id)) return root
        return {
            ...root,
            children: map.get(root.id).map(toTree)
        }
    }
    return [...rootArr.map(toTree)]
}