new的过程及实现
function myNew(Fun, ...args) {
// 1.创建一个新对象。
const obj = {}
// 2.新对象的__proto__指向构造函数的原型对象。
obj.__proto__ = Fun.prototype
// 3.执行构造函数,构造函数this指向新对象,然后给新对象添加属性和方法。
const result = Fun.apply(obj, args)
// 4.构造函数返回值如果是对象,那就返回这个对象,否则返回新对象。
return result instanceof Object ? result : obj
}
this指向
优先级:new绑定可以和bind一起使用,new绑定优先级更高,但不能和apply和call一同使用;apply、call、bind这些显示绑定优先级大于隐式绑定。
特殊规则:如果apply、bind、call的第一个参数传入null或者undefined,那么显示绑定this指向window;箭头函数不会绑定this和arguments,它的this会在自身所处的作用域中寻找;间接函数调用时this指向window,以下代码。
var obj1 = {
name: "obj1",
foo: function () {
console.log(this)
}
}
var obj2 = {
name: "obj2"
};
obj2.bar = obj1.foo
obj2.bar(); //obj2
(obj2.bar = obj1.foo)(); //window
面试题
var name = 'window'
function Person (name) {
this.name = name
this.obj = {
name: 'obj',
foo1: function () {
return function () {
console.log(this.name)
}
},
foo2: function () {
return () => {
console.log(this.name)
}
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.obj.foo1()() // window
person1.obj.foo1.call(person2)() // window
person1.obj.foo1().call(person2) // person2
person1.obj.foo2()() // obj
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj
apply、call、bind实现
Function.prototype.myApply = function (target, args) {
target = (target === null || target === undefined) ? window : Object(target)
const fn = Symbol()
target[fn] = this
var result
if (!args) {
result = target[fn]();
} else {
result = target[fn](...args);
}
delete target[fn]
return result
}
Function.prototype.myCall = function (target, ...args) {
target = (target === null || target === undefined) ? window : Object(target)
const fn = Symbol()
target[fn] = this
var result = target[fn](...args);
delete target[fn]
return result
}
Function.prototype.myBind = function (target, ...args) {
target = (target === null || target === undefined) ? window : Object(target)
const fn = Symbol()
target[fn] = this
return function (...newargs) {
var result = target[fn](...[...args, ...newargs])
delete target[fn]
return result
}
}
柯里化实现
function curry(fn, args) {
let length = fn.length
args = args || []
return function () {
let totalArgs = [...args, ...arguments]
if (totalArgs.length >= length) {
return fn.apply(this, totalArgs)
} else {
return curry.call(this, fn, totalArgs)
}
}
}
function add(x, y, z) {
return x + y + z
}
var curryAdd = curry(add)
console.log(curryAdd(10)(20)(30))
原型、原型链
对象中存在内置属性[[prototype]],它可以通过obj.__proto__获取;所有函数上都有一个prototype的属性。在new创建对象的过程中,这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性也就是:obj.__proto__ = Fun.prototype。对象的__proto__指向函数原型对象;构造函数的prototype也指向这个原型对象;原型对象上存在constructor指回构造函数。Fun的原型对象也是个对象,因此它也有__proto__,指向的是Obejct的原型对象。Object的原型对象上的constructor指回Object构造函数。Object构造函数存在prototype属性指向Object原型对象,形成原型链。
js继承方式
- 原型链继承
//父
function Person() {
this.name = "why"
this.friends = []
Person.prototype.eating = function () {
console.log(this.name + " eating~")
}
}
//子
function Student() {
this.sno = 111
}
Student.prototype = new Person()
弊端:父类实例的属性的值是引用类型时,子类其中一个实例修改了这个值,其他子类实例的值也会被改变;子类在实例化时不能给父类的构造函数传参。
- 借用构造函数继承
//父
function Person(name, friends) {
this.name = name
this.friends = friends
this.eating = function () {
console.log(this.name + " eating~")
}
}
//子
function Student(name, friends, sno) {
Person.call(this, name, friends)
this.sno = sno
}
弊端:虽然解决了引用类型、传递参数的问题,但是子类无法继承父类原型对象上的方法,因此方法只能定义在父类构造函数中,会造成浪费内存。
- 组合继承
//父
function Person(name, friends) {
this.name = name
this.friends = friends
Person.prototype.eating = function () {
console.log(this.name + " eating~")
}
}
//子
function Student(name, friends, sno) {
Person.call(this, name, friends)//继承属性
this.sno = sno
}
Student.prototype = new Person()//继承原型上方法
弊端:解决了原型链继承和借用构造函数继承的弊端,但是会调用两次父类构造函数。
- 原型式继承
function object(o) {
function F() { }
F.prototype = o
return new F()
}
let person = {
name: '小明',
colors: ['red', 'blue']
}
let person1 = object(person)//相当于浅拷贝
person1.colors.push('green')
let person2 = object(person)
person1.colors.push('yellow')
console.log(person.colors) //['red','blue','green','yellow']
弊端:引用值存在共享问题;新实例的属性都是后面添加的,无每复用。
- 寄生式继承
var personObj = {
running: function () {
console.log("running")
}
}
function object(o) {
function F() { }
F.prototype = o
return new F()
}
function createStudent(name) {
var stu = object(personObj)//创建一个对象
stu.name = name //添加属性、方法来增强这个对象
stu.studying = function () {
console.log("studying~")
}
return stu //最终返回这个对象
}
弊端:每创建一个对象都需要在对象上添加方法,导致方法不能复用造成内存浪费。
- 寄生组合式继承
function createObject(o) {
function Fn() { }
Fn.prototype = o
return new Fn()
}
function inheritPrototype(SubType, SuperType) {
SubType.prototype = Objec.create(SuperType.prototype)//继承父类原型副本
Object.defineProperty(SubType.prototype, "constructor", {//constructor指回自身
enumerable: false,
configurable: true,
writable: true,
value: SubType
})
}
function Person(name, age, friends) {
this.name = name
this.age = age
this.friends = friends
Person.prototype.running = function () {
console.log("running~")
}
Person.prototype.eating = function () {
console.log("eating~")
}
}
function Student(name, age, friends, sno, score) {
Person.call(this, name, age, friends)//继承属性
this.sno = sno
this.score = score
}
弥补了组合继承的缺点。
事件系统实现
function EventBus() {
this._event = {}
EventBus.prototype.on = function (event, fn) {
if (Array.isArray(event)) {
event.forEach(e => this.on(e, fn))
} else {
this._event[event] ? this._event[event].push(fn) : this._event[event] = [fn]
}
}
EventBus.prototype.emit = function (event) {
let fns = this._event[event]
const args = [...arguments].slice(1)
if (fns) {
fns.forEach(fn => fn(...args))
}
}
EventBus.prototype.off = function (event, fn) {
if (event === undefined && fn === undefined) {
return this._event = {}
}
if (Array.isArray(event)) {
event.forEach(e => this.off(e, fn))
} else {
if (fn === undefined) {
this._event[event] = null
} else {
const fns = this._event[event] || []
for (var i = fns.length - 1; i >= 0; i--) {
if (fns[i] === fn || fns[i].fn == fn) {
fns.splice(i, 1)
}
}
}
}
}
EventBus.prototype.once = function (event, fn) {
const _this = this
function on() {
console.log(arguments)
_this.off(event, on)
fn(...[...arguments])
}
on.fn = fn
this.on(event, on)
}
}
const bus = new EventBus()
function f1(a) {
console.log(a)
}
function f2(a, b) {
console.log(a + b)
}
bus.on('click', f1)
bus.emit('click', 1)
bus.on('change', f2)
bus.emit('change', 5, 6)
bus.off('click', f1)
bus.off('change')
数组转树
function ArrayToTree(arr, props = { id: "id", pid: 'pid', children: 'children' }) {
const map = {}
const result = []
arr.forEach(item => {
const key = props["id"]
map[item[key]] = item
})
arr.forEach(item => {
const keyId = props["id"]
const keyPid = props["pid"]
const keyChildren = props["children"]
if (map[item[keyPid]]) {
map[item[keyPid]][keyChildren] ? map[item[keyPid]][keyChildren].push(item) : map[item[keyPid]][keyChildren] = [item]
} else {
result.push(item)
}
})
return result
}
树转数组
function TreeToArray(tree, props = { children: "children" }, res = []) {
const keyChildren = props["children"]
tree.forEach(item => {
if (item[keyChildren] && item[keyChildren].length > 0) {
TreeToArray(item[keyChildren], props, res)
delete item[keyChildren]
res.push({ ...item })
} else {
res.push(item)
}
})
return res
}
深拷贝
function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1)
}
function isObject(value) {
return typeof value !== null && typeof value === "object"
}
function deepClone(originValue, map = new WeakMap()) {
if (getType(originValue) === 'Set') {
return new Set([...originValue])
}
if (getType(originValue) === 'Map') {
return new Map([...originValue])
}
if (getType(originValue) === 'Symbol') {
return Symbol(originValue.description)
}
if (getType(originValue) === 'Function' || !isObject(originValue)) {
return originValue
}
if (map.has(originValue)) {
return map.get(originValue)
}
const newObject = Array.isArray(originValue) ? [] : {}
map.set(originValue, newObject)
for (const key in originValue) {
newObject[key] = deepClone(originValue[key], map)
}
const symbolKeys = Object.getOwnPropertySymbols(originValue)
for (const sKey of symbolKeys) {
newObject[sKey] = deepClone(originValue[sKey], map)
}
return newObject
}
对象比较
export function looseEqual(a, b) {
if (a === b) return true;
const isObjectA = isObject(a);
const isObjectB = isObject(b);
if (isObjectA && isObjectB) {
try {
const isArrayA = Array.isArray(a);
const isArrayB = Array.isArray(b);
if (isArrayA && isArrayB) {
return (
a.length === b.length &&
a.every((e, i) => {
return looseEqual(e, b[i]);
})
);
} else if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime();
} else if (!isArrayA && !isArrayB) {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
return (
keysA.length === keysB.length &&
keysA.every(key => {
return looseEqual(a[key], b[key]);
})
);
} else {
return false;
}
} catch (e) {
return false;
}
} else if (!isObjectA && !isObjectB) {
return String(a) === String(b);
} else {
return false;
}
}
js判断类型
- typeof
typeof 1 //number
typeof 'abc' //string
typeof undefined //undefined
typeof true //boolean
typeof Symbol('a') //symbol
typeof function test() { } //function
typeof { age: 1 } //object
typeof null //object
typeof new Date() //object
- Object.prototype.toString
Object.prototype.toString.call(1) // [object Number]
Object.prototype.toString.call('abc') //[object String]
Object.prototype.toString.call(undefined) //[object Undefined]
Object.prototype.toString.call(true) //[object Boolean]
Object.prototype.toString.call(Symbol('a'))//[object Symbol]
Object.prototype.toString.call(function test() { }) //[object Function]
Object.prototype.toString.call({ age: 1 }) //[object Object]
Object.prototype.toString.call(null) //[object Null]
Object.prototype.toString.call(new Date()) //[object Date]
Object.prototype.toString.call([]) //[object Array]
- instanceof
console.log(1 instanceof Number) //false
console.log(new Number(1) instanceof Number) //true
console.log(new Boolean(false) instanceof Boolean) //true
console.log({ age: 1 } instanceof Object)//true
console.log([] instanceof Array) //true
console.log(function test(){} instanceof Function)//true
- constructor
var a = 1
console.log(a.constructor === Number) //true
console.log(false.constructor === Boolean) //true
console.log([].constructor === Array) //true
console.log({ age: 1 }.constructor === Object)//true
console.log(new Date().constructor === Date)//true
instanceof原理
function myInstanceOf(left, right) {
let proto = Object.getPrototypeOf(left)
let prototype = right.prototype
while (true) {
if (!proto) return false
if (proto === prototype) return true;
proto = Object.getPrototypeOf(proto)
}
}
隐式转换
- 通过ToPrimitive(val,type)将值转换为原始值
type=number:先调用valueOf(),如果得到原始值就直接返回,否则调用toString(),如果得到原始值就返回,否则报错。
type=string:先调用toString(),如果得到原始值就直接返回,否则调用valueOf(),如果得到原始值就返回,否则报错。
Date类型的type=string,其他都是type=number
console.log((1).valueOf()) //1
console.log((false).valueOf()) //false
console.log(([1, 2, 3]).valueOf()) //[1,2,3]
console.log(('abc').valueOf())//abc
console.log(({ age: 1 }).valueOf())//{ age: 1 }
console.log((new Date()).valueOf())//1663837507209
原始值的valueOf是值本身,引用值的valueOf返回本身这个对象,Date的valueOf返回时间戳。
console.log((1).toString()) //'1'
console.log((false).toString()) //'false'
console.log(('abc').toString())//'abc'
console.log(([1, 2, 3]).toString()) //'1,2,3'
console.log(({ age: 1 }).toString())//'[object Object]'
console.log((new Date()).toString())// 'Thu Sep 22 2022 23:02:59 GMT+0800 (中国标准时间)'
console.log((function () { }).toString())//'function () {}'
- ToNumber
undefined --> NaN
null --> 0
布尔值 -->true为1,false为0
字符串 --> '123'为123,'abc'为NaN
对象 --> ToPrimitive(val,number)得到原始值,再通过ToNumber进行转换
- ToString
undefined --> 'undefined'
null --> 'null'
布尔值 --> 'true'或'false'
数字 --> 123转为'123'
对象 --> ToPrimitive(val,string)得到原始值,再通过ToString进行转换
- ==
Number和String比较会转为Number再做比较
有Boolean值,Boolean会转为Number
一个Object类型,一个String或Number类型,将Object类型进行原始转换后,按上面流程进行原始值比较。
防抖
function debounce(fn, delay) {
let timer = null
const _debounce = function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, [...arguments])
}, delay)
}
return _debounce
}
节流
function throttle(fn, interval) {
let lastTime = 0
const _throttle = function () {
const nowTime = new Date().valueOf()
if ((nowTime - lastTime) >= interval) {
fn.apply(this, [...arguments])
lastTime = nowTime
}
}
return _throttle
}
前端跨页面通信
- localStorage
//pageA
document.querySelector('.btn').addEventListener('click', () => {
localStorage.setItem('key1', new Date().valueOf())
})
//pageB
window.addEventListener('storage', (e) => {
console.log(e, e.newValue)
})
- BroadcastChannel
//pageA
const channel = new BroadcastChannel('gkm')
document.querySelector('.btn').addEventListener('click', () => {
channel.postMessage('发送消息')
})
document.querySelector('.close').addEventListener('click', () => {
channel.close();
})
//pageB
new BroadcastChannel('gkm').addEventListener('message', e => {
console.log(e, e.data)
})
- SharedWorker
//pageA
const btn = document.querySelector('.btn')
const worker = new SharedWorker('./src/worker.js', 'gkm')
worker.port.onmessage = (e) => {
if (e.data.type == 'pageB') {
console.log(`获取到B页面的值为${e.data.number}`)
}
}
btn.addEventListener('click', () => {
worker.port.postMessage({ type: 'pageA', number: 1 })
})
//pageB
const btn = document.querySelector('.btn')
const worker = new SharedWorker('./src/worker.js', 'gkm')
worker.port.onmessage = (e) => {
if (e.data.type == 'pageA') {
console.log(`获取到A页面的值为${e.data.number}`)
}
}
btn.addEventListener('click', () => {
worker.port.postMessage({ type: 'pageB', number: 2 })
})
// ./src/worker.js
const portPool = [];
self.onconnect = (e) => {
const port = e.ports[0]
portPool.push(port)
port.onmessage = (event) => {
boardcast(event.data)
}
}
function boardcast(message) {
portPool.forEach(port => {
port.postMessage(message);
})
}
- 非同源通信(借用iframe的postMessage)
//pageA
window.addEventListener('message', (e) => { //监听iframe发来的消息
console.log(e)
})
window.frames[0].window.postMessage(123,'*') //发送给iframe页面消息
//iframe
window.addEventListener('message', (e) => { //监听pageA发来的消息
console.log(e)
})
window.parent.postMessage(456,'*')//传递给pageA
jsonp实现原理
function jsonp({ url, params, callback }) {
return new Promise((resolve, reject) => {
let container = document.getElementsByTagName("head")[0];
let script = document.createElement('script')
window[callback] = function (data) {
resolve(data)
container.removeChild(script)
}
params = { ...params, callback }
const query = Object.keys(params).map(key => `${key}=${params[key]}`).join('&')
script.src = `${url}?${query}`
script.onerror = function () {
reject('error')
container.removeChild(script)
}
container.appendChild(script)
})
}
jsonp({
url: 'http://127.0.0.1:5500/ab.txt',
params: { pid: 10550689 },
callback: 'test'
}).then(data => {
console.log(data)
}).catch(err => {
console.log(err)
})
ES6
var、let、const
var声明的变量会发生作用域提升,在声明之前可以被访问,它会被添加到window对象上。let和const在执行上下文的词法环境创建出来了,但由于存在暂时性死区,无法在变量声明前访问,let和const存在块级作用域,它们都不会添加到window上。const只能定义常量,需要初始值。
function show() {
let a = '1'
var b = '2'
var c = '6'
{
console.log(c) //annot access 'c' before initialization
let c = '3'
var d = '4'
let b = '5'
console.log(a, b) //1 5
}
console.log(a, b) //1 2
console.log(c, d) //6 4
}
show()
标签模板字符串
function foo(m, n, x) {
console.log(m, n, x, )
}
const name = "why"
const age = 18
foo`Hello${name}Wo${age}rld` //['Hello', 'Wo', 'rld'] why 18
Symbol
const s1 = Symbol('a')
const s2 = Symbol('b')
console.log(s1.description)//a
console.log(s1 === s2)//false
const s3 = Symbol.for('c')
const s4 = Symbol.for('c')
console.log(s3 === s4)//true
console.log(Symbol.keyFor(s3))//c
const obj = {
[s1]: '124',
[s2]: '456',
}
console.log(Object.getOwnPropertySymbols(obj))//[Symbol(a), Symbol(b)]
set和weakset区别
weakset成员只能是对象类型;weakset集合中对象的引用为弱引用,因此weakset不能被枚举也没有大小。
const set = new Set()
const weakset = new WeakSet()
let a = {
age: 1
}
let b = {
age: 2
}
set.add(a)
weakset.add(b)
a = null
b = null
setTimeout(() => {
console.log(set)//即使a对于{age:1}没有引用了,但set对其依旧是强引用不会被垃圾回收,因此打印{{age:1}}
console.log(weakset)//b对于{age:2}没有引用了,10秒后垃圾回收无视weakset的弱引用,会回收,因此打印{}
}, 10000)
map和weakmap区别
WeakMap的key只能使用对象,不接受其他的类型作为key;WeakMap的key对对象想的引用是弱引用,如果没有其他引用引用这个对象,那么GC可以回收该对象;WeakMap不可遍历,没用大小。
const map = new Map()
const weakmap = new WeakMap()
let p1 = {
age: 1
}
let p2 = {
age: 2
}
map.set(p1, "aaa")
weakmap.set(p2, "bbb")
p1 = null
p2 = null
setTimeout(() => {
console.log(map) //{{age:1}=>"aaa"}
console.log(weakmap)//{} 因为弱引用,被回收了
}, 10000)
Proxy和Reflect
const obj = {
_name: "why",
age: 10,
get name() {
return this._name
},
set name(val) {
this._name = val
}
}
const objProxy = new Proxy(obj, {
get: function (target, key, receiver) {//这里的receiver===objProxy
return Reflect.get(target, key, receiver)//通过第三个参数,使get name()中的this指向objProxy,因此就会访问objProxy的属性,而不是obj的
},
set: function (target, key, newValue, receiver) {
Reflect.set(target, key, newValue, receiver)//相比target[key]=newValue,Reflect.set可以获得返回值来判断是否设置值成功
},
})
var a = objProxy.name//会触发objProxy的两次get,第一次key是name,第二次key是_name
objProxy.name = 'abc'//会触发objProxy的两次set,第一次key是name,第二次key是_name
function Student(name, age) {
this.name = name
this.age = age
}
function Teacher() { }
const stu = Reflect.construct(Student, ["why", 18], Teacher)
console.log(stu.__proto__ === Teacher.prototype)//true
迭代器
const arr = [1, 2, 3,]
let index = 0
const arrIterator = {
next() {
if (index < arr.length) {
return { value: arr[index++], done: false }
} else {
return { value: undefined, done: true }
}
}
}
console.log(arrIterator.next()) // {value: 1, done: false}
console.log(arrIterator.next()) // {value: 2, done: false}
console.log(arrIterator.next()) // {value: 3, done: false}
console.log(arrIterator.next()) // {value: undefined, done: true}
可迭代对象
可迭代对象中需要存在[Symbol.iterator]方法,并且返回一个迭代器
const info = {
arr: [1, 2, 3],
[Symbol.iterator]: function () {
let index = 0
return {
next: () => {
if (index < this.arr.length) {
return { done: false, value: this.arr[index++] }
} else {
return { done: true, value: undefined }
}
}
}
}
}
for (let item of info) {
console.log(item)
}
生成器
function* foo(num) {
console.log("函数开始执行~")
const value1 = 1 * num
const param1 = yield value1
const value2 = 2 * param1
const param2 = yield value2
const value3 = 3 * param2
const param3 = yield value3
console.log("函数执行结束~")
return 4 * param3
}
const generator = foo(1)
console.log(generator.next()) // {value: 1, done: false}
console.log(generator.next(10)) // {value: 20, done: false} 参数会作为上一个yield的返回值
console.log(generator.next(20)) // {value: 60, done: false}
console.log(generator.next(30)) // {value: 120, done: true}
const info = {
arr: [1, 2, 3],
[Symbol.iterator]: function* () {
yield* this.arr
}
}
for (let item of info) {
console.log(item) // 1 2 3
}
Promise
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledFns = []
this.onRejectedFns = []
const resolve = (value) => {
if (this.status === PENDING) {
queueMicrotask(() => {
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
})
}
}
const reject = (reason) => {
if (this.status === PENDING) {
queueMicrotask(() => {
if (this.status !== PENDING) return
this.status = REJECTED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
})
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
const thenRes = new MyPromise((resolve, reject) => {
const fulfilled = () => {
try {
const result = onFulfilled(this.value)
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (err) {
reject(err)
}
}
const rejected = () => {
try {
const result = onRejected(this.reason)
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
reject(result)
}
} catch (err) {
reject(err)
}
}
if (this.status === FULFILLED) {
fulfilled()
} else if (this.status === REJECTED) {
rejected()
} else if (this.status === PENDING) {
this.onFulfilledFns.push(fulfilled)
this.onRejectedFns.push(rejected)
}
})
return thenRes
}
catch(onRejected) {
this.then(undefined, onRejected)
}
finally(onFinally) {
this.then(() => {
onFinally()
}, () => {
onFinally()
})
}
static resolve(value) {
return new MyPromise((resolve) => resolve(value))
}
static reject(reason) {
return new MyPromise((resolve, reject) => reject(value))
}
static all(promises) {
return new MyPromise((resolve, reject) => {
const values = []
promises.forEach(promise => {
if (promise instanceof MyPromise) {
promise.then(value => {
values.push(value)
if (values.length === promises.length) {
resolve(values)
}
}, err => {
reject(err)
})
} else {
values.push(promise)
if (values.length === promises.length) {
resolve(values)
}
}
})
})
}
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
if (promise instanceof MyPromise) {
promise.then(resolve, reject)
} else {
resolve(promise)
}
})
})
}
static any(promises) {
const reasons = []
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then((resolve, err => {
reasons.push(err)
if (reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
}))
})
})
}
}