Shane Jix

手撕 JavaScript 高频知识点,彻底征服面试官

create:April 12, 2022  update:April 12, 2022  ☕️☕️ 12 min read

同步链接: https://www.shanejix.com/posts/手撕 JavaScript 高频知识点,彻底征服面试官/

  1. 数组扁平化
const arr = [1, [2, [3, [4, 5]]], 6];

// => [1, 2, 3, 4, 5, 6]

/** */

// method 1 : flat(Infinity)

const res = arr.flat(Infinity);

console.log(res);

// method 2: arr.reduce

function flatDeep(arr, d = 1) {
  return d > 0
    ? arr.reduce(
        (acc, val) =>
          acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val),
        []
      )
    : arr.slice();
}

const res2 = flatDeep(arr, Infinity);

console.log(res2);

// method 2 another

function flatten(arr) {
  return arr.reduce((acc, curr) => {
    return acc.concat(Array.isArray(curr) ? flatten(curr) : curr);
  }, []);
}

console.log(flatten(arr));

// method 3 : RegExp

const res3 = JSON.stringify(arr).replace(/\[|\]/g, "").split(",");

console.log(res3);

// method 3 perform

const str = "[" + JSON.stringify(arr).replace(/\[|\]/g, "") + "]";

console.log(JSON.parse(str));

// method 4 : recursivity

const res4 = [];
const recursivity = (arr) => {
  for (let item of arr) {
    if (Array.isArray(item)) {
      recursivity(item);
    } else {
      res4.push(item);
    }
  }
};

recursivity(arr);

console.log(res4);

//

// references

// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
  1. 数组去重
const arr = [1, 1, "1", 17, true, true, false, false, "true", "a", {}, {}];

// => [1, '1', 17, true, false, 'true', 'a', {}, {}]

/** */

// method 1 : Set

// const res1 = Array.from(new Set(arr));
const res1 = [...new Set(arr)];

console.log("res1:", res1);

// method 2 : for

function unique(arr) {
  let len = arr.length - 1;

  for (let i = 0; i < len; i++) {
    for (let j = i + 1; j < len; j++) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1); // del
        j--;
        len--;
      }
    }
  }

  return arr;
}

console.log("res2:", unique(arr));

// method 3 : indexOf / includes

const unique2 = (arr) => {
  const res = [];

  for (let i = 0; i < arr.length; i++) {
    // if (res.indexOf(arr[i] === -1)) {
    if (!res.includes(arr[i])) {
      res.push(arr[i]);
    }
  }

  return res;
};

// console.log('res3:', unique2(arr));
console.log(
  "res3:",
  unique2([1, 1, "1", 17, true, true, false, false, "true", "a", {}, {}])
);

// method 4 : filter

const res4 = arr.filter((curr, idx) => {
  return arr.indexOf(curr) === idx;
});

console.log("res4", res4);

// method 5 : Map

const map = new Map();
const res5 = [];

for (let item of arr) {
  if (!map.has(item)) {
    res5.push(item);
    map.set(item, true);
  }
}

console.log("res5:", res5);

/** */

// references

// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set
  1. 类数组转化为数组
// Array.form
arr.from(document.querySelectorAll("div"));

// ...
[...document.querySelectorAll("div")];

// slice.call
Array.prototype.slice.call(document.querySelectorAll("div"));

// concat.apply
Array.prototype.concat.apply([], document.querySelectorAll("div"));
  1. Array.prototype.filter()
// Array.prototype.filter()

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

arr.filter((curr, idx, arr) => {}, null);

Array.prototype.myfilter = function (callback, thisArg) {
  const res = [];

  for (let i = 0; i < this.length; i++) {
    callback && callback.call(thisArg, this[i], i, this) && res.push(this[i]);
  }

  return res;
};
  1. Array.prototype.map()
// Array.prototype.map()

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

arr.map((curr, idx, arr) => {
  return curr + 1;
}, null);

Array.prototype.mymap = function (callback, thisArg) {
  const res = [];

  for (let i = 0; i < this.length; i++) {
    // callback && (res[i] = callback.call(thisArg, this[i], i, this));
    res[i] = callback && callback.call(thisArg, this[i], i, this);
  }

  return res;
};
  1. Array.prototype.forEach()
// Array.prototype.forEach()

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

arr.forEach((curr, idx, arr) => {
  // ...
}, null);

Array.prototype.myforEach = function (callback, thisArg) {
  const res = [];

  for (let i = 0; i < this.length; i++) {
    callback && callback.call(thisArg, this[i], i, this);
  }

  return res;
};
  1. Array.prototype.every()
// Array.prototype.every()

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

arr.every((curr, idx, arr) => {
  // ...
}, null);

