promise
Promise是一个类,类的构造函数需要传入一个执行器executorexecutor两个参数:resolvereject- 默认创建一个
promise状态由三个:pendingfulfilledrejected - 调用成功或失败时,需要传递一个成功原因或失败原因
- 如果状态转变成
resolved或者rejected状态后,状态不能再次变化 Promise的实例都有一个then方法, 可以调用多次,返回一个Promise对象- 抛出异常按照失败处理
promise A+实现
const status = {
PENDING: 'PENDING',
FULFILLED: 'FULFILLED',
REJECTED: 'REJECTED'
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) { // 避免循环引用
return reject(new TypeError('Error'));
}
// 判断x的类型,如果x是对象或者函数,说明x有可能是一个promise,否则就不可能是promise
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let called = false; // promise的实现可能有多个,但都要遵循promise a+规范,我们自己写的这个promise用不上called,但是为了遵循规范才加上这个控制的,因为别人写的promise可能会有多次调用的情况。
try {
// 因为then方法有可能是getter来定义的, 取then时有风险,所以要放在try...catch...中
// 别人写的promise可能是这样的
// Object.defineProperty(promise, 'then', {
// get() {
// throw new Error();
// }
// })
let then = x.then;
if (typeof then === 'function') {
// x.then(()=>{}, ()=>{}); 不要这么写,以防以下写法造成报错, 而且也可以防止多次取值
// let obj = {
// a: 1,
// get then() {
// if (this.a++ == 2) {
// throw new Error();
// }
// console.log(1);
// }
// }
// obj.then;
// obj.then
then.call(x, y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject); // 当前promise解析出来的结果可能还是一个promise, 直到解析到他是一个普通值
}, e => {
if (called) return;
called = true;
reject(e);
});
} else {
resolve(x); // 普通对象直接 resolve
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x); // 基本类型直接 resolve
}
}
class Promise {
constructor(executor) {
this.status = status.PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = []; // 存放成功回调
this.onRejectedCallbacks = []; // 存放失败回调
const resolve = (value) => {
if (value instanceof Promise) {
return value.then(resolve, reject); // 如果 value 是个 Promise,递归执行
}
if (this.status === status.PENDING) {
this.status = status.FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
const reject = (reason) => {
if (this.status === status.PENDING) {
this.status = status.REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected) {
// onFulfilled onRejected 为可选参数
// 参数透传 Promise.resolve(1).then().then((value) => console.log(value))
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : data => data;
onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e };
let promise2 = new Promise((resolve, reject) => { // then 必须返回一个新的 promise
if (this.status === status.FULFILLED) {
setTimeout(() => { // 保证 onFulfilled、onRejected异步执行,同时能拿到 promise2
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
}
if (this.status === status.REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
}
if (this.status === status.PENDING) { // 处理异步
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
})
}
})
return promise2;
}
}
promise 相关api实现
all
static all(promises) {
return new Promise((resolve, reject) => {
let res = [];
let count = 0;
const len = promises.length;
for (let i = 0; i < len; i++) {
const promise = promises[i];
promise.then(value => {
res[i] = value;
if (++count === len) {
resolve(res);
}
}, reject)
}
});
}
race
static race(promises) {
return new Promise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, reject)
})
});
}
allSettled
static allSettled(promises) {
return new Promise(resolve => {
let count = 0;
let res = [];
const callback = (value, index) => {
res[index] = value;
if (++count === promises.length) {
resolve(res);
}
}
promises.forEach((promise, index) => {
promise.then(value => {
callback(value, index);
}, reason => {
callback(reason, index);
})
});
});
}
resolve
static resolve(value) {
return new Promise(resolve => resolve(value));
}
reject
static reject(reason) {
return new Promise((resolve, reject) => reject(reason));
}
catch
catch (errCallback) {
return this.then(null, errCallback);
}
Event发布订阅实现
- Event是前端组件通信的依赖手段之一,主要有三个核心方法
- on: 传入需要监听的函数
fn及事件触发key - off: 传入需要移除的
key和fn - emit: 传入需要触发的事件
key及参数,调用监听的函数
class EventEmitter {
constructor() {
this._events = new Map();
}
on(eventName, fn) {
let handler = this._events.get(eventName);
if (!handler) {
this._events.set(eventName, [fn])
} else {
handler.push(fn);
}
return this;
}
off(eventName, fn) {
let handler = this._events.get(eventName);
if (Array.isArray(handler)) {
if (fn) {
let index = handler.indexOf(fn);
if (index !== -1) {
handler.splice(index, 1);
}
} else {
handler.length = 0;
}
}
return this;
}
emit(eventName, ...args) {
let handler = this._events.get(eventName);
if (Array.isArray(handler)) {
handler.forEach(fn => {
fn(...args);
})
}
return this;
}
}
防抖节流
函数防抖
- 原理: 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时
- 场景: 输入框输入内容查询、下载资源按钮
const debounce = (func, wait = 500, immediate = false) => {
let timer = null;
return function(...args) {
if (!timer && immediate) {
func.apply(this, args);
}
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
if (!immediate) {
func.apply(this, args)
}
}, wait);
}
}
函数节流
- 原理: 规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效
- 场景: 常用于监听滚动条
const throttle = (func, wait = 500) => {
let timer = null;
let prevTime = 0;
return function(...args) {
let now = new Date();
let remaining = wait - (now - prevTime);
if (remaining <= 0) {
// 两次间隔时间超过频率
timer = null;
prevTime = now;
func.apply(this, args);
} else if (!timer) {
timer = setTimeout(() => {
func.apply(this, args);
clearTimeout(timer);
timer = null;
prevTime = new Date();
}, remaining);
}
}
}
深拷贝
详情请点击我的博客深入了解浅拷贝与深拷贝,里面有非常详细的解释。
const arrayTag = '[object Array]'
const objectTag = '[object Object]'
const mapTag = '[object Map]'
const setTag = '[object Set]'
const functionTag = '[object Function]';
const boolTag = '[object Boolean]'
const dateTag = '[object Date]'
const errorTag = '[object Error]'
const numberTag = '[object Number]'
const regexpTag = '[object RegExp]'
const stringTag = '[object String]'
const symbolTag = '[object Symbol]'
function cloneArray(array) {
const { length } = array;
const result = new array.constructor(length);
if (length && typeof array[0] === 'string' && hasOwnProperty.call(array, 'index')) {
result.index = array.index;
result.input = array.input;
}
return result;
}
function cloneSymbol(symbol) {
return Object(Symbol.prototype.valueOf.call(symbol));
}
function cloneRegExp(regexp) {
const reFlags = /\w*$/;
const result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
result.lastIndex = regexp.lastIndex;
return result;
}
function initCloneTargetByTag(target, tag) {
const Ctor = target.constructor;
switch (tag) {
case boolTag:
case dateTag:
return new Ctor(+target);
case numberTag:
case stringTag:
case errorTag:
return new Ctor(target);
case objectTag:
case mapTag:
case setTag:
return new Ctor();
case arrayTag:
return cloneArray(target);
case symbolTag:
return cloneSymbol(target);
case regexpTag:
return cloneRegExp(target);
}
}
function isObject(target) {
const type = typeof target;
return target !== null && (type === 'object' || type === 'function');
}
function deepClone(target, cache = new WeakSet()) {
if (!isObject(target)) return target; // 拷贝基本类型值
if (cache.has(target)) return target;
cache.add(target);
const tag = Object.prototype.toString.call(target);
let cloneTarget = initCloneTargetByTag(target, tag); // 使用拷贝对象的构造方法创建对应类型的数据
if (tag === mapTag) {
target.forEach((value, key) => {
cloneTarget.set(key, deepClone(value, map));
});
return cloneTarget;
}
if (tag === setTag) {
target.forEach(value => {
cloneTarget.add(deepClone(value, map));
});
return cloneTarget;
}
if (tag === functionTag) {
return target;
}
Reflect.ownKeys(target).forEach(key => {
cloneTarget[key] = deepClone(target[key], cache); // 递归拷贝属性
});
return cloneTarget;
}
继承
call 继承
call继承:子类只能拿到父类的属性, 不能拿到原型上的方法
function Parent(name) {
this.parent = name;
}
Parent.prototype.say = function() {
console.log('this.parent :>> ', this.parent);
}
function Child(name, parent) {
Parent.call(this, parent); // 继承父类属性
this.child = name;
}
原型链继承
子类实例会公用同一个父类原型对象,会互相影响
function Parent(name) {
this.parent = name;
}
Parent.prototype.say = function() {
console.log('this.parent :>> ', this.parent);
}
function Child(name, parent) {
this.child = name;
}
Child.prototype = new Parent();
组合继承
将 call 继承和原型链继承结合起来,缺点是会执行两次 Parent 构造函数
function Parent(name) {
this.parent = name;
}
Parent.prototype.say = function() {
console.log('this.parent :>> ', this.parent);
}
function Child(name, parent) {
Parent.call(this, parent); // 继承父类属性
this.child = name;
}
Child.prototype = new Parent();
寄生组合继承
没有上面的这些缺点
function Parent(name) {
this.parent = name;
}
Parent.prototype.say = function() {
console.log('this.parent :>> ', this.parent);
}
function Child(name, parent) {
Parent.call(this, parent); // 继承父类属性
this.child = name;
}
Child.prototype = Object.create(Parent.prototype); // Object.create创建了父类原型的副本,与父类原型完全隔离
Child.prototype.say = function() {
console.log('this.child :>> ', this.child);
}
Child.prototype.constructor = Child;
new
详情请点击我的博客趣谈new
function selfNew(Ctor, ...args) {
let instance = Object.create(Ctor.prototype);
let res = Ctor.apply(instance, args);
if (/^(object|function)$/.test(typeof res)) return res;
return instance;
}
bind
详情请点击我的博客趣谈bind
Function.prototype.selfBind = function(context, ...bindArgs) {
if (typeof this !== 'function') {
throw new TypeError('参数类型错误');
}
const self = this;
let fBound = function(...args) {
return self.apply(this instanceof self ? this : context, bindArgs.concat(args));
}
fBound.prototype = Object.create(this.prototype);
return fBound;
}
call
call 做了什么:
- 接收一个对象,将函数设为对象的属性
- 执行&删除这个函数
- 指定
this到函数并传入给定参数执行函数 - 如果不传入参数,默认指向为
window
Function.prototype.selfCall = function (context, ...args) {
context = context || window;
const key = Symbol('key');
context[key] = this;
let res = context[key](...args);
delete context[key];
return res;
}
apply
和 bind 方法基本一样,只不过接收的参数类型不同
Function.prototype.selfApply = function(context, args) {
context = context || window;
const key = Symbol('key');
context[key] = this;
let res = context[key](...args);
delete context[key];
return res;
}
instanceof
instanceof 运算符是用来检测某个实例对象的原型链上是否存在构造函数的 prototype 属性。
- 先取得当前类的原型,当前实例对象的原型链
- 一直循环(执行原型链的查找机制)
- 取得当前实例对象原型链的原型链(
proto = proto.__proto__,沿着原型链一直向上查找) - 如果 当前实例的原型链
__proto__上找到了当前类的原型prototype,则返回true - 如果 一直找到
Object.prototype.__proto__ == null,Object的基类(null)上面都没找到,则返回false
function selfInstanceof(left, right) {
let proto = Object.getPrototypeOf(left);
while (true) {
if (proto === right.prototype) {
return true;
}
if (proto === null) {
return false;
}
proto = Object.getPrototypeOf(proto);
}
}
Object.create
Object.create方法的实质是新建一个空的构造函数F,然后让F.prototype属性指向参数对象proto,最后返回一个F的实例,从而实现让该实例继承proto的属性。
Object.prototype.create = function(proto) {
function F(){};
F.prototype = proto;
return new F();
}