// 原文链接:https://juejin.cn/post/6856213486633304078
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
function Promise(executor) {
const self = this
self.callbacks = []
self.state = PENDING
self.data = undefined
// 执行callbacks里的函数,并保存data,并将当前promise状态改为resolved
function resolve(val) {
// Promise的状态只能改变一次
if (self.state !== PENDING) {
return
}
self.data = val
self.state = RESOLVED
if (self.callbacks.length > 0) {
self.callbacks.forEach(cb => {
setTimeout(() => {
cb.onResolved(self.data)
});
})
}
}
function reject(err) {
if (self.state !== PENDING) {
return
}
self.data = err
self.state = REJECTED
if (self.callbacks.length > 0) {
self.callbacks.forEach(cb => {
setTimeout(() => {
cb.onRejected(self.data)
});
})
}
}
// 执行Promise调用传进来的函数
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
Promise.prototype.then = function (onResolved, onRejected) {
const self = this
// .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。值传透可以理解为,
// 当传入then的不是函数的时候,这个then是无效的。而实际原理上其实是当then中传入的不算函数,
// 则这个then返回的promise的data,将会保存上一个的promise.data。这就是发生值穿透的原因。
// 而且每一个无效的then所返回的promise的状态都为resolved。
onResolved = typeof onResolved === 'function' ? onResolved : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
return new Promise((resolve, reject) => {
// 调用指定回调函数的处理,根据执行结果。改变return的promise状态
function handle(callback) {
try {
const result = callback(self.data)
if (result instanceof Promise) {
// 2. 如果回调函数返回的是promise,return的promise的结果就是这个promise的结果
result.then(
value => { resolve(value) },
reason => { reject(reason) }
)
} else {
// 1. 如果回调函数返回的不是promise,return的promise的状态是resolved,value就是返回的值。
resolve(result)
}
} catch (e) {
// 3.如果执行onResolved的时候抛出错误,则返回的promise的状态为rejected
reject(e)
}
}
if (self.state === PENDING) {
self.callbacks.push({
onResolved: () => handle(onResolved),
onRejected: () => handle(onRejected)
})
} else if (self.state === RESOLVED) {
setTimeout(() => {
handle(onResolved)
});
} else {
setTimeout(() => {
handle(onRejected)
});
}
})
}
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected)
}
// 返回一个指定结果的promise对象
// 有这三种情况:和上面then有点像
// Promise.resolve(1)
// Promise.resolve(Promise.resolve(1))
// Promise.resolve(Promise.reject(1))
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(
val => resolve(val),
err => reject(err)
)
} else {
resolve(value)
}
})
}
// 返回一个指定reason的失败状态的promise对象
Promise.reject = function (value) {
return new Promise((resolve, reject) => {
reject(value)
})
}
// 返回一个promise对象,只有当所有promise都成功时返回的promise状态才成功
Promise.all = function (promises) {
const values = []
let resolveCount = 0
return new Promise((resolve, reject) => {
// 把不是promise的数字包装成promise
promises.forEach((p, i) => {
Promise.resolve(p).then(
res => {
resolveCount++
values.push(res)
if (resolveCount === promises.length) {
resolve(values)
}
},
// 如果遍历到promise状态为reject就直接返回reject的promise
err => {
reject(err)
}
)
})
})
}
// 返回一个promise对象,状态由第一个完成的promise决定
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach((p, i) => {
Promise.resolve(p).then(
val => {
// 只要有一个成功,返回的promise的状态九尾resolved
resolve(val)
}, err => {//只要有一个失败,return的promise状态就为reject
reject(err)
}
)
})
})
}
// Promise.any 的规则是这样:
// 空数组或者所有 Promise 都是 rejected,则返回状态是 rejected 的新 Promsie,且值为 AggregateError 的错误;
// 只要有一个是 fulfilled 状态的,则返回第一个是 fulfilled 的新实例;
// 其他情况都会返回一个 pending 的新实例;
Promise.any = function (promiseArr) {
let index = 0
return new Promise((resolve, reject) => {
if (promiseArr.length === 0) return
promiseArr.forEach((p, i) => {
Promise.resolve(p).then(
val => {
resolve(val)
}, err => {
index++
if (index === promiseArr.length) {
reject(new AggregateError('All promises were rejected'))
}
})
})
})
}
Promise.finally = function(callback) {
return this.then(res => {
callback()
return res
}, err => {
callback()
throw err
})
}
真实dom虚拟dom互转
虚拟dom转真实dom:
{
tag: 'DIV',
attrs:{
id:'app'
},
children: [
{
tag: 'SPAN',
children: [
{ tag: 'A', children: [] }
]
},
{
tag: 'SPAN',
children: [
{ tag: 'A', children: [] },
{ tag: 'A', children: [] }
]
}
]
}
把上诉虚拟Dom转化成下方真实Dom
<div id="app">
<span>
<a></a>
</span>
<span>
<a></a>
<a></a>
</span>
</div>
function render(vnode) {
if (typeof vnode === 'number') {
vnode = String(vnode)
}
if (typeof vnode === 'string') {
return document.createTextNode(vnode)
}
const dom = document.createElement(vnode.tag)
if(vnode.attrs){
Reflect.ownKeys(vnode.attrs).forEach((key)=>{
const val = vnode.attrs[key]
dom.setAttribute(key, val)
})
}
vnode.children.forEach((child) => {
dom.appendChild(render(child))
})
return dom
}
真实dom转虚拟dom:
function domToVdom (domTree){
const vdom = {}
vdom.tag = domTree.tagName
vdom.children = []
const children = Array.prototype.slice.call(domTree.children)
children.forEach((child)=>{
vdom.children.push(domToVdom(child))
})
return vdom
}
发布订阅模式
class Event {
constructor() {
// 储存事件的数据结构
// 为查找迅速, 使用对象(字典)
this._cache = {}
}
// 绑定
on(type, callback) {
// 为了按类查找方便和节省空间
// 将同一类型事件放到一个数组中
// 这里的数组是队列, 遵循先进先出
// 即新绑定的事件先触发
let fns = (this._cache[type] = this._cache[type] || [])
if (fns.indexOf(callback) === -1) {
fns.push(callback)
}
return this
}
// 触发
// emit
trigger(type, data) {
let fns = this._cache[type]
if (Array.isArray(fns)) {
fns.forEach((fn) => {
fn(data)
})
}
return this
}
// 解绑
off(type, callback) {
let fns = this._cache[type]
if (Array.isArray(fns)) {
if (callback) {
let index = fns.indexOf(callback)
if (index !== -1) {
fns.splice(index, 1)
}
} else {
// 全部清空
fns.length = 0
}
}
return this
}
}
实现数组原型方法
map
Array.prototype.myMap = function(cb, thisArgs){
if(typeof cb !== 'function'){
throw new TypeError(cb + 'is not a function~')
}
const target = this
const result = []
for(let i = 0; i < target.length; i ++){
const res = cb.call(thisArgs, target[i], i, target)
result.push(res)
}
return result
}
forEach
Array.prototype.myForEach = function (cb, thisArgs) {
if(typeof cb !== 'function'){
throw new TypeError(cb + 'is not a function~')
}
const target = this
for(let i = 0; i < target.length; i ++){
cb.call(thisArgs, target[i], i, target)
}
}
filter
Array.prototype.myFilter = function(cb, thisArgs){
if(typeof cb !== 'function'){
throw new TypeError(cb + 'is not a function~')
}
const target = this
const result = []
for(let i = 0; i < target.length; i ++){
const res = cb.call(thisArgs, target[i], i, target)
if(res) result.push(target[i])
}
return result
}
reduce
Array.prototype.myReduce = function (cb, initialVal){
if(typeof cb !== 'function'){
throw new TypeError(cb + 'is not a function~')
}
let target = this, result
if(initialVal){
result = initialVal
}else{
if(target.length > 0){
result = target[0]
}else{
throw new TypeError( 'Reduce of empty array with no initial value' );
}
}
for(let i = 1; i < target.length; i ++){
result = cb(result, target[i], i, target)
}
return result
}
Object.is
Object.is不会转换被比较的两个值的类型,这点和=更为相似,他们之间也存在一些区别。 1. NaN在=中是不相等的,而在Object.is中是相等的 2. +0和-0在===中是相等的,而在Object.is中是不相等的
Object.prototype.myIs = function (x, y){
if(x === y){
// x和y相等那就只需要处理 +0和-0的情况
return x !== 0 || 1 / x === 1 / y
}
// 不相等就只需要处理NaN的情况
return x !== x && y !== y
}
分片思想解决大数据量渲染问题
原理就是利用 window.requestAnimationFrame在每一帧插入一定的数量,然后递归执行
let total = 10000
const once = 20
const ul = document.createElement('ul')
document.body.appendChild(ul)
function loop(currTotal) {
window.requestAnimationFrame(() => {
const pageCount = Math.min(total, once)
for (let i = 0; i < pageCount; i++) {
const li = document.createElement('li')
li.innerText = '还有' + (currTotal - i)
ul.appendChild(li)
}
if(currTotal <= 0){
return
}
loop(currTotal - once)
})
}
loop(total)
判断对象是否有环
const isCycle = (obj) => {
const arr = [obj]
let flag = false
function cycle(o) {
const keys = Object.keys(o)
// 要遍历key不要遍历value,否则会出现不可遍历的情况
for (const key of keys) {
const temp = o[key]
if (typeof temp === 'object' && temp !== null) {
if (arr.indexOf(temp) >= 0) {
flag = true
return
}
arr.push(temp)
cycle(temp)
}
}
}
cycle(obj)
return flag
}
Promisify
以前我一直不知道promiseify怎么写,因为如果传入一个异步函数的话你是不知道异步函数啥时候
执行完毕的,也就不好进行promise的resolve,但是看了es6-promiseify的源码之后知道了解决方法:
因为node中的异步任务执行完毕都会执行传入的回调函数来返回错误和结果,所以我们可以自行传入
我们定义的回调函数,然后判断是否有错误执行reject,有结果执行resolve,这样也就知道了异步
函数的调用结束时机
const fs = require('fs')
// 源码
function promiseify(cb) {
function fn(...args) {
return new Promise((resolve, reject) => {
args.push(function (err, ret) {
if (err) {
return reject(err)
}
return resolve(ret)
})
cb.apply(this, args)
})
}
return fn
}
// 使用
const pfs = promiseify(fs.readFile)
pfs('./add.js', 'utf-8').then(ret => {
console.log(ret)
console.log('async')
}).catch((err) => {
console.log(err)
})
console.log('sync')
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)