共计 30381 个字符,预计需要花费 76 分钟才能阅读完成。
手写深度比拟 isEqual
思路:深度比拟两个对象,就是要深度比拟对象的每一个元素。=> 递归
-
递归退出条件:
- 被比拟的是两个值类型变量,间接用“===”判断
- 被比拟的两个变量之一为
null
,直接判断另一个元素是否也为null
-
提前结束递推:
- 两个变量
keys
数量不同 - 传入的两个参数是同一个变量
- 两个变量
- 递推工作:深度比拟每一个
key
function isEqual(obj1, obj2){
// 其中一个为值类型或 null
if(!isObject(obj1) || !isObject(obj2)){return obj1 === obj2;}
// 判断是否两个参数是同一个变量
if(obj1 === obj2){return true;}
// 判断 keys 数是否相等
const obj1Keys = Object.keys(obj1);
const obj2Keys = Object.keys(obj2);
if(obj1Keys.length !== obj2Keys.length){return false;}
// 深度比拟每一个 key
for(let key in obj1){if(!isEqual(obj1[key], obj2[key])){return false;}
}
return true;
}
实现 Object.is
Object.is
不会转换被比拟的两个值的类型,这点和 ===
更为类似,他们之间也存在一些区别
NaN
在===
中是不相等的,而在Object.is
中是相等的+0
和-
0 在===
中是相等的,而在Object.is
中是不相等的
Object.is = function (x, y) {if (x === y) {
// 当前情况下,只有一种状况是非凡的,即 +0 -0
// 如果 x !== 0,则返回 true
// 如果 x === 0,则须要判断 + 0 和 -0,则能够间接应用 1/+0 === Infinity 和 1/-0 === -Infinity 来进行判断
return x !== 0 || 1 / x === 1 / y;
}
// x !== y 的状况下,只须要判断是否为 NaN,如果 x!==x,则阐明 x 是 NaN,同理 y 也一样
// x 和 y 同时为 NaN 时,返回 true
return x !== x && y !== y;
};
实现观察者模式
观察者模式(基于公布订阅模式)有观察者,也有被观察者
观察者须要放到被观察者中,被观察者的状态变动须要告诉观察者 我变动了 外部也是基于公布订阅模式,收集观察者,状态变动后要被动告诉观察者
class Subject { // 被观察者 学生
constructor(name) {
this.state = 'happy'
this.observers = []; // 存储所有的观察者}
// 收集所有的观察者
attach(o){ // Subject. prototype. attch
this.observers.push(o)
}
// 更新被观察者 状态的办法
setState(newState) {
this.state = newState; // 更新状态
// this 指被观察者 学生
this.observers.forEach(o => o.update(this)) // 告诉观察者 更新它们的状态
}
}
class Observer{ // 观察者 父母和老师
constructor(name) {this.name = name}
update(student) {console.log('以后' + this.name + '被告诉了', '以后学生的状态是' + student.state)
}
}
let student = new Subject('学生');
let parent = new Observer('父母');
let teacher = new Observer('老师');
// 被观察者存储观察者的前提,须要先接收观察者
student. attach(parent);
student. attach(teacher);
student. setState('被欺侮了');
实现节流函数(throttle)
节流函数原理: 指频繁触发事件时,只会在指定的时间段内执行事件回调,即触发事件间隔大于等于指定的工夫才会执行回调函数。总结起来就是:事件,依照一段时间的距离来进行触发。
像 dom 的拖拽,如果用消抖的话,就会呈现卡顿的感觉,因为只在进行的时候执行了一次,这个时候就应该用节流,在肯定工夫内屡次执行,会晦涩很多
手写简版
应用工夫戳的节流函数会在第一次触发事件时立刻执行,当前每过 wait 秒之后才执行一次,并且最初一次触发事件不会被执行
工夫戳形式:
// func 是用户传入须要防抖的函数
// wait 是等待时间
const throttle = (func, wait = 50) => {
// 上一次执行该函数的工夫
let lastTime = 0
return function(...args) {
// 以后工夫
let now = +new Date()
// 将以后工夫和上一次执行函数工夫比照
// 如果差值大于设置的等待时间就执行函数
if (now - lastTime > wait) {
lastTime = now
func.apply(this, args)
}
}
}
setInterval(throttle(() => {console.log(1)
}, 500),
1
)
定时器形式:
应用定时器的节流函数在第一次触发时不会执行,而是在 delay 秒之后才执行,当最初一次进行触发后,还会再执行一次函数
function throttle(func, delay){
var timer = null;
returnfunction(){
var context = this;
var args = arguments;
if(!timer){timer = setTimeout(function(){func.apply(context, args);
timer = null;
},delay);
}
}
}
实用场景:
DOM
元素的拖拽性能实现(mousemove
)- 搜寻联想(
keyup
) - 计算鼠标挪动的间隔(
mousemove
) Canvas
模仿画板性能(mousemove
)- 监听滚动事件判断是否到页面底部主动加载更多
- 拖拽场景:固定工夫内只执行一次,避免超高频次触发地位变动
- 缩放场景:监控浏览器
resize
- 动画场景:防止短时间内屡次触发动画引起性能问题
总结
- 函数防抖:将几次操作合并为一次操作进行。原理是保护一个计时器,规定在 delay 工夫后触发函数,然而在 delay 工夫内再次触发的话,就会勾销之前的计时器而从新设置。这样一来,只有最初一次操作能被触发。
- 函数节流:使得肯定工夫内只触发一次函数。原理是通过判断是否达到肯定工夫来触发函数。
实现 Promise 相干办法
实现 Promise 的 resolve
实现 resolve 静态方法有三个要点:
- 传参为一个
Promise
, 则间接返回它。 - 传参为一个
thenable
对象,返回的Promise
会追随这个对象,采纳它的最终状态作为本人的状态。 - 其余状况,间接返回以该值为胜利状态的
promise
对象。
Promise.resolve = (param) => {if(param instanceof Promise) return param;
return new Promise((resolve, reject) => {if(param && param.then && typeof param.then === 'function') {
// param 状态变为胜利会调用 resolve,将新 Promise 的状态变为胜利,反之亦然
param.then(resolve, reject);
}else {resolve(param);
}
})
}
实现 Promise.reject
Promise.reject 中传入的参数会作为一个 reason 一成不变地往下传, 实现如下:
Promise.reject = function (reason) {return new Promise((resolve, reject) => {reject(reason);
});
}
实现 Promise.prototype.finally
后面的
promise
不论胜利还是失败,都会走到finally
中,并且finally
之后,还能够持续then
(阐明它还是一个 then 办法是要害),并且会将初始的promise
值一成不变的传递给前面的then
.
Promise.prototype.finally 最大的作用
finally
里的函数,无论如何都会执行,并会把后面的值一成不变传递给下一个then
办法中- 如果
finally
函数中有promise
等异步工作,会等它们全副执行结束,再联合之前的胜利与否状态,返回值
Promise.prototype.finally 六大状况用法
// 状况 1
Promise.resolve(123).finally((data) => { // 这里传入的函数,无论如何都会执行
console.log(data); // undefined
})
// 状况 2 (这里,finally 办法相当于做了两头解决,起一个过渡的作用)
Promise.resolve(123).finally((data) => {console.log(data); // undefined
}).then(data => {console.log(data); // 123
})
// 状况 3 (这里只有 reject,都会走到下一个 then 的 err 中)
Promise.reject(123).finally((data) => {console.log(data); // undefined
}).then(data => {console.log(data);
}, err => {console.log(err, 'err'); // 123 err
})
// 状况 4 (一开始就胜利之后,会期待 finally 里的 promise 执行结束后,再把后面的 data 传递到下一个 then 中)
Promise.resolve(123).finally((data) => {console.log(data); // undefined
return new Promise((resolve, reject) => {setTimeout(() => {resolve('ok');
}, 3000)
})
}).then(data => {console.log(data, 'success'); // 123 success
}, err => {console.log(err, 'err');
})
// 状况 5 (尽管一开始胜利,然而只有 finally 函数中的 promise 失败了,就会把其失败的值传递到下一个 then 的 err 中)
Promise.resolve(123).finally((data) => {console.log(data); // undefined
return new Promise((resolve, reject) => {setTimeout(() => {reject('rejected');
}, 3000)
})
}).then(data => {console.log(data, 'success');
}, err => {console.log(err, 'err'); // rejected err
})
// 状况 6 (尽管一开始失败,然而也要等 finally 中的 promise 执行完,能力把一开始的 err 传递到 err 的回调中)
Promise.reject(123).finally((data) => {console.log(data); // undefined
return new Promise((resolve, reject) => {setTimeout(() => {resolve('resolve');
}, 3000)
})
}).then(data => {console.log(data, 'success');
}, err => {console.log(err, 'err'); // 123 err
})
源码实现
Promise.prototype.finally = function (callback) {return this.then((data) => {
// 让函数执行 外部会调用办法,如果办法是 promise,须要期待它实现
// 如果以后 promise 执行时失败了,会把 err 传递到,err 的回调函数中
return Promise.resolve(callback()).then(() => data); // data 上一个 promise 的胜利态
}, err => {return Promise.resolve(callback()).then(() => {throw err; // 把之前的失败的 err,抛出去});
})
}
实现 Promise.all
对于 all 办法而言,须要实现上面的外围性能:
- 传入参数为一个空的可迭代对象,则间接进行
resolve
。 - 如果参数中有一个
promise
失败,那么Promise.all
返回的promise
对象失败。 - 在任何状况下,
Promise.all
返回的promise
的实现状态的后果都是一个数组
Promise.all = function(promises) {return new Promise((resolve, reject) => {let result = [];
let index = 0;
let len = promises.length;
if(len === 0) {resolve(result);
return;
}
for(let i = 0; i < len; i++) {// 为什么不间接 promise[i].then, 因为 promise[i]可能不是一个 promise
Promise.resolve(promise[i]).then(data => {result[i] = data;
index++;
if(index === len) resolve(result);
}).catch(err => {reject(err);
})
}
})
}
实现 promise.allsettle
MDN:
Promise.allSettled()
办法返回一个在所有给定的promise
都曾经
fulfilled或
rejected后的
promise,并带有一个对象数组,每个对象示意对应的
promise` 后果
当您有多个彼此不依赖的异步工作胜利实现时,或者您总是想晓得每个 promise
的后果时,通常应用它。
【译】
Promise.allSettled
跟Promise.all
相似, 其参数承受一个Promise
的数组, 返回一个新的Promise
, 惟一的不同在于, 其不会进行短路, 也就是说当 Promise 全副解决实现后咱们能够拿到每个Promise
的状态, 而不论其是否解决胜利。
用法 | 测试用例
let fs = require('fs').promises;
let getName = fs.readFile('./name.txt', 'utf8'); // 读取文件胜利
let getAge = fs.readFile('./age.txt', 'utf8');
Promise.allSettled([1, getName, getAge, 2]).then(data => {console.log(data);
});
// 输入后果
/*
[{ status: 'fulfilled', value: 1},
{status: 'fulfilled', value: 'zf'},
{status: 'fulfilled', value: '11'},
{status: 'fulfilled', value: 2}
]
*/
let getName = fs.readFile('./name123.txt', 'utf8'); // 读取文件失败
let getAge = fs.readFile('./age.txt', 'utf8');
// 输入后果
/*
[{ status: 'fulfilled', value: 1},
{
status: 'rejected',
value: [Error: ENOENT: no such file or directory, open './name123.txt'] {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: './name123.txt'
}
},
{status: 'fulfilled', value: '11'},
{status: 'fulfilled', value: 2}
]
*/
实现
function isPromise (val) {return typeof val.then === 'function'; // (123).then => undefined
}
Promise.allSettled = function(promises) {return new Promise((resolve, reject) => {let arr = [];
let times = 0;
const setData = (index, data) => {arr[index] = data;
if (++times === promises.length) {resolve(arr);
}
console.log('times', times)
}
for (let i = 0; i < promises.length; i++) {let current = promises[i];
if (isPromise(current)) {current.then((data) => {setData(i, { status: 'fulfilled', value: data});
}, err => {setData(i, { status: 'rejected', value: err})
})
} else {setData(i, { status: 'fulfilled', value: current})
}
}
})
}
实现 Promise.race
race 的实现相比之下就简略一些,只有有一个 promise 执行完,间接 resolve 并进行执行
Promise.race = function(promises) {return new Promise((resolve, reject) => {
let len = promises.length;
if(len === 0) return;
for(let i = 0; i < len; i++) {Promise.resolve(promise[i]).then(data => {resolve(data);
return;
}).catch(err => {reject(err);
return;
})
}
})
}
实现一个简版 Promise
// 应用
var promise = new Promise((resolve,reject) => {if (操作胜利) {resolve(value)
} else {reject(error)
}
})
promise.then(function (value) {// success},function (value) {// failure})
function myPromise(constructor) {
let self = this;
self.status = "pending" // 定义状态扭转前的初始状态
self.value = undefined; // 定义状态为 resolved 的时候的状态
self.reason = undefined; // 定义状态为 rejected 的时候的状态
function resolve(value) {if(self.status === "pending") {
self.value = value;
self.status = "resolved";
}
}
function reject(reason) {if(self.status === "pending") {
self.reason = reason;
self.status = "rejected";
}
}
// 捕捉结构异样
try {constructor(resolve,reject);
} catch(e) {reject(e);
}
}
// 增加 then 办法
myPromise.prototype.then = function(onFullfilled,onRejected) {
let self = this;
switch(self.status) {
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}
var p = new myPromise(function(resolve,reject) {resolve(1)
});
p.then(function(x) {console.log(x) // 1
})
应用 class 实现
class MyPromise {constructor(fn) {this.resolvedCallbacks = [];
this.rejectedCallbacks = [];
this.state = 'PENDING';
this.value = '';
fn(this.resolve.bind(this), this.reject.bind(this));
}
resolve(value) {if (this.state === 'PENDING') {
this.state = 'RESOLVED';
this.value = value;
this.resolvedCallbacks.map(cb => cb(value));
}
}
reject(value) {if (this.state === 'PENDING') {
this.state = 'REJECTED';
this.value = value;
this.rejectedCallbacks.map(cb => cb(value));
}
}
then(onFulfilled, onRejected) {if (this.state === 'PENDING') {this.resolvedCallbacks.push(onFulfilled);
this.rejectedCallbacks.push(onRejected);
}
if (this.state === 'RESOLVED') {onFulfilled(this.value);
}
if (this.state === 'REJECTED') {onRejected(this.value);
}
}
}
Promise 实现 - 具体
- 能够把
Promise
看成一个状态机。初始是pending
状态,能够通过函数resolve
和reject
,将状态转变为resolved
或者rejected
状态,状态一旦扭转就不能再次变动。 then
函数会返回一个Promise
实例,并且该返回值是一个新的实例而不是之前的实例。因为Promise
标准规定除了pending
状态,其余状态是不能够扭转的,如果返回的是一个雷同实例的话,多个then
调用就失去意义了。- 对于
then
来说,实质上能够把它看成是flatMap
// 三种状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// promise 接管一个函数参数,该函数会立刻执行
function MyPromise(fn) {
let _this = this;
_this.currentState = PENDING;
_this.value = undefined;
// 用于保留 then 中的回调,只有当 promise
// 状态为 pending 时才会缓存,并且每个实例至少缓存一个
_this.resolvedCallbacks = [];
_this.rejectedCallbacks = [];
_this.resolve = function (value) {if (value instanceof MyPromise) {
// 如果 value 是个 Promise,递归执行
return value.then(_this.resolve, _this.reject)
}
setTimeout(() => { // 异步执行,保障执行程序
if (_this.currentState === PENDING) {
_this.currentState = RESOLVED;
_this.value = value;
_this.resolvedCallbacks.forEach(cb => cb());
}
})
};
_this.reject = function (reason) {setTimeout(() => { // 异步执行,保障执行程序
if (_this.currentState === PENDING) {
_this.currentState = REJECTED;
_this.value = reason;
_this.rejectedCallbacks.forEach(cb => cb());
}
})
}
// 用于解决以下问题
// new Promise(() => throw Error('error))
try {fn(_this.resolve, _this.reject);
} catch (e) {_this.reject(e);
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
var self = this;
// 标准 2.2.7,then 必须返回一个新的 promise
var promise2;
// 标准 2.2.onResolved 和 onRejected 都为可选参数
// 如果类型不是函数须要疏忽,同时也实现了透传
// Promise.resolve(4).then().then((value) => console.log(value))
onResolved = typeof onResolved === 'function' ? onResolved : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : r => throw r;
if (self.currentState === RESOLVED) {return (promise2 = new MyPromise(function (resolve, reject) {
// 标准 2.2.4,保障 onFulfilled,onRjected 异步执行
// 所以用了 setTimeout 包裹下
setTimeout(function () {
try {var x = onResolved(self.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (reason) {reject(reason);
}
});
}));
}
if (self.currentState === REJECTED) {return (promise2 = new MyPromise(function (resolve, reject) {setTimeout(function () {
// 异步执行 onRejected
try {var x = onRejected(self.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (reason) {reject(reason);
}
});
}));
}
if (self.currentState === PENDING) {return (promise2 = new MyPromise(function (resolve, reject) {self.resolvedCallbacks.push(function () {
// 思考到可能会有报错,所以应用 try/catch 包裹
try {var x = onResolved(self.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (r) {reject(r);
}
});
self.rejectedCallbacks.push(function () {
try {var x = onRejected(self.value);
resolutionProcedure(promise2, x, resolve, reject);
} catch (r) {reject(r);
}
});
}));
}
};
// 标准 2.3
function resolutionProcedure(promise2, x, resolve, reject) {
// 标准 2.3.1,x 不能和 promise2 雷同,防止循环援用
if (promise2 === x) {return reject(new TypeError("Error"));
}
// 标准 2.3.2
// 如果 x 为 Promise,状态为 pending 须要持续期待否则执行
if (x instanceof MyPromise) {if (x.currentState === PENDING) {x.then(function (value) {
// 再次调用该函数是为了确认 x resolve 的
// 参数是什么类型,如果是根本类型就再次 resolve
// 把值传给下个 then
resolutionProcedure(promise2, value, resolve, reject);
}, reject);
} else {x.then(resolve, reject);
}
return;
}
// 标准 2.3.3.3.3
// reject 或者 resolve 其中一个执行过得话,疏忽其余的
let called = false;
// 标准 2.3.3,判断 x 是否为对象或者函数
if (x !== null && (typeof x === "object" || typeof x === "function")) {
// 标准 2.3.3.2,如果不能取出 then,就 reject
try {
// 标准 2.3.3.1
let then = x.then;
// 如果 then 是函数,调用 x.then
if (typeof then === "function") {
// 标准 2.3.3.3
then.call(
x,
y => {if (called) return;
called = true;
// 标准 2.3.3.3.1
resolutionProcedure(promise2, y, resolve, reject);
},
e => {if (called) return;
called = true;
reject(e);
}
);
} else {
// 标准 2.3.3.4
resolve(x);
}
} catch (e) {if (called) return;
called = true;
reject(e);
}
} else {
// 标准 2.3.4,x 为根本类型
resolve(x);
}
}
实现 Promisify
const fs = require('fs')
const path = require('path')
// node 中应用
// const fs = require('fs').promises 12.18 版
// const promisify = require('util').promisify
// 包装 node api promise 化 典型的高级函数
const promisify = fn=>{return (...args)=>{return new Promise((resolve,reject)=>{fn(...args, (err,data)=>{if(err) {reject(err)
}
resolve(data)
})
})
}
}
// const read = promisify(fs.readFile)
// read(path.join(__dirname, './promise.js'), 'utf8').then(d=>{// console.log(d)
// })
// promise 化 node 所有 api
const promisifyAll = target=>{Reflect.ownKeys(target).forEach(key=>{if(typeof target[key] === 'function') {target[key+'Async'] = promisify(target[key])
}
})
return target
}
// promise 化 fs 下的函数
const promisifyNew = promisifyAll(fs)
promisifyNew.readFileAsync(path.join(__dirname, './promise.js'), 'utf8').then(d=>{console.log(d)
})
module.exports = {
promisify,
promisifyAll
}
残缺实现 Promises/A+ 标准
/**
* Promises/A+ 标准 实现一个 promise
* https://promisesaplus.com/
*/
const EMUM = {
PENDING: 'PENDING',
FULFILLED: 'FULFILLED',
REJECTED: 'REJECTED'
}
// x 返回值
// promise2 then 的时候 new 的 promise
// promise2 的 resolve, reject
const resolvePromise = (x, promise2, resolve, reject)=>{
// 解析 promise 的值解析 promise2 是胜利还是失败 传递到上层 then
if(x === promise2) {reject(new TypeError('类型谬误'))
}
// 这里的 x 如果是一个 promise 的话 可能是其余的 promise,可能调用了胜利 又调用了失败
// 避免 resolve 的时候 又 throw err 抛出异样到 reject 了
let called
// 如果 x 是 promise 那么就采纳他的状态
// 有 then 办法是 promise
if(typeof x === 'object' && typeof x!== null || typeof x === 'function') {
// x 是对象或函数
try {
let then = x.then // 缓存,不必屡次取值
if(typeof then === 'function') {
// 是 promise,调用 then 办法外面有 this,须要传入 this 为 x 能力取到 then 办法外面的值 this.value
then.call(x, y=>{// 胜利
// y 值可能也是一个 promise 如 resolve(new Promise()) 此时的 y ==new Promise()
// 递归解析 y,直到拿到一般的值 resolve(x 进来)
if(called) return;
called = true;
resolvePromise(y, promise2, resolve, reject)
},r=>{// 一旦失败间接失败
if(called) return;
called = true;
reject(r)
})
} else {
// 一般对象不是 promise
resolve(x)
}
} catch (e) {
// 对象取值可能报错,用 defineProperty 定义 get 抛出异样
if(called) return;
called = true;
reject(e)
}
} else {
// x 是一般值
resolve(x) // 间接胜利
}
}
class myPromise {constructor(executor) {
this.status = EMUM.PENDING // 以后状态
this.value = undefined // resolve 接管值
this.reason = undefined // reject 失败返回值
/**
* 同一个 promise 能够 then 屡次(公布订阅模式)
* 调用 then 时 以后状态是期待态,须要将以后胜利或失败的回调寄存起来(订阅)* 调用 resolve 时 将订阅函数进行执行(公布)*/
// 胜利队列
this.onResolvedCallbacks = []
// 失败队列
this.onRejectedCallbacks = []
const resolve = value =>{
// 如果 value 是一个 promise,须要递归解析
// 如 myPromise.resolve(new myPromise()) 须要解析 value
if(value instanceof myPromise) {
// 不停的解析 直到值不是 promise
return value.then(resolve,reject)
}
if(this.status === EMUM.PENDING) {
this.status = EMUM.FULFILLED
this.value = value
this.onResolvedCallbacks.forEach(fn=>fn())
}
}
const reject = reason =>{if(this.status === EMUM.PENDING) {
this.status = EMUM.REJECTED
this.reason = reason
this.onRejectedCallbacks.forEach(fn=>fn())
}
}
try {executor(resolve,reject)
} catch(e) {reject(e)
}
}
then(onFulFilled, onRejected) {
// 透传 解决默认不传的状况
// new Promise((resolve,reject)=>{// resolve(1)
// }).then().then().then(d=>{})
// new Promise((resolve,reject)=>{// resolve(1)
// }).then(v=>v).then(v=>v).then(d=>{})
// new Promise((resolve,reject)=>{// reject(1)
// }).then().then().then(null, e=>{console.log(e)})
// new Promise((resolve,reject)=>{// reject(1)
// }).then(null,e=>{throw e}).then(null,e=>{throw e}).then(null,e=>{console.log(e)})
onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}
// 调用 then 创立一个新的 promise
let promise2 = new myPromise((resolve,reject)=>{
// 依据 value 判断是 resolve 还是 reject value 也可能是 promise
if(this.status === EMUM.FULFILLED) {setTimeout(() => {
try {
// 胜利回调后果
let x = onFulFilled(this.value)
// 解析 promise
resolvePromise(x, promise2,resolve,reject)
} catch (error) {reject(error)
}
}, 0);
}
if(this.status === EMUM.REJECTED) {setTimeout(() => {
try {let x = onRejected(this.reason)
// 解析 promise
resolvePromise(x, promise2,resolve,reject)
} catch (error) {reject(error)
}
}, 0);
}
// 用户还未调用 resolve 或 reject 办法
if(this.status === EMUM.PENDING) {this.onResolvedCallbacks.push(()=>{
try {let x = onFulFilled(this.value)
// 解析 promise
resolvePromise(x, promise2,resolve,reject)
} catch (error) {reject(error)
}
})
this.onRejectedCallbacks.push(()=>{
try {let x = onRejected(this.reason)
// 解析 promise
resolvePromise(x, promise2,resolve,reject)
} catch (error) {reject(error)
}
})
}
})
return promise2
}
catch(errCallback) {
// 等同于没有胜利,把失败放进去而已
return this.then(null, errCallback)
}
// myPromise.resolve 具备期待性能的 如果参数的 promise 会期待 promise 解析结束在向下执行
static resolve(val) {return new myPromise((resolve,reject)=>{resolve(val)
})
}
// myPromise.reject 间接将值返回
static reject(reason) {return new myPromise((resolve,reject)=>{reject(reason)
})
}
// finally 传入的函数 无论胜利或失败都执行
// Promise.reject(100).finally(()=>{console.log(1)}).then(d=>console.log('success',d)).catch(er=>console.log('faild',er))
// Promise.reject(100).finally(()=>new Promise()).then(d=>console.log(d)).catch(er=>)
finally(callback) {return this.then((val)=>{return myPromise.resolve(callback()).then(()=>val)
},(err)=>{return myPromise.resolve(callback()).then(()=>{throw err})
})
}
// Promise.all
static all(values) {return new myPromise((resolve,reject)=>{let resultArr = []
let orderIndex = 0
const processResultByKey = (value,index)=>{resultArr[index] = value
// 解决齐全部
if(++orderIndex === values.length) {resolve(resultArr) // 解决实现的后果返回去
}
}
for (let i = 0; i < values.length; i++) {const value = values[i];
// 是 promise
if(value && typeof value.then === 'function') {value.then((val)=>{processResultByKey(val,i)
},reject)
} else {
// 不是 promise 状况
processResultByKey(value,i)
}
}
})
}
static race(promises) {
// 采纳最新胜利或失败的作为后果
return new myPromise((resolve,reject)=>{for (let i = 0; i < promises.length; i++) {let val = promises[i]
if(val && typeof val.then === 'function') {
// 任何一个 promise 先调用 resolve 或 reject 就返回后果了 也就是返回执行最快的那个 promise 的后果
val.then(resolve,reject)
}else{
// 一般值
resolve(val)
}
}
})
}
}
/**
* ===== 测试用例 -====
*/
// let promise1 = new myPromise((resolve,reject)=>{// setTimeout(() => {// resolve('胜利')
// }, 900);
// })
// promise1.then(val=>{// console.log('success', val)
// },reason=>{// console.log('fail', reason)
// })
/**
* then 的应用形式 一般值象征不是 promise
*
* 1、then 中的回调有两个办法 胜利或失败 他们的后果返回(一般值)会传递给外层的下一个 then 中
* 2、能够在胜利或失败中抛出异样,走到下一次 then 的失败中
* 3、返回的是一个 promsie,那么会用这个 promise 的状态作为后果,会用 promise 的后果向下传递
* 4、错误处理,会默认先找离本人最新的错误处理,找不到就向下查找,找打了就执行
*/
// read('./name.txt').then(data=>{
// return '123'
// }).then(data=>{//}).then(null,err=>{//})
// // .catch(err=>{ // catch 就是没有胜利的 promise
// // })
/**
* promise.then 实现原理:通过每次返回一个新的 promise 来实现(promise 一旦胜利就不能失败,失败就不能胜利)*
*/
// function read(data) {// return new myPromise((resolve,reject)=>{// setTimeout(() => {// resolve(new myPromise((resolve,reject)=>resolve(data)))
// }, 1000);
// })
// }
// let promise2 = read({name: 'poetry'}).then(data=>{
// return data
// }).then().then().then(data=>{// console.log(data,'-data-')
// },(err)=>{// console.log(err,'-err-')
// })
// finally 测试
// myPromise
// .resolve(100)
// .finally(()=>{// return new myPromise((resolve,reject)=>setTimeout(() => {// resolve(100)
// }, 100))
// })
// .then(d=>console.log('finally success',d))
// .catch(er=>console.log(er, 'finally err'))
/**
* promise.all 测试
*
* myPromise.all 解决并发问题 多个异步并发获取最终的后果
*/
// myPromise.all([1,2,3,4,new myPromise((resolve,reject)=>{// setTimeout(() => {// resolve('ok1')
// }, 1000);
// }),new myPromise((resolve,reject)=>{// setTimeout(() => {// resolve('ok2')
// }, 1000);
// })]).then(d=>{// console.log(d,'myPromise.all.resolve')
// }).catch(err=>{// console.log(err,'myPromise.all.reject')
// })
// 实现 promise 中断请求
let promise = new Promise((resolve,reject)=>{setTimeout(() => {
// 模仿接口调用 ajax 调用超时
resolve('胜利')
}, 10000);
})
function promiseWrap(promise) {
// 包装一个 promise 能够管制原来的 promise 是胜利 还是失败
let abort
let newPromsie = new myPromise((resolve,reject)=>{abort = reject})
// 只有管制 newPromsie 失败,就能够管制被包装的 promise 走向失败
// Promise.race 任何一个先胜利或者失败 就能够取得后果
let p = myPromise.race([promise, newPromsie])
p.abort = abort
return p
}
let newPromise = promiseWrap(promise)
setTimeout(() => {
// 超过 3 秒超时
newPromise.abort('申请超时')
}, 3000);
newPromise.then(d=>{console.log('d',d)
}).catch(err=>{console.log('err',err)
})
// 应用 promises-aplus-tests 测试写的 promise 是否标准
// 全局装置 cnpm i -g promises-aplus-tests
// 命令行执行 promises-aplus-tests promise.js
// 测试入口 产生提早对象
myPromise.defer = myPromise.deferred = function () {let dfd = {}
dfd.promise = new myPromise((resolve,reject)=>{
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
// 提早对象用户
// ![](http://img-repo.poetries.top/images/20210509172817.png)
// promise 解决嵌套问题
// function readData(url) {// let dfd = myPromise.defer()
// fs.readFile(url, 'utf8', function (err,data) {// if(err) {// dfd.reject()
// }
// dfd.resolve(data)
// })
// return dfd.promise
// }
// readData().then(d=>{
// return d
// })
module.exports = myPromise
实现 apply 办法
思路: 利用
this
的上下文个性。apply
其实就是改一下参数的问题
Function.prototype.myApply = function(context = window, args) {
// this-->func context--> obj args--> 传递过去的参数
// 在 context 上加一个惟一值不影响 context 上的属性
let key = Symbol('key')
context[key] = this; // context 为调用的上下文,this 此处为函数,将这个函数作为 context 的办法
// let args = [...arguments].slice(1) // 第一个参数为 obj 所以删除, 伪数组转为数组
let result = context[key](...args); // 这里和 call 传参不一样
// 革除定义的 this 不删除会导致 context 属性越来越多
delete context[key];
// 返回后果
return result;
}
// 应用
function f(a,b){console.log(a,b)
console.log(this.name)
}
let obj={name:'张三'}
f.myApply(obj,[1,2]) //arguments[1]
参考 前端进阶面试题具体解答
实现 apply 办法
apply 原理与 call 很类似,不多赘述
// 模仿 apply
Function.prototype.myapply = function(context, arr) {var context = Object(context) || window;
context.fn = this;
var result;
if (!arr) {result = context.fn();
} else {var args = [];
for (var i = 0, len = arr.length; i < len; i++) {args.push("arr[" + i + "]");
}
result = eval("context.fn(" + args + ")");
}
delete context.fn;
return result;
};
实现 Array.isArray 办法
Array.myIsArray = function(o) {return Object.prototype.toString.call(Object(o)) === '[object Array]';
};
console.log(Array.myIsArray([])); // true
对象数组如何去重
依据每个对象的某一个具体属性来进行去重
const responseList = [{ id: 1, a: 1},
{id: 2, a: 2},
{id: 3, a: 3},
{id: 1, a: 4},
];
const result = responseList.reduce((acc, cur) => {const ids = acc.map(item => item.id);
return ids.includes(cur.id) ? acc : [...acc, cur];
}, []);
console.log(result); // -> [{ id: 1, a: 1}, {id: 2, a: 2}, {id: 3, a: 3} ]
实现 JSON.parse
var json = '{"name":"cxk","age":25}';
var obj = eval("(" + json + ")");
此办法属于黑魔法,极易容易被 xss 攻打,还有一种 new Function
大同小异。
实现 find 办法
find
接管一个办法作为参数,办法外部返回一个条件find
会遍历所有的元素,执行你给定的带有条件返回值的函数- 合乎该条件的元素会作为
find
办法的返回值 - 如果遍历完结还没有合乎该条件的元素,则返回
undefined
var users = [{id: 1, name: '张三'},
{id: 2, name: '张三'},
{id: 3, name: '张三'},
{id: 4, name: '张三'}
]
Array.prototype.myFind = function (callback) {// var callback = function (item, index) {return item.id === 4}
for (var i = 0; i < this.length; i++) {if (callback(this[i], i)) {return this[i]
}
}
}
var ret = users.myFind(function (item, index) {return item.id === 2})
console.log(ret)
手写 Promise.race
该办法的参数是 Promise 实例数组, 而后其 then 注册的回调办法是数组中的某一个 Promise 的状态变为 fulfilled 的时候就执行. 因为 Promise 的状态 只能扭转一次, 那么咱们只须要把 Promise.race 中产生的 Promise 对象的 resolve 办法, 注入到数组中的每一个 Promise 实例中的回调函数中即可.
Promise.race = function (args) {return new Promise((resolve, reject) => {for (let i = 0, len = args.length; i < len; i++) {args[i].then(resolve, reject)
}
})
}
手写防抖函数
函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则从新计时。这能够应用在一些点击申请的事件上,防止因为用户的屡次点击向后端发送屡次申请。
// 函数防抖的实现
function debounce(fn, wait) {
let timer = null;
return function() {
let context = this,
args = arguments;
// 如果此时存在定时器的话,则勾销之前的定时器从新记时
if (timer) {clearTimeout(timer);
timer = null;
}
// 设置定时器,使事件间隔指定事件后执行
timer = setTimeout(() => {fn.apply(context, args);
}, wait);
};
}
实现 every 办法
Array.prototype.myEvery=function(callback, context = window){
var len=this.length,
flag=true,
i = 0;
for(;i < len; i++){if(!callback.apply(context,[this[i], i , this])){
flag=false;
break;
}
}
return flag;
}
// var obj = {num: 1}
// var aa=arr.myEvery(function(v,index,arr){
// return v.num>=12;
// },obj)
// console.log(aa)
实现数组扁平化 flat 办法
题目形容: 实现一个办法使多维数组变成一维数组
let ary = [1, [2, [3, [4, 5]]], 6];
let str = JSON.stringify(ary);
第 0 种解决: 间接的调用
arr_flat = arr.flat(Infinity);
第一种解决
ary = str.replace(/(\[|\])/g, '').split(',');
第二种解决
str = str.replace(/(\[\]))/g, '');
str = '[' + str + ']';
ary = JSON.parse(str);
第三种解决:递归解决
let result = [];
let fn = function(ary) {for(let i = 0; i < ary.length; i++) }{let item = ary[i];
if (Array.isArray(ary[i])){fn(item);
} else {result.push(item);
}
}
}
第四种解决:用 reduce 实现数组的 flat 办法
function flatten(ary) {return ary.reduce((pre, cur) => {return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
}, []);
}
let ary = [1, 2, [3, 4], [5, [6, 7]]]
console.log(flatten(ary))
第五种解决:能用迭代的思路去实现
function flatten(arr) {if (!arr.length) return;
while (arr.some((item) => Array.isArray(item))) {arr = [].concat(...arr);
}
return arr;
}
// console.log(flatten([1, 2, [1, [2, 3, [4, 5, [6]]]]]));
第六种解决:扩大运算符
while (ary.some(Array.isArray)) {ary = [].concat(...ary);
}
数组去重办法汇总
首先: 我晓得多少种去重形式
1. 双层 for 循环
function distinct(arr) {for (let i=0, len=arr.length; i<len; i++) {for (let j=i+1; j<len; j++) {if (arr[i] == arr[j]) {arr.splice(j, 1);
// splice 会扭转数组长度,所以要将数组长度 len 和下标 j 减一
len--;
j--;
}
}
}
return arr;
}
思维: 双重
for
循环是比拟蠢笨的办法,它实现的原理很简略:先定义一个蕴含原始数组第一个元素的数组,而后遍历原始数组,将原始数组中的每个元素与新数组中的每个元素进行比对,如果不反复则增加到新数组中,最初返回新数组;因为它的工夫复杂度是O(n^2)
,如果数组长度很大,效率会很低
2. Array.filter() 加 indexOf/includes
function distinct(a, b) {let arr = a.concat(b);
return arr.filter((item, index)=> {//return arr.indexOf(item) === index
return arr.includes(item)
})
}
思维: 利用
indexOf
检测元素在数组中第一次呈现的地位是否和元素当初的地位相等,如果不等则阐明该元素是反复元素
3. ES6 中的 Set 去重
function distinct(array) {return Array.from(new Set(array));
}
思维: ES6 提供了新的数据结构 Set,Set 构造的一个个性就是成员值都是惟一的,没有反复的值。
4. reduce 实现对象数组去反复
var resources = [{ name: "张三", age: "18"},
{name: "张三", age: "19"},
{name: "张三", age: "20"},
{name: "李四", age: "19"},
{name: "王五", age: "20"},
{name: "赵六", age: "21"}
]
var temp = {};
resources = resources.reduce((prev, curv) => {
// 如果长期对象中有这个名字,什么都不做
if (temp[curv.name]) { }else {
// 如果长期对象没有就把这个名字加进去,同时把以后的这个对象退出到 prev 中
temp[curv.name] = true;
prev.push(curv);
}
return prev
}, []);
console.log("后果", resources);
这种办法是利用高阶函数
reduce
进行去重,这里只须要留神initialValue
得放一个空数组[],不然没法push
实现 Promise
var PromisePolyfill = (function () {
// 和 reject 不同的是 resolve 须要尝试开展 thenable 对象
function tryToResolve (value) {if (this === value) {
// 次要是避免上面这种状况
// let y = new Promise(res => setTimeout(res(y)))
throw TypeError('Chaining cycle detected for promise!')
}
// 依据标准 2.32 以及 2.33 对对象或者函数尝试开展
// 保障 S6 之前的 polyfill 也能和 ES6 的原生 promise 混用
if (value !== null &&
(typeof value === 'object' || typeof value === 'function')) {
try {
// 这里记录这次 then 的值同时要被 try 包裹
// 次要起因是 then 可能是一个 getter, 也也就是说
// 1. value.then 可能报错
// 2. value.then 可能产生副作用(例如屡次执行可能后果不同)
var then = value.then
// 另一方面, 因为无奈保障 then 的确会像预期的那样只调用一个 onFullfilled / onRejected
// 所以减少了一个 flag 来避免 resolveOrReject 被屡次调用
var thenAlreadyCalledOrThrow = false
if (typeof then === 'function') {
// 是 thenable 那么尝试开展
// 并且在该 thenable 状态扭转之前 this 对象的状态不变
then.bind(value)(
// onFullfilled
function (value2) {if (thenAlreadyCalledOrThrow) return
thenAlreadyCalledOrThrow = true
tryToResolve.bind(this, value2)()}.bind(this),
// onRejected
function (reason2) {if (thenAlreadyCalledOrThrow) return
thenAlreadyCalledOrThrow = true
resolveOrReject.bind(this, 'rejected', reason2)()}.bind(this)
)
} else {
// 领有 then 然而 then 不是一个函数 所以也不是 thenable
resolveOrReject.bind(this, 'resolved', value)()}
} catch (e) {if (thenAlreadyCalledOrThrow) return
thenAlreadyCalledOrThrow = true
resolveOrReject.bind(this, 'rejected', e)()}
} else {
// 根本类型 间接返回
resolveOrReject.bind(this, 'resolved', value)()}
}
function resolveOrReject (status, data) {if (this.status !== 'pending') return
this.status = status
this.data = data
if (status === 'resolved') {for (var i = 0; i < this.resolveList.length; ++i) {this.resolveList[i]()}
} else {for (i = 0; i < this.rejectList.length; ++i) {this.rejectList[i]()}
}
}
function Promise (executor) {if (!(this instanceof Promise)) {throw Error('Promise can not be called without new !')
}
if (typeof executor !== 'function') {
// 非标准 但与 Chrome 谷歌保持一致
throw TypeError('Promise resolver' + executor + 'is not a function')
}
this.status = 'pending'
this.resolveList = []
this.rejectList = []
try {executor(tryToResolve.bind(this), resolveOrReject.bind(this, 'rejected'))
} catch (e) {resolveOrReject.bind(this, 'rejected', e)()}
}
Promise.prototype.then = function (onFullfilled, onRejected) {
// 返回值穿透以及谬误穿透, 留神谬误穿透用的是 throw 而不是 return,否则的话
// 这个 then 返回的 promise 状态将变成 resolved 即接下来的 then 中的 onFullfilled
// 会被调用, 然而咱们想要调用的是 onRejected
if (typeof onFullfilled !== 'function') {onFullfilled = function (data) {return data}
}
if (typeof onRejected !== 'function') {onRejected = function (reason) {throw reason}
}
var executor = function (resolve, reject) {setTimeout(function () {
try {
// 拿到对应的 handle 函数解决 this.data
// 并以此为根据解析这个新的 Promise
var value = this.status === 'resolved'
? onFullfilled(this.data)
: onRejected(this.data)
resolve(value)
} catch (e) {reject(e)
}
}.bind(this))
}
// then 承受两个函数返回一个新的 Promise
// then 本身的执行永远异步与 onFullfilled/onRejected 的执行
if (this.status !== 'pending') {return new Promise(executor.bind(this))
} else {
// pending
return new Promise(function (resolve, reject) {this.resolveList.push(executor.bind(this, resolve, reject))
this.rejectList.push(executor.bind(this, resolve, reject))
}.bind(this))
}
}
// for prmise A+ test
Promise.deferred = Promise.defer = function () {var dfd = {}
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
// for prmise A+ test
if (typeof module !== 'undefined') {module.exports = Promise}
return Promise
})()
PromisePolyfill.all = function (promises) {return new Promise((resolve, reject) => {const result = []
let cnt = 0
for (let i = 0; i < promises.length; ++i) {promises[i].then(value => {
cnt++
result[i] = value
if (cnt === promises.length) resolve(result)
}, reject)
}
})
}
PromisePolyfill.race = function (promises) {return new Promise((resolve, reject) => {for (let i = 0; i < promises.length; ++i) {promises[i].then(resolve, reject)
}
})
}
实现 Ajax
步骤
- 创立
XMLHttpRequest
实例 - 收回 HTTP 申请
- 服务器返回 XML 格局的字符串
- JS 解析 XML,并更新部分页面
- 不过随着历史进程的推动,XML 曾经被淘汰,取而代之的是 JSON。
理解了属性和办法之后,依据 AJAX 的步骤,手写最简略的 GET 申请。
实现 bind 办法
bind
的实现比照其余两个函数稍微地简单了一点,波及到参数合并(相似函数柯里化),因为bind
须要返回一个函数,须要判断一些边界问题,以下是bind
的实现
bind
返回了一个函数,对于函数来说有两种形式调用,一种是间接调用,一种是通过new
的形式,咱们先来说间接调用的形式- 对于间接调用来说,这里抉择了
apply
的形式实现,然而对于参数须要留神以下状况:因为bind
能够实现相似这样的代码f.bind(obj, 1)(2)
,所以咱们须要将两边的参数拼接起来 - 最初来说通过
new
的形式,对于new
的状况来说,不会被任何形式扭转this
,所以对于这种状况咱们须要疏忽传入的this
简洁版本
- 对于一般函数,绑定
this
指向 - 对于构造函数,要保障原函数的原型对象上的属性不能失落
Function.prototype.myBind = function(context = window, ...args) {
// this 示意调用 bind 的函数
let self = this;
// 返回了一个函数,...innerArgs 为理论调用时传入的参数
let fBound = function(...innerArgs) {//this instanceof fBound 为 true 示意构造函数的状况。如 new func.bind(obj)
// 当作为构造函数时,this 指向实例,此时 this instanceof fBound 后果为 true,能够让实例取得来自绑定函数的值
// 当作为一般函数时,this 指向 window,此时后果为 false,将绑定函数的 this 指向 context
return self.apply(
this instanceof fBound ? this : context,
args.concat(innerArgs)
);
}
// 如果绑定的是构造函数,那么须要继承构造函数原型属性和办法:保障原函数的原型对象上的属性不失落
// 实现继承的形式: 应用 Object.create
fBound.prototype = Object.create(this.prototype);
return fBound;
}
// 测试用例
function Person(name, age) {console.log('Person name:', name);
console.log('Person age:', age);
console.log('Person this:', this); // 构造函数 this 指向实例对象
}
// 构造函数原型的办法
Person.prototype.say = function() {console.log('person say');
}
// 一般函数
function normalFun(name, age) {console.log('一般函数 name:', name);
console.log('一般函数 age:', age);
console.log('一般函数 this:', this); // 一般函数 this 指向绑定 bind 的第一个参数 也就是例子中的 obj
}
var obj = {
name: 'poetries',
age: 18
}
// 先测试作为结构函数调用
var bindFun = Person.myBind(obj, 'poetry1') // undefined
var a = new bindFun(10) // Person name: poetry1、Person age: 10、Person this: fBound {}
a.say() // person say
// 再测试作为一般函数调用
var bindNormalFun = normalFun.myBind(obj, 'poetry2') // undefined
bindNormalFun(12) // 一般函数 name: poetry2 一般函数 age: 12 一般函数 this: {name: 'poetries', age: 18}
留神:
bind
之后不能再次批改this
的指向,bind
屡次后执行,函数this
还是指向第一次bind
的对象
实现 instanceOf
// 模仿 instanceof
function instance_of(L, R) {
//L 示意左表达式,R 示意右表达式
var O = R.prototype; // 取 R 的显示原型
L = L.__proto__; // 取 L 的隐式原型
while (true) {if (L === null) return false;
if (O === L)
// 这里重点:当 O 严格等于 L 时,返回 true
return true;
L = L.__proto__;
}
}