# 头条
# 笔试
# 格式化发布时间
类似于微信朋友圈里面的发布时间,其中发布时间有几种特殊情况:
- 小于 1 min,则显示 刚刚;
- 小于 1 h,则显示 x 分钟前;
- 小于 1 天,则显示 x 小时前;
- 小于 1 个星期,则显示 x 天前;
- 否则显示具体时间。
declare function i18n(s: string): string
function format(date: number): string {
const MIN = 60 * 1000
const HOUR = 60 * MIN
const DAY = 24 * HOUR
const WEEK = 7 * DAY
if(date < MIN) return i18n('刚刚')
if(date < HOUR) return Math.floor(date / MIN) + i18n('分钟前')
if(date < DAY) return Math.floor(date / HOUR) + i18n('小时前')
if(date < WEEK) return Math.floor(date / DAY) + i18n('天前')
return new Date(Date.now() - date).toLocaleString()
}
console.log(format(60 * 1000 - 1)); // 59 秒 999
console.log(format(60 * 1000)); // 1 分
console.log(format(60 * 1000 * 59 + 59 * 1000 + 999)); // 59 分 59 秒 999
console.log(format(60 * 1000 * 60)); // 1 小时
console.log(format(60 * 1000 * 60 * 23 + 60 * 1000 * 59 + 59 * 1000 + 999)); // 23 小时 59 分 59 秒 999
console.log(format(60 * 1000 * 60 * 24)); // 1 天
console.log(format(60 * 1000 * 60 * 24 * 6)); // 6 天
console.log(format(60 * 1000 * 60 * 24 * 7)); // 7 天
console.log(format(1554111847534)); // 发布时的时间戳
# 格式化数字
为数字添加都好,使用正则和非正则实现
// 方法1:使用内置api
// 缺点:小数部分会保留三位小数
var num = 12345
console.log(num.toLocaleString()) // '12,345'
num = 12345.6789
console.log(num.toLocaleString()) // '12,345.679'
// 方法2:使用数组
function format(num: number): string {
if(typeof num !== 'number') return num
const [first, last] = `${num}`.split('.')
const len = first.length
const temp = first.split('').reverse().reduce((pre, cur, index) => {
if((i + 1) % 3 === 0 && i !== flen - 1) pre.push(cur, ',')
else pre.push(cur)
}, []).reverse().join('')
return last ? `${temp}.${last}` : temp
}
# 一面
# 实现 js ES5 数据类型的深拷贝
function deepClone<D>(data: D): D {
const isObj = (v: D) => Object.prototype.toString.call(v).slice(8, -1) === 'Object'
function _deepClone(val: any): D {
if(Array.isArray(val)) {
const source = val as any[]
return source.reduce((res, item) => {
res.push(_deepClone(item))
return res
}, [])
}
if(isObj(val)) {
const source = val as object
return Object.keys(source).reduce((res, key) => {
res[key] = _deepClone(source[key])
return res
}, {})
}
return val
}
return _deepClone(data)
}
# 二面
# 实现简易 MVVM
interface Handlers {
get(val): void
set(newVal, oldVal): void
}
class Observer<T extends object = {}> {
private data: T
constructor(data: T) {
this.data = data
this.proxyData()
}
private proxyData(): void {
const keys = Object.keys(this.data)
for (const key of keys) {
Object.defineProperty(this, key, {
get: () => {
return this.data[key]
},
set: (val) => {
this.data[key] = val
}
})
}
}
observe(key: keyof T, handlers: Handlers): void {
const value = this.data[key]
Object.defineProperty(this.data, key, {
get: () => {
handlers.get(value)
return value
},
set: (val) => {
if (val === value) return
handlers.set(val, value)
this.data[key] = val
}
})
}
}
# 三面
# 顺序发送4个请求a,b,c,d,要求按顺序输出
function getData(urls: string) {
return new Promise<object[]>((resolve, reject) => {
const len = urls.length
const res: object[] = []
urls.forEach((url, index) => {
fetch(url)
.then(res => res.json())
.then(data => {
res[index] = { data, printed: false }
let flag = true // 用来判断所有请求是否都已经跑完
for(let i = 0; i < len && flag; i++) {
if(res[i]) {
if(!res[i].printed) { // 循环内,所以去掉已经打印的
console.log(res[i].data)
res[i].printed = true
i === len - 1 && resolve(res.map(v => v.data)) // 当最后一个打印完成后,结束promise
}
} else {
flag = false
}
}
}, reject)
})
})
}
# 美团
# 二面
# 用 promise 实现一个请求超时功能
实现关键在于 promise 的状态改变是单向且唯一的,即只能由 pending -> fulfilled
或者 pending -> rejected
,所以可以使用 resolve + setTimeout
来实现。
function promiseWithTimeout(url, delay = 3000) {
return new Promise((resolve, reject) => {
setTimeout(function () {
reject(Error('time is out!'))
}, delay)
fetch(url).then(data => resolve(data.json()))
})
}