秋招复习--JS手写题

253 阅读12分钟

一、Array篇:

1.数组去重:

  //ES5实现(filter+indexOf)
   function unique (array) {
     let res
     res= array.filter((item,index,arr)=>{
       //每次查找第一个出现当前元素值的索引与当前索引值比较,如果为真,说明该元素第一次出现。添加到新生成的数组。
          return arr.indexOf(item)===index
      })
      return res 
   }
   //ES6实现:
   const unique = arr => [...new Set(arr)];
   //测试代码:
    console.log(uniq([1,2,2,3,3,4]));
   

2.every:

 /* 判断数组中的所有元素是否都通过传入函数的测试,所有都满足条件返回真,空数组在一切条件下都返回true */
    Array.prototype.sx_every = function(callback){
         if(this === [])return true;
         let count = 0;
         for(let i =0; i<this.length;i++){
            callback(this[i],i,this)&&count++;
         }
         if(count === this.length){
           return true;
         }else{
           return false;
         }
    }
   console.log( [].sx_every((item)=>{
        return item>0;
    }));

3.fill:

  /* fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
    如果索引值为负,索引值=索引值+length
    fill 方法是个可变方法, 它会改变调用它的 this 对象本身, 然后返回它, 而并不是返回一个副本。 */
      Array.prototype.sx_fill = function (value, start, end) {
        start = start < 0 ? start + length : start;
        end = end < 0 ? end + length : end;
        if (!end) {
          end = this.length;
        }
        for (let i = start; i < end; i++) {
          this[i] = value;
        }
        return this;
      };
      console.log([1, 2, 3, 4].fill(0, 0, 1));

4.filter:

   /* 创建一个新数组,其结果是返回条件为真的数组元素 */
      Array.prototype.sx_filter = function (callback) {
        const res = [];
        for (let i = 0; i < this.length; i++) {
          callback(this[i], i, this) && res.push(this[i]);
        }
        return res;
      };
      console.log(
        [1, 2, 3, 4].sx_filter((item) => {
          return item > 2;
        })
      );

5.find:

 /* 返回第一个满足条件的元素 */
      Array.prototype.sx_find = function (callback) {
        for (let i = 0; i < this.length; i++) {
          if (callback(this[i], i, this)) {
            return this[i];
          }
        }
      };
      console.log(
        [2, 1, 3, 1].sx_find((item) => {
          return item > 0;
        })
      );

6.flat:

/*depth 可选参数:指定要提取嵌套数组的结构深度,默认值为 1。
    */
      Array.prototype.sx_flat = function (depth = 1) {
        let arr = this;
        //如果数组中元素有一项为数组,且depth大于0,则对数组进行拍平。
        while (arr.some((item) => Array.isArray(item)) && depth > 0) {
          arr = [].concat(...arr);
          depth--;
        } //对空元素进行处理
        return arr.filter((item) => item !== undefined);
      };
      const arr = [
        1,
        ,
        3,
        4,
        [1, , 3, [1, 2, 3, [1, 2, 3]]],
        5,
        "string",
        { name: "大飞老师" },
      ];
      console.log(arr.sx_flat(1));

7.includes:

  /* 可查找NaN
    判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false。 */
      Array.prototype.sx_includes = function (value, start = 0) {
        start = start < 0 ? this.length + start : start;
        for (let i = start; i < this.length; i++) {
          if (value === this[i] || Object.is(this[i], value)) {
            return true;
          }
        }
        return false;
      };
      console.log([1, 2, NaN].sx_includes(2));

8.join:

/* 如果数组只有一个元素,则不使用分隔符。 */
      Array.prototype.sx_join = function (sep) {
        let str = "";
        if (this === []) return "";
        else {
          for (let i = 0; i < this.length; i++) {
            sep = i === this.length - 1 || this.length === 1 ? "" : sep;
            str += this[i] + sep;
          }
        }
        return str;
      };
      console.log([1, 2, , 3].sx_join("_"));

9.map:

      /* 创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。 */
      Array.prototype.sx_map = function (callback) {
        const res = [];
        for (let i = 0; i < this.length; i++) {
          res.push(callback(this[i], i, this));
        }
        return res;
      };

      console.log(
        [1, 2, 3, 4].sx_map((item, index, arr) => {
          return item;
        })
      );

