盒子
文章目录
  1. 节流与防抖
    1. 节流
    2. 防抖
  2. 函数柯里化
  3. 手写 bind
  4. 手写 new
  5. 实现一个 set
  6. 数组的操作
    1. 扁平化
    2. 去重
    3. 交集
    4. 并集
    5. 差集
  7. 深拷贝
  8. Promise 系列
    1. 手写 Promise
    2. 手写 PromiseAll
    3. 手写 allSettled
    4. 手写 promiseRace
  9. 发布订阅
  10. LRU 缓存
  11. 简易版的 ORM
  12. 异步流程控制

手写代码

节流与防抖

节流

function throttle(fn, delay) {
let time = Date.now()
let timer
clearTimeout(timer)
return (...args)=> {
if (Date.now() - time < delay) {
fn.apply(this, args)
time = Date.now()
} else {
timer = setTimeout(()=> {
fn.apply(this, args)
}, delay)
}
}
}

const log =()=> console.log('hello')

const debounceTask = throttle(log, 3000)

window.addEventListener('scroll', debounceTask)

防抖

let debounce = function(f, interval = 1000) {
let handler = null //handler
return function() {
// 返回闭包保持对 handler 的引用
// 如果不返回闭包,那么 handler 会被回收,又会生成 setTimeout,所以没有防抖效果
if (handler) {
clearTimeout(handler); //
}
let arg = arguments
handler = setTimeout(function() { // 如果没有 handler 则注册 setTimeout 事件
//使用appy一方面是为了传入arg参数
//一方面是为了改变 this 的指向
//否则,执行函数指向是 window 而不是 input dom对象了.
f.apply(this, arg);
clearTimeout(handler);
console.log(arg, this)
}, interval)
}
}

函数柯里化

function curry(fn) {
return function curried(...args1) {
// 「抵达」函数实际的需要的参数长度
if (args1.length >= fn.length) {
return fn.apply(this, [...args1])
} else {
return function (...args2) {
// 为撒小于的时候才递归呢?
return curried.apply(this, [...args1, ...args2])
}
}
}
}

手写 bind

Function.prototype.mybind = function(context, ...args) {
let fn = this
return function(...rest) {
return fn.apply(context, [...args, ...rest])
}
}

function log () {
console.log('hello world', this.name)
}

log()
const obj = {
name: '11'
}

const fn = log.mybind(obj)()

手写 new

function _new(obj, ...rest) {
const newTarget = Object.create(obj.prototype) // 新创建一个对象,将对象的 prototype 赋给 target

const result = obj.apply(newObj, rest) //为这个对象分配属性和防范
// 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象
return typeof result === 'object' ? result : newTarget
}

实现一个 set

class MySet {
constructor() {
this.set = []
this.map = new Map()
}

get size() {
return this.set.length
}

insert(n) {
if (this.map.has(n)) {
return false
} else {
this.set.push(n)
this.map.set(n, this.set.length - 1)
return true
}
}

remove(val) {
if (this.map.has(val)) {
// 找到要删除的值的下标
const deleteIndex = this.map.get(val)
// 得到最后一个元素
const lastVal = this.set.pop()

// 用最后一个数「代替」要删除的值
this.set[deleteIndex] = lastVal
// 更新本来是最后一个元素在 map 中记录的下标
this.map.set(lastVal, deleteIndex)

// 在map中删除
this.map.delete(val)

return true
} else {
//如果没有这个值就返回false
return false
}
}

getRadom() {

}
}

数组的操作

扁平化

function flatten(arr) {
return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur) ? flatten(cur) : cur), [])
}

去重

function uniq(arr) {
return [...new Set(arr)]
}

a = [1, 2, 3], b = [2, 4, 5]

// filter() 方法创建一个新数组,其包含通过所提供函数实现的测试的所有元素。
// const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
// const result = words.filter(word => word.length > 6);

交集

function uniq(arr1, arr2) {
return arr1.filter(num=> arr2.includes(num))
}

并集

function union(arr1, arr2) {
return [...new Set([...arr1, ...arr2])]
}

差集

function difference(arr1, arr2) {
return [...arr1, ...arr2].filter(num=> !arr1.includes(num) || !arr2.includes(num))
}

深拷贝

const deepClone = (value, hash = new WeakMap) => {
if (value == null) return value
if (value instanceof RegExp) return new RegExp(value)
if (value instanceof Date) return new Date(value)
if (typeof value !== 'object') return value

let obj = new value.constructor()
if (hash.get(value)){
return hash.get(value);
}

hash.set(value, obj);

for(var k in value) {
if(value.hasOwnProperty(k)){
obj[k] = deepClone(value[k], hash);
}
}
return obj
}

Promise 系列

手写 Promise

const PENDING = 'pending'
const RESOLVED = 'resoved'
const REJECTED = 'reject'

