第二阶段中的手撸答案

132 阅读11分钟

// 1、选择排序/冒泡排序

    const arr = [3, 1, 4, 5, 9, 2, 6, 8, 7, 0];

    /* 选择排序 */
    function fn1() {
        /* 选择排序 */
        let temp;

        // 依次选择椅子
        for (let p = 0; p <= arr.length - 2; p++) {
            // 选中第0位 将最小的元素置换到第0位
            let smallest = arr[p];
            let smallestIndex = p;
            for (let i = p; i < arr.length; i++) {
                if (arr[i] < smallest) {
                    smallest = arr[i];
                    smallestIndex = i;
                }
            }

            temp = arr[p];
            arr[p] = smallest;
            arr[smallestIndex] = temp;
        }
        console.log(arr);
    }
    // fn1();

/* 冒泡排序 */

    function fn2() {
        let temp;

        /* 外层循环由8递减为0 */
        for (let j = arr.length - 2; j >= 0; j--) {
            /* 通过相邻元素元素两两冒泡 将最大元素冒泡到末尾 */
            for (let i = 0; i <= j; i++) {
                if (arr[i] > arr[i + 1]) {
                    temp = arr[i];
                    arr[i] = arr[i + 1];
                    arr[i + 1] = temp;
                }
            }
        }
        console.log(arr);
    }
    // fn2();

// 2、深拷贝

    function fn14(data) {
        let copy
        if (typeof (data) === 'function' || typeof (data) !== 'object') {
            return data
        } else if (Array.isArray(data)) {
            copy = []
            data.forEach(function (item, index) {
                copy[index] = fn14(item)
            })
            return copy
        } else {
            copy = {}
            for (let key in data) {
                copy[key] = fn14(data[key])
            }
            return copy
        }
    }

//3、手撸函数防抖与节流

    const inputHandler = function (e) {
        console.log(e.target);
        console.log(e.target.value);
    };

    ip.addEventListener(
        "input",
        mydebounce(inputHandler, 1000)
    );

    /* 自定义防抖高阶函数 */
    function mydebounce(fn, delay) {
        // 将函数延时执行定时器放在闭包里
        let timer = null;
        return function (...args) {
            // 如果上一次函数调用还未执行(两次函数调用的时间间隔尚未达到1秒钟)
            // 就清除上一次的调用定时器
            if (timer) {
                clearTimeout(timer);
            }
            // 准备1秒钟后执行本次调用
            timer = setTimeout(() => {
                // fn(...args)
                fn.apply(null, args);

                // 执行完毕以后给一个标记
                timer = null;
            }, delay);
        };
    }

//节流

    const clickHandler = function (e) {
        console.log("hello");
    };

    btn.addEventListener(
        "click",
        mythrottle(clickHandler, 1000)
    );
    /* 标准timer版答案 */
    function mythrottle(fn, delay) {
        // 调用禁令(默认假)
        let timer = null;

        return function (...args) {
            /* 没有禁令则直接调用 */
            if (!timer) {
                fn.apply(null, args);

                // 设置禁令timer 并于1后撤销
                timer = setTimeout(() => {
                    // 到点撤销禁令
                    timer = null;
                }, delay);
            }
        };
    }
   
   

节流使用时间戳

  function mythrottle(fn, delay) {
 //调用禁令(默认假)
 let forbidden = false;

  return function (...args) {
   // 没有禁令则直接调用 
    if (!forbidden) {
      fn.apply(null, args);

     // 不允许在1秒内发生下一次调用
     forbidden = true;

       // 1后撤销禁令
      setTimeout(() => {
       forbidden = false;
      }, delay);
    }
  };
}

