The most complete handwritten JS interview questions

Posted by affc on Mon, 03 Jan 2022 10:08:52 +0100

1 compose

Title Description: implement a compose function

// The usage is as follows:
function fn1(x) {
  return x + 1;
}
function fn2(x) {
  return x + 2;
}
function fn3(x) {
  return x + 3;
}
function fn4(x) {
  return x + 4;
}
const a = compose(fn1, fn2, fn3, fn4);
console.log(a(1)); // 1+4+3+2+1=11
 Copy code

The implementation code is as follows:

function compose(...fn) {
  if (!fn.length) return (v) => v;
  if (fn.length === 1) return fn[0];
  return fn.reduce(
    (pre, cur) =>
      (...args) =>
        pre(cur(...args))
  );
}
Copy code

2. SetTimeout simulates the implementation of setinterval (version with clear timer)

Topic Description: setinterval is used to realize cyclic timing call. There may be some problems. Can settimeout be used to solve them

The implementation code is as follows:

function mySettimeout(fn, t) {
  let timer = null;
  function interval() {
    fn();
    timer = setTimeout(interval, t);
  }
  interval();
  return {
    cancel:()=>{
      clearTimeout(timer)
    }
  }
}
// let a=mySettimeout(()=>{
//   console.log(111);
// },1000)
// let b=mySettimeout(() => {
//   console.log(222)
// }, 1000)
Copy code

Extension: can we use setinterval simulation to implement settimeout in turn?

const mySetTimeout = (fn, time) => {
  const timer = setInterval(() => {
    clearInterval(timer);
    fn();
  }, time);
};
// mySetTimeout(()=>{
//   console.log(1);
// },1000)
Copy code

Extended thinking: why use settimeout simulation to implement setinterval? What is the defect of setinterval?

The answer is Baidu itself. In fact, the interviewer asks a lot of questions. The small series will not be launched here

3 publish subscribe mode

Topic Description: implement a publish subscribe mode with the on emit once off method

The implementation code is as follows:

class EventEmitter {
  constructor() {
    this.events = {};
  }
  // Implement subscription
  on(type, callBack) {
    if (!this.events[type]) {
      this.events[type] = [callBack];
    } else {
      this.events[type].push(callBack);
    }
  }
  // Delete subscription
  off(type, callBack) {
    if (!this.events[type]) return;
    this.events[type] = this.events[type].filter((item) => {
      return item !== callBack;
    });
  }
  // Execute subscription event only once
  once(type, callBack) {
    function fn() {
      callBack();
      this.off(type, fn);
    }
    this.on(type, fn);
  }
  // Trigger event
  emit(type, ...rest) {
    this.events[type] &&
      this.events[type].forEach((fn) => fn.apply(this, rest));
  }
}
// Use the following
// 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");
Copy code

4 array de duplication

The implementation code is as follows:

function uniqueArr(arr) {
  return [...new Set(arr)];
}
Copy code

5 array flattening

Title Description: implement a method to turn a multi-dimensional array into a one-dimensional array

The most common recursive versions are as follows:

function flatter(arr) {
  if (!arr.length) return;
  return arr.reduce(
    (pre, cur) =>
      Array.isArray(cur) ? [...pre, ...flatter(cur)] : [...pre, cur],
    []
  );
}
// console.log(flatter([1, 2, [1, [2, 3, [4, 5, [6]]]]]));
Copy code

Extended thinking: can we implement it with iterative ideas?

The implementation code is as follows:

function flatter(arr) {
  if (!arr.length) return;
  while (arr.some((item) => Array.isArray(item))) {
    arr = [].concat(...arr);
  }
  return arr;
}
// console.log(flatter([1, 2, [1, [2, 3, [4, 5, [6]]]]]));
Copy code

6 parasitic combinatorial inheritance

Title Description: implement a js inheritance method that you think is good

The implementation code is as follows:

function Parent(name) {
  this.name = name;
  this.say = () => {
    console.log(111);
  };
}
Parent.prototype.play = () => {
  console.log(222);
};
function Children(name) {
  Parent.call(this);
  this.name = name;
}
Children.prototype = Object.create(Parent.prototype);
Children.prototype.constructor = Children;
// let child = new Children("111");
// // console.log(child.name);
// // child.say();
// // child.play();
Copy code

7. Implement Promise scheduler with parallel restrictions

Title Description: JS implements an asynchronous Scheduler with concurrency constraints, which ensures that there are at most two tasks running at the same time

 addTask(1000,"1");
 addTask(500,"2");
 addTask(300,"3");
 addTask(400,"4");
 The output order is: 2 3 1 4

 Complete execution process of the whole:

At the beginning, 1 and 2 tasks are executed
500ms When, task 2 is completed, output 2, and task 3 starts to execute
800ms When, task 3 is completed, output 3, and task 4 begins to execute
1000ms When 1 task is executed, 1 is output, and only 4 tasks are left to execute
1200ms When the task is completed, output 4

