test

110 阅读9分钟
//防抖
function debounce(fn){
    let timeout = null
    return function(){
        clearTimeout(timeout)
        timeout = setTimeout(()=>{
            fn.apply(this,arguments)
        },500)
    }
}

//节流
function throttle(fn){
    let canRun = true
    return function(){
        if(!canRun) return false
        canRun = false
        setTimeout(()=>{
            fn.apply(this,arguments)
            canRun = true
        },500)
    }
}

//函数柯里化
function add(){
    let _args = Array.prototype.slice.call(arguments)
    var _adder = function(){
        _args.push(...arguments)
        return _adder
    }
    _adder.toString = function(){
        return _args.reduce((a,b)=>{
            return a+b
        })
    }
    return _adder
}
console.log(add(1)(2)(3));
console.log(add(1, 2)(3));

//函数柯理化 ----- 头条
function curry(fn,...args){
    return function cur(){
        if(arguments.length === 0){
            return fn.apply(this,args)
        }else{
            args = args.concat([...arguments])
            return cur
        }
    }
}
function sum(a, b, c) {
    return a + b + c
}

let myCurry = curry(sum, 3)
console.log(myCurry(2, 4)());

//js reduce: https://juejin.cn/post/6935737320335228936
/*1、定义和用法
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。reduce() 可以作为一个高阶函数,用于函数的 compose。
注意: reduce() 对于空数组是不会执行回调函数的。
2、语法
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
1、total (必需。初始值, 或者计算结束后的返回值)。
2、currentValue (必需。当前元素)
3、currentIndex (可选。当前元素的索引)
4、arr (可选。当前元素所属的数组对象。)
5、initialValue (可选。传递给函数的初始值)*/
//5、reduce简单用法:作为累加器函数,进行求和、求乘
var  arr = [1, 2, 3, 4];
var sum = arr.reduce((x,y)=>x+y)
var mul = arr.reduce((x,y)=>x*y)
console.log( sum ); //求和,10
console.log( mul ); //求乘积,24

//6、reduce高级用法:
//(1)计算数组中每个元素出现的次数
let  language = ['Chinese', 'English', 'Japanese', 'Chinese', 'English'];
let  languageNum = language.reduce((total,cur)=>{
    if(cur in total){
        total[cur]++
    }else{
        total[cur] = 1
    }
    return total
},{})
console.log(languageNum); //{Chinese: 2, English: 2, Japanese: 1}

//(2)数组去重
let arr2 = [1,2,3,4,4,1]
let newArr = arr2.reduce((total,cur)=>{
    if(!total.includes(cur)){
        return total.concat(cur)
    }else{
        return total
    }
},[])
console.log(newArr);// [1, 2, 3, 4]

//(3)求数组最大值(最小值同理)
let arr3 = [23,123,342,12];
let max = arr3.reduce((total,cur,index,arr) => {
    return total > cur ? total : cur
});
console.log(max) //342

//(4)求最大值
let arr4 = [68,168,666,12];
let max4 = arr4.reduce((total,cur,index,arr) => {
    return total > cur ? total : cur
});
console.log(max4) //666

//(5)将字符串转换为整数
let str = '1688';
let strParseInt = str.split('')                   // 得到 ['1', '6', '8', '8']
    .map(item => {return item.charCodeAt() - 48}) // 得到 [1, 6, 8, 8]
    .reduce((a, b) => {return a * 10 + b})        // 得到 1688
console.log(strParseInt)

//(6)二维数组转化为一维
let arr6= [[0, 1], [2, 3], [4, 5]]
let newArr6 = arr6.reduce((total,cur)=>{
    return total.concat(cur)
},[])
console.log(newArr6); // [0, 1, 2, 3, 4, 5]

//(7)多维数组转化为一维
/*let arr7 = [[0, 1], [2, 3], [4,[5,6,7]]]
const newArr7 = function(arr7){
    return arr7.reduce((total,cur)=>total.concat(Array.isArray(cur)?newArr7(cur):cur),[])
}
console.log(newArr7(arr7)); //[0, 1, 2, 3, 4, 5, 6, 7]*/