10. reduce:

 /* 
      1) reduce为数组中的每一个非undefined元素依次执行callback函数,
      2) 参数(callback,[initValue])
      3)  如果没有提供initialValue,reduce 会从索引1的地方开始执行 第一个元素作为acc的初始值。如果提供initialValue,从索引0开始。
      4)如果数组为空且没有提供initialValue,会抛出TypeError 。如果数组仅有一个元素(无论位置如何)并且没有提供initialValue, 或者有提供initialValue但是数组为空,那么此唯一值将被返回并且callback不会被执行。 */
      Array.prototype.sx_reduce = function (callback, initValue) {
        let acc = 0;
        let index = 0;
        //保存原数组
        let that = this;
        //去除掉空元素
        const _this = this.filter((item) => {
          return item !== undefined;
        });
        //如果没有初始值,curValue的下标为索引+1,则length应为length-1
        const len = !initValue ? _this.length - 1 : _this.length;
        if ((_this === undefined || _this === []) && !initValue) {
          throw new TypeError("this is null or not defined");
        } else if (
          (_this.length === 1 && !initValue) ||
          (_this.length === 0 && initValue)
        ) {
          return initValue || _this[0];
        } else {
          for (let i = 0; i < len; i++) {
            if (initValue !== undefined) {
              //有初始值
              if (that.indexOf(_this[i]) !== -1) {
                index = that.indexOf(_this[i]);
              }
              acc = i === 0 ? initValue : acc;
              acc = callback(acc, _this[i], index, _this);
            } else {
              //未传入初始值
              acc = i === 0 ? _this[i] : acc;
              //对索引值的处理
              if (that.indexOf(_this[i + 1]) !== -1) {
                index = that.indexOf(_this[i + 1]);
              }
              acc = callback(acc, _this[i + 1], index, _this);
            }
          }
        }
        return acc;
      };
      console.log(
        [1, , 3].sx_reduce((acc, cur, i, arr) => {
          console.log(i);
          return acc + cur;
        }, 1)
      );

11.splice:

/* splice(start[, deleteCount[, item1[, item2[, ...]]]]) */
      Array.prototype._splice = function (...args) {
        let deletes = [];
        let appends = [];
        let heads = [];
        let tails = [];
        let index, howmany;
        if (args.length === 0) return res;
        index = args[0];
        index = Number(index);
        index = index === index ? index : 0;
        if (args.length > 1) {
          howmany = args[1];
          howmany = Number(howmany);
          howmany = howmany === howmany ? howmany : 0;
        }
        if (args.length > 2) {
          appends = args.filter((v, i) => i > 1);
        }
        for (let i = 0; i < this.length; i++) {
          if (i < index) {
            heads.push(this.shift());
          }
          if (i <= index + howmany - 1) {
            deletes.push(this.shift());
          }
          if (index + howmany <= i) {
            tails.push(this.shift());
          }
        }
        this.unshift(...heads);
        this.push(...appends);
        this.push(...tails);
        return deletes;
      };

二、Function篇:

1. apply:

    Function.prototype.sx_apply = function (obj, ...args) {
        obj = obj || window;
        let fn = Symbol();
        obj[fn] = this;
        //传入的是数组:args形式:[[Array[*]]],注意展开数组。
        return obj[fn](...args.flat());
      };
      let obj1 = {
        name: "木木",
        message(age, weight) {
          console.log(`${this.name}${age}岁,体重是${weight}斤`);
        },
      };
      let obj2 = {
        name: "mumu",
      };
      obj1.message.sx_apply(obj2, [18, 60]);

2.call:

  Function.prototype.sx_call = function (obj, ...args) {
        //对传入的obj为null需要进行处理
        obj = obj || window;
        //生成一个唯一的fn属性
        const fn = Symbol();
        obj[fn] = this;
        return obj[fn](...args);
      };
      let obj1 = {
        name: "木木",
        message(age, weight) {
          console.log(`${this.name}${age}岁,体重是${weight}斤`);
        },
      };
      let obj2 = {
        name: "mumu",
      };
      obj1.message.sx_call(obj2, 18, 60);

3.bind:

   /* 需要解决的问题:
    1)返回一个函数
    2)返回的函数可能作为构造函数使用
    */
      Function.prototype.sx_bind = function (obj, ...args) {
        obj = obj || window;
        args = args ? args : [];
        const fn = Symbol();
        obj[fn] = this;
        return function newFunction(innerArgs) {
          innerArgs = innerArgs ? innerArgs : [];
          //判断是否作为构造函数方式使用[此时忽略指定的this:obj[fn]
          if (this instanceof newFunction) {
            return new obj[fn](...args, ...innerArgs);
          }
          return obj[fn](...args, ...innerArgs);
        };
      };
      let obj1 = {
        name: "木木",
        callAge(age, weight) {
          console.log(`${this.name}${age}岁,体重是${weight}斤`);
        },
      };
      let obj2 = {
        name: "mumu",
      };
      const res = obj1.callAge.sx_bind(obj2, 18, 60);
      res("nunu");
      function bar(name) {
        this.name = name;
        //如果当做构造函数使用,则忽略指定的this(此处为foo)。
        console.log(this.value); //undefined
      }
      let foo = {
        value: 1,
      };
      const result = bar.sx_bind(foo, "shy");
      const ob = new result();
      console.log(ob.name);

三、Object篇:

1.解析查询字符串参数为对象:

let url = "www.test.com/user?name='lihua'&age=18";
      const parseQueryParams = (url) => {
        let QueryParamsIndex = url.indexOf("?");
        let queryParams = url.slice(QueryParamsIndex + 1);
        let arr = queryParams.split("&");
        let obj = {};
        for (let item of arr) {
          let i = item.split("=");
          obj[i[0]] = i[1];
        }
        return obj;
      };
      let res = parseQueryParams(url);
      console.log(res);