Copy code

The implementation code is as follows:

class Scheduler {
  constructor(limit) {
    this.queue = [];
    this.maxCount = limit;
    this.runCounts = 0;
  }
  add(time, order) {
    const promiseCreator = () => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log(order);
          resolve();
        }, time);
      });
    };
    this.queue.push(promiseCreator);
  }
  taskStart() {
    for (let i = 0; i < this.maxCount; i++) {
      this.request();
    }
  }
  request() {
    if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {
      return;
    }
    this.runCounts++;
    this.queue
      .shift()()
      .then(() => {
        this.runCounts--;
        this.request();
      });
  }
}
const scheduler = new Scheduler(2);
const addTask = (time, order) => {
  scheduler.add(time, order);
};
addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
scheduler.taskStart();
Copy code

8 new operator

Title Description: implementation of handwritten new operator

The implementation code is as follows:

function myNew(fn, ...args) {
  let obj = Object.create(fn.prototype);
  let res = fn.call(obj, ...args);
  if (res && (typeof res === "object" || typeof res === "function")) {
    return res;
  }
  return obj;
}
The usage is as follows:
// // function Person(name, age) {
// //   this.name = name;
// //   this.age = age;
// // }
// // Person.prototype.say = function() {
// //   console.log(this.age);
// // };
// // let p1 = myNew(Person, "lihua", 18);
// // console.log(p1.name);
// // console.log(p1);
// // p1.say();
Copy code

9 call apply bind

Title Description: handwritten call apply bind implementation

The implementation code is as follows:

Function.prototype.myCall = function (context, ...args) {
  if (!context || context === null) {
    context = window;
  }
  // Create a unique key value as the internal method name of our constructed context
  let fn = Symbol();
  context[fn] = this; //this points to the function calling call
  // Executing the function and returning the result is equivalent to calling itself as the method of the passed in context
  return context[fn](...args);
};

// The principle of apply is the same, except that the second parameter is the passed in array
Function.prototype.myApply = function (context, args) {
  if (!context || context === null) {
    context = window;
  }
  // Create a unique key value as the internal method name of our constructed context
  let fn = Symbol();
  context[fn] = this;
  // Execute the function and return the result
  return context[fn](...args);
};

//The implementation of bind is a little more complicated because it considers many situations and involves parameter merging (similar to function coritization)

Function.prototype.myBind = function (context, ...args) {
  if (!context || context === null) {
    context = window;
  }
  // Create a unique key value as the internal method name of our constructed context
  let fn = Symbol();
  context[fn] = this;
  let _this = this;
  //  bind is a little more complicated
  const result = function (...innerArgs) {
    // The first case: if the function bound by bind is used as a constructor through the new operator, the incoming this will not be bound, but this will point to the instantiated object
    // At this time, because of the new operator, this points to the result instance object, and the result inherits from the passed in_ Based on the prototype chain knowledge, the following conclusions can be drawn
    // this.__proto__ === result.prototype   //this instanceof result =>true
    // this.__proto__.__proto__ === result.prototype.__proto__ === _this.prototype; //this instanceof _this =>true
    if (this instanceof _this === true) {
      // At this time, this points to the instance of result, and there is no need to change this point
      this[fn] = _this;
      this[fn](...[...args, ...innerArgs]); //Here, the method of es6 is used to make bind support parameter merging
      delete this[fn];
    } else {
      // If it is only called as an ordinary function, it is very simple. Directly change this point to the incoming context
      context[fn](...[...args, ...innerArgs]);
      delete context[fn];
    }
  };
  // If you bind a constructor, you need to inherit the constructor prototype properties and methods
  // How to implement inheritance: use object create
  result.prototype = Object.create(this.prototype);
  return result;
};

//The usage is as follows

// function Person(name, age) {
//   console.log(name); //' I passed in the parameter name '
//   console.log(age); //' I passed in the parameter age '
//   console.log(this); // The constructor this points to the instance object
// }
// //Constructor prototype method
// Person.prototype.say = function() {
//   console.log(123);
// }
// let obj = {
//   objName: 'I am the name passed in by obj name',
//   objAge: 'I'm the age from obj'
// }
// //Ordinary function
// function normalFun(name, age) {
//   console.log(name);   //' I passed in the parameter name '
//   console.log(age);   //' I passed in the parameter age '
//   console.log(this); // The normal function this points to the first parameter of bind, that is, obj in the example
//   console.log(this.objName); //' I'm name 'from obj
//   console.log(this.objAge); //' I'm from obj's age '
// }

// Call as constructor first
// let bindFun = Person.myBind(obj, 'I am the name passed in by the parameter')
// Let a = new bindfund ('I am the age passed in by the parameter ')
// a.say() //123

