防抖&节流
// 使用场景 表单提交 防止重复提交, input搜索
function debounce(fn, delay) {
let timer = null
return function(...args) {
if (timer) clearTimeout(timer)
setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
// 滚动,resize,节流
function throttle(fn, delay) {
let flag = false
return function(...args) {
if (flag) return
flag = true
setTimeout(() => {
fn.apply(this, args)
flag = false
}, delay)
}
}
【手写】发布订阅
// 发布&订阅者
class EventEmitter {
constructor() {
this.events = {}
}once = function(type, cb) {
const self = this
function temp(...args) {
cb.apply(null, args)
self.off(type, temp)
}
self.on(type, temp)
}emit = function(type, ...args) {
const events = this.events[type]
events && events.forEach(cb => {
cb.call(null, ...args)
})
}on = function(type, cb) {
if (this.events[type]) {
this.events[type].push(cb)
} else {
this.events[type] = [cb]
}
}off = function(type, cb) {
if (this.events[type]) {
const index = this.events[type].indexOf(cb)
this.events[type].splice(index, 1)
}
}
}
Scheduler,控制并发次数
class Scheduler {
constructor() {
this.count = 2
this.queue = []
this.run = []
}add = (task) => {
this.queue.push(task)
return this.scheduler()
}scheduler = () => {
if (this.run.length < this.count && this.queue.length) {
const p = this.queue.shift()
const promise = p().then(res => {
this.run.splice(this.run.indexOf(promise), 1)
})
this.run.push(promise)
return promise
} else {
return Promise.race(this.run).then(this.scheduler)
}
}
}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(10000, '1')
addTask(5000, '2')
addTask(3000, '3')
addTask(4000, '4')
// 2,3,1,4
Promise
class MyPromise {
constructor(exector) {
this.status = 'pending' // pending | resolved | rejected
this.callbacks = []
this.datahttps://www.it610.com/article/= ''
try {
exector(this.resolve, this.reject)} catch (err) {
throw new Error(err)
}
}resolve = (value) => {
if (this.status !== 'pending') {
return
}this.status = 'resolved'
this.data = https://www.it610.com/article/value
if (this.callbacks.length) {setTimeout(() => {
this.callbacks.forEach(({ onFulfill }) => {
onFulfill(this.data)
})
})}
}reject = (value) => {
if (this.status !== 'pending') {
return
}this.status = 'rejected'
this.data = https://www.it610.com/article/value
if (this.callbacks.length) {setTimeout(() => {
this.callbacks.forEach(({ onReject }) => {
onReject(this.data)
})
})}
}then = (resolvecb, rejectcb) => {const onFulfill = typeof resolvecb === 'function' ? resolvecb : (value) => value
const onReject = typeof rejectcb === 'function' ? rejectcb : (reason) => { throw new Error(reason) }return new MyPromise((resolve, reject) => {const handle = (cb) => {
try {
const res = cb(this.data)
if (res instanceof MyPromise) {
res.then(resolve, reject)
} else {
resolve(res)
}
} catch (e) {
reject(e)
}
}if (this.status === 'pending') {
this.callbacks.push({
onFulfill: () => handle(onFulfill),
onReject: () => handle(onReject)
})
} else if (this.status === 'resolved') {
setTimeout(() => {
handle(onFulfill)
})
} else {
setTimeout(() => {
handle(onReject)
})
}})}catch = (cb) => {
return new MyPromise((resolve, reject) => {
this.then(undefined, reason => cb(reason))
})}}MyPromise.resolve = function (val) {
return new MyPromise((resolve, reject) => {
if (val instanceof MyPromise) {
val.then(resolve, reject)
} else {
resolve(val)
}
})
}MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => {
if (reason instanceof MyPromise) {
reason.then(resolve, reject)
} else {
reject(reason)
}
})
}MyPromise.allSettled = function (arr) {
const res = []
return new MyPromise((resolve, reject) => {
arr.forEach((p, index) => {
p.then(val => {
res.push({
type: 'resolve',
data: val
})
if (res.length === arr.length) {
resolve(res)
}
}, reason => {
res.push({
type: 'reject',
data: reason
})
if (res.length === arr.length) {
resolve(res)
}
})
})
})
}
promise 限制并发次数
function asyncPools(limit, arr, iteratorFn) {
let ret = []
let executing = [] // 进行中
let i = 0const enqueue = function () {
if (i === arr.length) {
return Promise.resolve()
}
const item = arr[i++]
const p = iteratorFn(item)
ret.push(p)
executing.push(p)
p.then(() => executing.splice(executing.indexOf(p), 1))if (executing.length >= limit) {
return Promise.race(executing).then(enqueue)
} else {
return Promise.resolve().then(() => enqueue())
}
}return enqueue().then(() => {
console.log('ret', ret)
return Promise.all(ret)
})
}const timeout = i => new Promise(resolve => {
console.log(i);
setTimeout(() => {
console.log('cb', i)
return resolve(i)
}, i)
});
asyncPools(3, [1000, 5000, 3500, 2000], timeout).then((res) => {
console.log('res', res)
})
Generator模拟实现async await
function* generatorFun() {
let a = yield Promise.resolve('A')
let b = yield Promise.resolve('B')
return a + b
}function run(gen) {
return new Promise(function (resolve) {
// 执行Generator函数
let g = gen()
const next = (context) => {
let { done, value } = g.next(context)
if (done) {
// 完成返回
resolve(value)
} else {
// 未完成继续执行next函数,并传入本次执行结果
value.then(next)
}
}
next()
})
}// run 为执行函数
run(generatorFun).then(res => console.log(res)) // AB
call/apply/bind/new/create
// call
Function.prototype.myCall = function(context, ...args) {
context.fn = this
if (typeof context.fn !== 'function') {
throw new Error('not function')
}
const res = context.fn(...args)
delete context.fn
return res
}
// apply
Function.prototype.myApply = function(context, args) {
context.fn = this
if (typeof context.fn !== 'function') {
throw new Error('not function')
}
const res = context.fn(...args)
delete context.fn
return res
}
// Object.create
function create(prototype, properties = {}) {
function F() {}
F.prototype = prototype
const obj = new F()
Object.defineProperties(obj, properties)
return obj
}
// bind
function.prototype.myBind =function (parentContext, ...args1) {
if (typeof this !== 'function') {
throw new Error('not function')
}
const fn = this
const fBound = function (...args2) {
const context = this instanceof fBound ? this : parentContext
return fn.myApply(context, args1.concat(args2))
}
// 访问原函数的原型上的方法,通过Object.create拷贝
fBound.prototype = create(fn.prototype)
return fBound
}
// new
function myNew(fn, ...args) {
var obj = create(fn.prototype)
const res = fn.myApply(obj, args)
return typeof res === 'object' ? res : obj
}
组合继承
function SuperType(name) {
this.name = name
this.colors = ['red']
}
SuperType.prototype.getName = function() {
console.log('getName', this.name)
}function SubType(name, age) {
SuperType.call(this, name)
this.age = age
}
// 减少父类少调用一次
function inheritPrototype(subType, superType) {
let prototype = Object.create(superType.prototype)
prototype.constructor = subType
subType.prototype = prototype
}inheritPrototype(SubType, SuperType)
// 扩展原型方法
SubType.prototype.getAge = function() {
console.log('getAge', this.age)
}
JSONP的实现
// 客户端
function jsonp({url, params, callback}) {
return new Promise((resolve, reject) => {
//创建script标签
let script = document.createElement('script');
//将回调函数挂在 window 上
window[callback] = function(data) {
resolve(data);
//代码执行后,删除插入的script标签
document.body.removeChild(script);
}
//回调函数加在请求地址上
params = {...params, callback} //wb=b&callback=show
let arrs = [];
for(let key in params) {
arrs.push(`${key}=${params[key]}`);
}
script.src = https://www.it610.com/article/`${url}?${arrs.join('&')}`;
document.body.appendChild(script);
});
}// 使用
function show(data) {
console.log(data);
}
jsonp({
url: 'http://localhost:3000/show',
params: {
//code
},
callback: 'show'
}).then(data => {
console.log(data);
});
// 服务端
//express启动一个后台服务
let express = require('express');
let app = express();
app.get('/show', (req, res) => {
let {callback} = req.query;
//获取传来的callback函数名,callback是key
res.send(`${callback}('Hello!')`);
});
app.listen(3000);
柯里化&组合
// 柯里化
function curry(fn) {
return function next(...args) {
if (args.length < fn.length) {
return function(..._args) {
return next(...args.concat(..._args))
}
}
return fn(...args)
}
}
function add(a, b, c) {
return a + b + c
}const newAdd = curry(add)
console.log(newAdd(1)(2)(3))
console.log(newAdd(1,2)(3))
console.log(newAdd(1)(2,3))
console.log(newAdd(1,2,3))// 组合
function compose(...fns) {
return function(...args) {
return fns.reverse().reduce((acc, cur) => {
return typeof acc === 'function' ? cur(acc(...args)) : cur(acc)
})
}
}function fn1(x){
return x + 1
}function fn2(x){
return x * 10
}function fn3(x){
return x - 1
}
var fn = compose(fn1, fn2, fn3)
console.log(fn(2))
redux
function createStore(reducer, prePayload) {let currentState = prePayload;
let listeners = [];
function getState() {
return currentState;
}function dispatch(action) {
currentState = reducer(currentState, action);
for(let i = 0;
i < listeners.length;
i++) {
listeners[i]();
}
}function subscribe(func) {
let isSubscribed = false;
if (typeof func === 'function') {
if (!listeners.includes(func)) {
listeners.push(func);
isSubscribed = true;
}
}return function unSubscribe() {
if (!isSubscribed) {
return;
}let index = listeners.indexOf(func);
listeners.splice(index, 1);
}
}dispatch({type: 'INIT'});
return {
getState,
dispatch,
subscribe,
}
}export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers);
return function combine(state = {}, action) {let nextState = {};
let isChanged = false;
for(let i = 0;
i < reducerKeys.length;
i++) {
let key = reducerKeys[i];
let reducer = reducers[key];
let stateForKey = state[key];
nextState[key] = reducer(stateForKey, action);
isChanged = isChanged || nextState !== state;
}return isChanged ? nextState : state;
}
}
推荐阅读
- Promise面试题思考延伸
- 面试官居然要我用JS代码计算LocalStorage容量!
- [记录] TypeError: Invalid attempt to spread non-iterable instance.
- 《重构 JavaScript》读后感和部分摘录
- DNS域名解析过程简述
- 如何实现一个简单的发布订阅模式
- 选择篇(041)-下面代码的输出是什么?
- 首屏时间,你说你优化了,那你倒是计算出给给我看啊!
- 选择篇(021)-下面代码的输出是什么?