手写Promise、call、apply、bind和new

一、Promsie

promise翻译过来是承诺的意思,这个承诺会在未来有一个确切的答复,并且该承诺有三种状态,这个承诺一旦从等待状态变成为其他状态就永远不能更改状态了。

  • pedding 等待中
  • resolved 完成了
  • rejected 拒绝了

Promise还实现了then链式调用,解决回调地狱的问题

实现第一步

搭建构造函数的大体框架

const PENDING = 'pending'
const RESOLVE = 'resolved'
const REJECTED = 'rejected'

function MyPromise(fn){
  const that = this; 
  that.state =  PENDING
  that.value = null
  that.resolveCallbacks = []
  that.rejectCallbacks = []
  //resolve与reject函数
  //fn函数
}
  • 首先创建三个常量,用于表示Promise的三个状态
  • 在函数内部体内声明that,因为代码可能会异步执行,用于获取正确的this指向
  • value用于保存resolve和reject传入的值
  • resolveCallbacks 、rejectCallbacks 用于保存then中的回调,因为执行完Promise时状态可能还是等待中,需要把then中的回调保存起来,当状态改变时调用

第二步,完善resolve和reject函数

function resolve(value) {
  if(that.state === PENDING) {
    that.state = RESOLVED
    that.value = value
    that.resolvedCallbacks.map(cb => cb(that.value))
  }
}

function reject(value) {
  if(that.state === PENDING){
    that.state = REJECTED
    that.value = value;
    that.rejectedCallbacks.map(cb => cb(that.value));
  }
}

第三步,执行Promise传入的函数

try {
  fn(resolve,reject)
} catch(e) { 
  reject(e) 
}
  • 执行传入的函数,并将上一步写的resolve,reject函数作为参数传进去
  • 要注意,在执行函数过程中可能会报错,需要捕获错误并且执行reject

第四步,实现then函数

MyPromise.prototype.then = function (onFulfilled,onRejected) {

  const that = this;
  onFulfilled = typeof onFulfilled  === 'function' 
                      ?onFulfilled 
                      :v => v
  onRejected = typeof onRejected === 'function'
                      ?onRejected
                      :r =>  throw r
}

if(that.state === PENDING) {
    that.resolvedCallbacks.push(onFulfilled)
    that.rejectedCallbacks.push(onRejected)
} 
else if(that.state === RESOLVED) {
    onFulfilled(that.value)
  }
else {
    onRejected(that.value)
  }
  • 首先判断两个参数是否为函数类型,因为这两个参数是可选的。当参数不是函数类型时,需要创建一个函数赋值给对应的参数
  • 接下来进行一系列的逻辑判断,当状态不是等待态时,就去执行相对应得函数。如果是等待态,则将函数push进回调函数中

Promise完整代码

const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'

function MyPromise(fn){
  const that = this
  that.state = PENDING
  that.value = null
  that.resolvedCallbacks = []
  that.rejectedCallbacks = []
  
  function resolve(value) {
    if(that.state === PENDING) {
      that.state = RESOLVED
      that.value = value
      that.resolvedCallbacks.map(cb => cb(that.value))
    }
  }
  
  function reject(value) {
    if(that.state === PENDING){
      that.state = REJECTED
      that.value = value;
      that.rejectedCallbacks.map(cb => cb(that.value));
    }
  }
  try {
    fn(resolve, reject)
  } catch (e) {
    reject(e)
  }
}

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  const that = this
  //对传入的两个参数做判断,如果不是函数将其转为函数
  onFulfilled = 
    typeof onFulfilled === 'function'
    ? onFulfilled 
    : v => v  // onFulfilled = v => v
  onRejected = 
    typeof onRejected === 'function'
    ? onRejected
    : r => {
      throw r
    }
  
  if(that.state === PENDING) {
    that.resolvedCallbacks.push(onFulfilled)
    that.rejectedCallbacks.push(onRejected)
  }
  else if(that.state === RESOLVED) {
    onFulfilled(that.value)
  }
  else {
    onRejected(that.value)
  }
}

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功的回调数据')
  }, 1000)
}).then(value => {
  console.log('Promise.then:  ', value)
})

二、call

除了第一个参数外,call 可以接收一个参数列表,apply 只接受一个参数数组。

call的实现思路:

  • 不传入参数,默认为window
  • 给新的对象添加一个函数,执行完后删除

(其实call的实现非常简单,总共8行代码)

Funtion.prototype.myCall = function (context){
    //设置默认参数为window
    var context = context || window
    //给新对象添加方法fn
    //getValue.call(a,1,2) =>  a.fn = getValue
    //此处this.指向调用myCall的对象
    context.fn = this
    //取出除第一个以外的参数
    var args = [...arguments].slice(1)
    //
    var result = context.fn(...args)
    delete  context.fn
    return result
}

三、apply

apply则更简单,七行即可

Function.prototype.myApply = function (context){
  var context = context || window
  context.fn = this
  //判断是否有第二个参数,有则展开
  var result = arguments[1]? context.fn(...arguments[1]) : context.fn()
  delete context.fn
  return result
}

三、bind

bind和其他两个方法类似,只是会返回一个函数,可以通过bind实现函数柯里化

Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  var _this = this
  var args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

四、new

  • 新生成了一个对象
  • 链接到原型
  • 绑定到this
  • 返回新对象

function create() {
    // 创建一个空的对象
    let obj = new Object()
    // 获得构造函数
    let Con = [].shift.call(arguments)
    // 链接到原型
    obj.__proto__ = Con.prototype
    // 绑定 this,执行构造函数
    let result = Con.apply(obj, arguments)
    // 确保 new 出来的是个对象
    return typeof result === 'object' ? result : obj
}

你可能感兴趣的:(手写Promise、call、apply、bind和new)