Array.prototype.myevery = function (callback, thisArg) {
  for (let i = 0; i < this.length; i++) {
    if (callback && !callback.call(thisArg, this[i], i, this)) {
      return false;
    }
  }

  return true;
};
  1. Array.prototype.some()
// Array.prototype.some()

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

arr.some((curr, idx, arr) => {
  // ...
}, null);

Array.prototype.mysome = function (callback, thisArg) {
  for (let i = 0; i < this.length; i++) {
    if (callback && callback.call(thisArg, this[i], i, this)) {
      return true;
    }
  }

  return false;
};
  1. Array.prototype.reduce()
// Array.prototype.reduce()

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

arr.reduce((acc, curr, idx, arr) => {
  // ...

  return acc;
}, []);

Array.prototype.myreduce = function (callback, initial) {
  let acc;
  let start = 0;

  if (initial) {
    acc = initial;
  } else {
    acc = this[0];
    start = 1;
  }

  for (let i = start; i < this.length; i++) {
    callback && callback(acc, this[i], i, this);
  }

  return acc;
};
  1. Array.prototype.join
Array.prototype.myjoin = function (str = ",") {
  let resStr = "";

  for (let i = 0; i < this.length; i++) {
    let item = this[i];

    resStr = i === 0 ? item : `${resStr}${str}${item}`;
  }

  return resStr;
};
  1. Array.prototype.flat
Array.prototype.myflat = function (deep = Infinity) {
  let arr = this;
  let i = 0;

  while (arr.some((item) => Array.isArray(item))) {
    arr = [].concat(...arr);
    i++;
    if (i >= deep) {
      break;
    }
  }

  return arr;
};
  1. Array.prototype.splice
Array.prototype.mysplice = function (start, len, ...values) {
  if (len === 0) {
    return [];
  }

  len = start + len > this.length - 1 ? this.length - start : this.length;

  // todo
};

// 算是 Array接口中最复杂的一个了,比较有挑战性,没做出来,我是彩笔!
  1. Array.prototype.apply
Array.prototype.myapply = function (thisArg, argsArr) {
  if (typeof this !== "function") {
    throw Error("no function");
  }

  if (thisArg === null || thisArg === undefined) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }

  const func = new Symbol("func");

  thisArg[func] = this;

  const result = thisArg[func](...argsArr);

  delete thisArg[func];

  return result;
};

function foo(...args) {
  return args;
}

foo.myapply(null, [1, 2]);
  1. Array.prototype.call
Array.prototype.mycall = function (thisArg, ...argsArr) {
  if (typeof this !== "function") {
    throw Error("no function");
  }

  if (thisArg === null || thisArg === undefined) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }

  const func = new Symbol("func");

  thisArg[func] = this;

  const result = thisArg[func](...argsArr);

  delete thisArg[func];

  return result;
};

function foo(...args) {
  return args;
}

foo.myapply(null, [1, 2]);
  1. Array.prototype.bind
Array.prototype.mybind = function (thisArg, ...argsArr) {
  if (typeof this !== "function") {
    throw Error("no function");
  }

  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }

  let func = this;

  return function (...args) {
    return func.apply(this instanceof func ? this : thisArg, args);
  };
};

function foo(...args) {
  return args;
}

const bar = foo.mybind(null, [1, 2]);

bar(1, 2);
  1. debounce
function debounce(func, wait) {
  let timer = null;

  return function () {
    clearTimeout(timer);

    timer = setTimeout(() => func(), wait);
  };
}

function debounce2(func, wait, immediate) {
  let timer = null;

  return function () {
    clearTimeout(timer);

    if (immediate) {
      let callNow = !timer;

      timer = setTimeout(() => func(), wait);

      if (callNow) {
        func();
      }
    } else {
      timer = setTimeout(() => func(), wait);
    }
  };
}

function foo(...args) {
  console.log(1);
  return args;
}

const bar = debounce(foo, 3000);

const bar1 = debounce2(foo, 3000, true);

for (let i = 0; i < 30000; i++) {
  // bar()
  bar1();
}

// for (let i = 0; i < 30000000; i++) {
//   bar()
// }
  1. throttle
function throttle1(func, wait) {
  let start = +new Date();

  return function () {
    let end = +new Date();

    if (end - start >= wait) {
      func();
      start = +new Date();
    }
  };
}

function throttle2(func, wait) {
  let start = +new Date();
  let timer = null;

  return function () {
    let end = +new Date();
    let remaining = wait - (end - start);

    clearTimeout(timer);

    if (remaining <= 0) {
      func();
      start = +new Date();
    } else {
      timer = setTimeout(() => func(), remaining);
    }
  };
}

function foo() {
  console.log("1");
}

const bar = throttle2(foo, 3000);