class MyPromise {
constructor(fn) {

this.value = undefined
this.status = PENDING
this.callBacks = []

resolve = (value)=> {
if (this.status === PENDING) {
return
}
this.value = value
this.status = RESOLVED
this.callBacks.forEach((fn)=> fn(this.value))
}

reject = (err)=> {
if (this.status === PENDING) {
return
}
this.reason = err
this.status = REJECTED
this.callBacks.forEach(fn => fn(this.reason))
}

try {
fn(resolve, reject)
} catch (err) {
reject(err)
}
}

then = (onResolve, onReject)=> {
if (this.status === RESOLVED) {
onResolve(this.value)
}

if (this.status === REJECTED) {
onReject(this.reason)
}

if (this.status === PENDING) {
this.callBacks.push(()=> )
}
}
}

const promise = new MyPromise((resolve, reject)=> {
setTimeout(()=> {
resolve('hello world')
}, 1000)
});

promise.then(()=> {})

手写 PromiseAll

有一个错误都会错

function promiseAll (arr) {
const res = []
let count = 0
return new Promise((resovlve, reject)=> {
for ((key, index) of arr) {
Promise.resolve(arr[i]).then((result)=> {
count++
res[index] = result
if (count === arr.length) {
resovlve(res)
}
}).catch((err)=> {
reject()
})
}
})
}

手写 allSettled

MyPromise.allSettled = function(values) {
let promises = [].slice.call(values)
return new MyPromise((resolve, reject) => {
let result = [], count = 0
promises.forEach(promise => {
MyPromise.resolve(promise).then(value=>{
result.push({status: 'fulfilled', value})
}).catch(err=>{
result.push({status: 'rejected', value: err})
}).finally(()=>{
if(++count === promise.length) {
resolve(result)
}
})
})
})
}

手写 promiseRace

function promiseRace(args) {
return new Promise((resovlve, reject)=> {
for ((key, index) of args) {
Promise.resovlv(args[i]).then(resolve, reject)
}
})
}

发布订阅

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

on (name, callback) {
if (name === 'newListener') {
callback()
}
const callbacks = this.events[name] || []
callbacks.push(callback)
this.events[name] = callbacks
}

off (name, callback) {
const callbacks = this.events[name] || []
callbacks = callbacks.filter(fn=> fn !== callbacks)
this.events[name] = callbacks
}

emit(name, ...args) {
const callbacks = this.events[name] || []
callbacks.forEach(cb=> cb(args))
}

once(name, callback) {
function wrap(...args) {
callback(args)
this.off(name, callback)
}
this.on(name, wrap)
}
}

const events = new EventEmitter()

events.on("newListener", function(eventName){
console.log(`eventName`, eventName)
})

const event = new EventEmitter()
const handle = (...pyload) => console.log(pyload);

event.on("click", handle)
event.emit("click", 100, 200, 300, 100)
event.remove("click", handle)

event.once("dbclick", function() {
console.log("click")
})

event.emit("dbclick", 100)

LRU 缓存

// 最近最少使用,最少使用的在前面

class LRUCache {
constructor(max) {
this.max = max
this.keys = []
this.cache = {}
}

get = (k) => {
if (this.cache[k]) {
this.remove(this.keys, k)
this.keys.push(k)
return this.cache[k]
} else {
return -1
}
}

put = (k, v) => {
if (this.cache[k]) return
this.keys.push(k)

// put 操作为啥会超出长度呢,则要将最开始的
if (this.keys.length > this.max) {
delete this.cache[this.keys[0]]
this.keys.shift()
}
this.cache[k] = v
}

remove = (arr, item) => {
if (arr.length) {
const index = arr.indexOf(item)
if (index > -1) {
return arr.splice(index, 1)
}
}
}
}

简易版的 ORM

// 约定:
// title数据类型为String
// userId为主键,数据类型为Number
var data = [
{userId: 8, title: 'title1'},
{userId: 11, title: 'other'},
{userId: 15, title: null},
{userId: 19, title: 'title2'}
]

class Query {
constructor(data) {
this.data = data || []
}

where(query) {
const cdtions = Object.keys(query)

this.data = this.data.filter((item)=> {
return cdtions.every(key=> {
return query[key].test(item[key])
})
})

return this
}

orderBy(key, direction) {
this.data.sort((a, b) => {
return direction === 'desc' ? b[key] - a[key] : a[key] - b[key];
})

return this
}

}
var find = function(origin) {
return new Query(origin)
}

// 查找 data 中,符合条件的数据,并进行排序

var result = find(data).where({
'title': /\d$/
}).orderBy('userId', 'desc');

console.log(result);// [{ userId: 19, title: 'title2'}, { userId: 8, title: 'title1' }];

异步流程控制

class Scheduler {
constructor() {
this.count = 2
this.queue = []
this.run = []
}

add(task) {
this.queue.push(task)
}

excute() {
const helper = ()=> {
if (this.queue.length <= this.count) {
this.run = this.queue
Promise.all(
this.run
).then(()=> {
this.run = []
})
}

this.run = this.queue.splice(0, this.count)
Promise.all(this.run)
helper()
}
helper()
}
}

const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time)
})

const scheduler = new Scheduler()

const addTask = (time, order) => {
scheduler.add(() => timeout(time)).then(() => console.log(order))
}

addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')