2.json对象转为树:

  const list = [
        { id: 1, title: "tab1", pId: 0 },
        { id: 2, title: "tab2", pId: 0 },
        { id: 3, title: "tab1-1", pId: 1 },
        { id: 4, title: "tab2-2", pId: 2 },
      ];
      const jsonToTree = (list) => {
        let res = [];
        /* 通过一个map保存所有节点*/
        let map = list.reduce((res, cur) => ((res[cur.id] = cur), res), {});
        console.log(map, "df");
        for (const item of list) {
          //无父节点
          if (item.pId === 0) {
            res.push(item);
            continue;
          }
          //有父节点
          if (item.pId in map) {
            let parent = map[item.pId];
            parent.children = parent.children || [];
            parent.children.push(item);
          }
        }
        return res;
      };
      console.log(jsonToTree(list));

treeToflat(树转json)

//已知JSON树状形结构的数据data
let data = [
             {id:1,title:'标题1',parent_id:0,},
             {id:2,title:'标题2',parent_id:0,
              children:[
                 {id:3,title:'标题2-1',parent_id:2,
                  children:[
                    {id:4,title:'标题3-1',parent_id:3,
                     children:[
                        {id:5,title:'标题4-1',parent_id:4}
                      ]}
                  ]},
                {id:6,title:'标题2-2',parent_id:2}
              ]
            }
           ],
           
   function treeToFlat(data){
            return data.reduce((pre,cur)=>{
                // console.log(cur);
                //此处将对象的children属性和值都解构在空数组中,将对象的其他属性和值都解构在i里面。
                const {children=[],...i}=cur;  
                return pre.concat([{...i}],treeToFlat(children))  
            },[]);
        }
   console.log(treeToFlat(data)); 


3.assign:

 /* assign接收多个对象,并将多个对象合成一个对象
这些对象如果有重名属性,以后来的对象属性值为准
assign返回一个对象,这个对象 === 第一个对象 */
      Object.sx_assign = function (target, ...args) {
        //target不能为null与undefined
        if (target === null || target === undefined) {
          throw new Error("target不能为null和undefined");
        }
        target = Object(target);
        for (let nextObj of args) {
          for (let key in nextObj) {
            nextObj.hasOwnProperty(key) && (target[key] = nextObj[key]);
          }
        }
        return target;
      };
      const testA = {
        name: "mumu",
      };
      const testB = {
        name: "nvnv",
        age: 18,
      };
      const testC = {
        gender: "男",
      };
      console.log(Object.sx_assign(testA, testB, testC));

4.create:

 Object.sx_create = function (proto, propertyObj) {
        //对参数进行判断,第一个参数应该为对象或函数,第二个参数不能传入空对象
        if (typeof proto !== "object" && typeof proto !== "function") {
          throw new TypeError("Proto should be an Object");
        }
        if (propertyObj === null) {
          throw new TypeError("propertyObj is null");
        }
        //生成一个以proto为原型的对象
        function F() {}
        F.prototype = proto;
        const obj = new F();
        if (propertyObj !== undefined) {
          Object.defineProperties(obj, propertyObj);
        }
        return obj;
      };
      const obj = {
        name: "mumu",
      };
      const obj2 = {
        property1: {
          value: 1,
          writable: true,
        },
        property2: {
          value: "Hello", //与属性关联的值
          writable: false,
          enumerable: true, //是否可枚举
          configurable: true, //该属性是否可以修改或删除
        },
      };
      const obj1 = Object.sx_create(obj, obj2);
      console.log(obj1.name, obj1.property1, obj1.property2);

5.entries:

  /* 将对象转换为键值对数组形式 */
      Object.prototype.sx_entries = function (obj) {
        let res = [];
        for (let key in obj) {
          obj.hasOwnProperty(key) && res.push([key, obj[key]]);
        }
        return res;
      };
      const obj = {
        name: "mumu",
        age: 18,
        gender: "男",
      };
      console.log(Object.sx_entries(obj));

6.instanceof:

/* 用处:A instanceOf B,判断A是否经过B的原型链
      参数:(构造函数,实例对象) */
      function myInstanceof(father, child) {
        const fp = father.prototype;
        let cp = Object.getPrototypeOf(child);
        while (cp) {
          if (cp === fp) return true;

          cp = Object.getPrototypeOf(cp);
        }
        return false;
      }
      function Person(name, age) {
        this.name = name;
        this.age = age;
      }
      const obj = new Person("nvnv", 18);
      obj2 = {};
      console.log(myInstanceof(Person, obj2));

