手写各种方法汇总~ 持续更新

-1. JS排序算法

// 一、冒泡排序 --- 逐一比较(在比较交换的时候,就是计算机中最经典的交换策略,用临时变量temp保存值)

// 这里有两点需要注意:
// (1)外层循环,从最大值开始递减,因为内层是两两比较,因此最外层当>=2时即可停止;
// (2)内层是两两比较,从0开始,比较y与y+1,因此,临界条件是y<i -1

function bubleSort(arr) {
  var len = arr.length;
  for (let i = len; i >= 2; i--) {
    for (let y = 0; y <= i - 1; y++) {
      if (arr[y] > arr[y + 1]) {
        let temp = arr[y];
        arr[y] = arr[y + 1];
        arr[y + 1] = temp;
      }
    }
  }
  return arr;
}

// 其中,ES6的方法就是 -- 解构赋值  (冒泡排序优化)
arr2 = [1, 2, 3, 4];
[arr2[0], arr2[1]] = [arr2[1], arr2[0]]; //ES6解构赋值

function bubleSort(arr) {
  var len = arr.length;
  for (let i = len; i >= 2; i--) {
    for (let y = 0; y <= i - 1; y++) {
      if (arr[y] > arr[y + 1]) {
        [arr[y], arr[y + 1]] = [arr[y + 1], arr[y]];
      }
    }
  }
  return arr;
}

// 二、选择排序 --- 选择排序是从数组的开头开始,将第一个元素和其他元素作比较,检查完所有的元素后,
// 最小的放在第一个位置,接下来再开始从第二个元素开始,重复以上一直到最后。

function selectSort(arr) {
  var len = arr.length;
  for (let i = 0; i < len - 1; i++) {
    for (let j = i; j < len; j++) {
      if (arr[j] < arr[i]) {
        [arr[i], arr[j]] = [arr[j], arr[i]]; //交换位置
      }
    }
  }
  return arr;
}

// 外层循环的i表示第几轮,arr[i]就表示当前轮次最靠前(小)的位置;
// 内层从i开始,依次往后数,找到比开头小的,互换位置即可

// 三、高级排序算法 ============================================================================= 必考

// (1)快速排序

function quickSort(arr) {
  if (arr.length <= 1) {
    return arr; //递归出口
  }
  var left = [],
    right = [],
    current = arr.splice(0, 1); //注意splice后,数组长度少了一个
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] < current) {
      left.push(arr[i]); //放在左边
    } else {
      right.push(arr[i]); //放在右边
    }
  }
  return quickSort(left).concat(current, quickSort(right)); //递归
}

// 升级版--三路快排

function qSort3(arr) {
  //三路快排
  if (arr.length == 0) {
    return [];
  }
  var left = [];
  var center = [];
  var right = [];
  var pivot = arr[0]; //偷懒,直接取第一个,实际取值情况 参考[快速排序算法的优化思路总结]
  for (var i = 0; i < arr.length; i++) {
    if (arr[i] < pivot) {
      left.push(arr[i]);
    } else if (arr[i] == pivot) {
      center.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }
  return [...qSort3(left), ...center, ...qSort3(right)];
}

0、防抖、截流

防抖是将多次执行变为最后一次执行
// 防抖  防抖重在清零「clearTimeout(timer)」
// 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
// 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖


function debounce (fn, delay) {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn(...args)
    }, delay)
  }
}


节流是将多次执行变成每隔一段时间执行
// 所以节流就像是一个看门大爷,每一段时间它只会放一个人进去 
// 节流  节流重在加锁「flag = false」
// scroll 事件,每隔一秒计算一次位置信息等
// 浏览器播放事件,每个一秒计算一次进度信息等

function throttle (fn, delay) {
  let timer
  return (...args) => {
    if (timer)  return 
    timer = setTimeout(() => {
      fn(...args)
      timer = null
    }, delay)
  }
}

一、 数组扁平化

       while循环 、for循环 、reduce 

let arr = [
  [1, 2, 2],
  [3, 4, 5, 5],
  [6, 7, 8, 9, [11, 12, [12, 13, [14]]]],
];
function flatten(arr) {
  while (arr.some((item) => Array.isArray(item))) {
    arr = [].concat(...arr);
  }
  return arr;
}
flatten(arr); //[1, 2, 3, 1, 2, 3, 4, 2, 3, 4]

function flatten(data) {
  let arr = [];
  for (let i = 0; i < data.length; i++) {
    const element = data[i];
    if (Array.isArray(element)) {
      arr = arr.concat(flatten(element));
    } else {
      arr.push(element);
    }
  }
  return arr;
}

const flatten = (arr) => {
  return arr.reduce((a, b) => {
    if (b instanceof Array) {
      return a.concat(flatten(b));
    }
    return a.concat(b);
  }, []);
};