//(8)对象里的属性求和
var result = [{
    subject: 'male',
    score: 45
}, {
    subject: 'female',
    score: 50
}];

var sum = result.reduce(function(total, cur) {
    return cur.score + total;
}, 0);
console.log(sum) //95

/*
//reduce在海外H5的应用
import resources from '@/utils/locales/resources.js'
import i18n from "i18next";
//获取不确定路径深度的值
const getObj = (obj,path,defaultVal) => {
    let pathCopy = path.split('.')
    return pathCopy.reduce((pre,cur)=>{
        return (pre||{})[cur]
    },obj) || defaultVal
}
let t = (type)=>{
    let obj = resources[i18n.language]['translation']
    return getObj(obj,type,'')
}
export default t*/

//实现原生reduce函数
Array.prototype.reduceFunc = function(fn,initValue){
    let acc = initValue || this[0]
    const startIndex = initValue?0:1
    for(let i=startIndex;i<this.length;i++){
        acc = fn(acc,this[i],i,this)
    }
    return acc
}
var arr = [1,2,3,4]
var result=arr.reduceFunc((a,b)=>{
    return a+b
},0)
console.log(result)

//实现bind函数
Function.prototype.bind = function(fn){
    const args = [].slice.call(arguments,1)
    const context = this
    return function(){
        const _args = args.concat([].slice.call(arguments,1))
        return context.apply(fn,_args)
    }
}

//js实现filter
Array.prototype.my_filter = function (callback) {
    if (!Array.isArray(this) || !this.length || typeof callback !== 'function') {
        return []
    } else {
        let result = [];
        for (let index = 0; index < this.length; index++) {
            const element = this[index];
            if (callback(element, index, this)) {
                result.push(element)
            }
        }
        return result
    }
}

let arr = [1, 2, 3, 4, 5]
let res = arr.my_filter((ele, i) => {
    return ele % 2 === 0
})
console.log(res)//[2,4]

//js实现开方函数
/*
https://www.cnblogs.com/mapingchuan/p/13038961.html
js实现sqrt开方函数(二分法)
每次取一半的数和当前值做对比,如果比当前值大:下次的起始值不变,末尾值减一半(起始值与末尾值的和的一半);如果比当前值小:下次的起始值加一半(起始值与末尾值的和的一半),末尾值不变;直到起始值与末尾值的差值在指定范围内才结束,注意由于浮点数计算问题需要阻止死循环的情况(“起始值与末尾值的和的一半”与原来的值仍旧相同)
如1000:第一次的值为(0,1000)
1、 500*500        >1000  下一次用(0,500)
2、 250*250     >1000  下一次用(0,250)
3、 125*125              >1000        下一次用  (0,125)
4、 62.5*62.5            >1000        下一次用  (0,62.5)
5、 31.25*31.25        <1000        下一次用  (31.25,62.5)
6、 46.875*46.875   >1000         下一次用  (31.25,46.875)
...
...
...
*/
function sqrt(num) {
    function sqrtWrapper(min, max) {
        let current = (min + max) / 2;
        let nextMin = min, nextMax = max;
        if (current * current > num) {
            nextMax = current;
        } else {
            nextMin = current;
        }
        if (min === nextMin && max === nextMax) {
            return current
        }
        else if (nextMax - nextMin < (1 / new Array(17).fill(10).reduce((a, b) => a * b, 1))) {
            return current;
        } else {
            return sqrtWrapper(nextMin, nextMax);
        }
    }
    return sqrtWrapper(0, num);
}
console.time();
console.log(sqrt(3));
console.timeEnd();

