在面试中的常见的JavaScript手写代码题之小白新手教程(二)

417 阅读3分钟

前言

     书接上文,这里我们继续手写JS的面试手写题,依旧希望能帮助到大家伙。

正文

手写Promise

mindmap
      手写Promise
          使用es6语法中的class 创建一个MyPromise类
            构造函数constructor
    
            打造一个.then
            
    

构造函数 constructor

既然来到手写一个Promise就得先聊聊它的流程,方便我们实现代码。

  • 构造函数constructor 构造函数会接受一个形参执行函数 executor
  • executor执行函数 会接收到两个形参 resolve reject但是这两个形参自己本身也是函数的,需要被执行掉
  • 对于一个Promise对象来说存在三个状态属性
  • 新创建的一个Promise的实例化对象的status会是pending
  • 当调用resolve的时候,status会变成resolved
  • 当调用reject的时候,status会变成rejected
  • 我们需要知道的是,无论是.then(),还是.catch(),都是会接受一个回调函数的
  • 但是这个回调函数,需要被执行至于执行哪一个就是看status的值了
  • 如果status的值是pending的时候,回调函数就不会被执行,但是一旦status,的值发生变化,回调函数就会被全部执行。因此我们的回调函数需要被存储起来

大致上对于构造函数constructor()中的代码就包含这些,现在我们来写一下

constructor(executor){
        this.status = 'pending'
        
        this.value = undefined
        this.reason = undefined
     
        this.onResolvedCallbacks = []
        this.onRejectedCallbacks = []
        
        const resolve = (value)=>{
            if(this.status === 'pending'){
                this.status = 'resolved'
                this.value = value
                
                this.onResolvedCallbacks.forEach(fn=>fn())
            }
        }
        const reject = (reason)=>{
            if(this.status === 'pending'){
                this.status = 'rejected'
                this.reason = reason

                this.onRejectedCallbacks.forEach(fn=>fn())
            }
        }  
        executor(resolve,reject)

在构造函数中,我们定义一个常量status用于表示Promsie对象的状态,初始值为penging.这里创建了两个变量用于接收resolve以及reject的值,因为在resolvereject使用中确实可以传参。

image.png

image.png

然后创建两个数组,用于存放由于.then().catch()没有执行,导致没有执行的函数.创建reslovereject两个函数。在执行函数之前我们必须保证此时Promise对象的状态statuspending才可以执行,不然就不合理了,这里我们就必须保证是从初始状态才可以执行。然后接收传过来的参数。
executor()将这两个函数执行掉。

打造.then()

.then()中我们接受两个函数作为参数,第二个参数等同于reject,首先我们先判断接收的参数是否是一个函数。因为.then()也是可以接受非函数的参数的的。对于reject如果参数不是函数,就将这个参数抛出

then(onResolved,onRejected){
        onResolved = typeof onResolved === 'function' ? onResolved : value=>value
        onRejected = typeof onRejected === 'function' ? onRejected : reason=>{throw reason}

        return new MyPromise((resolve,reject)=>{
            if(this.status === 'resolved'){ 
                setTimeout(()=>{
                    try {
                        const res = onResolved(this.value)
                        resolve(res)
                    } catch (error) {
                        reject(error)
                    }
                })
            }
            if(this.status === 'rejected'){
                setTimeout(()=>{
                    try {
                        const res = onResolved(this.reason)
                        //then().then() 
                        resolve(res)
                    } catch (error) {
                        reject(error)
                    }
                })
            }
            if(this.status === 'pending'){
                this.onResolvedCallbacks.push((value)=>{
                    setTimeout(()=>{
                        onResolved(value)
                    })
                })
                this.onRejectedCallbacks.push((reason)=>{
                    setTimeout(()=>{
                        onRejected(reason)
                    })
                })
            }
        })
    }

当我们return new Promise()时,我们需要注意此时的this.status的状态是什么。 如果resolve()直接被执行掉了,此时this.status = 'fulfilled',我们就把参数传给下一个then(),然后这里的resolve(res)是由于我们的.then()后面是可以依旧接.then()的,这里就递归一下。如果此时的this.status ='pending'将自己的回调函数分别保存在 onFullfilledCallbacks[]onRejectedCallbacks[]中。
这样我们就实现了一个基础版本的Promise了,这里由于代码比较长,大家可以把上述两段代码这样放好

class MyPromise{
    constructed(){}
    then()
}

手写一个new

在JavaScript中,new操作符可以帮助开发者创建一个自定义的类型类型实例或者是一个具有构造函数的内置对象,比如Object,String,Array,Number,Boolean,Function等等都有。 这里我们来分析一下 const res = constructor.apply(obj,args)

function createNew(constructor,...args){
    let obj = {}
    obj.__proto__ = constructor.prototype;
    const res = constructor.apply(obj,args)
    return typeof res === 'object' && res!== null? res : obj
}
function Person(name,age){
    this.name = name
    this.age = age
}
Person.prototype.sayHello = function(){
    console.log(`Hello, my name is ${this.name}`)
}
const person = new createNew(Person,'Bob',18)

console.log(person.name);
console.log(person.age);
person.sayHello()

image.png

最后判断,拿到的res是否是一个对象,如果不是就返回空对象obj