// 4、使用连环异步回调实现求5的阶乘:

    function fn1() {
        /* 乘法函数 将结果由回调函数给出 */
        const multiply = (a, b, callback) => {
            setTimeout(() => callback(a * b), 2000);
        };
        // multiply(2,3,(ret)=>console.log("ret=",ret))

        /* 链式回调 */
        const mulPromise = (a, b) => {
            return new Promise(
                /* executor */
                (resolve, reject) => {
                    // multiply(2,3,(ret)=>console.log("ret=",ret))//回调函数中得到结果打印之
                    multiply(a, b, (ret) => resolve(ret)); //回调函数中得到结果履约之
                }
            );
        };

        mulPromise(2, 3) //开局一只【求二三之积的Promise】 将来履约6
            .then((ret) => 6 * 4) //前述履约履约6  继续向后履约24(臣妾很能 立即履约24)
            .then((ret) => mulPromise(ret, 5)) //前述履约履约24 继续返回一只【求24*5的Promise】 (臣妾没法立刻返回120) 将来履约120
            .then((ret) => console.log("ret=", ret)); //前述履约履约120 直接打印

        ~(async function awaitDemo() {
            try {
                // 死等await右侧的Promise履约
                let ret = await mulPromise(2, 3);

                // 死等await右侧的Promise履约
                ret = await mulPromise(ret, 4);

                // 死等await右侧的Promise履约
                ret = await mulPromise(ret, 5);
                console.log(ret);
            } catch (err) {
                // 如果try以内有Promise毁约 捕获其毁约的原因
                console.log("err=", err);
            }
        })();
    }
    // fn1();

// 5、提取URL中的查询参数

    function getSearchParams(url) {
        // 创建结果对象
        const obj = {};
        const reg = /\w+=\w+/g
        const arr = url.match(reg) //[子串1,子串2] [a=2,b=3]
        // 遍历上述数组 将每个元素以=肢解为 [a,2] 将这一组key-value收集到结果对象中
        arr.forEach((item) => {
            let [key, value] = item.split("=");
            obj[key] = value
        });
        // 返回结果对象
        return obj;
    }

// 6、封装ajax,实现POST一个表单

    ~ function () {
        function getSearchParams(url = '') {
            const obj = {}
            url.match(/\w+=\w+/g).forEach(str => {
                let [key, value] = str.split('=')
                obj[key] = value
            })
            return obj
        }

        function toGetParams(obj) {
            let str = ''
            for (let key in obj) {
                str += `&${key}=${obj[key]}`
            }
            return str.slice(1)
        }

        function ajax({
            url,
            method,
            data,
            dataType,
            onSuccess,
            onFail
        }) {
            const xhr = new XMLHttpRequest()
            if (!url) {
                throw new Error('没给url')
            }
            method = method || 'GET'
            onSuccess = onSuccess || (data => console.log('default onSuccess:', data))
            onFail = onFail || (err => console.log('default onFail:', err))
            xhr.open(method, url)
            let reqBody = null
            switch (true) {
                case dataType == 'form':
                    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
                    reqBody = toGetParams(data)
                    break
                case dataType == 'json':
                    xhr.setRequestHeader('Content-Type', 'application/json')
                    reqBody = JSON.stringify(data)
                    break
                default:
                    break
            }
            xhr.onload = () => onSuccess(xhr.responseText)
            xhr.onerror = err => onFail(err)
            xhr.send(reqBody)
        }
        // try {
        //   ajax({
        //     url: 'http://www.httpbin.org/post',
        //     method: 'POST',
        //     dataType: 'form',
        //     data: { name: 'admin', pwd: '123456' },
        //     onSuccess: data => console.log(data),
        //     onFail: err => console.log(err)
        //   })
        // } catch (err) {
        //   console.log('catch err:', err);
        // }
        function ajaxPromise(config) {
            return new Promise((resolve, reject) => {
                ajax({
                    ...config,
                    onSuccess: data => resolve(data),
                    onFail: err => reject(err),
                })
            })
        }
        ajaxPromise({
                url: 'http://www.httpbin.org/post',
                method: 'POST',
                dataType: 'form',
                data: {
                    name: 'admin',
                    pwd: '123456'
                },
            })
            .then(data => console.log(data))
            .catch(err => console.log(err))
    }