//js实现useState baidu
//useState
let _state=[]
let index = 0
const myUseState = num =>{
    const currentIndex = index
    _state[currentIndex] = _state[currentIndex] === undefined ?num:_state[currentIndex]
    const setN = num1=>{
        _state[currentIndex]=num1;
        render()
    }
    index+=1
    return [_state[currentIndex],setN]
}
function render(){
    index = 0
    ReactDOM.render(<App />,rootElement)
}

//instensof实现
function instensof(left,right){
    let leftVal = left.__proto__
    let rightVal = right.prototype
    while(true){
        if(leftVal === null){
            return false
        }
        if(leftVal === rightVal){
            return true
        }
        leftVal = leftVal.__proto__
    }

}


//
// new实现原理
function _new(Func, ...args) {
    //默认创建一个实例对象(而且是属于当前这个类的一个实例)
    let obj = {};

    //也会把类当做普通函数执行
    //执行的时候要保证函数中的this指向创建的实例
    let result = Func.call(obj, ...args);

    //若客户自己返回引用值,则以自己返回的为主,否则返回创建的实例
    if ((result !== null && typeof result === "object") || (typeof result === "function")) {
        return result;
    }
    return obj;
}

//js实现深拷贝
let deepCopy = (data) => {
    const t = typeOf(data);
    let o;

    if (t
        === 'array') {
        o = [];
    } else if (t === 'object') {
        o = {};
    } else {
        return data;
    }

    if (t === 'array') {
        for (let i = 0; i < data.length; i++) {
            o.push(deepCopy(data[i]));
        }
    } else if (t === 'object') {
        for (let i in data) {
            o[i] = deepCopy(data[i]);
        }

    }
    return o;
};
function typeOf(obj) {
    const toString = Object.prototype.toString;
    const map = {
        '[object Boolean]': 'boolean',
        '[object Number]': 'number',
        '[object String]': 'string',
        '[object Function]': 'function',
        '[object Array]': 'array',
        '[object Date]': 'date',
        '[object RegExp]': 'regExp',
        '[object Undefined]': 'undefined',
        '[object Null]': 'null',
        '[object Object]': 'object'
    };
    return map[toString.call(obj)];
}



//冒泡排序 https://segmentfault.com/a/1190000014175918
/*
数组中有 n 个数,比较每相邻两个数,如果前者大于后者,就把两个数交换位置;
这样一来,第一轮就可以选出一个最大的数放在最后面;
那么经过 n-1(数组的 length - 1) 轮,就完成了所有数的排序。
*/
/*
性能
时间复杂度: 平均时间复杂度O(n*n) 、最好情况O(n)、最差情况O(n*n)
空间复杂度: O(1)
稳定性:稳定

时间复杂度指的是一个算法执行所耗费的时间
空间复杂度指运行完一个程序所需内存的大小
稳定指,如果a=b,a在b的前面,排序后a仍然在b的前面
不稳定指,如果a=b,a在b的前面,排序后可能会交换位置*/
/*冒泡 升序*/
var arr = [3,4,1,2];
function bubbleSort(arr){
    var len = arr.length-1
    var flag = true
    for(var i = 0;i<len;i++){
        for(var j=0;j<len-i;j++){
            if(arr[j]>arr[j+1]){
                var temp = arr[j]
                arr[j]=arr[j+1]
                arr[j+1]=temp
                flag=false
            }
        }
        if(flag){
            break
        }
    }
    return arr

}
console.log(bubbleSort(arr))

//快速排序  https://segmentfault.com/a/1190000017814119
/*
快速排序的3个基本步骤:
从数组中选择一个元素作为基准点
排序数组,所有比基准值小的元素摆放在左边,而大于基准值的摆放在右边。每次分割结束以后基准值会插入到中间去。
最后利用递归,将摆放在左边的数组和右边的数组在进行一次上述的1和2操作。*/
/*在平均状况下,排序n个项目要O(nLogn)次比较。在最坏状况下则需要O(n^2)次比较,但这种状况并不常见*/