console.log(flatten(arr1)); //[1, 2, 3, 1, 2, 3, 4, 2, 3, 4]


 

二、深拷贝

function deepcopy(data) {
  let obj = data.constructor === Array ? [] : {};
  if (typeof data === "object") {
    for (const i in data) {
      const element = data[i];
      obj[i] = typeof element === "object" ? deepcopy(element) : element;
    }
  } else {
    obj = data;
  }
  return obj;
}

  • 深拷贝的终极解决方案 通过遍历的方式深拷贝(无视递归可能带来的爆栈问题)

function cloneLoop(obj) {
  const root = {};
  const stack = [
    {
      parent: root,
      key: undefined,
      data: obj,
    },
  ];
    // 深度优先
  while (stack.length) {
    const node = stack.pop();
    const parent = node.parent;
    const key = node.key;
    const data = node.data;
    // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
    let res = parent;
    if (typeof key !== "undefined") {
      res = parent[key] = {};
    }
    for (let k in data) {
      if (Object.hasOwnProperty.call(data, k)) {
        if (typeof data[k] === "object") {
          // 下一次循环
          stack.push({
            parent: res,
            key: k,
            data: data[k],
          });
        } else {
          res[k] = data[k];
        }
      }
    }
  }
  return root;
}

三、深度优先遍历、广度优先遍历

const tree = {
  name: "root",
  children: [
    {
      name: "c1",
      children: [
        {
          name: "c11",
          children: [],
        },
        {
          name: "c12",
          children: [],
        },
      ],
    },
    {
      name: "c2",
      children: [
        {
          name: "c21",
          children: [],
        },
        {
          name: "c22",
          children: [],
        },
      ],
    },
  ],
};

// 深度优先遍历
// ======== 循环 栈! 版本 ========
// arr = [] stack = [parent]
// arr = [parent] stack = [child3,child2,child1]
// arr = [parent, child1] stack = [child3,child2,child1-2,child1-1]
// arr = [parent, child1-1] stack = [child3,child2,child1-2]

function deeptraversal(root) {
  let arr = [];
  let stack = []; //	
  if (!root) return [];
  stack.push(root);
  while (stack.length) {
    let node = stack.pop();
    if (node === null) continue;
    arr.push(node);
    for (let i = node.children.length - 1; i >= 0; i--) {
      // 这里就是面试的重点,应该从后面的节点压入栈中
      stack.push(node.children[i]);
    }
  }
  return arr;
}

// 递归版本
let deepTraversal1 = (root, nodeList = []) => {
  if (!root) return [];
  nodeList.push(root);
  for (let i = 0; i < root.children.length; i++) {
    deepTraversal1(root.children[i], nodeList);
  }
  return nodeList;
};



// 广度优先遍历
let widthTraversal2 = (root) => {
  let arr = [];
  let stack = [];
  if (root) {
    stack.push(root);
    while (stack.length) {
      let item = stack.shift();
      let children = item.children;
      arr.push(item);
      // 队列,先进先出
      // arr = [] stack = [parent]
      // arr = [parent] stack = [child1,child2,child3]
      // arr = [parent, child1] stack = [child2,child3,child1-1,child1-2]
      // arr = [parent,child1,child2]
      for (let i = 0; i < children.length; i++) {
        stack.push(children[i]);
      }
    }
  }
  return arr;
};

四、new

  function myNew(func, ...args) {
            // 1. 判断方法体
            if (typeof func !== 'function') {
                throw '第一个参数必须是方法体'
            }
            // 2. 创建新对象
            const obj = {}
            // 3. 这个对象的 __proto__ 指向 func 这个类的原型对象
            // 即实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性
            obj.__proto__ = Object.create(func.prototype)

            // 为了兼容 IE 可以让步骤 2 和 步骤 3 合并
            // const obj = Object.create(func.prototype);

            // 4. 通过 apply 绑定 this 执行并且获取运行后的结果
            let result = func.apply(obj, args)

            // 5. 如果构造函数返回的结果是引用数据类型,则返回运行后的结果
            // 否则返回新创建的 obj
            const isObject = typeof result === 'object' && result !== null
            const isFunction = typeof result === 'function'
            return isObject || isFunction ? result : obj
  }

Object.create

//实现Object.create方法
function create(proto) {
    function Fn() {};
    Fn.prototype = proto;
    Fn.prototype.constructor = Fn;
    return new Fn();
}
let demo = {
    c : '123'
}
let cc = Object.create(demo)

五、call、apply、bind

// call
Function.prototype.call = function (context, ...args) {
  context = context || window;

  const fnSymbol = Symbol("fn");
  context[fnSymbol] = this;

  context[fnSymbol](...args);
  delete context[fnSymbol];
};

