高频JS手写题总结

一 继承

1.原型链继承

function Animal() {
    this.colors = ['black', 'white'];
}

Animal.prototype.getColor = function() {
    return this.colors
}

function Dog() {}

Dog.prototype =  new Animal();

let dog1 = new Dog();
dog1.colors.push('brown');
let dog2 = new Dog();
console.log(dog2.colors)  // ['black', 'white', 'brown']

原型链继承存在的问题:

问题1:原型中包含的引用类型属性将被所有实例共享

问题2:子类在实例化的时候不能给夫类构造函数传参

2.借用构造函数实现继承

function Animal(name) {
    this.name = name;
    this.getName = function() {
        return this.name;
    }
}

function Dog(name) {
    Animal.call(this, name);
}

Dog.prototype =  new Animal();

借用构造函数实现继承解决了原型链继承的2个问题:引用类型共享问题以及传参问题。但是由于方法必须定义在构造函数中,所以会导致每次创建子类实例都会创建一遍方法。

3.组合继承

组合继承结合了原型链和借用构造函数,将两者的优点集中了起来。基本的思路是使用原型链继承原型上的属性和方法,而通过借用构造函数继承实例属性。这样既可以把方法定义在原型上以实现重用,又可以让每个实例都有自己的属性。

function Animal(name) {
    this.name = name;
    this.colors = ['black', 'white'];
}

Animal.prototype.getName = function() {
    return this.name;
}

function Dog(name, age) {
    Animal.call(this, name);
    this.age = age;
}

Dog.prototype =  new Animal();
Dog.prototype.constructor = Dog;

let dog1 = new Dog('奶昔', 2);
dog1.colors.push('brown');
let dog2 = new Dog('哈赤', 1);
console.log(dog2) ; // { name: "哈赤", colors: ["black", "white"], age: 1 }

4.寄生式组合继承

组合继承已经相对完善了,但还是存在问题,他的问题就是调用了两次夫类构造函数,第一次是在new Animal(),第二次是在Animal.call()这里。

解决方案:不直接调用父类构造函数给子类原型赋值,而是通过创建空函数获取父类原型的副本。

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

function inheritPrototype(child, parent) {
    let prototype = object(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
}

inheritPrototype(Dog, Animal);

5.class实现继承

class Animal {
    constructor(name) {
        this.name = name
    } 
    getName() {
        return this.name
    }
}

class Dog extends Animal {
    constructor(name, age) {
        super(name);
        this.age = age;
    }
}

二 数组去重

const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];

// 利用Set
const res = Array.from(new Set(arr));

// 两层for循环+splice
const unique = arr => {
  let len = arr.length;
  for (let i = 0; i < len; i++) {
    for (let j = i + 1; j < len; j++) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1);
        len--;
        j--;
      }
    }
  }
  return arr;
}

// 利用indexOf
const unique = arr => {
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (res.indexOf(arr[i]) === -1) res.push(arr[i]);
  }
  return res;
}

// 利用include
const unique = arr => {
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!res.includes(arr[i])) res.push(arr[i]);
  }
  return res;
}

// 利用filter
const unique = arr => {
  return arr.filter((item, index) => {
    return arr.indexOf(item) === index;
  });
}

// 利用Map
const unique = arr => {
  const map = new Map();
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!map.has(arr[i])) {
      map.set(arr[i], true);
      res.push(arr[i]);
    }
  }
  return res;
}

三 数组扁平化

const arr = [1, [2, [3, [4, 5]]], 6];
// 使用flat 优点:会跳过空位,返回新数组,不会修改原数组
const res = arr.flat(Infinity);

// 利用正则
const res = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g, '') + ']');

// 使用reduce
const flatten = arr => {
  return arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
  }, [])
};
const res = flatten(arr);

// 函数递归
const res = [];
const fn = arr => {
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      fn(arr[i]);
    } else {
      res.push(arr[i]);
    }
  }
}

fn(arr);

// toString 优点:简单,方便,对原数据没有影响 缺点:最好数组元素全是数字或字符,不会跳过空位
const arr = [1, 2, 3, [4, 5, [6, 7]]];
const flatten = arr.toString().split(',');
console.log(flatten);