var arrQuick = [15,13,3,2,7]
function quickSort(arrQuick){
    if(arrQuick.length<=1){
        return arrQuick
    }
    var pivotIndex = Math.floor(arrQuick.length/2)
    var pivot = arrQuick.splice(pivotIndex,1)[0]
    var left = []
    var right = []
    for(var i=0;i<arrQuick.length;i++){
        if(arrQuick[i]<pivot){
            left.push(arrQuick[i])
        }else{
            right.push(arrQuick[i])
        }
    }
    return quickSort(left).concat([pivot],quickSort(right))

}
console.log(quickSort(arrQuick))

//promise实现控制并发
//使用递归控制并发
function request(urls,max,callback){
    let urlsCopy = [...urls] //浅拷贝复制参数,防止修改外部变量
    let index = 0 //当前并发数
    function req(){
        index++
        console.log('当前并发数-----',index)
        fetch(urlsCopy.shift()).then(res=>{ //把数组的第一个元素从其中删除,并返回第一个元素的值,且改变原数组
            index--
            if(urlsCopy.length>0){ //如果传入的urlsCopy还有长度,则递归
                req()
            }else if(index===0){ //如果传入的urlsCopy长度为0,且当前并发数也为0,说明所有任务执行完毕,则调用callback
                callback && callback()
            }
        })
        if(index<max){ //这里如果当前并发数小于设置的最大并发数max,则递归
            req()
        }
    }
    req()
}
 function fetch(url){
    let time = Math.random()*1000 +1000 //模拟异步任务耗时,1000ms~2000ms随机
     console.log("time-------",time)
     return new Promise((resolve,reject)=>{ //模拟网络请求
         try{
             setTimeout(()=>{resolve()},time)
         }catch(e){
             reject(e)
         }
     })
 }
let urlArray=[1,2,3,4,5,6,7,8,9] //模拟请求的url数组
function cb(){ //任务都执行完成后的回调
    console.log('finally')
    console.timeEnd()
    //console.log(11,urlArray)
}
console.time()//打印任务开始时间
request(urlArray,8,cb)

//js转为树层结构
var source = [{
    id: 1,
    pid: 0,
    name: 'body'
}, {
    id: 2,
    pid: 1,
    name: 'title'
}, {
    id: 3,
    pid: 1,
    name: 'div'
}, {
    id: 4,
    pid: 3,
    name: 'span'
}, {
    id: 5,
    pid: 3,
    name: 'icon'
}, {
    id: 6,
    pid: 4,
    name: 'subspan'
}]

function toTree(data) {
    let result = []
    if(!Array.isArray(data)) {
        return result
    }
    data.forEach(item => {
        delete item.children;
    });
    let map = {};
    data.forEach(item => {
        map[item.id] = item;
    });
    data.forEach(item => {
        let parent = map[item.pid];
        if(parent) {
            (parent.children || (parent.children = [])).push(item);
        } else {
            result.push(item);
        }
    });
    return result;
}
console.log(toTree(source))

//A-1 B-2
function convert(num) {
    let len = str.length
    let index = 0
    let res = ''

    function fn(num) {
        let _s = Math.floor(num / len) //取商
        let _y = num % len //取余
        if (num < len) {
            res = str[num - 1]
            return res
        }
        if (_s > len) {
            index++
            fn(_s)
        } else {
            let _str = ''
            for (let i = 0; i < _s; i++) {
                _str += str[i]
            }
            res = _str += str[_y - 1]
            return res
        }
        return res
    }
    fn(num)
    return res
}
console.log(convert(1));
console.log(convert(27));
console.log(convert(79));
console.log(convert(556));

//比较版本号
function compareVersion(version1, version2) {
    version1 = version1.split(".");
    version2 = version2.split(".");
    var len = Math.max(version1.length,version2.length);
    //如果当前版本号小于len,补0
    while (version1.length < len){
        version1.push("0")
    }
    while (version2.length < len){
        version2.push("0")
    }
    for(var i = 0; i < len; i++){
        var num1 = parseInt(version1[i]);
        var num2 = parseInt(version2[i]);
        if(num1 > num2){
            return 1
        }else{
            return -1
        }

    }
    return 0
}