// Retest as a normal function call
// let bindFun = normalFun.myBind(obj, 'I am the name passed in by the parameter')
//  Bindfund ('I am the age passed in by the parameter ')
Copy code

10 deep copy (considering replication Symbol type)

Title Description: implementation of handwritten new operator

The implementation code is as follows:

function isObject(val) {
  return typeof val === "object" && val !== null;
}

function deepClone(obj, hash = new WeakMap()) {
  if (!isObject(obj)) return obj;
  if (hash.has(obj)) {
    return hash.get(obj);
  }
  let target = Array.isArray(obj) ? [] : {};
  hash.set(obj, target);
  Reflect.ownKeys(obj).forEach((item) => {
    if (isObject(obj[item])) {
      target[item] = deepClone(obj[item], hash);
    } else {
      target[item] = obj[item];
    }
  });

  return target;
}

// var obj1 = {
// a:1,
// b:{a:2}
// };
// var obj2 = deepClone(obj1);
// console.log(obj1);
Copy code

11 instanceof

Title Description: implementation of handwritten instanceof operator

The implementation code is as follows:

function myInstanceof(left, right) {
  while (true) {
    if (left === null) {
      return false;
    }
    if (left.__proto__ === right.prototype) {
      return true;
    }
    left = left.__proto__;
  }
}
Copy code

12 coritization

Topic Description: Currying, also known as Partial Evaluation, is a technology that transforms a function that accepts multiple parameters into a function that accepts a single parameter (the first parameter of the original function), and returns a new function that accepts the remaining parameters and returns the result. The core idea is to divide the function passed in by multiple parameters into single parameter (or part) functions, and then call the next single parameter (or part) function to process the remaining parameters in turn.

The implementation code is as follows:

function currying(fn, ...args) {
  const length = fn.length;
  let allArgs = [...args];
  const res = (...newArgs) => {
    allArgs = [...allArgs, ...newArgs];
    if (allArgs.length === length) {
      return fn(...allArgs);
    } else {
      return res;
    }
  };
  return res;
}

// The usage is as follows:
// const add = (a, b, c) => a + b + c;
// const a = currying(add, 1);
// console.log(a(2,3))
Copy code

13 bubble sort -- time complexity n^2

Title Description: realize a bubble sorting

The implementation code is as follows:

