手写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
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函...
    neromous阅读 8,703评论 1 56
  • 一、Promise的含义 Promise在JavaScript语言中早有实现,ES6将其写进了语言标准,统一了用法...
    Alex灌汤猫阅读 820评论 0 2
  • 目录:Promise 的含义基本用法Promise.prototype.then()Promise.prototy...
    BluesCurry阅读 1,490评论 0 8
  • Prepending(进行时),Resolve(成功了),Reject(失败了),then......等 1.Pr...
    _菩提本无树_阅读 49,003评论 0 21
  • 00、前言Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区...
    夜幕小草阅读 2,129评论 0 12