7. Object.is:

   /* 判断两个值是否相等,不会进行类型转换(区别于==),+0!=-0,NaN=NaN[与===相反] */
    Object.prototype.sx_is = function (x, y) {
      //对+/-0进行处理【先判断是否为0,再决定是否判断符号】
      if (x === y) {
        return x !== 0 || 1 / x === 1 / y;
      }
      //对NaN进行处理
      return x !== x && y !== y;
    };
    console.log(Object.sx_is("123", "123"));

四、String篇:

1.实现模板字符串:

 let name = "lihua";
    let age = 18;
    let str = "${name}${age}岁了";
    str = str.replace(/\$\{([\w]*)\}/g, function () {
      // console.log(arguments); match匹配到的子串、捕获到的分组的内容即()中的内容...
      return eval(arguments[1]);
    });
    console.log(str);

2.slice:

    /* 参数代表含义
start:开始截取的字符索引(包含此字符)
end:结束截取的字符索引(不包含此字符)
注意点:
start > end:返回空字符串
start < 0:start = 数组长度 + start */
    String.prototype.sx_slice = function (start, end) {
      start = start < 0 ? this.length + start : start;
      end = !end && end !== 0 ? this.length : end;
      let str = "";
      if (start >= end) return "";
      for (let i = start; i < end; i++) {
        str += this[i];
      }
      return str;
    };
    let str1 = "hello";
    console.log(str1.sx_slice(-3));
    console.log(str1.slice(-3));

3.substring:

          /*  substring 提取从 indexStart 到 indexEnd(不包括)之间的字符。特别地:

如果 indexStart 等于 indexEnd,substring 返回一个空字符串。
如果省略 indexEnd,substring 提取字符一直到字符串末尾。
如果任一参数小于 0 或为 NaN,则被当作 0。
如果任一参数大于 stringName.length,则被当作 stringName.length。
如果 indexStart 大于 indexEnd,则 substring 的执行效果就像两个参数调换了一样。 */
      String.prototype.sx_substring = function (start, end) {
        if (start === end) return "";
        start =
          start < 0 || !start ? 0 : start > this.length ? this.length : start;
        end = !end || end < 0 ? 0 : end > this.length ? this.length : end;
        if (start > end) {
          let temp;
          temp = start;
          start = end;
          end = temp;
        }
        let str = "";
        for (let i = start; i < end; i++) {
          str += this[i];
        }
        return str;
      };
      let str1 = "hello";
      console.log(str1.sx_substring(2, 0));
      console.log(str1.substring(2, 0));

五、Promise篇:

1.all:

    /*   传入的所有 Promsie 都是 fulfilled,则返回由他们的值组成的,状态为 fulfilled 的新 Promise;
只要有一个 Promise 是 rejected,则返回 rejected 状态的新 Promsie,且它的值是第一个 rejected 的 Promise 的值;
只要有一个 Promise 是 pending,则返回一个 pending 状态的新 Promise;
*/
    const p1 = Promise.resolve(1);
    const p2 = Promise.reject(5);
    const p3 = Promise.resolve(3);
    const p4 = Promise.reject(4);
    //let pArr = [p1,p2,p3,p4];
    let pArr = [p1, p3];
    Promise.all = function (pArr) {
      let index = 0,
        result = [];
      return new Promise((resolve, reject) => {
        pArr.forEach((p, i) => {
          Promise.resolve(p).then(
            (value) => {
              index++;
              result[i] = value;
              if (result.length === pArr.length) {
                resolve(result);
              }
            },
            (err) => {
              reject(err);
            }
          );
        });
      });
    };
    let res = Promise.all(pArr);
    console.log(res);

2.allSettled:

   /* 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
把每一个Promise的结果,集合成数组,返回 */
    Promise.allSettled = function (pArr) {
      let result = [];
      return new Promise((resolve, reject) => {
        pArr.forEach((p) => {
          Promise.resolve(p).then(
            (value) => {
              result.push({
                status: "fulfilled",
                value,
              });
              if (pArr.length === result.length) {
                resolve(result);
              }
            },
            (reason) => {
              result.push({
                status: "rejected",
                reason,
              });
              if (pArr.length === result.length) {
                resolve(result);
              }
            }
          );
        });
      });
    };
    const p1 = Promise.resolve(1);
    const p2 = Promise.reject(2);
    const p3 = Promise.resolve(3);
    const p4 = Promise.reject(4);
    let pArr = [p1, p2, p3, p4];
    console.log(Promise.allSettled(pArr));

3.any:

  /* any与all相反
接收一个Promise数组,数组中如有非Promise项,则此项当做成功
如果有一个Promise成功,则返回这个成功结果
如果所有Promise都失败,则报错 */
    Promise.any = function (pArr) {
      let index = 0,
        result = [];
      return new Promise((resolve, reject) => {
        pArr.foreach((p, i) => {
          Promise.resolve(p).then(
            (value) => {
              resolve(value);
            },
            (reason) => {
              index++;
              result[i] = reject(reason);
              if (result.length === pArr.length) {
                reject(new AggregateError("All promises were rejected"));
              }
            }
          );
        });
      });
    };
    const p1 = Promise.resolve(1);
    const p2 = Promise.reject(2);
    const p3 = Promise.resolve(3);
    const p4 = Promise.reject(4);
    let pArr = [p2, p4];
    console.log(Promise.any(pArr));