// join 优点和缺点同toString
const arr = [1, 2, 3, [4, 5, [6, 7]]];
const flatten = arr.join(',').split(',');
console.log(flatten);

// 使用stack无限反嵌套多层嵌套数组
function flattenDeep(input) {
  const stack = [...input];
  const res = [];
  while (stack.length) {
    // 使用pop从stack中取出并移除值
    const next = stack.pop();
    if (Array.isArray(next)) {
      // 使用push送回内层数组中的元素,不会改动原始输入 original input
      stack.push(...next);
    } else {
      res.push(next);
    }
  }

  // 使用reverse恢复原数组的顺序
  return res.reverse();
}
console.log(flattenDeep([1, [2, [3, [4]],5]]));

四 实现数组原型方法

Array.prototype.forEach = function(callback, thisArg) {
    if (this == null) {
        throw new TypeError('this is null or not defined');
    }
    if (typeof callback !== "function") {
        throw new TypeError(callback + ' is not a function');
    }
    const O = Object(this)  // this就是当前的数组
    const len = O.length >>> 0  // 后面有解释
    let k = 0;
    while (k < len) {
        if (k in O) {
            callback.call(thisArg, O[k], k, O);
        }
        k++;
    }
}

Array.prototype.map = function(callback, thisArg) {
    if (this == null) {
        throw new TypeError('this is null or not defined');
    }
    if (typeof callback !== "function") {
        throw new TypeError(callback + ' is not a function');
    }
    const O = Object(this);
    const len = O.length >>> 0;
    let k = 0, res = [];
    while (k < len) {
        if (k in O) {
           res[k] = callback.call(thisArg, O[k], k, O);
        }
        k++;
    }
    return res;
}

Array.prototype.filter = function(callback, thisArg) {
    if (this == null) {
        throw new TypeError('this is null or not defined');
    }
    if (typeof callback !== "function") {
        throw new TypeError(callback + ' is not a function');
    }
    const O = Object(this);
    const len = O.length >>> 0;
    let k = 0, res = [];
    while (k < len) {
        if (k in O) {
            if (callback.call(thisArg, O[k], k, O)) {
                res.push(O[k]);               
            }
        }
        k++;
    }
    return res;
}

Array.prototype.find = function(callback, thisArg) {
    if (this == null) {
        throw new TypeError('this is null or not defined');
    }
    if (typeof callback !== "function") {
        throw new TypeError(callback + ' is not a function');
    }
    const O = Object(this);
    const len = O.length >>> 0;
    let k = 0;
    while (k < len) {
        if (k in O) {
            if (callback.call(thisArg, O[k], k, O)) {
                return O[k];               
            }
        }
        k++;
    }
    return null;
}

Array.prototype.some = function(callback, thisArg) {
    if (this == null) {
        throw new TypeError('this is null or not defined');
    }
    if (typeof callback !== "function") {
        throw new TypeError(callback + ' is not a function');
    }
    const O = Object(this);
    const len = O.length >>> 0;
    let k = 0;
    while (k < len) {
        if (k in O) {
            if (callback.call(thisArg, O[k], k, O)) {
                return true;
            }
        }
        k++;
    }
    return false;
}

Array.prototype.every = function(callback, thisArg) {
    if (this == null) {
        throw new TypeError('this is null or not defined');
    }
    if (typeof callback !== "function") {
        throw new TypeError(callback + ' is not a function');
    }
    const O = Object(this);
    const len = O.length >>> 0;
    let k = 0;
    while (k < len) {
        if (k in O) {
            if (!callback.call(thisArg, O[k], k, O)) {
                return false;
            }
        }
        k++;
    }
    return true;
}

Array.prototype.reduce = function(callback, initialValue) {
    if (this == null) {
        throw new TypeError('this is null or not defined');
    }
    if (typeof callback !== "function") {
        throw new TypeError(callback + ' is not a function');
    }
    const O = Object(this);
    const len = O.length >>> 0;
    let k = 0, acc;
    
    if (arguments.length > 1) {
        acc = initialValue;
    } else {
        // 没传入初始值的时候,取数组中第一个非empty的值为初始值
        while (k < len && !(k in O)) {
            k++;
        }
        if (k > len) {
            throw new TypeError( 'Reduce of empty array with no initial value' );
        }
        acc = O[k++];
    }
    while (k < len) {
        if (k in O) {
            acc = callback(acc, O[k], k, O);
        }
        k++;
    }
    return acc;
}