// 测试用例
console.log(compareVersion("0.20.7", "0.20.8") === -1);  // -1
console.log(compareVersion("0.20.9", "0.20.8") === 1);  // 1
console.log(compareVersion("0.20.08", "0.20.8") === 0);  // 0
console.log(compareVersion("0.20.08", "0.20.8.1") === -1); // -1
console.log(compareVersion("0.20.8.0", "0.20.8") === 0);  // 0
console.log(compareVersion("0.20.8.1", "0.20.8") === 1);  // 1
console.log(compareVersion("0.020", "0.20") === 0);  // 0

//js实现反转链表:https://leetcode-cn.com/problems/reverse-linked-list-ii/solution/fan-zhuan-lian-biao-ii-by-leetcode-solut-teyq/
//js实现反转单向链表:https://segmentfault.com/a/1190000022040766
/*function ListNode(x){
    this.val = x;
    this.next = null;
}*/
//时间复杂度O(n),空间复杂度O(1)
function ReverseList(pHead) {
    //保存当前结点
    let cur=pHead;
    if(cur==null||cur.next==null) return cur;
    let prev=null;   //前一个结点
    let next=null;   //下一个结点
    while(cur){
        next=cur.next;  //保存下一个结点
        cur.next=prev;   //当前结点的下一个结点指向前面的节点
        prev=cur;        //把所有节点往后挪
        cur=next;
    }
    return prev;
}


// 二叉树路径和为target的所有路径
function fn(root,target){
    let result = []
    function handle(root,target,paths,path){
        if(root === null) return []
        path = [...path,root.value]
        paths.push(root.value)
        if(root.left === null && root.right === null && sum === target){
            result.push(paths)
            return
        }
        handle(root.left,target-root.value,paths,path)
        handle(root.right,target-root.value,paths,path)
    }
    handle(root,target,result,[])
}

//中间件
function fn1(ctx,next){
    console.log(1);
    next();
    console.log(11)
}

function fn2(ctx,next){
    console.log(2);
    next()
    ctx.id = 5
    console.log(22,ctx)
}

function fn3(ctx,next){
    ctx.id++
    console.log(3);
    next();
    console.log(33)
}

function compose(arr){
    let index = 0
    let len = arr.length
    return function (ctx){
        function next(){
            index++
            if(index>=len) return
            arr[index](ctx,next)
        }
        arr[index](ctx,next)
    }
}
compose([fn1,fn2,fn3])({id:4})

// 判断小括号是否成对出现
function isToggle(str){
  let arr= []
  for(let i =0;i<str.length;i++){
    if (str[i] === '('){
      arr.push('(')
    }else if (str[i] === ')'){
      if (arr.length === 0){
        return false
      }else{
        arr.pop()
      }
    }
  }
  if (arr.length === 0 ) {
    return true
  }else{
    return false
  }
}
console.log(isToggle(')221px(2323)(238(23)324)'));
console.log(isToggle('221px(2323)(238(23)324)('));