for (let i = 0; i < 300000000000; i++) {
  bar();
}
  1. 函数柯里化
function add(...argsArr) {
  const _argsArr = [...argsArr];

  function func(...args) {
    _argsArr.push(...args);

    return func;
  }

  func.console = function () {
    console.log(_argsArr);
  };

  func.sum = function () {
    return _argsArr.reduce((sum, curr) => {
      return (sum += curr);
    }, 0);
  };

  func.toString = function () {
    return _argsArr.reduce((sum, curr) => {
      return (sum += curr);
    }, 0);
  };

  return func;
}

const res1 = add(1)(2)(3)(4);
const res2 = add(1, 2, 4)(2)(3)(4);

console.log(res1.console());
console.log(res2.console());
console.log(res1.sum());
console.log(res2.sum());
console.log(res1.toString());
console.log(res2.toString());
  1. new
function mynew(F, ...args) {
  let obj = {};

  Object.setPrototypeOf(obj, F.prototype);

  const res = F.apply(obj, args);

  const isObject = typeof res === "object" && typeof res !== "null";
  const isFunction = typeof res === "function";

  return isObject || isFunction ? res : obj;
}

function Foo(...args) {
  this.args = args;
}

const res = mynew(Foo, 1, 2, 3);

console.log(res);
  1. instanceof
function myinstanceof(left, right) {
  if (
    (typeof left !== "object" && typeof left !== "function") ||
    typeof left === null
  ) {
    return false;
  }

  let proto = Object.getPrototypeOf(left);

  while (true) {
    if (proto === null) {
      return false;
    }
    if (proto === right.prototype) {
      return true;
    }

    proto = Object.getPrototypeOf(proto);
  }
}

function foo() {}

// const bar = new foo()

// console.log(Object.getPrototypeOf(bar) === foo.prototype)

// const res = myinstanceof(bar, foo)
const res = myinstanceof(foo, Function);

console.log(res);
  1. 寄生组合式继承
function Parent(...args) {
  this.args = args;
  this.getArgs = function () {
    console.log(this.args);
  };
}

Parent.prototype.method = function () {
  console.log("perent method");
};

function Child(...args) {
  Parent.call(this, ...args);
  this.childArgs = args;
}

function _extends(Child, Parent) {
  let proto = Object.create(Parent.prototype);
  Child.prototype = proto;
  Child.prototype.constructor = Child;
}

_extends(Child, Parent);

const child = new Child(1, 2);

console.log(child);
console.log(child.__proto__.__proto__);
console.log(Object.getPrototypeOf(child));

child.method();
  1. Object.is
function is(x, y) {
  // 0 === -0 false
  if (x === y) {
    return x !== 0 || y !== 0 || 1 / x === 1 / y;
  } else {
    // NaN === NaN true
    return x !== x && y !== y;
  }
}

console.log(is(0, -0));
console.log(is(NaN, NaN));
  1. Object.assign
Object.defineProperty(Object, "myassign", {
  value: function (target, ...args) {
    if (target === null) {
      throw Error("not null");
    }

    const to = Object(target);

    for (let source of args) {
      if (typeof source !== null) {
        for (let key in source) {
          if (Object.prototype.hasOwnProperty.call(source, key)) {
            to[key] = source[key];
          }
        }
      }
    }

    return to;
  },
  enumerable: false,
  writable: false,
  configurable: true,
});

const res = Object.myassign({}, { foo: 1, bar: "2" });

console.log(res);
  1. 深、浅拷贝
const shallowClone = (obj) => {
  const newObj = {};

  for (let key of obj) {
    if (Object.hasOwnProperty.call(obj, key)) {
      newObj[key] = obj[key];
    }
  }

  return newObj;
};