// 7、带有超时功能的Promise

    /* Promise版 */
    function fn2() {
        function executeWithTimeout(ajaxPromise, ms) {
            /* 
            返回一个Promise对象: 
            ms到期时强制reject
            网络数据回来则resolve
            */
            return new Promise((resolve, reject) => {
                /* ms到期时强制reject */
                let timer = setTimeout(() => {
                    reject("timeout");
                }, ms);
                /* 
                网络数据回来则resolve 
                网络数据什么时候回来?要看ajaxPromise什么时候履约
                */
                ajaxPromise()
                    // 网络Promise5秒后履约
                    .then((data) => {
                        // 先清除延时定时器
                        clearTimeout(timer);

                        // 对外resolve数据
                        resolve(data);
                    });
            });
        }

        /* 网络promise */
        function ajaxPromise() {
            return new Promise((resolve) =>
                setTimeout(() => {
                    resolve(`来自淘宝的数据`);
                }, 1000)
            );
        }

        executeWithTimeout(ajaxPromise, 3000)
            .then((data) => console.log(data))
            .catch((err) => console.log(err));
    }


    //async版
    function fn2X() {
        async function executeWithTimeout(fn, ms) {
            return new Promise(async (resolve, reject) => {
                // ms毫秒以后暴力超时
                let timer = setTimeout(() => {
                    // reject超时
                    reject("timeout");
                }, ms);
                /* 死等履约 成功后清除定时器+resolve数据 */
                const data = await fn();
                clearTimeout(timer);
                resolve(data);
            });
        }
        const timeout = 5000;

        function rq(url) {
            return new Promise((resolve) => {
                /* 超时后reject */
                setTimeout(() => {
                    resolve(`来自${url}的数据`);
                }, timeout);
            });
        }
        async function fn1() {
            return rq("https://www.taobao.com");
        }
        executeWithTimeout(fn1, 3000)
            .then((value) => console.log("value=", value))
            .catch((err) => console.log("err=", err));
    }

// 7、一次性获取多个指定页面

    function fn3() {
        /* url进去 获取对应页面的Promise对象出来 */
        async function getContentByUrl(url) {
            // 返回一个1秒后履约页面内容的Promise对象
            return new Promise((resolve, reject) =>
                setTimeout(() => {
                    Math.random() > 0.5 ? resolve(`${url}的页面内容`) : reject("timeout");
                }, 1000)
            );
        }

        const urls = [
            "https://www.taobao.com",
            "https://www.baidu.com",
            "https://web.taobao.com",
        ];

        /* 分步骤版 */
        async function fetchDataSplited(urls = []) {
            // 过滤掉非淘宝的url
            urls = urls.filter((url) => url.endsWith("taobao.com"));

            // 将url数组映射成Promise数组(以便调度)
            const ajaxPromises = urls.map((url) => getContentByUrl(url));

            // 用【完全期约:要求每个Promise都落地】调度这个Promise数组
            const allSettledPromise = Promise.allSettled(ajaxPromises);

            // 返回这个完全期约
            return allSettledPromise;
        }

        async function fetchData(urls = []) {
            // 返回这个完全期约
            return Promise.allSettled(
                urls
                .filter((url) => url.endsWith("taobao.com"))
                .map((url) => getContentByUrl(url))
            );
        }

        // 返回一个【完全期约】
        fetchData(urls)
            // 【完全期约】中的每一个Promise都落地时回调 results是一个结果数组
            .then((results) => console.log(results));
    }
    fn3();

// 8、IP地址比大小

    function fn1() {
        /* ip1大返1 小返-1 平返0 */
        function compare(ip1 = "", ip2 = "") {
            // 将ip炸碎为number数组 ["1","2","3","4"] 映射为 [1,2,3,4]
            const arr1 = ip1.split(".").map((item) => item * 1);
            const arr2 = ip2.split(".").map((item) => item * 1);

            // 按照数组顺序从前往后比较
            for (let i = 0; i < arr1.length; i++) {
                // 在任何序号下 发现大小关系 就直接返回1/-1 中断比较
                if (arr1[i] > arr2[i]) {
                    return 1;
                }
                if (arr1[i] < arr2[i]) {
                    return -1;
                }
            }

            // 四段都走完 没有发现大小关系 直接返回0
            return 0;
        }

        console.log(compare("1.2.3.4", "1.1.3.4")); //1
        console.log(compare("1.11.3.4", "1.2.3.4")); //1
        console.log(compare("1.2.3.4", "1.11.3.4")); //-1
        console.log(compare("1.2.3.4", "1.2.3.4")); //0
    }