// apply
Function.prototype.apply = function (context, argsArr) {
  context = context || window;

  const fnSymbol = Symbol("fn");
  context[fnSymbol] = this;

  context[fnSymbol](...argsArr);
  delete context[fnSymbol];
};

// bind
Function.prototype.bind = function (context, ...args) {
  context = context || window;
  const fnSymbol = Symbol("fn");
  context[fnSymbol] = this;

  return function (..._args) {
    args = args.concat(_args);

    context[fnSymbol](...args);
    delete context[fnSymbol];
  };
};


六、实现柯里化 

function createCurry(func, args) {
  var argity = func.length;
  var args = args || [];

  return function () {
    var _args = [].slice.apply(arguments);
    args.push(..._args);
    if (args.length < argity) {
      return createCurry.call(this, func, args);
    }
    return func.apply(this, args);
  };
}

七、数组去重

// 最高性能数组去重方法 10万数量级:3毫秒,100万数量级:6毫秒,1000万数量级36毫秒
// 利用对象的属性不会重复这一特性,校验数组元素是否重复
let arr = [2, 45, 6, 6, 7, 8, 9, 0];
function myset(data) {
  let arr = [];
  let obj = {};
  for (const item of data) {
    if (!obj[item]) {
      obj[item] = 1;
      arr.push(item);
    }
  }
  return arr;
}

// ES6 SET  Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。
function distinct5(a, b) {
  //   let arr = a.concat(b);
  return Array.from(new Set([...a, ...b]));
}

八、对象数组去重

const responseList = [
  { id: 1, a: 1 },
  { id: 2, a: 2 },
  { id: 3, a: 3 },
  { id: 1, a: 4 },
];
const result = responseList.reduce((acc, cur) => {
    const ids = acc.map(item => item.id);
    return ids.includes(cur.id) ? acc : [...acc, cur];
}, []);
console.log(result); // -> [ { id: 1, a: 1}, {id: 2, a: 2}, {id: 3, a: 3} ]

九、实现一个发布订阅模式拥有 on emit once off 方法

class EventEmitter {
  constructor() {
    this.events = {};
  }
  // 实现订阅
  on(type, callBack) {
    if (!this.events[type]) {
      this.events[type] = [callBack];
    } else {
      this.events[type].push(callBack);
    }
  }
  // 删除订阅
  off(type, callBack) {
    if (!this.events[type]) return;
    this.events[type] = this.events[type].filter((item) => {
      return item !== callBack;
    });
  }
  // 只执行一次订阅事件
  once(type, callBack) {
    function fn() {
      callBack();
      this.off(type, fn);
    }
    this.on(type, fn);
  }
  // 触发事件
  emit(type, ...rest) {
    this.events[type] &&
      this.events[type].forEach((fn) => fn.apply(this, rest));
  }
}
// 使用如下
// const event = new EventEmitter();

// const handle = (...rest) => {
//   console.log(rest);
// };

// event.on("click", handle);

// event.emit("click", 1, 2, 3, 4);

// event.off("click", handle);

// event.emit("click", 1, 2);

// event.once("dbClick", () => {
//   console.log(123456);
// });
// event.emit("dbClick");
// event.emit("dbClick");

十、在限定并发数下用 Promise 并发完成请求

class PromisePool {
  constructor(max, fn) {
    this.max = max; // 最大并发数
    this.fn = fn;   // 自定义的请求函数
    this.pool = []; // 并发池
    this.urls = []; // 剩余的请求地址
  }

  start(urls) {
    this.urls = urls;
    // 先循环把并发池塞满
    while (this.pool.length < this.max) {
      let url = this.urls.shift();
      this.setTask(url);
    }
    // 利用Promise.race 方法来获得并发池中某任务完成的信号
    let race = Promise.race(this.pool);
    return this.run(race);
  }

  run(race) {
    race
      .then(res => {
        // 每当并发池跑完一个任务,就再塞入一个任务
        let url = this.urls.shift();
        this.setTask(url);
        return this.run(Promise.race(this.pool));
      });
  }
  setTask(url) {
    if (!url) return;
    let task = this.fn(url);
    this.pool.push(task); // 将该任务推入pool并发池中
    console.log(`\x1B[43m ${url} 开始,当前并发数:${this.pool.length}`);
    task.then(res => {
      // 请求结束后将该Promise任务从并发池中移除
      this.pool.splice(this.pool.indexOf(task), 1);
      console.log(`\x1B[43m ${url} 结束,当前并发数:${this.pool.length}`);
    });
  }
}