function deepClone(obj, has = new WeakMap()) {
  if (obj === null || obj === undefined) {
    return obj;
  }

  if (obj instanceof Date) {
    return new Date(obj);
  }

  if (obj instanceof RegExp) {
    return new RegExp(obj);
  }

  if (obj !== "object") {
    return obj;
  }

  if (has.get(obj)) {
    return has.get(obj);
  }

  let cloneObj = new obj.constructor();
  hash.set(obj, cloneObj);

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // cloneObj[key] = obj[key]
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }

  return cloneObj;
}
  1. Promise
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
  constructor(excutor) {
    this.status = PENDING;
    this.value = null;
    this.reason = null;
    this.onFullfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resovle = (value) => {
      if (this.status === PENDING) {
        this.value = value;
        this.status = FULFILLED;
        this.onFullfilledCallbacks.forEach((cb) => cb());
      }
    };

    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((cb) => cb());
      }
    };

    try {
      excutor(resovle, reject);
    } catch (e) {
      // throw e
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;

    onRejected = typeof onRejected === "function" ? onRejected : (r) => r;

    const self = this;

    return new Promise((resolve, reject) => {
      if (self.status === PENDING) {
        self.onFullfilledCallbacks.push(() => {
          try {
            setTimeout(() => {
              const result = onFulfilled(self.value);

              result instanceof Promise
                ? result.then(
                    (res) => resolve(res),
                    (rej) => reject(rej)
                  )
                : resolve(result);
            });
          } catch (error) {
            reject(error);
          }
        });

        self.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            const result = onRejected(self.reason);

            result instanceof Promise
              ? result.then(
                  (res) => resolve(res),
                  (rej) => reject(rej)
                )
              : reject(result);
          });
        });
      }

      if (self.status === FULFILLED) {
        try {
          setTimeout(() => {
            const result = onFulfilled(self.value);

            result instanceof Promise
              ? result.then(resolve, reject)
              : resolve(result);
          });
        } catch (error) {
          reject(error);
        }
      }

      if (self.status == REJECTED) {
        try {
          setTimeout(() => {
            const result = onRejected(self.reason);

            result instanceof Promise
              ? result.then(resolve, reject)
              : reject(result);
          });
        } catch (error) {
          reject(error);
        }
      }
    });
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  static resolve(value) {
    // return new Promise((resolve, reject) => {
    //   // resolve(null)

    // })

    if (value instanceof Promise) {
      return value;
    } else {
      return new Promise((resolve, reject) => {
        resolve(value);
      });
    }
  }

  static reject(reason) {
    return new Promise((resolve, reject) => reject(reason));
  }

  all(promises) {
    return new Promise((resolve, reject) => {
      const result = [];
      let count = 0;

      for (let i = 0; i < promises.length; i++) {
        const promise = Promise.resolve(promises[i]);

        promise
          .then((res) => {
            result[i] = res;
            count++;
            if (count === promises.length) {
              resolve(result);
            }
          })
          .catch((error) => {
            reject(error);
          });
      }
    });
  }

  trace(promises) {
    return new Promise((resolve, reject) => {
      promises.forEach((p) => {
        const promise = Promise.resolve(p);

        promise
          .then((res) => {
            resolve(res);
          })
          .catch((err) => {
            reject(err);
          });
      });
    });
  }

  allSetted(promises) {
    return new Promise((resovle, reject) => {
      try {
        const result = [];
        let count = 0;

        for (let i = 0; i < promises.length; i++) {
          promises[i]
            .then((res) => {
              result[i] = {
                status: "fulfilled",
                value: res,
              };
              count++;
              if (count === promises.length) {
                resovle(result);
              }
            })
            .catch((err) => {
              result[i] = {
                status: "rejected",
                value: err,
              };
              count++;
              if (count === promises.length) {
                resovle(result);
              }
            });
        }
      } catch (error) {
        reject(error);
      }
    });
  }
}

https://www.shanejix.com/posts/Promises%20implementation%20with%20ES6%20class/

  1. 获取页面中所有的 tagName
function func() {
  return [
    ...new Set([...document.querySelectorAll("*")].map((el) => el.tagName)),
  ].length;
}
  1. 数组乱序
const func = (arr) => {
  return arr.sort(() => (Math.random() > 0.5 ? 1 : -1));
};
  1. 对象扁平化
const objTmp = {
  a: { b: { c: 1 } },
  d: 2,
  e: [3, { f: 4, g: [5] }, [6, 7]],
  h: 8,
};

// const res = {
//   "a.b.c": 1,
//   d: 2,
//   "e[0]": 3,
//   "e[1].f": 4,
//   "e[1].g[0]": 5,
//   "e[2][0]": 6,
//   "e[2][1]": 7,
//   h: 8
// };

function fattenObj(obj, res = {}, prevKey = "", isArr = false) {
  for (let [key, value] of obj.entries()) {
    // console.log(key, value)

    if (typeof value === "object" && value !== null) {
      const theKey = isArr ? prevKey + "[" + key + "]" : prevKey + key;
      fattenObj(value, res, theKey);
    }

    if (Array.isArray(value)) {
      const theKey = isArr ? prevKey + "[" + key + "]" : prevKey + key + ".";
      fattenObj(value, res, theKey, true);
    }

    const theKey = isArr ? prevKey + "[" + key + "]" : prevKey + key;
    res[theKey] = value;
  }
}

const res = fattenObj(objTmp, {});

console.log("res", res);

作者:shanejix 出处:https://www.shanejix.com/posts/手撕 JavaScript 高频知识点,彻底征服面试官/ 版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。 声明:转载请注明出处!

Edit on GitHubDiscuss on GitHub


Shane Jix

Personal blog by Shane Jix. I explain with words and code.

LinksTools
© 2019 - 2022, Built withGatsby