//js实现链表:https://segmentfault.com/a/1190000019076293
class LinkedList {
    constructor() {
        this.head = null;
        this.tail = null;
        this.length = 0;
    }
    //isEmpty() 方法是一个帮助函数,如果链表为空,则返回true。
    isEmpty() {
        return this.length === 0;
    }
    //这个实用程序方法用于打印链表中的节点,仅用于调试目的。
    printList () {
        const nodes = [];
        let current = this.head;
        while (current) {
            nodes.push(current.value);
            current = current.next;
        }
        return nodes.join(' -> ');
    }
    push(value) {
        const node = Node(value);
        // The list is empty
        if (this.head === null) {
            this.head = node;
            this.tail = node;
            this.length++;
            return node;
        }
        this.tail.next = node;
        this.tail = node;
        this.length++;
    }
    pop() {
        if (this.isEmpty()) {
            return null;
        }
        const nodeToRemove = this.tail;
        // There's only one node!
        if (this.head === this.tail) {
            this.head = null;
            this.tail = null;
            this.length--;
            return nodeToRemove;
        }

        let currentNode = this.head;
        let secondToLastNode;

        // Start at the front and iterate until
        // we find the second to last node
        while (currentNode) {
            if (currentNode.next === this.tail) {
                // Move the pointer for the second to last node
                secondToLastNode = currentNode;
                break;
            }
            currentNode = currentNode.next;
        }
        // Pop off that node
        secondToLastNode.next = null;
        // Move the tail to the second to last node
        this.tail = secondToLastNode;
        this.length--;

        // Initialized to this.tail
        return nodeToRemove;
    }
    get(index) {
        // Index is outside the bounds of the list
        if (index < 0 || index > this.length) {
            return null;
        }

        if (this.isEmpty()) {
            return null;
        }

        // We're at the head!
        if (index === 0 )  {
            return this.head;
        }

        let current = this.head;
        let iterator =  0;

        while (iterator < index) {
            iterator++;
            current = current.next;
        }

        return current;
    }
    delete(index) {
        // Index is outside the bounds of the list
        if (index < 0 || index > this.length - 1) {
            return null;
        }

        if (this.isEmpty()) {
            return null;
        }

        if (index === 0) {
            const nodeToDelete = this.head;
            this.head = this.head.next;
            this.length--;
            return nodeToDelete;
        }

        let current = this.head;
        let previous;
        let iterator = 0;

        while (iterator < index) {
            iterator++;
            previous = current;
            current = current.next;
        }
        const nodeToDelete = current;
        // Re-direct pointer to skip the element we're deleting
        previous.next = current.next;

        // We're at the end
        if(previous.next === null) {
            this.tail = previous;
        }

        this.length--;

        return nodeToDelete;
    }
}



//JavaScript 发布-订阅模式:https://segmentfault.com/a/1190000019260857
/*
1. 定义
发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。
订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),
当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,
由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。*/
/*实现思路:
1、创建一个对象
2、在该对象上创建一个缓存列表(调度中心)
3、on方法用来把函数fn都加到缓存列表中(订阅者注册事件到调度中心)
4、emit方法取到arguments里第一个当作event,根据event值去执行对应缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码)
5、off方法可以根据event值取消订阅(取消订阅)
6、once方法只监听一次,调用完毕后删除缓存函数(订阅一次)*/
/*1. 优点
对象之间解耦
异步编程中,可以更松耦合的代码编写
2. 缺点
创建订阅者本身要消耗一定的时间和内存
虽然可以弱化对象之间的联系,多个发布者和订阅者嵌套一起的时候,程序难以跟踪维护*/
let eventEmitter = {
    // 缓存列表
    list: {},
    // 订阅
    on (event, fn) {
        let _this = this;
        // 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表
        // 如有对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里
        (_this.list[event] || (_this.list[event] = [])).push(fn);
        return _this;
    },
    // 监听一次
    once (event, fn) {
        // 先绑定,调用后删除
        let _this = this;
        function on () {
            _this.off(event, on);
            fn.apply(_this, arguments);
        }
        on.fn = fn;
        _this.on(event, on);
        return _this;
    },
    // 取消订阅
    off (event, fn) {
        let _this = this;
        let fns = _this.list[event];
        // 如果缓存列表中没有相应的 fn,返回false
        if (!fns) return false;
        if (!fn) {
            // 如果没有传 fn 的话,就会将 event 值对应缓存列表中的 fn 都清空
            fns && (fns.length = 0);
        } else {
            // 若有 fn,遍历缓存列表,看看传入的 fn 与哪个函数相同,如果相同就直接从缓存列表中删掉即可
            let cb;
            for (let i = 0, cbLen = fns.length; i < cbLen; i++) {
                cb = fns[i];
                if (cb === fn || cb.fn === fn) {
                    fns.splice(i, 1);
                    break
                }
            }
        }
        return _this;
    },
    // 发布
    emit () {
        let _this = this;
        // 第一个参数是对应的 event 值,直接用数组的 shift 方法取出
        let event = [].shift.call(arguments),
            fns = [..._this.list[event]];
        // 如果缓存列表里没有 fn 就返回 false
        if (!fns || fns.length === 0) {
            return false;
        }
        // 遍历 event 值对应的缓存列表,依次执行 fn
        fns.forEach(fn => {
            fn.apply(_this, arguments);
        });
        return _this;
    }
};

