前言
最近裁员找工作,在此记录一下面试中常见的手写函数实现,每天闲时不定时更新。
防抖函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
const debounce = (callback, delay = 500, immediate = false) => { let timer = null let isImmediateInvoke = false if (typeof callback !== 'function') { throw new TypeError('callback is not a function') } const _debounce = (...args) => { let context = this timer && clearTimeout(timer) if (!isImmediateInvoke && immediate) { callback.apply(context, args) isImmediateInvoke = true } else { timer = setTimeout(() => { callback.apply(context, args) isImmediateInvoke = false }, delay) } } _debounce.cache = () => { clearTimeout(timer) timer = null } return _debounce }
|
测试:
1 2 3 4 5 6
| <button id="btn">点击</button>
function fn() { console.log('点击按钮') } document.querySelector('#btn').addEventListener('click', debounce(fn, 1000, true))
|
节流函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
const throttle = (callback, delay = 500, immediate = false) => { let preTime = 0 if (typeof callback !== 'function') { throw new TypeError('callback is not a function') } return (...args) => { let context = this, nowTime = +new Date(), flag = nowTime - preTime >= delay if (!flag) return preTime = nowTime immediate ? callback.apply(context, args) : setTimeout(() => { callback.apply(context, args) }, delay) } }
|
测试:
1 2 3 4 5 6
| <button id="btn">点击</button>
function fn() { console.log('点击按钮') } document.querySelector('#btn').addEventListener('click', throttle(fn, 1000, true))
|
函数柯里化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
const curry = callback => judge = (...args) => args.length >= callback.length ? callback(...args) : (...args2) => judge(...args, ...args2)
const curry = callback => { if (typeof callback !== 'function') { throw new TypeError('callback is not a function') } return judge = (...args) => { if (args.length >= callback.length) { return callback(...args) } else { return (...args2) => { return judge(...args, ...args2) } } } }
|
测试:
1 2 3 4 5 6 7 8
| function add(a, b, c) { return a + b + c } let addCurry = curry(add) console.log(addCurry(1, 2, 3)) console.log(addCurry(1, 2)(3)) console.log(addCurry(1)(2)(3)) console.log(addCurry(1)(2, 3))
|
深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
const deepClone = target => { const isObject = target => typeof target === 'object' && target !== null if (!isObject) { throw new TypeError('Target is not a Object') } const result = Array.isArray(target) ? [...target] : { ...target } Reflect.ownKeys(result).forEach(key => { result[key] = isObject(target[key]) ? deepClone(target[key]) : target[key] }) return result }
|
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const data = { name: 'Cyan', age: 27, desc: { jobs: '前端开发' } }
const cloneData = deepClone(data)
data.name = '白雾茫茫丶' data.desc.jobs = '后端开发'
console.log(data) console.log(cloneData)
|
Instanceof 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
const _instanceof = (_leftObj, _rightClass) => { const isObject = target => (typeof target === 'object' || typeof target === 'function') && target !== null if (!isObject(_leftObj) || !isObject(_rightClass)) { throw new TypeError('Incorrect parameter type') } const leftProto = Object.getPrototypeOf(_leftObj) const rightProto = _rightClass.prototype while (true) { if (leftProto === null) { return false } if (leftProto === rightProto) { return true } leftProto = Object.getPrototypeOf(leftProto) } }
|
测试:
1 2 3 4
| const dataA = [1, 2] const dataO = { name: 'Cyan' } console.log(_instanceof(dataA, Array)) console.log(_instanceof(dataO, Object))
|
Object.create 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
Object.myCreate = (prototype) => { const isObject = target => typeof target === 'object' && target !== null if (!isObject(prototype)) { throw new TypeError('prototype is not a Object') } function Fn() { } Fn.prototype = prototype Object.defineProperty(Fn.prototype, 'constructor', { value: Fn, enumerable: false }) return new Fn() }
|
测试:
1 2 3 4 5 6 7 8
| const Person = { name: 'Cyan', sayName: function () { console.log(`我的名字是${this.name}`) } } const child = Object.myCreate(Person) child.sayName()
|
New 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
const _new = (callback, ...args) => {
let obj = Object.create(callback.prototype)
const result = callback.call(obj, ...args)
return typeof result === 'object' && result !== null ? result : obj }
|
测试:
1 2 3 4 5 6
| function Person(name, age) { this.name = name this.age = age } const child = _new(Person, 'Cyan', 27) console.log(child)
|
手写 Bind
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
Function.prototype.myBind = function (ctx) {
let args = Array.prototype.slice.call(arguments, 1)
const fn = this return function () { return fn.apply(ctx, args) } }
|
测试:
1 2 3 4 5
| function fn(name, age) { console.log(this, name, age) } const newFn = fn.myBind({}, 'Cyan', 27) newFn()
|
手写 Apply
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
Function.prototype.myApply = function (ctx, args) {
ctx = (ctx === null || ctx === undefined) ? globalThis : Object(ctx)
let key = Symbol('fn')
Object.defineProperty(ctx, key, { enumerable: false, value: this })
return ctx[key](...args) }
|
测试:
1 2 3 4
| function fn(name, age) { console.log(this, name, age) } fn.myApply({}, ['Cyan', 27])
|
手写 Call
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
Function.prototype.myCall = function (ctx, ...args) {
ctx = (ctx === null || ctx === undefined) ? globalThis : Object(ctx)
let key = Symbol('fn')
Object.defineProperty(ctx, key, { enumerable: false, value: this })
return ctx[key](...args) }
|
测试:
1 2 3 4
| function fn(name, age) { console.log(this, name, age) } fn.myCall({}, 'Cyan', 27)
|
基于 Promise 封装 Ajax
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
function ajax(url, methods = 'get') {
return new Promise((resovle, reject) => { const xhr = new XMLHttpRequest() xhr.open(url, methods, true) xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { resovle(xhr.responseText) } else if (xhr.status === 404) { reject(new Error('404')) } } else { reject('请求数据失败') } } }) }
|
手写 Map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
Array.prototype.myMap = function (callback, thisValue) {
if (typeof callback !== 'function') { throw new TypeError('callback is not a function') }
const result = []
const context = this for (let i = 0; i < context.length; i++) { result[i] = callback.call(thisValue, context[i], i, context) } return result }
|
测试:
1 2 3
| const arr = [1, 2, 3] const res = arr.myMap(item => item * 2) console.log(res)
|
手写 forEach
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
Array.prototype.myForEach = function (callback, thisValue) {
if (typeof callback !== 'function') { throw new TypeError('callback is not a function') }
const context = this for (let i = 0; i < context.length; i++) { callback.call(thisValue, context[i], i, context) } }
|
测试:
1 2 3 4 5
| const arr = [1, 2, 3] arr.myForEach((item, index) => { arr[index] = item * 2 }) console.log(arr)
|
手写 Filter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
Array.prototype.myFilter = function (callback, thisValue) {
if (typeof callback !== 'function') { throw new TypeError('callback is not a function') }
const context = this
const result = [] for (let i = 0; i < context.length; i++) { callback.call(thisValue, context[i], i, context) && result.push(context[i]) } return result }
|
测试:
1 2 3 4 5
| const arr = [1, 2, 3] const arr2 = arr.myFilter(item => { return item >= 2 }) console.log(arr2)
|
手写 Some
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
Array.prototype.mySome = function (callback, thisValue) {
if (typeof callback !== 'function') { throw new TypeError('callback is not a function') }
const context = this
let result = false for (let i = 0; i < context.length; i++) { callback.call(thisValue, context[i], i, context) && (result = true) } return result }
|
测试:
1 2 3 4 5
| const arr = [1, 2, 3] const arr2 = arr.mySome(item => { return item > 2 }) console.log(arr2)
|
手写 Every
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
Array.prototype.myEvery = function (callback, thisValue) {
if (typeof callback !== 'function') { throw new TypeError('callback is not a function') }
const context = this
let result = true for (let i = 0; i < context.length; i++) { !callback.call(thisValue, context[i], i, context) && (result = false) } return result }
|
测试:
1 2 3 4 5
| const arr = [1, 2, 3] const arr2 = arr.myEvery(item => { return item > 2 }) console.log(arr2)
|
手写 Reduce
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
Array.prototype.myReduce = function (callback, initValue) {
if (typeof callback !== 'function') { throw new TypeError('callback is not a function') }
const context = this
const flag = initValue === undefined
let result = flag ? context[0] : initValue
for (let i = (flag ? 1 : 0); i < context.length; i++) { result = callback(result, context[i], i, context) } return result }
|
测试:
1 2 3 4 5
| const arr = [1, 2, 3, 4] const arr2 = arr.myReduce((perv, current) => { return perv + current }, 5) console.log(arr2)
|
手写 Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
|
const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECTED = 'rejected' class MyPromise { constructor(handler) { this.initValue() if (typeof handler !== 'function') { throw new TypeError('handler is not a function') } try { handler(this.resolve.bind(this), this.reject.bind(this)) } catch (e) { this.reject(e) } }
initValue() { this.PromiseState = PENDING this.PromiseResult = null this.onFulfilledCallbacks = [] this.onRejectedCallbacks = [] }
resolve(value) { if (this.PromiseState !== PENDING) return this.PromiseState = FULFILLED this.PromiseResult = value while (this.onFulfilledCallbacks.length) { this.onFulfilledCallbacks.shift()(this.PromiseResult) } }
reject(reason) { if (this.PromiseState !== PENDING) return this.PromiseState = REJECTED this.PromiseResult = reason while (this.onRejectedCallbacks.length) { this.onRejectedCallbacks.shift()(this.PromiseResult) } }
then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : promiseResult => promiseResult onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw (reason) }
const thenPromise = new MyPromise((resolve, reject) => { queueMicrotask(() => { const resolvePromise = cb => { try { const result = cb(this.PromiseResult) if (result instanceof MyPromise) { result.then(resolve, reject) } else { resolve(result) } } catch (error) { reject(err) throw new Error(error) } } switch (this.PromiseState) { case FULFILLED: resolvePromise(onFulfilled) break; case REJECTED: resolvePromise(onRejected) break; case PENDING: this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled)) this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected)) break; } }) }) return thenPromise }
catch(onRejected) { return this.then(undefined, onRejected) }
finally(callback) { queueMicrotask(() => { callback.call(this) }) } }
|
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const p = new MyPromise((resolve, reject) => { console.log('MyPromise立即执行') resolve('100') }) p.then(res => res, reason => { console.log('then是微任务') }).then(res => { console.log(res * 2) return res * 2 }).then(res => { console.log(res * 2) }) p.catch(err => { console.log(err) }) p.finally((a, b) => { console.log('不管什么状态都会执行') })
console.log(3)
|
扁平数组转成树形数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
const transformTreeData = (source,id,parentId,children)=>{ if(!Array.isArray(source)){ return [] } let cloneData = JSON.parse(JSON.stringify(source)) return cloneData.filter(father=>{ let branceChild = cloneData.filter(child=>father[id] == child[parentId]) branceChild.length > 0?father[children] = branceChild:'' return father[parentId] == 0 }) }
|
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| const arr = [ { id:1, parentId:0, name:'一级-1' }, { id:2, parentId:0, name:'一级-2' }, { id:3, parentId:0, name:'一级-3' }, { id:4, parentId:1, name:'二级-1' }, { id:5, parentId:4, name:'三级-1' }, { id:6, parentId:2, name:'二级-2' }, { id:7, parentId:3, name:'二级-3' }, ] console.log(transformTreeData(arr,'id','parentId','children'))
|
树形数组转成扁平数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
const transformFlatData = (source) => { if (!Array.isArray(source)) { return [] } let result = [] source.forEach(item => { result.push(item) item.children && result.push(...transformFlatData(item.children)) delete item.children }) return result }
|
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| const arr = [ { id: 1, parentId: 0, name: '一级-1', children: [ { id: 2, parentId: 1, name: '一级-2' }, { id: 3, parentId: 1, name: '一级-3', children: [ { id: 4, parentId: 3, name: '三级-1' }, { id: 5, parentId: 5, name: '三级-2' } ] } ]
} ] console.log(transformFlatData(arr))
|