function bubbleSort(arr) {
  // Cache array length
  const len = arr.length;
  // The outer loop is used to control how many rounds of comparison + exchange from beginning to end
  for (let i = 0; i < len; i++) {
    // The inner loop is used to complete the repeated comparison + exchange in each round of traversal
    for (let j = 0; j < len - 1; j++) {
      // If the number in front of adjacent elements is greater than that in the back
      if (arr[j] > arr[j + 1]) {
        // Exchange both
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }
  // Return array
  return arr;
}
// console.log(bubbleSort([3, 6, 2, 4, 1]));
Copy code

14 select sort -- time complexity n^2

Title Description: realize a selection sorting

The implementation code is as follows:

function selectSort(arr) {
  // Cache array length
  const len = arr.length;
  // Define minIndex to cache the index of the minimum value of the current interval. Note the index
  let minIndex;
  // i is the starting point of the current sorting interval
  for (let i = 0; i < len - 1; i++) {
    // Initialize minIndex as the first element of the current interval
    minIndex = i;
    // i. J defines the upper and lower bounds of the current interval respectively. I is the left boundary and j is the right boundary
    for (let j = i; j < len; j++) {
      // If the data item at j is smaller than the current minimum value, the minimum value index is updated to j
      if (arr[j] < arr[minIndex]) {
        minIndex = j;
      }
    }
    // If the corresponding element of minIndex is not the current header element, exchange the two
    if (minIndex !== i) {
      [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
    }
  }
  return arr;
}
// console.log(quickSort([3, 6, 2, 4, 1]));
Copy code

15 insert sort -- time complexity n^2

Title Description: implement an insertion sort

The implementation code is as follows:

function insertSort(arr) {
  for (let i = 1; i < arr.length; i++) {
    let j = i;
    let target = arr[j];
    while (j > 0 && arr[j - 1] > target) {
      arr[j] = arr[j - 1];
      j--;
    }
    arr[j] = target;
  }
  return arr;
}
// console.log(insertSort([3, 6, 2, 4, 1]));
Copy code

Complexity between n ^ 16 and n ^ 2

Title Description: realize a fast scheduling

The implementation code is as follows:

function quickSort(arr) {
  if (arr.length < 2) {
    return arr;
  }
  const cur = arr[arr.length - 1];
  const left = arr.filter((v, i) => v <= cur && i !== arr.length - 1);
  const right = arr.filter((v) => v > cur);
  return [...quickSort(left), cur, ...quickSort(right)];
}
// console.log(quickSort([3, 6, 2, 4, 1]));
Copy code

17 merge sort -- time complexity nlog(n)

Title Description: implement a sorting algorithm with time complexity of nlog(n)

The implementation code is as follows:

function merge(left, right) {
  let res = [];
  let i = 0;
  let j = 0;
  while (i < left.length && j < right.length) {
    if (left[i] < right[j]) {
      res.push(left[i]);
      i++;
    } else {
      res.push(right[j]);
      j++;
    }
  }
  if (i < left.length) {
    res.push(...left.slice(i));
  } else {
    res.push(...right.slice(j));
  }
  return res;
}

function mergeSort(arr) {
  if (arr.length < 2) {
    return arr;
  }
  const mid = Math.floor(arr.length / 2);

  const left = mergeSort(arr.slice(0, mid));
  const right = mergeSort(arr.slice(mid));
  return merge(left, right);
}
// console.log(mergeSort([3, 6, 2, 4, 1]));
Copy code

18 binary search -- time complexity log2(n)

Topic Description: how to determine the position of a number in an ordered array

The implementation code is as follows:

function search(arr, target, start, end) {
  let targetIndex = -1;

  let mid = Math.floor((start + end) / 2);

  if (arr[mid] === target) {
    targetIndex = mid;
    return targetIndex;
  }

  if (start >= end) {
    return targetIndex;
  }

  if (arr[mid] < target) {
    return search(arr, target, mid + 1, end);
  } else {
    return search(arr, target, start, mid - 1);
  }
}
// const dataArr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// const position = search(dataArr, 6, 0, dataArr.length - 1);
// if (position !== -1) {
//   console.log(` position of the target element in the array: ${position} `);
// } else {
//   console.log("the target element is not in the array");
// }
Copy code

19 implementation of LazyMan

Title Description:

Implement a LazyMan,Can be called as follows:
LazyMan("Hank")output:
Hi! This is Hank!

LazyMan("Hank").sleep(10).eat("dinner")output
Hi! This is Hank!
//Wait 10 seconds
Wake up after 10
Eat dinner~

LazyMan("Hank").eat("dinner").eat("supper")output
Hi This is Hank!
Eat dinner~
Eat supper~

LazyMan("Hank").eat("supper").sleepFirst(5)output
//Wait 5 seconds
Wake up after 5
Hi This is Hank!
Eat supper
 Copy code

The implementation code is as follows:

class _LazyMan {
  constructor(name) {
    this.tasks = [];
    const task = () => {
      console.log(`Hi! This is ${name}`);
      this.next();
    };
    this.tasks.push(task);
    setTimeout(() => {
      // Put this Next () is executed after the call stack is cleared
      this.next();
    }, 0);
  }
  next() {
    const task = this.tasks.shift(); // Take the first task to execute
    task && task();
  }
  sleep(time) {
    this._sleepWrapper(time, false);
    return this; // call chaining 
  }
  sleepFirst(time) {
    this._sleepWrapper(time, true);
    return this;
  }
  _sleepWrapper(time, first) {
    const task = () => {
      setTimeout(() => {
        console.log(`Wake up after ${time}`);
        this.next();
      }, time * 1000);
    };
    if (first) {
      this.tasks.unshift(task); // Put at the top of the task queue
    } else {
      this.tasks.push(task); // Put at the end of the task queue
    }
  }
  eat(name) {
    const task = () => {
      console.log(`Eat ${name}`);
      this.next();
    };
    this.tasks.push(task);
    return this;
  }
}
function LazyMan(name) {
  return new _LazyMan(name);
}
Copy code

20 anti shake throttle

Title Description: handwritten anti shake throttle

The implementation code is as follows:

// Anti shake
function debounce(fn, delay = 300) {
  //Default 300 ms
  let timer;
  return function () {
    const args = arguments;
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      fn.apply(this, args); // Change this to point to the object that debounce refers to
    }, delay);
  };
}

window.addEventListener(
  "scroll",
  debounce(() => {
    console.log(111);
  }, 1000)
);

// throttle
// Set a flag
function throttle(fn, delay) {
  let flag = true;
  return () => {
    if (!flag) return;
    flag = false;
    timer = setTimeout(() => {
      fn();
      flag = true;
    }, delay);
  };
}

window.addEventListener(
  "scroll",
  throttle(() => {
    console.log(111);
  }, 1000)
);
Copy code

21 write version number sorting method

Title Description: there is a set of version numbers as follows ['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5']. Now you need to sort them. The sorting results are ['4.3.5', '4.3.4.5', '2.3.3', '0.302.1', '0.1.1']

The implementation code is as follows:

arr.sort((a, b) => {
  let i = 0;
  const arr1 = a.split(".");
  const arr2 = b.split(".");

  while (true) {
    const s1 = arr1[i];
    const s2 = arr2[i];
    i++;
    if (s1 === undefined || s2 === undefined) {
      return arr2.length - arr1.length;
    }

    if (s1 === s2) continue;

    return s2 - s1;
  }
});
console.log(arr);
Copy code

22 LRU algorithm

Title Description:

The implementation code is as follows:

//  A Map object will be iterated according to the insertion order of elements in the object
// The newly added element will be inserted at the end of the map, and the whole stack will be viewed in reverse order
class LRUCache {
  constructor(capacity) {
    this.secretKey = new Map();
    this.capacity = capacity;
  }
  get(key) {
    if (this.secretKey.has(key)) {
      let tempValue = this.secretKey.get(key);
      this.secretKey.delete(key);
      this.secretKey.set(key, tempValue);
      return tempValue;
    } else return -1;
  }
  put(key, value) {
    // key exists, only modify the value
    if (this.secretKey.has(key)) {
      this.secretKey.delete(key);
      this.secretKey.set(key, value);
    }
    // key does not exist, cache is not full
    else if (this.secretKey.size < this.capacity) {
      this.secretKey.set(key, value);
    }
    // Add new key and delete old key
    else {
      this.secretKey.set(key, value);
      // Delete the first element of the map, which is the longest unused element
      this.secretKey.delete(this.secretKey.keys().next().value);
    }
  }
}
// let cache = new LRUCache(2);
// cache.put(1, 1);
// cache.put(2, 2);
// console.log("cache.get(1)", cache.get(1)) / / returns 1
// cache.put(3, 3);//  This operation will invalidate key 2
// console.log("cache.get(2)", cache.get(2)) / / returns - 1 (not found)
// cache.put(4, 4);//  This operation will invalidate key 1
// console.log("cache.get(1)", cache.get(1)) / / return - 1 (not found)
// console.log("cache.get(3)", cache.get(3)) / / returns 3
// console.log("cache.get(4)", cache.get(4)) / / returns 4
 Copy code

23 Promise and implementation of related methods

Title Description: handwritten promise and promise all Promise. Implementation of race

The implementation code is as follows:

class Mypromise {
  constructor(fn) {
    // Indicates the status
    this.state = "pending";
    // Indicates the successful function of then registration
    this.successFun = [];
    // A function that represents the failure of then registration
    this.failFun = [];

    let resolve = (val) => {
      // Keep the state change immutable (only one of resolve and reject can be triggered)
      if (this.state !== "pending") return;

      // Successfully trigger the opportunity to change the state and execute the callback event registered in then
      this.state = "success";
      // In order to ensure that the then event is registered first (mainly considering writing synchronous code in promise), the promise specification here is to simulate asynchronous
      setTimeout(() => {
        // Execute all registration functions in the current event
        this.successFun.forEach((item) => item.call(this, val));
      });
    };

    let reject = (err) => {
      if (this.state !== "pending") return;
      // Failure triggers the opportunity to change the state and execute the callback event registered in then
      this.state = "fail";
      // In order to ensure that the then event is registered first (mainly considering writing synchronous code in promise), the promise specification simulates asynchronous
      setTimeout(() => {
        this.failFun.forEach((item) => item.call(this, err));
      });
    };
    // Call function
    try {
      fn(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  // Instance method then

  then(resolveCallback, rejectCallback) {
    // Determine whether the callback is a function
    resolveCallback =
      typeof resolveCallback !== "function" ? (v) => v : resolveCallback;
    rejectCallback =
      typeof rejectCallback !== "function"
        ? (err) => {
            throw err;
          }
        : rejectCallback;
    // In order to keep the chained call, continue to return promise
    return new Mypromise((resolve, reject) => {
      // Register the callback in the successFun event collection
      this.successFun.push((val) => {
        try {
          //    Execute callback function
          let x = resolveCallback(val);
          //(the hardest point)
          // If the result of the callback function is a normal value, then resolve will be sent out to the next then chain call. If it is a promise object (representing another asynchronous), then call the then method of X to pass in resolve and reject. When the asynchronous execution inside x is completed (the state is completed), the incoming resolve will be automatically executed, which controls the sequence of chain calls
          x instanceof Mypromise ? x.then(resolve, reject) : resolve(x);
        } catch (error) {
          reject(error);
        }
      });

      this.failFun.push((val) => {
        try {
          //    Execute callback function
          let x = rejectCallback(val);
          x instanceof Mypromise ? x.then(resolve, reject) : reject(x);
        } catch (error) {
          reject(error);
        }
      });
    });
  }
  //Static method
  static all(promiseArr) {
    let result = [];
    //Declare a counter and add one for each promise return
    let count = 0;
    return new Mypromise((resolve, reject) => {
      for (let i = 0; i < promiseArr.length; i++) {
      //Promise Resolve is wrapped to prevent non promise types from being passed in
        Promise.resolve(promiseArr[i]).then(
          (res) => {
            //You can't directly push the array here, because you want to control the order one-to-one correspondence (thanks for pointing in the comment area)
            result[i] = res;
            count++;
            //resolve will not be released until all promise s are successfully executed
            if (count === promiseArr.length) {
              resolve(result);
            }
          },
          (err) => {
            reject(err);
          }
        );
      }
    });
  }
  //Static method
  static race(promiseArr) {
    return new Mypromise((resolve, reject) => {
      for (let i = 0; i < promiseArr.length; i++) {
        Promise.resolve(promiseArr[i]).then(
          (res) => {
            //Promise array can be returned as long as there is any promise state change
            resolve(res);
          },
          (err) => {
            reject(err);
          }
        );
      }
    });
  }
}

// use
// let promise1 = new Mypromise((resolve, reject) => {
//   setTimeout(() => {
//     resolve(123);
//   }, 2000);
// });
// let promise2 = new Mypromise((resolve, reject) => {
//   setTimeout(() => {
//     resolve(1234);
//   }, 1000);
// });

// Mypromise.all([promise1,promise2]).then(res=>{
//   console.log(res);
// })

// Mypromise.race([promise1, promise2]).then(res => {
//   console.log(res);
// });

// promise1
//   .then(
//     res => {
//       console.log(res); // Output 123 in two seconds
//       return new Mypromise((resolve, reject) => {
//         setTimeout(() => {
//           resolve("success");
//         }, 1000);
//       });
//     },
//     err => {
//       console.log(err);
//     }
//   )
//   .then(
//     res => {
//       console.log(res); // Output success in another second
//     },
//     err => {
//       console.log(err);
//     }
//   );
Copy code

Extended thinking: how to cancel promise

Promise. Promise () can be used to compete with its own empty wrapper

function wrap(pro) {
  let obj = {};
  // Construct a new promise to compete
  let p1 = new Promise((resolve, reject) => {
    obj.resolve = resolve;
    obj.reject = reject;
  });

  obj.promise = Promise.race([p1, pro]);
  return obj;
}

let testPro = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(123);
  }, 1000);
});

