Preface
I was looking for a job a few weeks ago, and I wrote more than 100 questions, here is a summary
1. Implement curry
https://bigfrontend.dev/zh/problem/implement-curry
const join = (a, b, c) => {
return `${a}_${b}_${c}`
}
const curriedJoin = curry(join)
curriedJoin(1, 2, 3) // '1_2_3'
curriedJoin(1)(2, 3) // '1_2_3'
curriedJoin(1, 2)(3) // '1_2_3'
// 实现curry
const curry = (fn, ...initParams) => {
return (...args) => {
return ((params) => {
return params.length >= fn.length ? fn(...params) : curry(fn, ...params)
})([ ...initParams, ...args])
}
}
2. Implement curry() that supports placeholder
https://bigfrontend.dev/zh/problem/implement-curry-with-placeholder
/**
* 合并参数(替换占位符)
*/
function merge(params1, params2) {
for (let i = 0; i < params1.length; i += 1) {
if (params2.length) {
if (typeof params1[i] === 'symbol') {
const first = params2.shift()
params1[i] = first
}
} else {
break
}
}
return [...params1, ...params2]
}
/**
* @param { (...args: any[]) => any } fn
* @returns { (...args: any[]) => any }
*/
function curry(fn, ...initParams) {
return (...args) => {
const params = merge([...initParams], [...args])
return ((params) => {
params = params.slice(0, fn.length)
// 判断是否可以执行
const isCall = params.filter((item) => typeof item !== 'symbol').length >= fn.length
return isCall ? fn(...params) : curry(fn, ...params)
})(params)
}
}
curry.placeholder = Symbol()
3. Implement Array.prototype.flat()
https://bigfrontend.dev/zh/problem/implement-Array-prototype.flat
/**
* @param { Array } arr
* @param { number } depth
* @returns { Array }
*/
function flat(arr, depth = 1) {
const result = []
if (depth) {
let isExistArray = false;
for (let i = 0; i < arr.length; i += 1) {
const item = arr[i]
if (Array.isArray(item)) {
isExistArray = true
result.push(...item)
} else {
result.push(item)
}
}
if (isExistArray) {
return flat(result, depth - 1)
} else {
return result
}
} else {
return arr
}
}
4. Handwriting throttle()
https://bigfrontend.dev/zh/problem/implement-basic-throttle
/**
* @param {Function} func
* @param {number} wait
*/
function throttle(func, wait) {
let timer
let lastArgs
return function (...param) {
if (!timer) {
func.call(this, ...param)
timer = window.setTimeout(() => {
lastArgs && func.call(this, ...lastArgs)
lastArgs = null
timer = null
}, wait)
} else {
lastArgs = [...param]
}
}
}
6. Handwritten debounce()
https://bigfrontend.dev/zh/problem/implement-basic-debounce
/**
* @param {Function} func
* @param {number} wait
*/
function debounce(func, wait) {
let timer
return function (...param) {
function clear() {
window.clearTimeout(timer)
timer = null
}
function run() {
timer = window.setTimeout(() => {
func.call(this, ...param)
clear()
}, wait)
}
if (!timer) {
run()
} else {
clear()
run()
}
}
}
8. Handwriting shuffle() randomly shuffle an array (implement a shuffle algorithm)
https://bigfrontend.dev/zh/problem/can-you-shuffle-an-array
The conventional
const randomIndex = Math.floor(Math.random() * arr.length)
generating random indexes is not random enough
/**
* @param {any[]} arr
*/
function shuffle(arr) {
for (let i = 0; i < arr.length; i += 1) {
const j = i + Math.floor(Math.random() * (arr.length - 1));
[arr[i], arr[j]] = [arr[j], arr[i]]
}
return arr
}
9. Decrypt the message
https://bigfrontend.dev/zh/problem/decode-message
/**
* @param {string[][]} message
* @return {string}
*/
function decode(message) {
// 为空的判断
if (!message.length || !message[0].length) {
return ''
}
let power = true
let str = ''
let i = 0
let j = 0
let direction = 'LowerRight' // LowerRight | UpperRight
let w = message[0].length
let h = message.length
function lowerRight() {
if (i + 1 < h && j + 1 < w) {
i = i + 1
j = j + 1
} else {
if (i - 1 > 0 && j + 1 < w) {
direction = 'UpperRight'
i = i - 1
j = j + 1
} else {
power = false
}
}
}
function upperRight() {
if (i - 1 > 0 && j + 1 < w) {
i = i - 1
j = j + 1
} else {
if (i + 1 < h && j + 1 < w) {
direction = 'LowerRight'
i = i + 1
j = j + 1
} else {
power = false
}
}
}
while (power) {
str += message[i][j]
if (direction === 'LowerRight') {
lowerRight()
} else if (direction === 'UpperRight') {
upperRight()
}
}
return str
}
10. Find the first bad version
https://bigfrontend.dev/zh/problem/first-bad-version
Simple binary search
/*
type TypIsBad = (version: number) => boolean
*/
/**
* @param {TypIsBad} isBad
*/
function firstBadVersion(isBad) {
return (version) => {
let start = 0
let end = version
let result = isBad(version) ? version : -1
while (end >= start) {
let midd = Math.floor((start + end) / 2)
if (isBad(midd)) {
// 如果是坏的
end = midd - 1
result = midd
} else {
// 如果是好的
start = midd + 1
}
}
return result
}
}
11. What is Composition? Implement pipe()
https://bigfrontend.dev/zh/problem/what-is-composition-create-a-pipe
Implement a pipeline function
/**
* @param {Array<(arg: any) => any>} funcs
* @return {(arg: any) => any}
*/
function pipe(funcs) {
return function (arg) {
let param = arg
for (let i = 0; i < funcs.length; i += 1) {
const func = funcs[i]
param = func(param)
}
return param
}
}
13. Use Stack to Create Queue
https://bigfrontend.dev/zh/problem/implement-a-queue-by-using-stack
You can use two Stacks to implement Queue
A simpler way is to save it every time you enqueue.
class Stack {
constructor () {
this.stack = []
}
push(element) {
this.stack.push(element)
}
peek() {
return this.stack[this.stack.length - 1]
}
pop() {
return this.stack.pop()
}
size() {
return this.stack.length
}
}
// 使用Stack实现Queue
class Queue {
constructor () {
this.enqueueStack = new Stack()
this.dequeueStack = new Stack()
}
_enqueueSyncDequeue () {
const dequeueTemp = new Stack()
const enqueueTemp = new Stack()
while (this.enqueueStack.size()) {
const p = this.enqueueStack.pop()
dequeueTemp.push(p)
enqueueTemp.push(p)
}
while (enqueueTemp.size()) {
this.enqueueStack.push(enqueueTemp.pop())
}
this.dequeueStack = dequeueTemp
}
_dequeueSyncEnqueue () {
const dequeueTemp = new Stack()
const enqueueTemp = new Stack()
while (this.dequeueStack.size()) {
const p = this.dequeueStack.pop()
dequeueTemp.push(p)
enqueueTemp.push(p)
}
while (dequeueTemp.size()) {
this.dequeueStack.push(dequeueTemp.pop())
}
this.enqueueStack = enqueueTemp
}
enqueue(element) {
this.enqueueStack.push(element)
this._enqueueSyncDequeue()
}
peek() {
return this.dequeueStack.peek()
}
dequeue() {
const p = this.dequeueStack.pop()
this._dequeueSyncEnqueue()
return p
}
size() {
return this.enqueueStack.size()
}
}
// 改进版
class Queue {
constructor () {
this.stack = new Stack()
this.queue = new Stack()
}
enqueue(element) {
while (this.queue.size()) {
this.stack.push(this.queue.pop())
}
this.queue.push(element)
while (this.stack.size()) {
this.queue.push(this.stack.pop())
}
}
peek() {
return this.queue.peek()
}
dequeue() {
return this.queue.pop()
}
size() {
return this.queue.size()
}
}
14. Implement memo()
https://bigfrontend.dev/zh/problem/implement-general-memoization-function
/**
* @param {Function} func
* @param {(args:[]) => string } [resolver] - cache key generator
*/
function memo(func, resolver) {
const map = new Map();
return function (...params) {
let key
if (typeof resolver === 'function') {
key = resolver(...params)
} else {
key = [...params].join('-')
}
if (map.has(key)) {
return map.get(key)
} else {
const val = func.apply(this, [...params])
map.set(key, val)
return val
}
}
}
15. Implement a DOM wrapper similar to jQuery
https://bigfrontend.dev/zh/problem/implement-a-simple-DOM-wrapper-to-support-method-chaining-like-jQuery
/**
* @param {HTMLElement} el - element to be wrapped
*/
function $(el) {
el.css = function (key, value) {
el.style[key] = value
return el
}
return el
}
16. Implement an Event Emitter
https://bigfrontend.dev/zh/problem/create-an-Event-Emitter
// please complete the implementation
class EventEmitter {
constructor () {
this.map = {}
}
subscribe(eventName, callback) {
const event = {eventName, callback}
const that = this
if (this.map[eventName]) {
this.map[eventName].push(event)
} else {
this.map[[eventName]] = [event]
}
return {
release () {
that.map = {
...that.map,
[eventName]: that.map[eventName].filter((e) => e !== event)
}
}
}
}
emit(eventName, ...args) {
if (this.map[eventName]) {
this.map[eventName].forEach((event) => {
const { callback } = event
callback(...args)
})
}
}
}
17. Implement a DOM element store
https://bigfrontend.dev/zh/problem/create-a-simple-store-for-DOM-node
Pay attention to the time and space complexity, the time complexity of the has method should be o(1)
How to implement a map polyfill
class NodeStore {
constructor () {
this.map = {}
}
/**
* @param {Node} node
* @param {any} value
*/
set(node, value) {
if (!node.__mapKey__) {
node.__mapKey__ = Symbol()
this.map[node.__mapKey__] = value
} else {
this.map[node.__mapKey__] = value
}
}
/**
* @param {Node} node
* @return {any}
*/
get(node) {
return this.map[node.__mapKey__]
}
/**
* @param {Node} node
* @return {Boolean}
*/
has(node) {
return !!node.__mapKey__ && node.__mapKey__ in this.map
}
}
18. Optimize a function
https://bigfrontend.dev/zh/problem/improve-a-function
Time complexity before optimization O(m * n)
// items是一个array
// 包含的元素有 >=3 个属性
let items = [
{color: 'red', type: 'tv', age: 18},
{color: 'silver', type: 'phone', age: 20},
{color: 'blue', type: 'book', age: 17}
]
// 一个由key和value组成的array
const excludes = [
{k: 'color', v: 'silver'},
{k: 'type', v: 'tv'},
...
]
function excludeItems(items, excludes) {
excludes.forEach( pair => {
items = items.filter(item => item[pair.k] === item[pair.v])
})
return items
}
Optimized:
function excludeItems(items, excludes) {
const excludesMap = {}
excludes.forEach(({k, v}) => {
if (excludesMap[k]) {
excludesMap[k].add(v)
} else {
excludesMap[k] = new Set()
excludesMap[k].add(v)
}
})
return items.filter((item) => {
return Object.keys(item).every((key) => {
if (excludesMap[key]) {
return !excludesMap[key].has(item[key])
}
return true
})
})
}
19. Find the corresponding node on the DOM tree of the same structure
https://bigfrontend.dev/zh/problem/find-corresponding-node-in-two-identical-DOM-tree
/**
* @param {HTMLElement} rootA
* @param {HTMLElement} rootB - rootA and rootB are clone of each other
* @param {HTMLElement} nodeA
*/
const findCorrespondingNode = (rootA, rootB, target) => {
const paths = []
let isSearchEnd = false
let targetB = rootB
const find = (node, index) => {
if (index !== undefined && !isSearchEnd) {
paths.push(index)
}
if (node !== target) {
const children = [...node.children]
children.forEach((item, index) => {
find(item, index)
})
if (!isSearchEnd) {
paths.pop()
}
} else {
isSearchEnd = true
}
}
find(rootA)
if (paths.length !== 0) {
while (paths.length) {
const index = paths.shift()
targetB = [...targetB.children][index]
}
}
return targetB
}
20. Detect data type
https://bigfrontend.dev/zh/problem/detect-data-type-in-JavaScript
/**
* @param {any} data
* @return {string}
*/
function detectType(data) {
if(data instanceof FileReader) {
return 'object'
}
const type = Object.prototype.toString.call(data);
return /^\[object\s+([A-Za-z]+)\]$/.exec(type)[1].toLocaleLowerCase()
}
21. Handwriting JSON.stringify()
https://bigfrontend.dev/zh/problem/implement-JSON-stringify
/**
* @param {any} data
* @return {string}
*/
function stringify(data) {
if(typeof data === 'bigint') {
throw new Error()
}
if(typeof data === 'string') {
return `"${data}"`
}
if(typeof data === 'function') {
return undefined;
}
if(data !== data) {
return 'null'
}
if(data === Infinity) {
return 'null'
}
if(data === -Infinity) {
return 'null'
}
if(typeof data === 'number') {
return `${data}`
}
if(typeof data === 'boolean') {
return `${data}`
}
if(data === null) {
return 'null'
}
if(data === undefined) {
return 'null'
}
if(typeof data === 'symbol') {
return 'null'
}
if(data instanceof Date) {
return `"${data.toISOString()}"`
}
if (Array.isArray(data)) {
const arr = data.map((item) => stringify(item))
return `[${arr.join(',')}]`
}
if (typeof data === 'object') {
const arr = []
Object.entries(data).forEach(([key, value]) => {
if (value !== undefined) {
arr.push(`"${key}":${stringify(value)}`)
}
})
return `{${arr.join(',')}}`
}
}
22. Handwritten JSON.parse()
https://bigfrontend.dev/zh/problem/implement-JSON-parse
/**
* @param {any} data
* @return {string}
*/
function parse(data) {
function equal(text) {
if (text && text !== current) {
throw new Error()
}
return true
}
function next() {
current = text.charAt(index)
index += 1
return current
}
function white() {
while (current && current == ' ') {
next()
}
}
function object() {
let key
let obj = Object.create(null)
if (current === '{') {
next()
white()
if (current === '}') {
next()
return obj
}
while (current) {
key = string()
// 读取下一个非空的字符
white()
// 下一个非空字符应该是":", 否则就抛出错误
equal(':')
next()
obj[key] = value()
// 读取下一个非空的字符
white()
if (current === '}') {
next()
return obj
}
// 如果不是'}', 下一个字符应该是',', 否则就抛出错误
equal(',')
// 读取下一个非空的字符
white()
}
}
throw new Error()
}
function string() {
let str = ''
if (current === '"') {
let startIndex = index
while (next()) {
if (current === '"') {
if (index - 1 > startIndex) {
str += text.substring(startIndex, index - 1)
}
next()
return str
}
}
} else {
throw new Error()
}
}
function array() {
let arr = []
if (current === '[') {
next()
// 读取下一个非空字符串
white()
if (current === ']') {
next()
return arr
}
while (current) {
// 读取数组的内容
arr.push(value())
// 读取下一个字符
white()
if (current === ']') {
next()
return arr
}
equal(',')
next()
white()
}
}
throw new Error()
}
function number() {
let number = ''
let string = ''
if (current === '-') {
string = ''
next()
}
let temp = Number(current)
while (temp >= 0 && temp <= 9 && current) {
string += current
next()
temp = Number(current)
}
if (current === '.') {
string += '.'
next()
temp = Number(current)
while (temp >= 0 && temp <= 9 && current) {
string += current
next()
temp = Number(current)
}
}
number = Number(string)
return number
}
function word() {
switch (current) {
case 't':
next()
equal('r')
next()
equal('u')
next()
equal('e')
next()
return true
case 'f':
next()
equal('a')
next()
equal('l')
next()
equal('s')
next()
equal('e')
next()
return false
case 'n':
next()
equal('u')
next()
equal('l')
next()
equal('l')
next()
return null
}
throw new Error()
}
function value() {
white()
switch (current) {
// 如果是对象
case '{':
return object()
// 如果是数组
case '[':
return array()
// 如果是字符串
case '"':
return string()
// 如果包含负号,说明是数字
case "-":
return number()
default:
let temp = Number(current)
if (temp >= 0 && temp <= 9) {
return number()
} else {
return word()
}
}
}
let text = data + ''
let index = 0
let current = ' '
let result = value()
white()
if (current) {
throw new Error()
}
return result
}
23. Implement a sum() method
https://bigfrontend.dev/zh/problem/create-a-sum
/**
* @param {number} num
*/
function sum(num) {
const fn = function (arg) {
return sum(num + arg)
}
fn.toString = function () {
return num
}
return fn
}
24. Hand write a Priority Queue with JavaScript
https://bigfrontend.dev/zh/problem/create-a-priority-queue-in-JavaScript
// complete the implementation
class PriorityQueue {
/**
* @param {(a: any, b: any) => -1 | 0 | 1} compare -
* compare function, similar to parameter of Array.prototype.sort
*/
constructor(compare) {
this.compare = compare;
}
/**
* return {number} amount of items
*/
size() {
}
/**
* returns the head element
*/
peek() {
}
/**
* @param {any} element - new element to add
*/
add(element) {
}
/**
* remove the head element
* @return {any} the head element
*/
poll() {
}
}
25. Update the order of the array
https://bigfrontend.dev/zh/problem/reorder-array-with-new-indexes
/**
* @param {any[]} items
* @param {number[]} newOrder
* @return {void}
*/
// 不实用额外空间
function sort(items, newOrder) {
// 使用简单的冒泡排序
for (let i = 0; i < items.length; i += 1) {
for (let j = i + 1; j < items.length; j += 1) {
if (newOrder[i] > newOrder[j]) {
// 更新items的顺序以及newOrder的顺序
let otemp = newOrder[j]
let itemp = items[j]
newOrder[j] = newOrder[i]
newOrder[i] = otemp
items[j] = items[i]
items[i] = itemp
}
}
}
return items
}
26. Implement Object.assign()
https://bigfrontend.dev/zh/problem/implement-object-assign
/**
* @param {any} target
* @param {any[]} sources
* @return {object}
*/
function objectAssign(target, ...sources) {
if (target === null || target === undefined) {
throw new Error()
}
if (typeof target === 'number') {
target = new Number(target)
}
if (typeof target === 'boolean') {
target = new Boolean(target)
}
if (typeof target === 'string') {
target = new String(target)
}
sources.forEach(source => {
if (source === undefined || source === null) {
return
}
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(source)
)
})
return target
}
27. Implement completeAssign()
function completeAssign(target, ...sources) {
if (target === null || target === undefined) {
throw new Error()
}
if (typeof target === 'number') {
target = new Number(target)
}
if (typeof target === 'boolean') {
target = new Boolean(target)
}
if (typeof target === 'string') {
target = new String(target)
}
sources.forEach(source => {
if (source === undefined || source === null) {
return
}
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(source)
)
})
return target
}
28. Implement clearAllTimeout()
https://bigfrontend.dev/zh/problem/implement-clearAllTimeout
const beforeSetTimeout = window.setTimeout
window.timers = new Set()
// 重写setTimeout
window.setTimeout = function (handler, timeout, ...arg) {
const that = this
const timer = beforeSetTimeout(
function (...arg) {
handler.apply(that, [...arg])
},
timeout,
...arg
)
window.timers.add(timer)
return timer
}
/**
* cancel all timer from window.setTimeout
*/
function clearAllTimeout() {
window.timers.forEach((timer) => {
window.clearTimeout(timer)
})
}
29. Implement async helper- sequence()
https://bigfrontend.dev/zh/problem/implement-async-helper-sequence
/*
type Callback = (error: Error, data: any) => void
type AsyncFunc = (
callback: Callback,
data: any
) => void
*/
function promisify(func) {
return function (num) {
return new Promise(function(resolve, reject) {
func(function (err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
}, num)
})
}
}
/**
* @param {AsyncFunc[]} funcs
* @return {(callback: Callback) => void}
*/
function sequence(funcs) {
funcs = funcs.map(func => promisify(func))
return async function (callback, data) {
try {
for (let i = 0; i < funcs.length; i += 1) {
data = await funcs[i](data)
}
callback(undefined, data)
} catch (error) {
callback(error, undefined)
}
}
}
30. Implement async helper- parallel()
https://bigfrontend.dev/zh/problem/implement-async-helper-parallel
/*
type Callback = (error: Error, data: any) => void
type AsyncFunc = (
callback: Callback,
data: any
) => void
*/
function promisify(func) {
return function (...args) {
return new Promise(function(resolve, reject) {
func(function (err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
}, ...args)
})
}
}
/**
* @param {AsyncFunc[]} funcs
* @return {(callback: Callback) => void}
*/
function parallel(funcs){
funcs = funcs.map(func => promisify(func))
return function (callback, data) {
let count = 0
let handleError = false
const result = []
for (let i = 0; i < funcs.length; i += 1) {
funcs[i](data).then(function (res) {
result[i] = res
count += 1
if (count === funcs.length) {
callback(undefined, result)
}
}).catch(function (err) {
if (!handleError) {
callback(err, undefined)
handleError = true
}
})
}
}
}
31. Implement async helper- race()
https://bigfrontend.dev/zh/problem/implement-async-helper-race
/*
type Callback = (error: Error, data: any) => void
type AsyncFunc = (
callback: Callback,
data: any
) => void
*/
function promisify(func) {
return function (...args) {
return new Promise(function(resolve, reject) {
func(function (err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
}, ...args)
})
}
}
/**
* @param {AsyncFunc[]} funcs
* @return {(callback: Callback) => void}
*/
function race(funcs){
funcs = funcs.map(func => promisify(func))
return function (callback, data) {
let handleEnd = false
for (let i = 0; i < funcs.length; i += 1) {
funcs[i](data).then(function (res) {
if (!handleEnd) {
callback(undefined, res)
}
handleEnd = true
}).catch(function (err) {
if (!handleEnd) {
callback(err, undefined)
}
handleEnd = true
})
}
}
}
32. Implement Promise.all()
https://bigfrontend.dev/zh/problem/implement-Promise-all
/**
* @param {Array<any>} promises - notice input might have non-Promises
* @return {Promise<any[]>}
*/
function all(promises) {
return new Promise((resolve, reject) => {
if (!promises.length) {
return resolve([])
}
const result = [];
let count = 0;
for (let i = 0; i < promises.length; i++) {
const promise = promises[i] instanceof Promise ? promises[i] : Promise.resolve(promises[i])
promise.then((res) => {
result[i] = res
count += 1
if (count === promises.length) {
resolve(result)
}
}).catch((err) => {
reject(err)
})
}
})
}
33. Implement Promise.allSettled()
https://bigfrontend.dev/zh/problem/implement-Promise-allSettled
/**
* @param {Array<any>} promises - notice that input might contains non-promises
* @return {Promise<Array<{status: 'fulfilled', value: any} | {status: 'rejected', reason: any}>>}
*/
function allSettled(promises) {
return new Promise((resolve, reject) => {
if (!promises.length) {
return resolve([])
}
const result = [];
let count = 0;
for (let i = 0; i < promises.length; i++) {
const promise = promises[i] instanceof Promise ?
promises[i] :
Promise.resolve(promises[i])
promise.then((res) => {
result[i] = {status:"fulfilled",value: res}
count += 1
if (count === promises.length) {
resolve(result)
}
}).catch((err) => {
result[i] = {status:"rejected",reason: err}
count += 1
if (count === promises.length) {
resolve(result)
}
})
}
})
}
34. Implement Promise.any()
https://bigfrontend.dev/zh/problem/implement-Promise-any
/**
* @param {Array<Promise>} promises
* @return {Promise}
*/
function any(promises) {
return new Promise((resolve, reject) => {
if (!promises.length) {
return reject(
new AggregateError(
'No Promise in Promise.any was resolved',
[]
)
)
}
const errors = [];
let count = 0;
for (let i = 0; i < promises.length; i += 1) {
const promise = promises[i] instanceof Promise ? promises[i] : Promise.resolve(promises[i])
promise.then((res) => {
resolve(res)
}).catch((err) => {
errors[i] = err
count += 1
if (count === promises.length) {
reject(
new AggregateError(
'No Promise in Promise.any was resolved',
errors
)
)
}
})
}
})
}
35. Implement Promise.race()
https://bigfrontend.dev/zh/problem/implement-Promise-race
/**
* @param {Array<Promise>} promises
* @return {Promise}
*/
function race(promises) {
return new Promise((resolve, reject) => {
if (promises.length) {
for (let i = 0; i < promises.length; i += 1) {
const promise = promises[i] instanceof Promise ?
promises[i]
:
Promise.resolve(promises[i])
promise.then((res) => {
resolve(res)
}).catch((err) => {
reject(err)
})
}
}
})
}
37. Handwriting Binary Search (unique)
https://bigfrontend.dev/zh/problem/implement-Binary-Search-Unique
/**
* @param {number[]} arr - ascending unique array
* @param {number} target
* @return {number}
*/
function binarySearch(arr, target){
if (arr.length === 0) return -1
const _binarySearch = (arr, base) => {
if (arr.length) {
const midd = Math.floor(arr.length / 2)
if (arr[midd] === target) {
return midd + base
} else if (arr[midd] > target) {
return _binarySearch(arr.splice(midd + 1), midd)
} else {
return _binarySearch(arr.splice(0, midd), base)
}
} else {
return -1
}
}
return _binarySearch([...arr], 0)
}
38. Implement jest.spyOn()
https://bigfrontend.dev/zh/problem/implement-spyOn
/**
* @param {object} obj
* @param {string} methodName
*/
function spyOn(obj, methodName) {
if (!(obj[methodName] instanceof Function)) {
throw new Error()
}
const before = obj[methodName]
const calls = []
obj[methodName] = function (...args) {
calls.push([...args])
before.call(this, ...args)
}
return {
calls
}
}
39. Handwriting range()
https://bigfrontend.dev/zh/problem/implement-range
/**
* @param {integer} from
* @param {integer} to
*/
function range(from, to) {
const result = []
while (from <= to) {
result.push(from)
from += 1
}
return result
}
40. Handwritten bubble sort
https://bigfrontend.dev/zh/problem/implement-Bubble-Sort
Bubble sort is a stable sort, with space complexity O(n) and time complexity O(n^2). Stable ordering means that the relative position of elements will not change
/**
* @param {number[]} arr
*/
function bubbleSort(arr) {
for (let i = 0; i < arr.length; i += 1) {
for (let j = i + 1; j < arr.length; j += 1) {
if (arr[i] > arr[j]) {
[arr[i], arr[j]] = [arr[j], arr[i]]
}
}
}
return arr
}
41. Handwritten merge sort
https://bigfrontend.dev/zh/problem/implement-Merge-Sort
Divide and conquer, merge sort is a stable sort, and the time complexity is O(nlogn)
Reference article: https://stackabuse.com/merge-sort-in-javascript
// Note: 需要直接在原数组上修改
function merge (left, right) {
const arr = []
while (left.length && right.length) {
if (left[0] > right[0]) {
arr.push(right.shift())
} else {
arr.push(left.shift())
}
}
return [...arr, ...left, ...right]
}
/**
* @param {number[]} arr
*/
function mergeSort(arr) {
if (arr.length <= 1) {
return arr
}
const midd = Math.floor(arr.length / 2)
const left = arr.splice(0, midd)
return merge(mergeSort(left), mergeSort(arr))
}
// 原地修改
// 递归最外层的right就是原数组
function merge (left, right) {
const arr = []
while (left.length && right.length) {
if (left[0] > right[0]) {
arr.push(right.shift())
} else {
arr.push(left.shift())
}
}
while (arr.length) {
right.unshift(arr.pop())
}
if (left.length) {
right.push(left.pop())
}
return right
}
/**
* @param {number[]} arr
*/
function mergeSort(arr) {
if (arr.length <= 1) {
return arr
}
const midd = Math.floor(arr.length / 2)
const left = arr.splice(0, midd)
return merge(mergeSort(left), mergeSort(arr))
}
42. Handwriting insertion sort
https://bigfrontend.dev/zh/problem/implement-Insertion-Sort
Insertion sort is a stable sort, and the time complexity is O(n^2)
/**
* @param {number[]} arr
*/
function insertionSort(arr) {
for (let i = 0; i < arr.length; i += 1) {
for (let j = i + 1; j < arr.length; j += 1) {
if (arr[i] > arr[j]) {
const item = arr.splice(j,1)[0]
arr.splice(i,0,item)
}
}
}
return arr
}
43. Handwriting Quick Sort
https://bigfrontend.dev/zh/problem/implement-Quick-Sort
Quicksort is an unstable sort, and the worst-case time complexity is O(n^2). The average time complexity is O(nlogn)
// Note: 需要直接在原数组上修改
/**
* @param {number[]} arr
*/
function quickSort(arr) {
if (arr.length <= 1) {
return arr
}
const referenceValue = arr[0]
const max = []
const min = []
for (let i = 1; i < arr.length; i += 1) {
if (arr[i] >= referenceValue) {
max.push(arr[i])
} else {
min.push(arr[i])
}
}
return quickSort(min).concat(referenceValue, quickSort(max))
}
// 原地修改
/**
* @param {number[]} arr
*/
function quickSort(arr) {
if (arr.length <= 1) {
return arr
}
const referenceValue = arr[0]
let max = []
let min = []
for (let i = 1; i < arr.length; i += 1) {
if (arr[i] >= referenceValue) {
max.push(...arr.splice(i,1))
} else {
min.push(...arr.splice(i,1))
}
i -= 1
}
min = quickSort(min)
max = quickSort(max)
while (max.length) {
arr.push(max.shift())
}
while (min.length) {
arr.unshift(min.pop())
}
return arr
}
44. Handwriting selection sort
https://bigfrontend.dev/zh/problem/implement-Selection-Sort
https://www.geeksforgeeks.org/selection-sort/
Selection sort is unstable sort, and the time complexity is O(n^2)
/**
* @param {number[]} arr
*/
function selectionSort(arr) {
for (let i = 0; i < arr.length; i += 1) {
let minIndex = i
for (let j = i + 1; j < arr.length; j += 1) {
if (arr[j] < arr[minIndex]) {
minIndex = j
}
}
const min = arr.splice(minIndex, 1)[0]
arr.splice(i, 0, min)
}
return arr
}
45. Find the Kth largest element in the unsorted array
https://bigfrontend.dev/zh/problem/find-the-K-th-largest-element-in-an-unsorted-array
// 分治
/**
* @param {number[]} arr
* @param {number} k
*/
function findKThLargest(arr, k) {
let result
const divideAndConquer = (arr, base) => {
if (arr.length <= 1) {
result = arr[0]
}
const min = []
const max = []
let maxLen
const referenceValue = arr[0]
for (let i = 1; i < arr.length; i += 1) {
if (arr[i] > referenceValue) {
max.push(arr[i])
} else {
min.push(arr[i])
}
}
max.push(arr[0])
maxLen = max.length + base
if (maxLen >= k && max.length) {
divideAndConquer(max, base)
} else if (maxLen < k && min.length) {
divideAndConquer(min, maxLen)
}
}
divideAndConquer(arr, 0)
return result
}
46. Implement _.once()
https://bigfrontend.dev/zh/problem/implement-once
/**
* @param {Function} func
* @return {Function}
*/
function once(func) {
let result
let once = true
return function (...args) {
if (once) {
result = func.call(this, ...args)
once = false
}
return result
}
}
47. Reverse Linked List
https://bigfrontend.dev/zh/problem/Reverse-a-linked-list
/**
* class Node {
* new(val: number, next: Node);
* val: number
* next: Node
* }
*/
/**
* @param {Node} list
* @return {Node}
*/
const reverseLinkedList = (list) => {
if (!list) return
const initNode = list
let newHead = initNode
let newHeadNextNode = initNode
while (initNode.next) {
newHead = initNode.next
if (newHead.next) {
initNode.next = newHead.next
} else {
initNode.next = null
}
newHead.next = newHeadNextNode
newHeadNextNode = newHead
}
return newHead
}
48. Return the position of the first occurrence of a specific element in an array with repeated elements
https://bigfrontend.dev/zh/problem/search-first-index-with-Binary-Search-duplicate-array
/**
* @param {number[]} arr - ascending array with duplicates
* @param {number} target
* @return {number}
*/
function firstIndex(arr, target){
if (arr.length === 0) return -1
let index = -1
const _firstIndex = (start, end) => {
if (start <= end) {
const midd = Math.floor((start + end) / 2)
if (arr[midd] === target) {
if (index === -1) {
index = midd
} else {
index = Math.min(index, midd)
}
_firstIndex(start, midd - 1)
} else if (arr[midd] > target) {
_firstIndex(start, midd - 1)
} else {
_firstIndex(midd + 1, end)
}
}
}
_firstIndex(0, arr.length - 1)
return index
}
49. Return the last position of a specific element in an array with repeated elements
https://bigfrontend.dev/zh/problem/search-last-index-with-Binary-Search-possible-duplicate-array
/**
* @param {number[]} arr - ascending array with duplicates
* @param {number} target
* @return {number}
*/
function lastIndex(arr, target){
if (arr.length === 0) return -1
let index = -1
const _lastIndex = (start, end) => {
if (start <= end) {
const midd = Math.floor((start + end) / 2)
if (arr[midd] === target) {
if (index === -1) {
index = midd
} else {
index = Math.max(index, midd)
}
_lastIndex(midd + 1, end)
} else if (arr[midd] > target) {
_lastIndex(start, midd - 1)
} else {
_lastIndex(midd + 1, end)
}
}
}
_lastIndex(0, arr.length - 1)
return index
}
50. Return the element before a specific element in an array with repeated elements
https://bigfrontend.dev/zh/problem/search-element-right-before-target-with-Binary-Search-possible-duplicate-array
/**
* @param {number[]} arr - ascending array with duplicates
* @param {number} target
* @return {number}
*/
function firstIndex(arr, target){
if (arr.length === 0) return -1
let index = -1
const _firstIndex = (start, end) => {
if (start <= end) {
const midd = Math.floor((start + end) / 2)
if (arr[midd] === target) {
if (index === -1) {
index = midd
} else {
index = Math.min(index, midd)
}
_firstIndex(start, midd - 1)
} else if (arr[midd] > target) {
_firstIndex(start, midd - 1)
} else {
_firstIndex(midd + 1, end)
}
}
}
_firstIndex(0, arr.length - 1)
return index
}
/**
* @param {number[]} arr - ascending array with duplicates
* @param {number} target
* @return {number}
*/
function elementBefore(arr, target){
if (arr.length === 0) return undefined
const _firstIndex = firstIndex(arr, target)
if (_firstIndex === 0 || _firstIndex === -1) return undefined
return arr[_firstIndex - 1]
}
51. Return elements after a specific element in an array with repeated elements
https://bigfrontend.dev/zh/problem/search-element-right-after-target-with-Binary-Search-possible-duplicate-array
/**
* @param {number[]} arr - ascending array with duplicates
* @param {number} target
* @return {number}
*/
function lastIndex(arr, target){
if (arr.length === 0) return -1
let index = -1
const _lastIndex = (start, end) => {
if (start <= end) {
const midd = Math.floor((start + end) / 2)
if (arr[midd] === target) {
if (index === -1) {
index = midd
} else {
index = Math.max(index, midd)
}
_lastIndex(midd + 1, end)
} else if (arr[midd] > target) {
_lastIndex(start, midd - 1)
} else {
_lastIndex(midd + 1, end)
}
}
}
_lastIndex(0, arr.length - 1)
return index
}
/**
* @param {number[]} arr - ascending array with duplicates
* @param {number} target
* @return {number}
*/
function elementAfter(arr, target){
if (arr.length === 0) return undefined
const _lastIndex = lastIndex(arr, target)
if (_lastIndex === arr.length - 1 || _lastIndex === -1) return undefined
return arr[_lastIndex + 1]
}
53. Implement middleware
https://bigfrontend.dev/zh/problem/create-a-middleware-system
class Middleware {
constructor () {
this.callbacks = []
this.handleErrors = []
this.handleNextError = null
}
// 洋葱模型
_compose(funcs, type) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
if (type === 'callback') {
return funcs.reduce((a, b) => {
return (req, next) => {
return a(req, (err) => {
if (err) {
this.handleNextError(err)
} else {
b(req, next)
}
})
}
})
} else if (type === 'handleError') {
return funcs.reduce((a, b) => {
return (err, req, next) => {
return a(err, req, (err) => {
b(err, req, next)
})
}
})
}
}
/**
* @param {MiddlewareFunc} func
*/
use(func) {
if (func.length === 2) {
this.callbacks.push(func)
} else {
this.handleErrors.push(func)
}
}
/**
* @param {Request} req
*/
start(req) {
const callback = this._compose(this.callbacks, 'callback')
const handleError = this._compose(this.handleErrors, 'handleError')
this.handleNextError = (err) => {
handleError(err, req, () => {})
}
try {
callback(req, (err) => {
if (err) {
this.handleNextError(err)
}
})
} catch (err) {
handleError(err, req, () => {})
}
}
}
const middleware = new Middleware()
middleware.use((req, next) => {
console.log(1)
next()
console.log(2)
})
// since error occurs, this is skipped
middleware.use((req, next) => {
console.log(3)
next(new Error())
console.log(4)
})
// since error occurs, this is called
middleware.use((error, req, next) => {
console.log(error)
console.log(req)
})
middleware.start({})
class Middleware {
constructor () {
this.callbacks = []
this.handleErrors = []
}
/**
* @param {MiddlewareFunc} func
*/
use(func) {
if (func.length === 2) {
this.callbacks.push(func)
} else {
this.handleErrors.push(func)
}
}
/**
* @param {Request} req
*/
start(req) {
let callbackIndex = 0
let handleErrorIndex = 0
const next = (error) => {
const args = [req, next]
let func
if (error) {
func = this.handleErrors[handleErrorIndex]
args.unshift(error)
handleErrorIndex += 1
} else {
func = this.callbacks[callbackIndex]
callbackIndex += 1
}
try {
func && func(...args)
} catch (err) {
next(err)
}
}
next()
}
}
🐜 Ant Financial Interview Questions: Realize compose
function f(next) {console.log(1);next();console.log(2);}
function g(next) {console.log(3);next();console.log(4);}
function h(next) {console.log(5);next();console.log(6);}
// ((next) => {
// ((next) => {
// return f(() => {
// g(next)
// })
// })(() => { h(next) })
// })(() => {
// console.log('ok')
// })
// 最终需要实现的目标
// f(() => {
// g(() => {
// h(() => {
// })
// })
// })
// 实现compose
function compose(...funcs) {
r
return function () {
return funcs.reduce((a,b) => {
return (next) => {
return a(() => {
b(next)
})
}
})(() => {})
}
}
// 1,3,5,6,4,2
// 第一次返回(next) => f(() => g(next))
// 第二次a: (next) => f(() => g(next)) b: h
// 第二次返回 (next) => ((next) => f(() => g(next))(() => h(next))
// (next) =>f(() => g(() => h(next)))
compose(f,g,h)()
53. Implement extends
https://bigfrontend.dev/zh/problem/write-your-own-extends-in-es5
const myExtends = (SuperType, SubType) => {
function Child (...args) {
SuperType.call(this, ...args)
SubType.call(this, ...args)
// 这里Child其实本质还是SubType,还是这里要修改下
Object.setPrototypeOf(this, SubType.prototype)
}
SubType.prototype = Object.create(SuperType.prototype)
Child.prototype = Object.create(SubType.prototype)
SubType.prototype.constructor = SubType
Child.prototype.constructor = Child
// 模拟es6额外的继承链
Object.setPrototypeOf(Child, SuperType)
return Child
}
55. Highlight keywords in HTML strings
https://bigfrontend.dev/zh/problem/highlight-keywords-in-HTML-string
// 获取所有关键词的排列组合
/**
* @param {string} html
* @param {string[]} keywords
*/
function highlightKeywords(html, keywords) {
const combination = []
let arr = html.split(' ')
const getCombination = (head, arr) => {
for (let i = 0; i < arr.length; i += 1) {
const temp = [...arr]
temp.splice(i, 1)
const name = `${head}${arr[i]}`
combination.push(name)
getCombination(name, temp)
}
}
getCombination('', keywords)
arr = arr.map((item) => {
if (combination.includes(item)) {
return `<em>${item}</em>`
} else if (keywords.some((keyword) => item.includes(keyword))) {
for (let i = 0; i < keywords.length; i += 1) {
const keyword = keywords[i]
if (item.includes(keyword)) {
const reg = new RegExp(keyword, 'g')
item = item.replace(reg, `<em>${keyword}</em>`)
break
}
}
return item
} else {
return item
}
})
return arr.join(' ')
};
// 单纯的使用正则
58. Return the height of the DOM tree
https://bigfrontend.dev/zh/problem/get-DOM-tree-height
/**
* @param {HTMLElement | null} tree
* @return {number}
*/
function getHeight(tree) {
if (!tree) return 0
const result = []
const bfs = (nodes) => {
if (nodes.length) {
let childs = []
for (let i = 0; i < nodes.length; i += 1) {
const children = nodes[i].children
childs = [...childs, ...children]
}
result.push(childs)
bfs(childs)
}
}
bfs([tree])
return result.length
}
59. Realize browser history
https://bigfrontend.dev/zh/problem/create-a-browser-history
class BrowserHistory {
constructor(url) {
this.history = [url || undefined]
this.index = 0
}
visit(url) {
if (this.index === this.history.length - 1) {
this.history.push(url)
} else {
this.history = this.history.splice(0, this.index + 1)
this.history.push(url)
}
this.index = this.history.length - 1
}
get current() {
return this.history[this.index]
}
goBack() {
this.index -= 1
this.index = this.index < 0 ? 0 : this.index
return this.history[this.index]
}
forward() {
this.index += 1
this.index = this.index > this.history.length - 1 ? this.history.length - 1 : this.index
return this.history[this.index]
}
}
60. Realize your own new
https://bigfrontend.dev/zh/problem/create-your-own-new-operator
/**
* @param {Function} constructor
* @param {any[]} args - argument passed to the constructor
* `myNew(constructor, ...args)` should return the same as `new constructor(...args)`
*/
const myNew = (constructor, ...args) => {
const obj = Object.create({})
const returnValue = constructor.call(obj, ...args)
Object.setPrototypeOf(obj, constructor.prototype)
return returnValue || obj
}
61. Implement Function.prototype.call
https://bigfrontend.dev/zh/problem/create-call-method
Function.prototype.mycall = function(thisArg, ...args) {
if (thisArg === undefined || thisArg === null) {
thisArg = window
}
if (typeof thisArg === 'string') {
thisArg = new String(thisArg)
}
if (typeof thisArg === 'number') {
thisArg = new Number(thisArg)
}
if (typeof thisArg === 'boolean') {
thisArg = new Boolean(thisArg)
}
const key = Symbol()
thisArg[key] = this
const result = thisArg[key](...args)
delete thisArg[key]
return result
}
Handwriting apply
Function.prototype.myapply = function (thisArg, argsArray = []) {
if (!Array.isArray(argsArray)) {
throw new Error()
}
if (thisArg === undefined || thisArg === null) {
thisArg = window
}
if (typeof thisArg === 'string') {
thisArg = new String(thisArg)
}
if (typeof thisArg === 'number') {
thisArg = new Number(thisArg)
}
if (typeof thisArg === 'boolean') {
thisArg = new Boolean(thisArg)
}
const key = Symbol()
thisArg[key] = this
const result = thisArg[key](...argsArray)
delete thisArg[key]
return result
}
Handwriting bind
Function.prototype.mybind = function (thisArg, ...initArgs) {
if (thisArg === undefined || thisArg === null) {
thisArg = window
}
if (typeof thisArg === 'string') {
thisArg = new String(thisArg)
}
if (typeof thisArg === 'number') {
thisArg = new Number(thisArg)
}
if (typeof thisArg === 'boolean') {
thisArg = new Boolean(thisArg)
}
const that = this
return function (...args) {
const key = Symbol()
thisArg[key] = that
const result = thisArg[key](...initArgs, ...args)
delete thisArg[key]
return result
}
}
63. Handwriting _.cloneDeep()
https://bigfrontend.dev/zh/problem/create-cloneDeep
// 规避循环引用 Avoid circular references
const hash = new WeakMap()
function isObject(value) {
return value != null && (typeof value === "object" || typeof value === "function")
}
function getSymbolKeys(value) {
let keys = Object.getOwnPropertySymbols(value)
keys = keys.filter((key) => value.propertyIsEnumerable(key))
return keys
}
function getAllKeys(value) {
let keys = Object.keys(value)
keys = [...keys, ...getSymbolKeys(value)]
return keys
}
function cloneDeep(data) {
let result = null
if (!isObject(data)) {
return data
}
const isArray = Array.isArray(data)
if (isArray) {
result = []
} else {
result = Object.create(Object.getPrototypeOf(data))
}
if (hash.has(data)) {
return hash.get(data)
} else {
hash.set(data, result)
}
const keys = getAllKeys(data)
for (let i = 0; i < keys.length; i += 1) {
const key = keys[i]
const val = data[key]
result[key] = cloneDeep(val)
}
return result
}
64. Automatic retry when Promise reject
https://bigfrontend.dev/zh/problem/retry-promise-on-rejection
/**
* @param {() => Promise<any>} fetcher
* @param {number} maximumRetryCount
* @return {Promise<any>}
*/
function fetchWithAutoRetry(fetcher, maximumRetryCount) {
return new Promise(async (resolve, reject) => {
let error
for (let i = 0; i <= maximumRetryCount; i += 1) {
await fetcher().then((res) => {
resolve(res)
}).catch((err) => {
error = err
})
}
reject(error)
})
}
65. Add thousands separator
https://bigfrontend.dev/zh/problem/add-comma-to-number
/**
* @param {number} num
* @return {string}
*/
function addComma(num) {
num = `${num}`
const reg = /\B(?=(\d{3})+$)/g
const decimalPlaces = num.split('.')[1]
let integerBits = num.split('.')[0]
integerBits = integerBits.replace(reg, ',')
if (decimalPlaces) {
return `${integerBits}.${decimalPlaces}`
}
return `${integerBits}`
}
66. Remove duplicate elements in the array
https://bigfrontend.dev/zh/problem/remove-duplicates-from-an-array
/**
* @param {any[]} arr
*/
function deduplicate(arr) {
const map = new Map()
for (let i = 0; i < arr.length; i += 1) {
if (map.has(arr[i])) {
arr.splice(i, 1)
i -= 1
} else {
map.set(arr[i], true)
}
}
return arr
}
69. Implement _.isEqual()
https://bigfrontend.dev/zh/problem/implement-deep-equal-isEqual
const getSymbolKeys = (obj) => {
let symbolKeys = Object.getOwnPropertySymbols(obj)
symbolKeys = [...symbolKeys].filter((key) => {
return obj.propertyIsEnumerable(key)
})
return symbolKeys
}
const getAllKeys = (obj) => {
let keys = Object.keys(obj)
keys = [...keys, ...getSymbolKeys(obj)]
return keys
}
function isObject(value) {
return value != null && (typeof value === "object" || typeof value === "function")
}
const hash = new WeakMap()
/**
* @param {any} a
* @param {any} b
* @return {boolean}
*/
function isEqual(a, b) {
let result = true
const equal = (a, b) => {
if (!isObject(a) || !isObject(b)) {
result = (a === b)
return
}
if (hash.has(a) && hash.has(b)) {
return true
}
const aKeys = getAllKeys(a)
const bKeys = getAllKeys(b)
if (aKeys.length !== bKeys.length) {
result = false
return
}
hash.set(a, true)
hash.set(b, true)
for (let i = 0; i < aKeys.length; i += 1) {
const key = aKeys[i]
const aValue = a[key]
const bValue = b[key]
if (!isObject(aValue) || !isObject(bValue)) {
if (aValue !== bValue) {
result = false
break
}
} else {
isEqual(aValue, bValue)
}
}
}
equal(a, b)
return result
}
79. Convert snake_case to camelCase
https://bigfrontend.dev/zh/problem/convert-snake_case-to-camelCase
/**
* @param {string} str
* @return {string}
*/
function snakeToCamel(str) {
const reg = /([^_])_([^_])/g
return str.replaceAll(reg, (_, p1, p2) => `${p1}${p2.toUpperCase()}`)
}
Convert camelCase to snake_case
Not tested by boundary conditions
/*
* @param {string} str
* @return {string}
*/
function camelToSnake(str) {
const reg = /\B(\w)([A-Z])\B/g
return str.replaceAll(reg, (_, p1, p2) => `${p1}_${p2.toLowerCase()}`)
}
81. Combine Sorted Arrays
https://bigfrontend.dev/zh/problem/merge-sorted-arrays
/**
* @param {number[][]} arrList
* non-descending integer array
* @return {number[]}
*/
function merge(arrList) {
let result = []
let offset = false
const isMerge = () => {
let count = 0
for (let i = 0; i < arrList.length; i += 1) {
if (arrList[i].length > 0) count += 1
if (count >= 2) break
}
return count >= 2 ? true : false
}
offset = isMerge()
while (offset) {
let index = 0
let min = Number.MAX_VALUE
for (let i = 0; i < arrList.length; i += 1) {
if (arrList[i][0] <= min) {
index = i
min = arrList[i][0]
}
}
result.push(...arrList[index].splice(0, 1))
offset = isMerge()
}
for (let i = 0; i < arrList.length; i += 1) {
result = [...result, ...arrList[i]]
}
return result
}
85. Implement _.get()
https://bigfrontend.dev/zh/problem/implement-lodash-get
/**
* @param {object} source
* @param {string | string[]} path
* @param {any} [defaultValue]
* @return {any}
*/
function get(source, path, defaultValue = undefined) {
if (typeof path === 'string') {
path = path.split('.')
}
const newPath = []
const reg = /^([A-Za-z]+)\[([0-9]+)]$/g
let result = source
for (let i = 0; i < path.length; i += 1) {
if (path[i].includes('[')) {
const arr = reg.exec(path[i])
newPath.push(arr[1])
newPath.push(arr[2])
} else {
newPath.push(path[i])
}
}
for (let i = 0; i < newPath.length; i += 1) {
if (result === undefined || result === null) {
result = defaultValue
break
}
result = result[newPath[i]]
}
console.log(result)
return result ? result : defaultValue
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。