// 9、实现闭包管理全班学生成绩

    function score(name) {
        //实现闭包内容
        const scores = {
            chinese: 0,
            math: 0,
            coding: 0,
        };

        return {
            // 设置学习成绩
            set(key, value) {
                scores[key] = value;
            },

            // 查询所有
            getAll() {
                return `${name}:${JSON.stringify(scores)}`;
            },
        };
    }
    const arr1 = ["张三疯", "尼古拉斯赵四", "隔壁老王"]
    const obj = {}
    /* 一人一个成绩闭包的操作API */
    arr1.forEach(
        // {张三疯:张三疯的成绩闭包操作API}
        name => obj[name] = scrore(name)
    )

// 10、手封MyMap实现以下效果

    class MyMap {
        /* 实现构造函数 */
        constructor() {
            // 创建一个空对象用于存储key-value
            this.obj = {};
            this.size = 0;
        }

        set(key, value) {
            // 将入参的key-value挂载到this.obj身上
            this.obj[key] = value;

            // 更新一下this.size
            this.updateSize();
        }

        get(key) {
            return this.obj[key];
        }

        updateSize() {
            // 获取this.obj中的所有key形成的数组 查看其长度
            this.size = Object.keys(this.obj).length;
        }

        /* 删除一个key-value */
        delete(key) {
            // 从this.obj中删除一个key
            delete this.obj[key];

            // 更新一下this.size
            this.updateSize();
        }

        /* 清空所有key-value */
        clear() {
            this.obj = {};
            this.size = 0;
        }

        /* 判断key是否存在 */
        has(key) {
            /* 判断对象是否有自己的属性(从原型链中继承的不算) */
            return this.obj.hasOwnProperty(key);
        }

        /* 批处理之遍历 */
        forEach(handler) {
            /* 有几个key-value就把handler执行几次 */
            for (let key in this.obj) {
                // handler(this.obj[key], key, this);

                // 确保handler中的this依然为当前Map对象
                handler.apply(this, [this.obj[key], key, this]);
            }
        }

        /* 查看所有Key */
        keys() {
            return Object.keys(this.obj);
        }

        /* 查看所有value */
        values() {
            /* 将Key数组映射为value数组并返回 */
            return Object.keys(this.obj).map((key) => this.obj[key]);
        }

        /* 查看所有条目(entry/item) */
        entries() {
            /* 将Key数组映射为{key,value}数组并返回 */
            return Object.keys(this.obj).map(
                (key) => ({
                    key,
                    value: this.obj[key]
                })
            );
        }
    }
    
    ~(function () {
        const map = new MyMap()
        ap.setm("name", "张三")
        map.set("age", 20)
        map.set("gender", "男")

        map.forEach(
            (value, key) => console.log(key, value)
        ) //"name","张三" "age",20 "gender","男"

        for (let entry of map.entries()) {
            console.log(entry)
        } //{key:"name",value:"张三"} {key:"age",value:20} {key:"gender",value:"男"}

        console.log(map.size) //3
        console.log(map.get("name")) //张三
        console.log(map.get("age")) //20

        console.log(map.has("name")) //true
        console.log(map.has("myname")) //false

        map.delete("age")
        map.clear()
        console.log(map.size) //0
    })