4.finally:

  /*  接收一个回调函数,无论成功失败状态,都会执行finally指定的回调 */
    Promise.prototype.sx_finally = function (callback) {
      return this.then(
        (value) => {
          callback();
          return value;
        },
        (reason) => {
          callback();
          throw reason;
        }
      );
    };

5.race:

  /* 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
哪个Promise最快得到结果,就返回那个结果,无论成功失败 */
    Promise.race = function (pArr) {
      return new Promise((resolve, reject) => {
        pArr.forEach((p, i) => {
          resolve(p).then(
            (value) => {
              resolve(p);
            },
            (reason) => {
              reject(p);
            }
          );
        });
      });
    };
    const p1 = Promise.resolve(1);
    const p2 = Promise.reject(2);
    const p3 = Promise.resolve(3);
    const p4 = Promise.reject(4);
    let pArr = [p2, p1, p3, p4];
    console.log(Promise.race(pArr));

6.reject:

  /* Promise.reject() 会实例化一个 rejected 状态的 Promise。但与 Promise.resolve() 不同的是,如果给 Promise.reject() 传递一个 Promise 对象,则这个对象会成为新 Promise 的值。 */
    Promise.sx_reject = function (reason) {
      return new Promise((_, reject) => {
        reject(reason);
      });
    };
    let p = Promise.resolve(2);
    console.log(Promise.sx_reject(p));

7.resolve:

 /* 将一个非promise值转换为成功promise对象,如果传入的是promise对象直接返回 */
    Promise.sx_resolve = function (value) {
      if (value instanceof Promise) {
        return value;
      } else {
        return new Promise((resolve) => resolve(value));
      }
    };
    let p2 = Promise.resolve(1);
    console.log(Promise.sx_resolve(p2));

8.手写一个完整的Promise:

   /**
     * Promise构造函数:
     * executor: 执行器函数(同步执行)
     */
    const PENDING = "pending";
    const RESOLVED = "resolved";
    const REJECTED = "rejected";
    function Promise(executor) {
      const self = this;
      self.status = PENDING;
      self.data = undefined;
      self.callbacks = [];
      function resolve(value) {
        if (self.status !== PENDING) {
          return;
        }
        self.status = RESOLVED;
        self.data = value;
        //如果有待执行的callback函数,立即异步执行回调函数onResolved
        if (self.callbacks.length > 0) {
          setTimeout(() => {
            self.callbacks.forEach((callbacksObj) => {
              callbacksObj.onResolved(value);
            });
          });
        }
      }
      function reject(reason) {
        if (self.status !== PENDING) {
          return;
        }
        self.status = REJECTED;
        self.data = reason;
        //如果有待执行的callback函数,立即异步执行回调函数onResolved
        if (self.callbacks.length > 0) {
          setTimeout(() => {
            self.callbacks.forEach((callbacksObj) => {
              callbacksObj.onRejected(reason);
            });
          });
        }
      }

      try {
        executor(resolve, reject);
      } catch (error) {
        // 如果执行器抛出异常,primose对象变成rejected状态
        reject(error);
      }
    }
    /* then方法,指定回调 */
    Promise.prototype.then = function (onResolved, onRejected) {
      const self = this;
      //向后传递成功的value
      onResolved =
        typeof onResolved === "function"
          ? onResolved
          : (value = self.data) => value;
      //指定默认的失败的回调(实现错误/异常穿透的关键点),向后传递失败的reason
      reason = self.result;
      onRejected =
        typeof onRejected === "function"
          ? onRejected
          : (reason = self.data) => {
              throw reason;
            };

      //返回一个新的Promise对象
      return new Promise((resolve, reject) => {
        //执行指定回调,并处理回调的返回值。
        function handle(callback) {
          try {
            const result = callback(self.data);
            if (result instanceof Promise) {
              result.then(resolve, reject);
            } else {
              resolve(result);
            }
          } catch (error) {
            console.log(error, "error");
            reject(error);
          }
        }

        if (self.status === PENDING) {
          //假设当前还是pending状态,将回调函数保存起来
          self.callbacks.push({
            onResolved() {
              handle(onResolved);
            },
            onRejected() {
              handle(onRejected);
            },
          });
        } else if (self.status === RESOLVED) {
          //如果当前是resolved状态,异步执行onResolved函数并改变return的promise状态
          setTimeout(() => {
            handle(onResolved);
          });
        } else {
          //如果当前是rejected状态,异步执行onRejected函数并改变return的promise状态
          setTimeout(() => {
            handle(onRejected);
          });
        }
      });
    };

    /**
     * Promise原型对象的catch()
     * 指定失败的回调函数
     * 返回一个新的promise函数
     */
    Promise.prototype.catch = function (onRejected) {
      return this.then(undefined, onRejected);
    };
    //测试代码
    var p = new Promise((resolve, reject) => {
      // setTimeout(()=>{//放入队列中

      //  resolve('成功的回调');

      //  console.log('promise对象封装异步操作,异步执行微队列中的Onresolved回调函数')
      // });
      setTimeout(() => {
        //放入队列中
        reject("失败的回调");

        console.log(
          "promise对象封装异步操作,异步执行微队列中的Onrejected回调函数"
        );
      });
    });
    p.then(
      (value) => {
        console.log(value, "value");
      },
      (reason) => {
        console.log(reason, "reason");
        return new Promise((resolve, reject) => {
          reject(3);
        });
      }
    ).catch((reason) => {
      console.log("2", reason);
    });