function user1 (content) {
    console.log('用户1订阅了:', content);
}

function user2 (content) {
    console.log('用户2订阅了:', content);
}

function user3 (content) {
    console.log('用户3订阅了:', content);
}

function user4 (content) {
    console.log('用户4订阅了:', content);
}

// 订阅
eventEmitter.on('article1', user1);
eventEmitter.on('article1', user2);
eventEmitter.on('article1', user3);

// 取消user2方法的订阅
eventEmitter.off('article1', user2);

eventEmitter.once('article2', user4)

// 发布
eventEmitter.emit('article1', 'Javascript 发布-订阅模式');
eventEmitter.emit('article1', 'Javascript 发布-订阅模式');
eventEmitter.emit('article2', 'Javascript 观察者模式');
eventEmitter.emit('article2', 'Javascript 观察者模式');

// eventEmitter.on('article1', user3).emit('article1', 'test111');
/*
    用户1订阅了: Javascript 发布-订阅模式
    用户3订阅了: Javascript 发布-订阅模式
    用户1订阅了: Javascript 发布-订阅模式
    用户3订阅了: Javascript 发布-订阅模式
    用户4订阅了: Javascript 观察者模式
*/


/*Promise规定:
1、promise存在三个状态(state)pending、fulfilled、rejected
2、pending(等待态)为初始态,并可以转化为fulfilled(成功态)和rejected(失败态)
3、成功时,不可转为其他状态,且必须有一个不可改变的值(value)
4、失败时,不可转为其他状态,且必须有一个不可改变的原因(reason)
5、new Promise((resolve,reject)=>{resolve(value)}) resolve为成功,接收参数value,状态改变为fulfilled,不可再次改变
6、new Promise((resolve,reject)=>{reject(reason)}) reject为失败,接收参数为reason,状态改为rejected,不可再次改变
7、若是executor函数报错直接执行reject()
*/
// Promise 实现
function myPromise(fn){
    let self = this
    self.value = null
    self.callbackList = []
    self.status = 'pendding'
    function resolve(value){
        if(self.status === 'pendding'){
            self.status = 'fulfilled'
            self.value = value
            self.callbackList.forEach((callback)=>{
                callback && callback(value)
            })
        }
    }
    function reject(value){
        if(self.status === 'pendding'){
            self.status = 'rejected'
            self.value = value
            self.callbackList.forEach((callback)=>{
                callback && callback(value)
            })
        }
    }
    fn(resolve,reject)
}
myPromise.prototype.then = function (callback){
    this.callbackList.push(callback)
}

//Promise.all实现
Promise.myall = function(arr){
    let result = []
    let count = 0
    return new Promise((resolve, reject)=>{
        for (let i = 0;i<arr.length;i++){
            Promise.resolve(arr[i]).then((res)=>{
                result[i] = res
                count++
                if(count >= arr.length){
                    resolve(result)
                }
            },(err)=>{
                reject(err)
            })
        }
    })
}
var p1 = new Promise((resolve)=>{setTimeout(()=>{return resolve('p1');},2000)});
var p2 =new Promise((resolve,reject)=>{setTimeout(()=>{return reject(2);},2000)});
var p3 =Promise.resolve(2);
var p = Promise.myall([p1,p2,p3]);
p.then(e=>{console.log(e)});