// 11、全班人马排除空座位后抽取N名幸运观众

    function fn3() {
    let stus = [];
    for (let i = 1; i < 100; i++) {
        stus.push(i);
    }

    /* 排除 */
    const empties = [12, 34, 56, 78]; //空座位
    stus = stus.filter((s) => empties.indexOf(s) === -1);
    // console.log(stus);

    /* 抽人头 */
    const pick = (arr, n) => {
        // 幸运观众
        let luckies = [];

        // 算法一:只要幸运观众的人头数还不满N 就一直循环
        // while (luckies.length < n) {
        //   // 随机抽取幸运观众的序号
        //   let randomIndex = parseInt(Math.random() * arr.length);
        //   let stu = stus[randomIndex];

        //   // 如果幸运观众中不含stu 就将stu丢入
        //   !luckies.includes(stu) && luckies.push(stu);
        // }

        /* 算法二:只允许循环N次 每次弹出一个元素 */
        for(let i=0;i<n;i++){
            let randomIndex = parseInt(Math.random() * arr.length);
            // [24].concat([1,2,3])
            luckies = arr.splice(randomIndex,1).concat(luckies)
        }

        // 丢还幸运观众数组
        return luckies;
    };
    console.log(pick(stus, 5));
    }
    // fn3();

// 12、手撸观察者模式:实现彩票机周期性地发布【开售】【开奖】事件,玩家开售时下注,开奖时购买,一旦中奖则彩票机停止工作;

    function observerDemo() {
  class Observable {
    /* 构造函数:初始化观察者列表 */
    constructor(name) {
      this.name = name;
      this.observers = [];
    }
    /* 注册观察者 */
    register(ob) {
      this.observers.push(ob);

      //让观察者持有当前被观察者对象
      ob.observable = this;
    }
    /* 注销观察者 */
    unregister(ob) {
      this.observers = this.observers.filter((o) => o !== ob);
    }
    /* 发布事件 */
    emit(event) {
      // 让所有观察者都来响应之
      this.observers.forEach((ob) => ob.onEvent(event));
    }
  }
  class Observer {
    /* 创建实例时赋予一个name属性 */
    constructor(name) {
      this.name = name;
    }

    /* 响应事件 */
    onEvent(event) {
      console.log(this.name, "响应事件", event);
    }
  }
  class Lottery extends Observable {
    /* 彩票需要一个周期性定时器属性 */
    constructor(name) {
      // 继承父类属性name
      super(name); //原型写法Observable.call(this,name)
      this.timer = null;
    }
    /* 自动运行 */
    start() {
      if (!this.timer) {
        this.timer = setInterval(() => {
          // 发布开奖事件
          const code = parseInt(Math.random() * 3);
          console.log(this.name, "发布开奖事件", code);
          this.emit({ type: "开奖", code });

          // 发布开售事件
          this.emit({ type: "开售" });
        }, 2000);
      }
    }
    /* 停止运行 */
    stop() {
      if (this.timer) {
        clearInterval(this.timer);
        console.log(this.name, "已停止");
        this.timer = null;
      }
    }
  }
  class Player extends Observer {
    /* 玩家有注单属性 */
    constructor(name) {
      // 继承父类的name属性
      super(name);
      this.code = null;
    }
    /* 下注 */
    buy() {
      this.code = parseInt(Math.random() * 3);
      console.log(this.name, "下注", this.code);
    }
    /* 兑奖 */
    check(event) {
      const isLucky = this.code === event.code;
      console.log(this.name, isLucky ? "中奖了" : "未中奖");

      // 让彩票机停止(需要事先持有彩票机对象)(在观察它的时候就拿到它的对象)
      isLucky && this.observable.stop();
    }

    /* 个性化的响应方式:覆写override父类的onEvent */
    onEvent(event) {
      super.onEvent(event);

      switch (event.type) {
        /* 遇到开售就下注 */
        case "开售":
          this.buy();
          break;

        /* 遇到开奖就兑奖 */
        case "开奖":
          this.check(event);
          break;

        default:
          break;
      }
      console.log("");
    }
  }

  /* 业务逻辑 */
  ~(function main() {
    const lot = new Lottery("六合彩");
    const tiger = new Player("打老虎");
    const gaojin = new Player("高进");

    lot.register(tiger);
    lot.register(gaojin);
    lot.unregister(gaojin);

    lot.start();
  })();
}
// observerDemo();