9.并行发送多个任务:

 const myFun = async (list, count = 10) => {
      let len = list.length;
      let temp = len % count;
      let res = [];
      let queue = [];
      //需要循环几次
      times = !temp ? len / count : Math.floor(len / count) + 1;
      for (i = 0; i < times; i++) {
        let start = i * count;
        let end = count + start < len ? count * (i + 1) : len;
        queue = list.slice(start, end);
        res.push(...(await Promise.all(queue)));
      }
      return res;
    };
    //test
    console.log(myFun([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 4));

10.使用Promies预加载图片:

  //单个图片加载的方法
    const imgPreloader = (url) => {
      return new Promise((resolve, reject) => {
        let image = new Image();
        image.onload = () => {
          resolve("图片加载成功");
        };
        image.onerror = () => {
          reject("图片加载出错");
        };
        image.src = url;
      });
    };

    /**
     * 遍历图片路径,利用promise.all进行并行响应
     * @param imgs 图片路径数组
     * @returns {Promise<unknown[]>}
     */
    const allImgPreloader = (imgs) => {
      let promiseArr = [];
      imgs.forEach((src) => {
        promiseArr.push(imgPreloader(src));
      });
      return Promise.all(promiseArr);
    };
    //使用
    let imgs = [
      //图片路径数组
      "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F14%2F20210714150114_698ae.thumb.1000_0.png&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1658470369&t=a22bcc92c65ae42512ef880a00a3c5b0",
      "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202008%2F05%2F20200805155341_hitag.thumb.1000_0.jpg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1658470369&t=68e80f45c093aa3ef3a1d9e45693aa5a",
    ];

    console.log("开始渲染---渲染中。。。");
    const res = allImgPreloader(imgs);
    res.then((val) => {
      console.log("渲染成功");
      console.log( val); //['图片加载成功', '图片加载成功']
    });

六、 其他高频:

1.事件订阅与发布:

 //创建一个消息中心,存放事件回调
    let eventMap = {};
    //发布者发布消息,然后执行每个订阅了该消息的订阅者保存在消息中心的事件回调。
    function pub(msg, ...rest) {
      eventMap[msg] &&
        eventMap[msg].forEach((cb) => {
          cb(...rest);
        });
    }
    //订阅者把自己的回调保存到消息中心,供发布者消费。
    function sub(msg, cb) {
      eventMap[msg] = eventMap[msg] || [];
      eventMap[msg].push(cb);
    }
    //测试
    sub("name", (name) => {
      console.log("我是" + name);
    });
    pub("name", "大飞老师");

2.实现原生ajax:

 function myAjax(url, method, data) {
      let promise = new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
            if (
              (xhr.status >= 200 && xhr.status < 300) ||
              xhr.status === 304
            ) {
              resolve(xhr.response);
            } else {
              reject(new Error("error"));
            }
          }
        };
        if (method.toUpperCase() === "GET") {
          let paramslist = [];
          for (key in data) {
            paramslist.push(key + "=" + data[key]);
          }
          //根据get请求方法对url进行拼接
          let params = paramslist.join("&");
          url = url + "?" + params;
          xhr.open("get", url, false);
          //使用get请求将内容连接在url后面
          xhr.send();
        }
        if (method.toUpperCase() === "POST") {
          xhr.open("post", url, false);
          xhr.setRequestHeader(
            "Content-Type",
            "application/x-www-form-urlencoded;charset=utf-8"
          );
          xhr.send(data);
          //使用post请求时将内容放在send里面
        }
      });
      return promise;
    }

3.jsonp:

 function jsonp({ url, params, callback }) {
      return new Promise((resolve, reject) => {
        let script = document.createElement("script");
        window[callback] = function (data) {
          resolve(data);
          document.body.removeChild(script);
        };
        params = { ...params, callback }; // wd=b&callback=show
        let arrs = [];
        for (let key in params) {
          arrs.push(`${key}=${params[key]}`);
        }
        script.src = `${url}?${arrs.join("&")}`;
        document.body.appendChild(script);
      });
    }
    jsonp({
      url: "http://localhost:3000/say",
      params: { wd: "hahaha" },
      callback: "show",
    }).then((data) => {
      console.log(data);
    });