let wrapPro = wrap(testPro);
wrapPro.promise.then((res) => {
  console.log(res);
});
wrapPro.resolve("Intercepted");
Copy code

24 implement an add method

Title Description: implement an add method to make the calculation result meet the following expectations: add(1)(2)(3)()=6 add(1,2,3)(4)()=10

In fact, it is Cauchy function

The implementation code is as follows:

function add(...args) {
  let allArgs = [...args];
  function fn(...newArgs) {
    allArgs = [...allArgs, ...newArgs];
    return fn;
  }
  fn.toString = function () {
    if (!allArgs.length) {
      return;
    }
    return allArgs.reduce((sum, cur) => sum + cur);
  };
  return fn;
}
Copy code

Solving coin change problem by dynamic programming

Title Description: give coins of different denominations coins and a total amount. Write a function to calculate the minimum number of coins needed to make up the total amount. If no coin combination can form a total amount, return - 1

Example 1:
input: coins = [1, 2, 5], amount = 11
 output: 3
 explain: 11 = 5 + 5 + 1

Example 2:
input: coins = [2], amount = 3
 output: -1
 Copy code

The implementation code is as follows:

const coinChange = function (coins, amount) {
  // Used to save the minimum number of coins corresponding to each target total
  const f = [];
  // Define known situations in advance
  f[0] = 0;
  // Traverse the total amount of coins in the interval [1, amount]
  for (let i = 1; i <= amount; i++) {
    // It is the minimum value, so we preset it to infinity to ensure that it will be updated by a smaller number
    f[i] = Infinity;
    // Loop through the denomination of each available coin
    for (let j = 0; j < coins.length; j++) {
      // If the coin denomination is less than the target total, the problem is established
      if (i - coins[j] >= 0) {
        // State transition equation
        f[i] = Math.min(f[i], f[i - coins[j]] + 1);
      }
    }
  }
  // If the solution corresponding to the target total amount is infinite, it means that there is no qualified total number of coins to update it. This problem has no solution and returns - 1
  if (f[amount] === Infinity) {
    return -1;
  }
  // If there is a solution, directly return the content of the solution
  return f[amount];
};
Copy code