五 实现函数原型方法

Function.prototype.call = function (context) {
    const context = context || window;
    const fn = Symbol('fn);
    context[fn] = this;

    const args = [...arguments].slice(1);
    const result = context.fn(...args);

    delete context[fn];
    return result;
}

Function.prototype.apply = function (context, rest) {
    const context = context || window;
    const fn = Symbol('fn);
    context[fn] = this;

    let result;
    if (!rest) {
        result = context.fn();
    } else {
        result = context.fn(...rest);
    }

    delete context[fn];
    return result;
}

Function.prototype.bind = function (context) {
		const context = context || window;
    const args = [...arguments].slice(1);
		const self = this;
    
    const fNOP = function () {};

    const fBound = function () {
        const bindArgs = [...arguments];
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}

六 类数组转化为数组

// Array.from
Array.from(document.querySelectorAll('div'));
// Array.prototype.slice.call()
Array.prototype.slice.call(document.querySelectorAll('div'));
// 扩展运算符
[...document.querySelectorAll('div')];
// 利用concat
Array.prototype.concat.apply([], document.querySelectorAll('div'));

七 深浅拷贝

// 浅拷贝:只考虑对象类型
function shallowCopy(obj) {
    if (typeof obj !== 'object') return;
    
    let newObj = obj instanceof Array ? [] : {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = obj[key];
        }
    }
    return newObj;
}

// 深拷贝
const isObject = (target) => (typeof target === "object" || typeof target === "function") && target !== null;

function deepClone(target, map = new WeakMap()) {
    if (map.get(target)) {
        return target;
    }
    // 获取当前值的构造函数:获取它的类型
    let constructor = target.constructor;
    // 检测当前对象target是否与正则、日期格式对象匹配
    if (/^(RegExp|Date)$/i.test(constructor.name)) {
        // 创建一个新的特殊对象(正则类/日期类)的实例
        return new constructor(target);  
    }
    if (isObject(target)) {
        map.set(target, true);  // 为循环引用的对象做标记
        const cloneTarget = Array.isArray(target) ? [] : {};
        for (let prop in target) {
            if (target.hasOwnProperty(prop)) {
                cloneTarget[prop] = deepClone(target[prop], map);
            }
        }
        return cloneTarget;
    } else {
        return target;
    }
}

八 发布订阅模式

class EventEmitter {
    constructor() {
        this.cache = {};
    }
    on(name, fn) {
        if (this.cache[name]) {
            this.cache[name].push(fn);
        } else {
            this.cache[name] = [fn];
        }
    }
    off(name, fn) {
        let tasks = this.cache[name];
        if (tasks) {
            const index = tasks.findIndex(f => f === fn || f.callback === fn);
            if (index >= 0) {
                tasks.splice(index, 1);
            }
        }
    }
    emit(name, once = false, ...args) {
        if (this.cache[name]) {
            // 创建副本,如果回调函数内继续注册相同事件,会造成死循环
            let tasks = this.cache[name].slice();
            for (let fn of tasks) {
                fn(...args);
            }
            if (once) {
                delete this.cache[name];
            }
        }
    }
}

// 测试
let eventBus = new EventEmitter();

let fn1 = function(name, age) {
	console.log(`${name} ${age}`); // '布兰 12'
};

let fn2 = function(name, age) {
	console.log(`hello, ${name} ${age}`); // 'hello, 布兰 12'
};

eventBus.on('aaa', fn1);
eventBus.on('aaa', fn2);
eventBus.emit('aaa', false, '布兰', 12);

九 解析URL参数为对象

function parseParam(url) {
    const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将?后面的字符串取出来
    const paramsArr = paramsStr.split('&'); // 将字符串以&分割后存到数组中
    let paramsObj = {};
    // 将params存到对象中
    paramsArr.forEach(param => {
        if (/=/.test(param)) { // 处理有 value 的参数
            let [key, val] = param.split('='); // 分割key和value
            val = decodeURIComponent(val); // 解码
            val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字
    
            if (paramsObj.hasOwnProperty(key)) { // 如果对象有key,则添加一个值
                paramsObj[key] = [].concat(paramsObj[key], val);
            } else { // 如果对象没有这个key,创建key并设置值
                paramsObj[key] = val;
            }
        } else { // 处理没有value的参数
            paramsObj[param] = true;
        }
    })
    
    return paramsObj;
}

十 字符串模版

function render(template, data) {
    const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
    if (reg.test(template)) { // 判断模板里是否有模板字符串
        const name = reg.exec(template)[1]; // 查找当前模板里第一个模板字符串的字段
        template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
        return render(template, data); // 递归的渲染并返回渲染后的结构
    }
    return template; // 如果模板没有模板字符串直接返回
}

let template = '我是{{name}},年龄{{age}},性别{{sex}}';

let person = {
    name: '布兰',
    age: 12
};

render(template, person); // 我是布兰,年龄12,性别undefined

十一 函数防抖

触发高频事件N秒后只会执行一次,如果N秒内事件再次触发,则会重新计时。

支持以下功能:

  • 支持立即执行;
  • 函数可能有返回值;
  • 支持取消功能;
function debounce(func, wait, immediate) {
    var timeout, result;
    
    var debounced = function () {
        var context = this;
        var args = arguments;
        
        if (timeout) clearTimeout(timeout);
        if (immediate) {
            // 如果已经执行过,不再执行
            var callNow = !timeout;
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
            if (callNow) result = func.apply(context, args)
        } else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
        return result;
    };

    debounced.cancel = function() {
        clearTimeout(timeout);
        timeout = null;
    };

    return debounced;
}

十二 函数节流

触发高频事件,且N秒内只执行一次。

支持取消节流,另外通过传入第三个参数,options.leading来表示是否可以立即执行一次,opitons.trailing表示结束调用的时候是否还要执行一次,默认都是true,注意设置的时候不能同时将leading或trailing设置为false。

function throttle(func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    if (!options) options = {};

    var later = function() {
        previous = options.leading === false ? 0 : new Date().getTime();
        timeout = null;
        func.apply(context, args);
        if (!timeout) context = args = null;
    };

    var throttled = function() {
        var now = new Date().getTime();
        if (!previous && options.leading === false) previous = now;
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            func.apply(context, args);
            if (!timeout) context = args = null;
        } else if (!timeout && options.trailing !== false) {
            timeout = setTimeout(later, remaining);
        }
    };
    
    throttled.cancel = function() {
        clearTimeout(timeout);
        previous = 0;
        timeout = null;
    }
    return throttled;
}

十三 函数柯里化

函数柯里化其实就是将使用多个参数的函数转换成一系列使用一个参数的函数的技术。

function curry(fn) {
    const curried = (...args) => {
        if (args.length >= fn.length) return fn(...args);
        return (...arg) => curried(...args, ...arg);
    };
    return curried;
}

十四 偏函数

偏函数就是将一个n参的函数转换成固定x参的函数,剩余参数(n - x)将在下次调用全部传入。

function partial(fn, ...args) {
    return (...arg) => {
        return fn(...args, ...arg);
    }
}

偏函数可以和柯里化一样能实现占位功能,比如:

function clg(a, b, c) {
    console.log(a, b, c)
}

let partialClg = partial(clg, '_', 2)
partialClg(1, 3)  // 依次打印:1, 2, 3

十五 JSONP

JSONP核心原理:script标签不受同源策略约束,所以可以用来进行跨域请求,优点是兼容性好,但是只能用于GET 请求。

const jsonp = ({ url, params, callbackName }) => {
    const generateUrl = () => {
        let dataSrc = '';
        for (let key in params) {
            if (params.hasOwnProperty(key)) {
                dataSrc += `${key}=${params[key]}&`;
            }
        }
        dataSrc += `callback=${callbackName}`;
        return `${url}?${dataSrc}`;
    };
    return new Promise((resolve, reject) => {
        const scriptEle = document.createElement('script');
        scriptEle.src = generateUrl();
        document.body.appendChild(scriptEle);
        window[callbackName] = data => {
            resolve(data);
            document.removeChild(scriptEle);
        }
    })
};

十六 AJAX

const getJSON = function(url) {
    return new Promise((resolve, reject) => {
        const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp');
        xhr.open('GET', url, false);
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.onreadystatechange = function() {
            if (xhr.readyState !== 4) return;
            if (xhr.status === 200 || xhr.status === 304) {
                resolve(xhr.responseText);
            } else {
                reject(new Error(xhr.responseText));
            }
        }
        xhr.send();
    })
}

十七 实现new关键字

new运算符用来创建用户自定义的对象类型的实例或者具有构造函数的内置对象的实例。

实现要点:

  • new会产生一个新对象
  • 新对象需要能够访问到构造函数的属性,所以需要重新指定它的原型
  • 构造函数可能会显示返回
function newOperator(ctor, ...args) {
  if (typeof ctor !== 'function') {
    throw new TypeError('Type Error');
  }
  const obj = Object.create(ctor.prototype);
  const res = ctor.apply(obj, args);

  const isObject = typeof res === 'object' && res !== null;
  const isFunction = typeof res === 'function';
  return isObject || isFunction ? res : obj;
}

十八 实现instanceof关键字

instanceof就是判断构造函数的prototype属性是否出现在实例的原型链上

function instanceOf(left, right) {
    let proto = left.__proto__;
    while (true) {
        if (proto === null) return false
        if (proto === right.prototype) {
            return true;
        }
        proto = proto.__proto__;
    }
}

十九 实现Object.create

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。

Object.create = function(proto, propertyObject = undefined) {
    if (typeof proto !== 'object' && typeof proto !== 'function') {
        throw new TypeError('Object prototype may only be an Object or null.');
    if (propertyObject == null) {
        new TypeError('Cannot convert undefined or null to object');
    }
    
    function F() {}
    F.prototype = proto;
    const obj = new F();
    
    if (propertyObject != undefined) {
        Object.defineProperties(obj, propertyObject)
    }
    
    if (proto === null) {
        // 创建一个没有原型对象的对象,Object.create(null)
        obj.__proto__ = null
    }
    return obj;
}

二十 实现Object.assign

Object.assign = function(target, ...source) {
    if (target == null) {
        throw new TypeError('Cannot convert undefined or null to object');
    }
    let ret = Object(target);
    source.forEach(function(obj) {
        if (obj != null) {
            for (let key in obj) {
                if (obj.hasOwnProperty(key)) {
                    ret[key] = obj[key]
                }
            }
        }
    })
    return ret;
}

二十一 Object.is

Object.is解决的主要是这两个问题:

  1. +0 === -0  // true
  2. NaN === NaN // false

源码实现:

const is= (x, y) => {
  if (x === y) {
    // +0和-0应该不相等
    return x !== 0 || y !== 0 || 1/x === 1/y;
  } else {
    return x !== x && y !== y;
  }
}

二十二 实现JSON.stringify

基本数据类型:

  • undefined转换之后仍是 undefined(类型也是undefined)
  • boolean值转换之后是字符串"false"/"true"
  • number类型(除了NaN和Infinity)转换之后是字符串类型的数值
  • symbol转换之后是undefined
  • null转换之后是字符串"null"
  • string转换之后仍是string
  • NaN和Infinity转换之后是字符串"null"

函数类型:转换之后是undefined

对象类型(非函数):

  • 如果是一个数组:如果属性值中出现了undefined、任意的函数以及symbol,转换成字符串"null" ;
  • 如果是RegExp对象:返回 {} (类型是string);
  • 如果是Date对象,返回 Date 的toJSON 字符串值;
  • 如果是普通对象:
    • 如果有 toJSON() 方法,那么序列化 toJSON() 的返回值。
    • 如果属性值中出现了undefined、任意的函数以及symbol值,忽略。
    • 所有以symbol为属性键的属性都会被完全忽略掉。
function jsonStringify(data) {
    let dataType = typeof data;
    
    if (dataType !== 'object') {
        let result = data;
        //data 可能是 string/number/null/undefined/boolean
        if (Number.isNaN(data) || data === Infinity) {
            //NaN 和 Infinity 序列化返回 "null"
            result = "null";
        } else if (dataType === 'function' || dataType === 'undefined' || dataType === 'symbol') {
            //function 、undefined 、symbol 序列化返回 undefined
            return undefined;
        } else if (dataType === 'string') {
            result = '"' + data + '"';
        }
        //boolean 返回 String()
        return String(result);
    } else if (dataType === 'object') {
        if (data === null) {
            return "null"
        } else if (data.toJSON && typeof data.toJSON === 'function') {
            return jsonStringify(data.toJSON());
        } else if (data instanceof Array) {
            let result = [];
            //如果是数组
            //toJSON 方法可以存在于原型链中
            data.forEach((item, index) => {
                if (typeof item === 'undefined' || typeof item === 'function' || typeof item === 'symbol') {
                    result[index] = "null";
                } else {
                    result[index] = jsonStringify(item);
                }
            });
            result = "[" + result + "]";
            return result.replace(/'/g, '"');
            
        } else {
            //普通对象
            /**
             * 循环引用抛错(暂未检测,循环引用时,堆栈溢出)
             * symbol key 忽略
             * undefined、函数、symbol 为属性值,被忽略
             */
            let result = [];
            Object.keys(data).forEach((item, index) => {
                if (typeof item !== 'symbol') {
                    //key 如果是symbol对象,忽略
                    if (data[item] !== undefined && typeof data[item] !== 'function'
                        && typeof data[item] !== 'symbol') {
                        //键值如果是 undefined、函数、symbol 为属性值,忽略
                        result.push('"' + item + '"' + ":" + jsonStringify(data[item]));
                    }
                }
            });
            return ("{" + result + "}").replace(/'/g, '"');
        }
    }
}

注意事项:对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。

二十三 实现JSON.parse

两种方法实现:

  • eval实现
  • new Function实现
// eval实现
var json = '{"a":"1", "b":2}';
var obj = eval("(" + json + ")");  // obj就是json反序列化之后得到的对象

// new Function实现
var json = '{"name":"小姐姐", "age":20}';
var obj = (new Function('return ' + json))();

二十四 实现Promise

1.手写Promise

const PENDING = 'PENDING';      // 进行中
const FULFILLED = 'FULFILLED';  // 已成功
const REJECTED = 'REJECTED';    // 已失败

class Promise {
  constructor(exector) {
    // 初始化状态
    this.status = PENDING;
    // 将成功、失败结果放在this上,便于then、catch访问
    this.value = undefined;
    this.reason = undefined;
    // 成功态回调函数队列
    this.onFulfilledCallbacks = [];
    // 失败态回调函数队列
    this.onRejectedCallbacks = [];

    const resolve = value => {
      // 只有进行中状态才能更改状态
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        // 成功态函数依次执行
        this.onFulfilledCallbacks.forEach(fn => fn(this.value));
      }
    }
    const reject = reason => {
      // 只有进行中状态才能更改状态
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        // 失败态函数依次执行
        this.onRejectedCallbacks.forEach(fn => fn(this.reason))
      }
    }
    try {
      // 立即执行executor
      // 把内部的resolve和reject传入executor,用户可调用resolve和reject
      exector(resolve, reject);
    } catch(e) {
      // executor执行出错,将错误内容reject抛出去
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function'? onRejected:
      reason => { throw new Error(reason instanceof Error ? reason.message:reason) }
    // 保存this
    const self = this;
    return new Promise((resolve, reject) => {
      if (self.status === PENDING) {
        self.onFulfilledCallbacks.push(() => {
          // try捕获错误
          try {
            // 模拟微任务
            setTimeout(() => {
              const result = onFulfilled(self.value);
              // 分两种情况:
              // 1. 回调函数返回值是Promise,执行then操作
              // 2. 如果不是Promise,调用新Promise的resolve函数
              result instanceof Promise ? result.then(resolve, reject) : resolve(result);
            })
          } catch(e) {
            reject(e);
          }
        });
        self.onRejectedCallbacks.push(() => {
          // 以下同理
          try {
            setTimeout(() => {
              const result = onRejected(self.reason);
              // 不同点:此时是reject
              result instanceof Promise ? result.then(resolve, reject) : reject(result);
            })
          } catch(e) {
            reject(e);
          }
        })
      } else if (self.status === FULFILLED) {
        try {
          setTimeout(() => {
            const result = onFulfilled(self.value);
            result instanceof Promise ? result.then(resolve, reject) : resolve(result);
          });
        } catch(e) {
          reject(e);
        }
      } else if (self.status === REJECTED){
        try {
          setTimeout(() => {
            const result = onRejected(self.reason);
            result instanceof Promise ? result.then(resolve, reject) : reject(result);
          })
        } catch(e) {
          reject(e);
        }
      }
    });
  }
  catch(onRejected) {
    return this.then(null, onRejected);
  }
  finally(fn) {
  	this.then((res) => {
    	Promise.resolve(fn()).then((res) => {
      	return res;
      });
    }, (err) => {
    	Promise.reject(fn()).then((err) => {
      	throw err;
      });
    });
  }
  static resolve(value) {
    if (value instanceof Promise) {
      // 如果是Promise实例,直接返回
      return value;
    } else {
      // 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED
      return new Promise((resolve, reject) => resolve(value));
    }
  }
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    })
  }
}