// 13、实现任意多个入参的函数fn的柯里化

function curryDemo() {
  const curry = (fn) => {
    return function cfn(...args) {
      /* 递归终止条件:参数凑齐 */
      if (args.length === fn.length) {
        return fn.apply(null, args);
      }

      return function (...b) {
        // 修改外层闭包中的参数列表长度 逐渐接近递归终止条件
        args = args.concat(b);
        return cfn(...args);
      };
    };
  };

  const cadd = curry(add);
  console.log(cadd(1, 2, 3, 4)); //10
  console.log(cadd(1, 2)(3)(4)); //10
}
// curryDemo();

// 14、实现任意多个函数的管道与组合

function pipeDemo() {
    const pipe = (...fns) => (v => fns.reduce(
        (pv,fn,index)=>fn(pv),
        v
    ))
    const compose = (...fns) => (v => fns.reverse().reduce(
        (pv,fn,index)=>fn(pv),
        v
    ))

  //求数值的长度
  const len = (n) => (n + "").length;
  //求n的平方
  const pow = (n) => n * n;
  // 求立方根
  const cubicRoot = (n) => Math.cbrt(n);

  // 从左往右处理
  console.log(pipe(len, pow, cubicRoot)(10)); //10的长度为2 平方后得4 开三方得1.x

  // 从右往左处理
  console.log(compose(len, pow, cubicRoot)(1000)); //1000立方根10 平方后100 长度3
}
// pipeDemo();

// 15、函数的Promise化

async function promDemo() {
    function promisify(fn){
        return function(...args){
            return new Promise(
                (resolve,reject)=>{
                    try {
                        resolve(fn.apply(null,args))
                    } catch (err) {
                        reject(err)
                    }
                }
            )
        }
    }

    const add = (a, b, c, d) => {
        if(Math.random() > 0.7) {
            throw new Error("人品槽已空,请尽快充100块钱的人品")
        }
        return a + b + c + d
    }
    const pow = (a, b) => {
        if(Math.random() > 0.7) {
            throw new Error("人品槽已空,请尽快充100块钱的人品")
        }
        return Math.pow(a,b)
    }

    const padd = promisify(add)
    const ppow = promisify(pow)

    // padd(1,2,3,4).then(
    //     data => {
    //         console.log("data=",data)
    //         return ppow(data,2)
    //     }
    // )
    // .then(
    //     data => console.log("data=",data)
    // )
    // .catch(
    //     err => console.error("err=",err)
    // )

    try {
        let data = await padd(1,2,3,4)
        console.log("data=",data)
    
        data = await ppow(data,2)
        console.log("data=",data)
    } catch (err) {
        console.error("err=",err)
    }

}
// promDemo()

// 16、封装MongoDB数据引擎层

async function fn3() {
  /* 通用的获取collection连接 */
  function getCollection(dbName, collectionName) {
    //实现之
    // 连接数据库需要时间 并不能立刻返回err/collection
    return new Promise(
      /* excutor 在执行器内部去连接数据库*/
      (resolve, reject) => {
        var MongoClient = require("mongodb").MongoClient;
        var url = "mongodb://localhost:27017/";

        MongoClient.connect(url, function (err, conn) {
          if (err) {
            //   reject(err)
            resolve({
              err,
              collection: undefined,
              conn: undefined,
            });
            return;
          }

          var dbo = conn.db(dbName);
          const collection = dbo.collection(collectionName);
          resolve({
            err: undefined,
            collection,
            conn,
          });
        });
      }
    );
  }

  /* 通用插入数据 */
  async function doCreate(dbName, collectionName, dataObj) {
    //实现之
    // 延迟满足:稍后获得{err,collection}
    const { err, collection, conn } = await getCollection(
      dbName,
      collectionName
    );

    /* await以下的代码 全部相当于写在then的回调中 */
    if (err) {
      // 在then的回调中 return就是向后履约
      return {
        err,
        res: undefined,
      };
    }

    collection.insertOne(dataObj, function (err, res) {
      conn.close();

      // 将数据和插入的一并履约
      return {
        err,
        res,
      };
    });
  }

  // 模型层调用
  async function addUser(user) {
    // 对控制层返回的是一个Promise对象
    const ret = await doCreate(dbName, collectionName, user);
    return ret;
  }
}
// fn3();

