函数的链式调用
题目:实现一个Robot函数,能以如下方式调用Robot("Mike").eat("lunch").wait(3000).eat("dinner"),使其输出结果为:
Hello, I'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)]
}