实现 new
new 原理:
创建Ctor的一个实例对象
实例.__proto__ = Ctor.prototype
把构造函数当做普通函数执行(让方法中的 this -> 实例对象)
确认方法的返回值(如果没有返回值或者返回的是原始值,我们让其默认返回实例对象即可)
需要考虑的点:
Symbol、BigInt 不能被 new
我们知道 instanceof 是不能检测基本数据类型的
Symbol() instanceof Symbol
是 false。不过可以利用 call 方法来强迫装箱:当然这个也是 Function 和 Object 的实例注意:使用
Object.prototype.toString.call(f)
也是 symbol,因为会先去找Symbol.toStringTag
这个属性(返回的是'[object Symbol]'
),所以需要结合 typeof 来区分(进行了装箱操作把基本数据类型转换为对应的引用数据类型)1
2
3
4
5let f = function () { return this }.call(Symbol(''))
f instanceof Symbol // true
Object.prototype.toString.call(f) // '[object Symbol]'
typeof f // 'object'生成器函数不需要基于 new 执行,就可以创造函数实例。其实相当于内部做了
f.__proto__ = fn.prototype
的处理1
2
3function* fn() {}
let f = fn()
f instanceof fn // true箭头函数不能被 new
箭头函数没有 this,this 继承的是外层代码块的 this(也不能用 call 方法改变 this)
箭头函数不能使用 arguments,不过可以使用 rest 运算符
箭头函数不能使用 yield,箭头函数不能用作 Generator 函数
1 | /* |
实现 Object.create
Object.create([obj])
:创建一个空对象,并让 空对象.__proto__
指向 [obj]
需要考虑的点:
[obj]
可以是一个对象或者是 null,但不能是其他的值Object.create(null)
创建一个不具备__proto__
属性的对象(不是任何类的实例)
1 | Object.create = function create(prototype) { |
实现 instanceof
原理:查找 [构造函数][Symbol.hasInstance](实例)
- 如果存在这个属性方法,则方法执行返回的值就是最后检测的结果
- 如果不存在这个属性方法,则会查找当前实例的原型链(一直找到 Object.prototype 为止),如果查找中,找到某个原型等于构造函数的原型,则返回true,反之false
注意: 基于 ES6 设置静态私有属性 static [Symbol.hasInstance](value) { return true }
是有效的
获取原型链需要考虑的点:
Object.getPrototypeOf()
不能获取null/undefined
的原型
instanceof 需要考虑的点:
- 检测的所属类型(instanceof 右侧的所属类型)不能是箭头函数及非函数(基本数据类型的值、
{}
…)
1 | function instance_of(obj, Ctor) { |
实现 forEach 方法(支持对象遍历)
需要考虑的点:
- Symbol 无法用
for...in...
遍历的,可以使用Object.getOwnPropertySymbols()
获取,之后结合 for 循环或Object.keys()
使用即可
1 | function each(obj, callback) { |
其它常用数组方法的封装可以看我这篇文章:JS 常用数组方法封装(包含splice)
实现 call bind
需要考虑的点:
临时设置的属性,不能和原始对象冲突,所以属性采用唯一值处理
使用
Symbol
或 生成随机字符串Math.random() * new Date()
如果 context 不是对象(基本数据类型的值),需要将其处理成对象。如果 context 是 null 则需要单独处理
1 | function call(context, ...params) { |
bind 不是立即把函数执行,只是预先把 this 和后期需要传递的参数存储起来(柯理化函数)
1 | function bind(context, ...params) { |
实现 Promise
先看一下 Promise 具有的方法:
Promise 原理:
首先会给当前实例增加状态(默认是 pending 等待态)和两个事件池 成功的事件池和失败的事件池(用来存放 then 中还不知道实例状态的事件)
其次最重要的就是 Promise 内部传递的 executor 函数,此函数会立即执行并会传递两个回调函数 resolve 和 reject,当执行任何一个回调函数时,都会做两件事情:更改状态、发布对应事件池的状态方法
注意:更改完状态就不能再更改了(是 pending 才会改状态);执行回调函数时如果基于 then 存储过方法,依次通知对应状态的事件池方法执行,且执行是异步的微任务
new Promise 会创造实例,实例可以调用其所属类原型的方法,之后重构了一下 Promise 构造函数原型上的方法
then 方法返回一个全新的 Promise 实例,有两种情况:
- 如果已经知道对应实例的状态,则创建一个异步的微任务,后期执行对应的 onFulfilled/onRejected
- 如果此时还不知道实例状态,就先把 onFulfilled/onRejected 存储起来,后期更改其状态之后,再通知方法执行即可,也是异步微任务
注意:返回新的 Promise 实例不能和执行结果是同一个
let p2 = p1.then(() => p2)
如何验证返回的是否是一个新的 Promise 实例:类型是 object 或 function 且它得具有 then 方法(是一个函数)。如果返回的不是 Promise 实例,且执行没有报错,则返回一个成功的 Promise 实例
如果 then 中的某个回调函数(resolve、reject)不设置,其默认具备穿透性,顺延到下一个 then 中的同状态的方法上
如果没有传 onFulfilled 或 onRejected,就给对应的默认传递 resolve 或 reject 即可
catch 相当于调用 then,第一个参数不写,第二个参数传递错误原因
all 传入的必须是一个数组,如果数组中某一项不是 Promise 实例,需要把它转换为成功的 Promise 实例。之后把每一项依次执行,只要有一项失败返回就是失败的,必须全部成功才是成功
注意:返回结果的顺序跟传入的顺序一致(不能使用 push,因为不能确定谁先到,需要使用索引)
需要注意的点:
- Promise 是一个构造函数,需要 new 执行
- Promise 参数必须是一个函数
- Promise then 里的方法实际上是异步的微任务,可以基于 queueMicrotask 去创造异步的微任务(兼容性差);这里采用基于定时器创造一个异步的宏任务,来模拟微任务
ES6 方法实现 Promise,可以参考我另外一篇文章 手撕 Promise(内附then链实现)
1 | ;(function () { |