Please implement a function of DOM2JSON, which can output a DOM node in JSON format

Title Description:

<div>
  <span>
    <a></a>
  </span>
  <span>
    <a></a>
    <a></a>
  </span>
</div>

Appeal dom The structure is transformed into the following JSON format

{
  tag: 'DIV',
  children: [
    {
      tag: 'SPAN',
      children: [
        { tag: 'A', children: [] }
      ]
    },
    {
      tag: 'SPAN',
      children: [
        { tag: 'A', children: [] },
        { tag: 'A', children: [] }
      ]
    }
  ]
}
Copy code

The implementation code is as follows:

function dom2Json(domtree) {
  let obj = {};
  obj.name = domtree.tagName;
  obj.children = [];
  domtree.childNodes.forEach((child) => obj.children.push(dom2Json(child)));
  return obj;
}
Copy code

Extended thinking: how to parse a given html string instead of a Dom tree structure?

Then this problem is similar to Vue's template compilation principle. We can use regular matching html string to generate the corresponding ast after encountering the start tag, end tag and text parsing, establish the corresponding parent-child Association, and constantly intercept the remaining strings until all html is parsed. Those interested can see it here

Method for converting class 27 array into array

Topic Description: class arrays have the length attribute. You can use subscripts to access elements, but you can't use array methods. How to convert class arrays into arrays?

