一些重要 API 的实现
new
new
操作符行为如下:
- 创建一个全新的空对象。
- 将对象的
__proto__
指向构造函数的prototype
。 - 将构造函数的
this
绑定到该对象上并执行 - 如果执行结果返回的是一个对象,就正常返回;如果不是就返回全新的对象。
function customNew(constructor, ...args) {
if (typeof constructor !== 'function') throw 'constructor is not a function'
const obj = Object.create(constructor.prototype)
// 等于
// const obj = {}
// obj.__proto__ = constructor.prototype
const res = constructor.apply(obj, args)
return res instanceof Object ? res : obj
}
Object.create()
Object.create
接受两个参数:
proto
是需要继承的原型对象。propertiesObject
是需要添加到原型对象中的属性集合。该属性遵从defineProperties
第二个参数的格式。
在第一个参数为 null
的时候,Object.create
会返回一个没有 __proto__
属性(为 null
)的对象。
在第一个参数不为 null
时,生成对象的 __proto__
就会指向传入的 proto
。
function customCreate(proto, propertiesObject = {}) {
if (typeof proto !== 'object') throw 'Object prototype may only be an Object or null'
const obj = {}
obj.__proto__ = proto
Object.defineProperties(obj, propertiesObject)
return obj
}
instanceof
instanceof
会检测左边的对象是否为右边对象的实例,实际上就是判断:
left.__proto__ === right.prototype
并且会沿着原型链寻找。
function F() {}
const obj = new F()
console.log(obj instanceof F) // true
console.log(obj instanceof Object) // true
因此我们需要利用循环沿着原型链向上寻找:
function customInstanceof(left, right) {
let proto = left.__proto__
while (true) {
if (proto === null) return false
if (proto === right.prototype) return true
proto = proto.__proto__
}
}
Promise.all
Promise.customAll = function (promises) {
/**
* 将数组中的非Promise转换为Promise
* 只要有一个Promise转换为rejected,返回该错误信息
* 全部fulfilled,返回结果数组
*/
return new Promise((resolve, reject) => {
const result = []
let count = 0
promises.forEach((promise, index) => {
const cpyPromise = promise
if (Object.prototype.toString.call(cpyPromise).slice(8, -1) !== 'Object') {
promise = new Promise((resolve) => {
resolve(cpyPromise)
})
}
promise.then(
(res) => {
result[index] = res
count++
if (count === promises.length) {
resolve(result)
}
},
(err) => {
reject(err)
}
)
})
})
}
compose
实现以下功能:
function fn1(x) {
return x + 1
}
function fn2(x) {
return x + 2
}
function fn3(x) {
return x + 3
}
function fn4(x) {
return x + 4
}
const a = compose(fn1, fn2, fn3, fn4)
console.log(a(1)) // 1+4+3+2+1=11
实现如下:
function compose(...fns) {
return function (x) {
return fns.reduce((acc, cur) => {
return cur(acc)
}, x)
}
}
扁平化数组
function flat(arr) {
const result = []
for (let item of arr) {
if (Array.isArray(item)) {
result.push(...flat(item))
} else {
result.push(item)
}
}
return result
}
Promise.race
Promise.customRace = function (promises) {
/**
* 将数组中的非Promise转换为Promise
* 只要有一个Promise的状态改变,就返回该Promise的结果
*/
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
const cpyPromise = promise
if (Object.prototype.toString.call(cpyPromise).slice(8, -1) !== 'Object') {
promise = new Promise((resolve) => {
resolve(cpyPromise)
})
}
promise.then(
(res) => {
resolve(res)
},
(err) => {
reject(err)
}
)
})
})
}
String.prototype.trim
String.prototype.customTrim = function () {
return this.replace(/^\s+|\s+$/g, '')
// or
// return this.replace(/^\s+(.*?)\s+$/, '$1')
}
DeepClone
function deepClone(origin, target, hash = new WeakMap()) {
//origin:要被拷贝的对象
// 需要完善,克隆的结果和之前保持相同的所属类
const target = target || {}
// 处理特殊情况
if (origin == null) return origin //null 和 undefined 都不用处理
if (origin instanceof Date) return new Date(origin)
if (origin instanceof RegExp) return new RegExp(origin)
if (typeof origin !== 'object') return origin // 普通常量直接返回
// 防止对象中的循环引用爆栈,把拷贝过的对象直接返还即可
if (hash.has(origin)) return hash.get(origin)
hash.set(origin, target) // 制作一个映射表
// 拿出所有属性,包括可枚举的和不可枚举的,但不能拿到symbol类型
const props = Object.getOwnPropertyNames(origin)
props.forEach((prop, index) => {
if (origin.hasOwnProperty(prop)) {
if (typeof origin[prop] === 'object') {
if (Object.prototype.toString.call(origin[prop]) == '[object Array]') {
//数组
target[prop] = []
deepClone(origin[prop], target[prop], hash)
} else if (Object.prototype.toString.call(origin[prop]) == '[object Object]') {
//普通对象
target[prop] = {}
deepClone(origin[prop], target[prop], hash)
} else if (origin[prop] instanceof Date) {
// 处理日期对象
target[prop] = new Date(origin[prop])
} else if (origin[prop] instanceof RegExp) {
// 处理正则对象
target[prop] = new RegExp(origin[prop])
} else {
//null
target[prop] = null
}
} else if (typeof origin[prop] === 'function') {
const _copyFn = function (fn) {
const result = new Function('return ' + fn)()
for (let i in fn) {
deepClone[(fn[i], result[i], hash)]
}
return result
}
target[prop] = _copyFn(origin[prop])
} else {
//除了object、function,剩下都是直接赋值的原始值
target[prop] = origin[prop]
}
}
})
// 单独处理symbol
const symKeys = Object.getOwnPropertySymbols(origin)
if (symKeys.length) {
symKeys.forEach((symKey) => {
target[symKey] = origin[symKey]
})
}
return target
}
setInterval
const customSetInterval = (callback, delay, ...args) => {
let timerId = null
const func = () => {
timerId = setTimeout(
() => {
func()
callback()
},
delay,
...args
)
}
func()
return () => clearTimeout(timerId)
}
setTimeout
const customSetTimeout = (callback, delay, ...args) => {
let timerId = null
timerId = setInterval(
() => {
callback()
clearInterval(timerId)
},
delay,
...args
)
return () => clearInterval(timerId)
}
判断循环引用
const isCyclic = (obj) => {
let stackSet = new Set()
let detected = false
const detect = (obj) => {
if (obj && typeof obj != 'object') {
return
}
if (stackSet.has(obj)) {
return (detected = true)
}
stackSet.add(obj)
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
detect(obj[key])
}
}
stackSet.delete(obj)
}
detect(obj)
return detected
}
并行限制的 Promise
JS 实现一个带并发限制的异步调度器 Scheduler
,保证同时运行的任务最多有两个。
class Scheduler {
constructor() {
this.queue = []
this.maxCount = 2
this.runCount = 0
}
// promiseCreator执行后返回的是一个Promise
add(promiseCreator) {
// 小于等于2,直接执行
this.queue.push(promiseCreator)
this.runQueue()
}
runQueue() {
// 队列中还有任务才会被执行
if (this.queue.length && this.runCount < this.maxCount) {
// 执行先加入队列的函数
const promiseCreator = this.queue.shift()
// 开始执行任务 计数+1
this.runCount += 1
promiseCreator().then(() => {
// 任务执行完毕,计数-1
this.runCount -= 1
this.runQueue()
})
}
}
}
const timeout = (time) => {
return 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')
发布订阅模式
class EventEmitter {
constructor() {
this.events = {}
}
// 事件监听
on(evt, callback, ctx) {
if (!this.events[evt]) {
this.events[evt] = []
}
this.events[evt].push(callback)
return this
}
// 发布事件
emit(evt, ...payload) {
const callbacks = this.events[evt]
if (callbacks) {
callbacks.forEach((cb) => cb.apply(this, payload))
}
return this
}
// 删除订阅
off(evt, callback) {
// 啥都没传,所有的事件都取消
if (typeof evt === 'undefined') {
delete this.events
} else if (typeof evt === 'string') {
// 删除指定事件的回调
if (typeof callback === 'function') {
this.events[evt] = this.events[evt].filter((cb) => cb !== callback)
} else {
// 删除整个事件
delete this.events[evt]
}
}
return this
}
// 只进行一次的事件订阅
once(evt, callback, ctx) {
const proxyCallback = (...payload) => {
callback.apply(ctx, payload)
// 回调函数执行完成之后就删除事件订阅
this.off(evt, proxyCallback)
}
this.on(evt, proxyCallback, ctx)
}
}
防抖
const debounce = function (func, delay) {
let timer = null
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, delay)
}
}
节流
const throttle = function (func, delay) {
let timer = null
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
func.apply(this, args)
timer = null
}, delay)
}
}
}
柯里化
const curry = (func, ...args) => {
const fnLen = func.length
return function (...innerArgs) {
innerArgs = args.concat(innerArgs)
if (innerArgs.length < fnLen) {
return curry(func, ...innerArgs)
} else {
func.apply(this, innerArgs)
}
}
}