2.Promise.all

Promise.all = function(promiseArr) {
  const promises = Array.from(promiseArr);
  return new Promise((resolve, reject) => {
    const result = [];
    let index = 0;
    for (let i = 0; i < promises.length; i++) {
      Promise.resolve(promises[i])
      .then(res => {
        result[i] = res;
        index++;
        if (index === promises.length) {
          resolve(result);
        }
      })
      .catch(err => reject(err));
    }
  })
}

3.Promise.race

Promise.race = function(promiseArr) {
  const promises = Array.from(promiseArr);
  return new Promise((resolve, reject) => {
    promises.forEach(p => {
      // 如果不是Promise实例需要转化为Promise实例
      Promise.resolve(p).then(
        val => resolve(val),
        err => reject(err),
      )
    })
  })
}

4.Promise.allSettled

规则是这样:

  • 所有 Promise 的状态都变化了,那么新返回一个状态是 fulfilled 的 Promise,且它的值是一个数组,数组的每项由所有 Promise 的值和状态组成的对象;
  • 如果有一个是 pending 的 Promise,则返回一个状态是 pending 的新实例;
Promise.allSettled = function(promiseArr) {
    let result = [];
    const promises = Array.from(promiseArr);
    
    return new Promise((resolve, reject) => {
        promises.forEach((p, i) => {
            Promise.resolve(p).then(val => {
                result.push({
                    status: 'fulfilled',
                    value: val
                });
                if (result.length === promiseArr.length) {
                    resolve(result) ;
                }
            }, err => {
                result.push({
                    status: 'rejected',
                    reason: err
                });
                if (result.length === promiseArr.length) {
                    resolve(result) ;
                }
            })
        })  
    })   
}

5.Promie.any

规则是这样:

  • 所有Promise都是rejected,则返回状态是rejected的新Promsie,且值为AggregateError的错误;
  • 只要有一个是fulfilled状态的,则返回第一个是fulfilled的新实例;
  • 其他情况都会返回一个pending的新实例;
Promise.any = function(promiseArr) {
    let index = 0;
    const promises = Array.from(promiseArr);
    
    return new Promise((resolve, reject) => {
        if (promises.length === 0) return; 
        promises.forEach((p, i) => {
            Promise.resolve(p).then(val => {
                resolve(val);
            }, err => {
                index++;
                if (index === promises.length) {
                  reject(new AggregateError('All promises were rejected'));
                }
            })
        })
    })
}

二十五 实现async-await

function asyncToGenerator(genF) {
	return new Promise(function (resolve, reject) {
  	const gen = genF(); // 生成一个迭代器
    const step = (type, args) => {
    	let next;
      try {
      	next = gen[type](args);
      } catch(e) {
      	return reject(e);
      }
      
      const { done, value } = next;
      
      if (done) return resolve(value);
      Promise.resolve(value).then(val => step('next', val), err => step('throw', err));
    };
    
    step('next');
  });
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值