4.函数防抖:

  /* 
  延迟动作的执行,在延迟时间内当之前的动作未完成,再次触发动作,会取消上一次的动作,开始新的动作并重新开始计时{即一段特定的时间内,只会执行最后一次动作}。
  实现:定时器
   */
    function debounce(fn, delay = 500) {
      let timeId; //使用闭包,可以在内部修改外部函数的局部变量。【该变量必须被所有的定时器所共享】
      return function () {
        if (timeId) {
          clearTimeout(timeId);
        }
        timeId = setTimeout(() => {
          fn.apply(this, arguments); //注意this问题
        }, delay);
      };
    }
    //测试代码
    var node = document.getElementById("test");
    function getUserAction(e) {
      console.log(this, e);
    }
    node.onmousemove = debounce(getUserAction, 1000);

5.函数节流:

 /* 
  函数节流:在设定的时间内只执行一次操作,当前操作执行完了才会执行之后操作[刚开始会立即执行]
  实现:定时器与标识符flag
  当前操作未执行完,flag为false,不会执行定时器部分代码;当前操作执行结束flag置为true,下次触发动作时会执行
   */
    function throttle(fn, delay = 500) {
      let flag = true; //保证第一次可以执行
      return function () {
        if (!flag) {
          return;
        }
        flag = false; //将其置为false,下次判断时,如果为false,则说明本次动作为执行完成,也不会执行定时器部分的代码
        setTimeout(() => {
          fn.apply(this, arguments);
          flag = true; //本次动作完成,置为true,可以继续下一个动作执行
        }, delay);
      };
    }
    let node = document.querySelector("#test");
    node.addEventListener("mousemove", throttle(test, 1000));
    function test(e) {
      console.log(this, e);
    }

6.函数科里化:

 /* 函数调用后会接着返回一个函数,这样可以实现多次传参,最后统一处理 [参数固定、不太好传参]
 思路: 要判断当前传入函数的参数个数 (args.length) 是否大于等于原函数所需参数个数 (fn.length) ,如果是,则执行当前函数;如果是小于,则返回一个函数。*/
    const curry = (fn, ...args) =>
      args.length >= fn.length
        ? fn(...args)
        : (..._args) => curry(fn, ...args, ..._args);

    const add = (x, y, z) => {
      return x + y + z;
    };
    const add1 = curry(add);
    console.log(add1(1, 2, 3));
    console.log(add1(1)(2)(3));

7.深拷贝:

/* 递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。
*/
    function deepClone(obj, hash = new WeakMap()) {
      if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
      if (obj instanceof Date) return new Date(obj);
      if (obj instanceof RegExp) return new RegExp(obj);
      if (typeof obj !== "object") return obj;
      //如果hash中存在我们传入的对象,说明已经进行过深拷贝,直接返回该对象,这样就解决了循环引用问题。
      if (hash.get(obj)) return hash.get(obj);
      let cloneObj = new obj.constructor();
      // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
      hash.set(obj, cloneObj);
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          // 实现一个递归拷贝
          cloneObj[key] = deepClone(obj[key], hash);
        }
      }
      return cloneObj;
    }
    let obj = { name: 1, address: { x: 100 } };
    obj.o = obj; // 对象存在循环引用的情况
    let d = deepClone(obj);
    obj.address.x = 200;
    console.log(d);

8.图片懒加载:

  /*  <!--   在懒加载的实现中,有两个关键的数值:一个是当前可视区域的高度( window.innerHeight(ie9以上)||document.documentElement.clientHeight),另一个是元素距离可视区域顶部的高度。(getBoundingClientRect().top) --> */
    // 获取所有的图片标签
    const imgs = document.getElementsByTagName("img");
    // 获取可视区域的高度
    const viewHeight =
      window.innerHeight || document.documentElement.clientHeight;
    // num用于统计当前显示到了哪一张图片,避免每次都从第一张图片开始检查是否露出
    let num = 0;
    function lazyload() {
      for (let i = num; i < imgs.length; i++) {
        // 用可视区域高度减去元素顶部距离可视区域顶部的高度
        let distance = viewHeight - imgs[i].getBoundingClientRect().top;
        // 如果可视区域高度大于等于元素顶部距离可视区域顶部的高度,说明元素露出
        if (distance >= 0) {
          // 给元素写入真实的src,展示图片
          imgs[i].src = imgs[i].getAttribute("data-src");
          // 前i张图片已经加载完毕,下次从第i+1张开始检查是否露出
          num = i + 1;
        }
      }
    }
    // 监听Scroll事件
    window.addEventListener("scroll", lazyload, false);

9.自定义迭代器:

function iterator(list) {
      let index = 0;
      let len = list.length;
      return {
        next: function () {
          let done = index >= len;
          let value = !done ? list[index++] : undefined;
          return { value, done };
        },
      };
    }
    var iterator1 = iterator(["1号选手", "2号选手", "3号选手"]);
    console.log(iterator1.next());
    console.log(iterator1.next());
    console.log(iterator1.next());
    console.log(iterator1.next());