The implementation code is as follows:

const arrayLike=document.querySelectorAll('div')

// 1. Extension operator
[...arrayLike]
// 2.Array.from
Array.from(arrayLike)
// 3.Array.prototype.slice
Array.prototype.slice.call(arrayLike)
// 4.Array.apply
Array.apply(null, arrayLike)
// 5.Array.prototype.concat
Array.prototype.concat.apply([], arrayLike)

Copy code

28 Object.is implementation

Title Description:

Object.is The types of the two values being compared are not converted, and===More similar, there are some differences between them.
    1. NaN stay===Is not equal in, and in Object.is Are equal in
    2. +0 and-0 stay===Is equal in, and in Object.is Are not equal
 Copy code

The implementation code is as follows:

Object.is = function (x, y) {
  if (x === y) {
    // In the current case, only one case is special, that is + 0 - 0
    // If x== 0, returns true
    // If x === 0, you need to judge + 0 and - 0. You can directly use 1/+0 === Infinity and 1/-0 === -Infinity to judge
    return x !== 0 || 1 / x === 1 / y;
  }

  // x !==  In the case of Y, you only need to judge whether it is NaN, if x== x. Then x is NaN, and so is y
  // Returns true when x and y are both NaN
  return x !== x && y !== y;
};
Copy code

29 AJAX

Title Description: handwritten AJAX implementation using XMLHttpRequest

The implementation code is as follows:

const getJSON = function (url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url, false);
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.onreadystatechange = function () {
      if (xhr.readyState !== 4) return;
      if (xhr.status === 200 || xhr.status === 304) {
        resolve(xhr.responseText);
      } else {
        reject(new Error(xhr.responseText));
      }
    };
    xhr.send();
  });
};
Copy code

30 slice idea to solve the problem of large amount of data rendering

Title Description: how to use the idea of fragmentation to optimize rendering when rendering millions of pieces of big data with simple structure

The implementation code is as follows:

let ul = document.getElementById("container");
// Insert 100000 pieces of data
let total = 100000;
// Insert 20 at a time
let once = 20;
//PageCount 
let page = total / once;
//Index of each record
let index = 0;
//Cyclic loading data
function loop(curTotal, curIndex) {
  if (curTotal <= 0) {
    return false;
  }
  //How many entries per page
  let pageCount = Math.min(curTotal, once);
  window.requestAnimationFrame(function () {
    for (let i = 0; i < pageCount; i++) {
      let li = document.createElement("li");
      li.innerText = curIndex + i + " : " + ~~(Math.random() * total);
      ul.appendChild(li);
    }
    loop(curTotal - pageCount, curIndex + pageCount);
  });
}
loop(total, index);
Copy code

Extended thinking: for simple dom structure rendering with large amount of data, the idea of fragmentation can be used. How to deal with complex dom structure rendering?

At this time, we need to use the virtual list. Baidu ha virtual list and virtual table are still used frequently in daily projects

31 convert virtual Dom to real Dom

Title Description: how to convert a virtual Dom in JSON format into a real Dom

{
  tag: 'DIV',
  attrs:{
  id:'app'
  },
  children: [
    {
      tag: 'SPAN',
      children: [
        { tag: 'A', children: [] }
      ]
    },
    {
      tag: 'SPAN',
      children: [
        { tag: 'A', children: [] },
        { tag: 'A', children: [] }
      ]
    }
  ]
}
Virtual appeal Dom Convert to reality below Dom
<div id="app">
  <span>
    <a></a>
  </span>
  <span>
    <a></a>
    <a></a>
  </span>
</div>

Copy code

The implementation code is as follows:

// Real rendering function
function _render(vnode) {
  // If it is a numeric type, convert it to a string
  if (typeof vnode === "number") {
    vnode = String(vnode);
  }
  // The string type is the text node directly
  if (typeof vnode === "string") {
    return document.createTextNode(vnode);
  }
  // Normal DOM
  const dom = document.createElement(vnode.tag);
  if (vnode.attrs) {
    // traversal attributes 
    Object.keys(vnode.attrs).forEach((key) => {
      const value = vnode.attrs[key];
      dom.setAttribute(key, value);
    });
  }
  // Recursive operation of subarray
  vnode.children.forEach((child) => dom.appendChild(_render(child)));
  return dom;
}
Copy code

32 realize the template string parsing function

Title Description:

let template = 'I am{{name}},Age{{age}},Gender{{sex}}';
let data = {
  name: 'full name',
  age: 18
}
render(template, data); // My name is, age 18, gender undefined
 Copy code

The implementation code is as follows:

function render(template, data) {
  let computed = template.replace(/\{\{(\w+)\}\}/g, function (match, key) {
    return data[key];
  });
  return computed;
}
Copy code

33 implement the flatten method of an object

Title Description:

const obj = {
 a: {
        b: 1,
        c: 2,
        d: {e: 5}
    },
 b: [1, 3, {a: 2, b: 3}],
 c: 3
}

flatten(obj) The results are returned as follows
// {
//  'a.b': 1,
//  'a.c': 2,
//  'a.d.e': 5,
//  'b[0]': 1,
//  'b[1]': 3,
//  'b[2].a': 2,
//  'b[2].b': 3
//   c: 3
// }
Copy code

The implementation code is as follows:

function isObject(val) {
  return typeof val === "object" && val !== null;
}

function flatten(obj) {
  if (!isObject(obj)) {
    return;
  }
  let res = {};
  const dfs = (cur, prefix) => {
    if (isObject(cur)) {
      if (Array.isArray(cur)) {
        cur.forEach((item, index) => {
          dfs(item, `${prefix}[${index}]`);
        });
      } else {
        for (let k in cur) {
          dfs(cur[k], `${prefix}${prefix ? "." : ""}${k}`);
        }
      }
    } else {
      res[prefix] = cur;
    }
  };
  dfs(obj, "");

  return res;
}
flatten();
Copy code

34 convert list to tree structure

Title Description:

[
    {
        id: 1,
        text: 'Node 1',
        parentId: 0 //Here, 0 is represented as the top-level node
    },
    {
        id: 2,
        text: 'Node 1_1',
        parentId: 1 //This field determines the child parent
    }
    ...
]

Turn into
[
    {
        id: 1,
        text: 'Node 1',
        parentId: 0,
        children: [
            {
                id:2,
                text: 'Node 1_1',
                parentId:1
            }
        ]
    }
]
Copy code

The implementation code is as follows:

function listToTree(data) {
  let temp = {};
  let treeData = [];
  for (let i = 0; i < data.length; i++) {
    temp[data[i].id] = data[i];
  }
  for (let i in temp) {
    if (+temp[i].parentId != 0) {
      if (!temp[temp[i].parentId].children) {
        temp[temp[i].parentId].children = [];
      }
      temp[temp[i].parentId].children.push(temp[i]);
    } else {
      treeData.push(temp[i]);
    }
  }
  return treeData;
}
Copy code

35 convert tree structure to list

Title Description:

[
    {
        id: 1,
        text: 'Node 1',
        parentId: 0,
        children: [
            {
                id:2,
                text: 'Node 1_1',
                parentId:1
            }
        ]
    }
]
Turn into
[
    {
        id: 1,
        text: 'Node 1',
        parentId: 0 //Here, 0 is represented as the top-level node
    },
    {
        id: 2,
        text: 'Node 1_1',
        parentId: 1 //This field determines the child parent
    }
    ...
]

Copy code

The implementation code is as follows:

function treeToList(data) {
  let res = [];
  const dfs = (tree) => {
    tree.forEach((item) => {
      if (item.children) {
        dfs(item.children);
        delete item.children;
      }
      res.push(item);
    });
  };
  dfs(data);
  return res;
}
Copy code

36 addition of large numbers

Title Description: implement an add method to complete the addition of two large numbers

let a = "9007199254740991";
let b = "1234567899999999999";

function add(a ,b){
   //...
}

Copy code

The implementation code is as follows:

function add(a ,b){
   //Take the maximum length of two numbers
   let maxLength = Math.max(a.length, b.length);
   //Use 0 to supplement the length
   a = a.padStart(maxLength , 0);//"0009007199254740991"
   b = b.padStart(maxLength , 0);//"1234567899999999999"
   //Define the variables needed in the addition process
   let t = 0;
   let f = 0;   //"Carry"
   let sum = "";
   for(let i=maxLength-1 ; i>=0 ; i--){
      t = parseInt(a[i]) + parseInt(b[i]) + f;
      f = Math.floor(t/10);
      sum = t%10 + sum;
   }
   if(f!==0){
      sum = '' + f + sum;
   }
   return sum;
}

Topics: Javascript ECMAScript