Promise求阶乘

function fn1() {
  /* 乘法函数 将结果由回调函数给出 */
  const multiply = (a, b, callback) => {
    setTimeout(() => callback(a * b), 2000);
  };
  // multiply(2,3,(ret)=>console.log("ret=",ret))

  /* 链式回调 */
  const mulPromise = (a, b) => {
    return new Promise(
      /* executor */
      (resolve, reject) => {
        // multiply(2,3,(ret)=>console.log("ret=",ret))//回调函数中得到结果打印之
        multiply(a, b, (ret) => resolve(ret)); //回调函数中得到结果履约之
      }
    );
  };

  mulPromise(2, 3) //开局一只【求二三之积的Promise】 将来履约6
    .then((ret) => 6 * 4) //前述履约履约6  继续向后履约24(臣妾很能 立即履约24)
    .then((ret) => mulPromise(ret, 5)) //前述履约履约24 继续返回一只【求24*5的Promise】 (臣妾没法立刻返回120) 将来履约120
    .then((ret) => console.log("ret=", ret)); //前述履约履约120 直接打印

  ~(async function awaitDemo() {
    try {
      // 死等await右侧的Promise履约
      let ret = await mulPromise(2, 3);

      // 死等await右侧的Promise履约
      ret = await mulPromise(ret, 4);

      // 死等await右侧的Promise履约
      ret = await mulPromise(ret, 5);
      console.log(ret);
    } catch (err) {
      // 如果try以内有Promise毁约 捕获其毁约的原因
      console.log("err=", err);
    }
  })();
}
// fn1();

函数的Promise化

function fn2() {
  // 同步函数
  function add(a, b) {
    return a + b;
  }

  // 带有异步回调的函数
  function multiply(a, b, callback) {
    setTimeout(() => callback(a * b), 1000);
  }
  // multiply(2, 3, (ret) => console.log("ret=", ret));

  /* 异步函数的Promise化 */
  function promisify(fn) {
    //fn进去 Promise化的fn出来 实现之
    return function pfn(...args) {
      return new Promise(
        /* executor */
        (resolve, reject) => {
          /* 想要履约6 还得调用multiply */
          // const ret = fn(...args,(...a) => resolve(a))
          const ret = fn.apply(null, [...args, (...a) => resolve(a)]); //[6] 异步回调函数从这里resolve

          // 有返回值则履约之
          ret && resolve(ret); //同步函数从这里resolve
        }
      );
    };
  }

  /* 同步返回函数的Promise化 */
  //   const padd = promisify(add);
  //   padd(2, 3).then(
  //     (value) => console.log("value=", value) //5
  //   );

  /* 异步回调函数的Promise化 */
  const pmul = promisify(multiply);
  pmul(2, 3)
    .then((values) => {
      console.log("value=", values); //[6]
      return pmul(values[0], 4); //没法立刻履约24 就返回一个Promise 等待其继续向后履约24
    })
    .then((values) => {
      console.log("value=", values); //[24]
      return pmul(values[0], 5);
    })
    .then((values) => console.log(values[0])); //[120]
}
// fn2();

- 调用一次的函数柯里化

 ~function fn1(){
        const once = (fn)=>{
            // 设置开关
            let flag = false
             
            return function(...args){
                if(!flag){

                    let ret = fn(...args)//fn()=once(2,3)所以需要使用...args将数组[2,3]打散
                    flag = true
                    return ret
                }else{
                    console.log('函数以调用过了!');
                }
            }
        }
    

    const add =(a,b)=>a+b
    const addonce = once(add)
    console.log(addonce(2,3));
    console.log(addonce(3,3));
    console.log(addonce(7,3));
}()
</script>
<!-- 高级函数,参数是函数,返回值为函数,只可以调用一次函数 -->