10.原型链继承:

//1.让子类的原型成为父类的实例Child.prototype = new parent()
   //2.会让子类原型的constructor构造器属性丢失,所以需要让construct重新指向子类
  function Parent (name,age) {
     this.name = name
     this.age = age
  
  }
  function Child(score) {
    this.score = score
    this.sayScore = function (){
      console.log(this.score)
    }
  }
  Child.prototype = new Parent('ldh',18)
  Child.prototype.constructor = Child
  const child1 = new Child(45)
  console.log(child1.name)
  child1.sayScore()
  console.log(Child.prototype)
  console.log(Child)

11.自定义new操作符:

/*创建一个新对象,包含原型和实例本身的属性和方法,如果构造函数返回一个对象则直接返回构造函数返回值,否则返回新创建的对象*/
    function myNew(Func, ...args) {
      let obj = Object.create(Func.prototype);
      const result = Func.apply(obj, args);
      //判断返回值是否为对象。
      return result instanceof Object ? result : obj;
    }
    function Test(name, age) {
      this.name = name;
      this.age = age;
    }
    Test.prototype.sayName = function () {
      console.log(this.name);
    };
    const obj1 = myNew(Test, "lihua", 15);
    obj1.sayName();
    console.log(obj1.age);

12.trim:

 String.prototype.trim = function () {
      return this.replace(/^\s+/, "").replace(/\s+&/, "");
    };
    const str = "   qweqweqwe    ";
    console.log(str);
    console.log(str.trim());

七、排序算法:

1.冒泡:

 function bubbleSort(arr) {
      const len = arr.length;
      let flag = false;
      //外层循环代表需要比较几趟(比较length趟)
      for (let i = 0; i < len; i++) {
        //内层循环用于控制每一趟比较的次数,比较了i次,说明最后i个元素已有序无序需比较
        for (let j = 0; j < len - 1 - i; j++) {
          if (arr[j] > arr[j + 1]) {
            [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
            //如果一次都没交换则说明有序(flag=false)
            flag = true;
          }
        }
        if (!flag) {
          console.log(flag);
          return arr;
        }
      }
      return arr;
    }
    let arr1 = [1, 2, 3, 4];
    let arr2 = [5, 2, 1, 4];
    res1 = bubbleSort(arr1);
    res2 = bubbleSort(arr2);
    console.log(res1);
    console.log(res2);

2.插入排序:


function insertionSort(arr) {
  var len = arr.length;
  var preIndex, current;
  for (var i = 1; i < len; i++) {
      preIndex = i - 1;
      current = arr[i];
      while(preIndex >= 0 && arr[preIndex] > current) {
          arr[preIndex+1] = arr[preIndex];
          preIndex--;
      }
      arr[preIndex+1] = current;
  }
  return arr;
}

3.快速排序:

//每次以中点为基准值,比基准值小放入leftArr,大放到rightArr,然后分别对左右数组进行取基准值放入对应数组的操作,递归结束这些基准值的位置都有序了,再按序拼接合并;也就是先分治后合并。
const quickSort = (arr) => {
      const len = arr.length;
      if (len <= 1) return arr;
      const midIndex = Math.floor(len / 2);
      const leftArr = [];
      const rightArr = [];
      //取出基准值(小 --》 基准值 --》大)
      const midValue = arr.splice(midIndex, 1);
      let index = 0;
      //遍历数组,将元素放到适合他的子数组中
      while (index < len - 1) {
        if (arr[index] < midValue) {
          leftArr.push(arr[index]);
        } else {
          rightArr.push(arr[index]);
        }
        index++;
      }
      return quickSort(leftArr).concat(midValue).concat(quickSort(rightArr));
    };
    const arr = [3, 5, 1, 2];
    const res = quickSort(arr);
    console.log(res);

4.归并排序:

  /*  **归并排序:时间复杂度O(nlogn)** ,分的时间复杂度`O(logn)`,合并的过程的复杂度是`O(n)`

-   分:把数组分成两半,递归子数组,进行分割操作,直到分成一个数
-   合:把两个字数组合并成一个有序数组,直到全部子数组合并完毕,合并前先准备一个空数组,存放合并之后的结果,然后不断取出两个子数组的第一个元素,比较他们的大小,小的先进入之前准备的空数组中,然后继续遍历其他元素,直到子数组中的元素都完成遍历*/
  
function mergeSort(arr) {  //采用自上而下的递归方法
  var len = arr.length;
  if(len < 2) {
      return arr;
  }
  var middle = Math.floor(len / 2),
      left = arr.slice(0, middle),
      right = arr.slice(middle);
  return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right)
{
  var result = [];

  while (left.length && right.length) {
      if (left[0] <= right[0]) {
          result.push(left.shift());
      } else {
          result.push(right.shift());
      }
  }

  while (left.length)
      result.push(left.shift());

  while (right.length)
      result.push(right.shift());

  return result;
}
console.log(mergeSort([3, 2, 2, 13, 4, 2, 1]));