JavaScript基础(一)

229 阅读10分钟
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继承方式
  1. 原型链继承
//父
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()

弊端:父类实例的属性的值是引用类型时,子类其中一个实例修改了这个值,其他子类实例的值也会被改变;子类在实例化时不能给父类的构造函数传参。

  1. 借用构造函数继承
//父
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
}

弊端:虽然解决了引用类型、传递参数的问题,但是子类无法继承父类原型对象上的方法,因此方法只能定义在父类构造函数中,会造成浪费内存。

  1. 组合继承
//父
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()//继承原型上方法

弊端:解决了原型链继承和借用构造函数继承的弊端,但是会调用两次父类构造函数。

  1. 原型式继承
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']

弊端:引用值存在共享问题;新实例的属性都是后面添加的,无每复用。

  1. 寄生式继承
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 //最终返回这个对象
}

弊端:每创建一个对象都需要在对象上添加方法,导致方法不能复用造成内存浪费。

  1. 寄生组合式继承
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))
                    }
                }))
            })
        })
    }
}