// test
const URLS = [
  'bytedance.com',
  'tencent.com',
  'alibaba.com',
  'microsoft.com',
  'apple.com',
  'hulu.com',
  'amazon.com'
];
// 自定义请求函数
var requestFn = url => {
  return new Promise(resolve => {
    setTimeout(_ => {
      resolve(`任务 ${url} 完成`);
    }, 1000*dur++)
  }).then(res => {
    console.log('外部逻辑 ', res);
  })
}

const pool = new PromisePool(3, requestFn); // 并发数为3
pool.start(URLs);

十一、对对象属性访问的解析方法

eg:访问 a.b.c.d

函数柯里化 + 闭包 + 递归

function createGetValueByPath(path) {
  let paths = path.split("."); // [ xxx, yyy, zzz ]

  return function getValueByPath(obj) {
    let res = obj;
    let prop;
    while ((prop = paths.shift())) {
      res = res[prop];
    }
    return res;
  };
}

let getValueByPath = createGetValueByPath("a.b.c.d");

var o = {
  a: {
    b: {
      c: {
        d: {
          e: "正确了",
        },
      },
    },
  },
};
var res = getValueByPath(o);
console.log(res);

十二、vue处理响应式 defineReactive 实现+reactify

// 简化后的版本
function defineReactive(target, key, value, enumerable) {
  // 折中处理后, this 就是 Vue 实例
  let that = this;

  // 函数内部就是一个局部作用域, 这个 value 就只在函数内使用的变量 ( 闭包 )
  if (typeof value === "object" && value != null && !Array.isArray(value)) {
    // 是非数组的引用类型
    reactify(value); // 递归
  }

  Object.defineProperty(target, key, {
    configurable: true,
    enumerable: !!enumerable,

    get() {
      console.log(`读取 ${key} 属性`); // 额外
      return value;
    },
    set(newVal) {
      console.log(`设置 ${key} 属性为: ${newVal}`); // 额外

      value = reactify(newVal);
    },
  });
}


// 将对象 o 响应式化
function reactify(o, vm) {
  let keys = Object.keys(o);

  for (let i = 0; i < keys.length; i++) {
    let key = keys[i]; // 属性名
    let value = o[key];
    if (Array.isArray(value)) {
      // 数组
      value.__proto__ = array_methods; // 数组就响应式了
      for (let j = 0; j < value.length; j++) {
        reactify(value[j], vm); // 递归
      }
    } else {
      // 对象或值类型
      defineReactive.call(vm, o, key, value, true);
    }
  }
}

十三、终极 Promise

class MyPromise {
  constructor(exec) {
    this.status = "pending";
    this.value = undefined;
    this.reason = undefined;
    this.onResolved = [];
    this.onRejected = [];
    this.onFinally = [];

    const resolve = (value) => {
      if (this.status !== "pending") return;
      this.status = "resolved";
      this.value = value;
      this.onResolved.forEach((fn) => fn());
    };

    const reject = (reason) => {
      if (this.status !== "pending") return;
      this.status = "rejected";
      this.reason = reason;
      this.onRejected.forEach((fn) => fn());
    };

    try {
      exec(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onResolved, onRejected) {
    return new MyPromise((resolve, reject) => {
      const resolved = () => {
        try {
          const res = typeof onResolved === "function" ? onResolved(this.value) : this.value;
          return res instanceof MyPromise ? res.then(resolve, reject) : resolve(res);
        } catch (error) {
          reject(error);
        }
      };
      const rejected = () => {
        try {
          const res = typeof onRejected === "function" ? onRejected(this.reason) : this.reason;
          return res instanceof MyPromise ? res.then(resolve, reject) : reject(res);
        } catch (error) {
          reject(error);
        }
      };

      switch (this.status) {
        case "pending":
          this.onResolved.push(resolved);
          this.onRejected.push(rejected);
          break;
        case "resolved":
          resolved();
          break;
        case "rejected":
          rejected();
          break;
      }
    });
  }

  catch(onRejected) {
    // catch是then(undefined, onRejected)的语法糖
    return this.then(undefined, onRejected);
  }

  finally(onFinally) {
    return this.then(
      (value) => MyPromise.resolve(onFinally()).then(() => value),
      (reason) => MyPromise.resolve(onFinally()).then(() =>          
      MyPromise.reject(reason))
    );
  }

  static resolve(value) {
    // 静态方法,用于快速返回一个已解析的Promise
    return new MyPromise((resolve) => resolve(value));
  }

  static reject(reason) {
    // 静态方法,用于快速返回一个已拒绝的Promise
    return new MyPromise((_, reject) => reject(reason));
  }

  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const results = [];
      let count = 0;

      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then((value) => {
          results[index] = value;
          count++;
          if (count === promises.length) {
            resolve(results);
          }
        }, reject);
      });
    });
  }

  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach((promise) => {
        MyPromise.resolve(promise).then(resolve, reject);
      });
    });
  }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值