32 handwritten JS to consolidate your JS Foundation (high frequency interview)

Posted by Mistat2000 on Fri, 31 Dec 2021 10:35:47 +0100

32 handwritten JS to consolidate your JS Foundation (high frequency interview)

As a front-end development, JS is the top priority. Recently, the peak of the interview has been ended. Basically, the offer has been decided. Wait for the lottery. Take this time to summarize 32 handwritten JS questions. These are high-frequency interview questions. I hope they can be helpful to you.

As for the source code, they all follow the specifications and can run through the MDN examples. Most of the rest will involve some application questions about JS and my interview process

01. Array flattening

Array flattening refers to changing a multi-dimensional array into a one-dimensional array

const arr = [1, [2, [3, [4, 5]]], 6];
// => [1, 2, 3, 4, 5, 6]
Copy code

Method 1: use flat()

const res1 = arr.flat(Infinity);
Copy code

Method 2: using regular

const res2 = JSON.stringify(arr).replace(/\[|\]/g, '').split(',');
Copy code

But the data type will change to string

Method 3: regular improved version

const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g, '') + ']');
Copy code

Method 4: use reduce

const flatten = arr => {
  return arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
  }, [])
}
const res4 = flatten(arr);
Copy code

Method 5: function recursion

const res5 = [];
const fn = arr => {
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      fn(arr[i]);
    } else {
      res5.push(arr[i]);
    }
  }
}
fn(arr);
Copy code

02. Array de duplication

const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// => [1, '1', 17, true, false, 'true', 'a', {}, {}]
Copy code

Method 1: use Set

const res1 = Array.from(new Set(arr));
Copy code

Method 2: two-layer for loop + splice

const unique1 = arr => {
  let len = arr.length;
  for (let i = 0; i < len; i++) {
    for (let j = i + 1; j < len; j++) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1);
        // Every time a tree is deleted, j -- ensure that the value of j remains unchanged after self addition. At the same time, len --, reduce the number of cycles and improve the performance
        len--;
        j--;
      }
    }
  }
  return arr;
}
Copy code

Method 3: using indexOf

const unique2 = arr => {
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (res.indexOf(arr[i]) === -1) res.push(arr[i]);
  }
  return res;
}
Copy code

Of course, you can also use include and filter. The ideas are similar.

Method 4: use include

const unique3 = arr => {
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!res.includes(arr[i])) res.push(arr[i]);
  }
  return res;
}
Copy code

Method 5: using filter

const unique4 = arr => {
  return arr.filter((item, index) => {
    return arr.indexOf(item) === index;
  });
}
Copy code

Method 6: using Map

const unique5 = arr => {
  const map = new Map();
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!map.has(arr[i])) {
      map.set(arr[i], true)
      res.push(arr[i]);
    }
  }
  return res;
}
Copy code

03. Convert class array to array

Class array has the length attribute, but does not have methods on the array prototype. Common class arrays include arguments and the results returned by DOM operation methods.

Method 1: array from

Array.from(document.querySelectorAll('div'))
Copy code

Method 2: array prototype. slice. call()

Array.prototype.slice.call(document.querySelectorAll('div'))
Copy code

Method 3: extension operator

[...document.querySelectorAll('div')]
Copy code

Method 4: using concat

Array.prototype.concat.apply([], document.querySelectorAll('div'));
Copy code

04.Array.prototype.filter()

Array.prototype.filter = function(callback, thisArg) {
  if (this == undefined) {
    throw new TypeError('this is null or not undefined');
  }
  if (typeof callback !== 'function') {
    throw new TypeError(callback + 'is not a function');
  }
  const res = [];
  // Object passing that makes O a callback function (cast object)
  const O = Object(this);
  // >>>0 ensures that len is number and a positive integer
  const len = O.length >>> 0;
  for (let i = 0; i < len; i++) {
    // Check whether i is in the attribute of O (the prototype chain will be checked)
    if (i in O) {
      // Callback function call pass parameter
      if (callback.call(thisArg, O[i], i, O)) {
        res.push(O[i]);
      }
    }
  }
  return res;
}
Copy code

Questions about > > > 0: Explain the role of > > > 0

05.Array.prototype.map()

Array.prototype.map = function(callback, thisArg) {
  if (this == undefined) {
    throw new TypeError('this is null or not defined');
  }
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }
  const res = [];
  // Similarly
  const O = Object(this);
  const len = O.length >>> 0;
  for (let i = 0; i < len; i++) {
    if (i in O) {
      // Call the callback function and pass in a new array
      res[i] = callback.call(thisArg, O[i], i, this);
    }
  }
  return res;
}
Copy code

06.Array.prototype.forEach()

forEach is similar to map, except that forEach does not return a value.

Array.prototype.forEach = function(callback, thisArg) {
  if (this == null) {
    throw new TypeError('this is null or not defined');
  }
  if (typeof callback !== "function") {
    throw new TypeError(callback + ' is not a function');
  }
  const O = Object(this);
  const len = O.length >>> 0;
  let k = 0;
  while (k < len) {
    if (k in O) {
      callback.call(thisArg, O[k], k, O);
    }
    k++;
  }
}
Copy code

07.Array.prototype.reduce()

Array.prototype.reduce = function(callback, initialValue) {
  if (this == undefined) {
    throw new TypeError('this is null or not defined');
  }
  if (typeof callback !== 'function') {
    throw new TypeError(callbackfn + ' is not a function');
  }
  const O = Object(this);
  const len = this.length >>> 0;
  let accumulator = initialValue;
  let k = 0;
  // If the second parameter is undefined
  // The first valid value of the array is used as the initial value of the accumulator
  if (accumulator === undefined) {
    while (k < len && !(k in O)) {
      k++;
    }
    // TypeError if the initial value of the accumulator has not been found beyond the bounds of the array
    if (k >= len) {
      throw new TypeError('Reduce of empty array with no initial value');
    }
    accumulator = O[k++];
  }
  while (k < len) {
    if (k in O) {
      accumulator = callback.call(undefined, accumulator, O[k], k, O);
    }
    k++;
  }
  return accumulator;
}
Copy code

08.Function.prototype.apply()

The first parameter is bound this, which defaults to window, and the second parameter is array or class array

Function.prototype.apply = function(context = window, args) {
  if (typeof this !== 'function') {
    throw new TypeError('Type Error');
  }
  const fn = Symbol('fn');
  context[fn] = this;

  const res = context[fn](...args);
  delete context[fn];
  return res;
}
Copy code

09.Function.prototype.call

The only difference from call is that the call() method accepts a list of parameters

Function.prototype.call = function(context = window, ...args) {
  if (typeof this !== 'function') {
    throw new TypeError('Type Error');
  }
  const fn = Symbol('fn');
  context[fn] = this;

  const res = context[fn](...args);
  delete context[fn];
  return res;
}
Copy code

10.Function.prototype.bind

Function.prototype.bind = function(context, ...args) {
  if (typeof this !== 'function') {
    throw new Error("Type Error");
  }
  // Save the value of this
  var self = this;

  return function F() {
    // Consider the case of new
    if(this instanceof F) {
      return new self(...args, ...arguments)
    }
    return self.apply(context, [...args, ...arguments])
  }
}
Copy code

11.debounce (anti shake)

After triggering the high-frequency time, the function will only be executed once in n seconds. If the high-frequency time is triggered again in n seconds, the time will be recalculated.

const debounce = (fn, time) => {
  let timeout = null;
  return function() {
    clearTimeout(timeout)
    timeout = setTimeout(() => {
      fn.apply(this, arguments);
    }, time);
  }
};
Copy code

Anti shake is often applied to search input by users to save request resources. When the window triggers the resize event, anti shake is triggered only once.

12.throttle

High frequency time trigger, but it will only be executed once in n seconds, so throttling will dilute the execution frequency of the function.

const throttle = (fn, time) => {
  let flag = true;
  return function() {
    if (!flag) return;
    flag = false;
    setTimeout(() => {
      fn.apply(this, arguments);
      flag = true;
    }, time);
  }
}
Copy code

Throttling is often used to trigger and monitor scrolling events by clicking the mouse continuously.

13. Function coritization

It refers to changing a function that accepts multiple parameters into a fixed form that accepts a parameter and returns a function, which is convenient for calling again, such as f(1)(2)

Classic interview questions: add(1)(2)(3)(4)=10 add(1)(1,2,3)(2)=9;

function add() {
  const _args = [...arguments];
  function fn() {
    _args.push(...arguments);
    return fn;
  }
  fn.toString = function() {
    return _args.reduce((sum, cur) => sum + cur);
  }
  return fn;
}
Copy code

14. Simulate new operation

3 steps:

  1. In ctor Prototype creates an object for the prototype.
  2. Execute the constructor and bind this to the newly created object.
  3. Judge whether the result returned by the constructor execution is a reference data type. If so, return the result of the constructor execution. Otherwise, return the created object.
function newOperator(ctor, ...args) {
  if (typeof ctor !== 'function') {
    throw new TypeError('Type Error');
  }
  const obj = Object.create(ctor.prototype);
  const res = ctor.apply(obj, args);

  const isObject = typeof res === 'object' && res !== null;
  const isFunction = typeof res === 'function';
  return isObject || isFunction ? res : obj;
}
Copy code

15.instanceof

The instanceof operator is used to detect whether the prototype attribute of the constructor appears on the prototype chain of an instance object.

const myInstanceof = (left, right) => {
  // All basic data types return false
  if (typeof left !== 'object' || 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);
  }
}
Copy code

16. Prototype inheritance

Only parasitic combination inheritance is written here. There are several evolved inheritance in the middle, but they all have some defects

function Parent() {
  this.name = 'parent';
}
function Child() {
  Parent.call(this);
  this.type = 'children';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Copy code

17.Object.is

Object.is mainly solves these two problems:

+0 === -0  // true
NaN === NaN // false
 Copy code
const is= (x, y) => {
  if (x === y) {
    // +0 and - 0 should not be equal
    return x !== 0 || y !== 0 || 1/x === 1/y;
  } else {
    return x !== x && y !== y;
  }
}
Copy code

18.Object.assign

Object. The assign () method is used to copy the values of all enumerable properties from one or more source objects to the target object. It will return the target object (note that this operation is a shallow copy)

Object.defineProperty(Object, 'assign', {
  value: function(target, ...args) {
    if (target == null) {
      return new TypeError('Cannot convert undefined or null to object');
    }
    
    // The target object needs to be a unified reference data type. If it is not, it will be automatically converted
    const to = Object(target);

    for (let i = 0; i < args.length; i++) {
      // Each source object
      const nextSource = args[i];
      if (nextSource !== null) {
        // Use for In and hasOwnProperty double judge to ensure that only their own properties and methods (excluding inherited ones) are obtained
        for (const nextKey in nextSource) {
          if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
    }
    return to;
  },
  // countless
  enumerable: false,
  writable: true,
  configurable: true,
})
Copy code

19. Deep copy

Full version of recursion (taking into account the Symbol attribute):

const cloneDeep1 = (target, hash = new WeakMap()) => {
  // For incoming parameter processing
  if (typeof target !== 'object' || target === null) {
    return target;
  }
  // There is a direct return in the hash table
  if (hash.has(target)) return hash.get(target);

  const cloneTarget = Array.isArray(target) ? [] : {};
  hash.set(target, cloneTarget);

  // For Symbol Properties
  const symKeys = Object.getOwnPropertySymbols(target);
  if (symKeys.length) {
    symKeys.forEach(symKey => {
      if (typeof target[symKey] === 'object' && target[symKey] !== null) {
        cloneTarget[symKey] = cloneDeep1(target[symKey]);
      } else {
        cloneTarget[symKey] = target[symKey];
      }
    })
  }

  for (const i in target) {
    if (Object.prototype.hasOwnProperty.call(target, i)) {
      cloneTarget[i] =
        typeof target[i] === 'object' && target[i] !== null
        ? cloneDeep1(target[i], hash)
        : target[i];
    }
  }
  return cloneTarget;
}
Copy code

20.Promise

Implementation idea: Promise source code implementation

// Simulation implementation Promise
// Promise uses three means to solve the problem:
// 1. Delayed binding of callback function
// 2. Return value
// 3. Error bubbling

// Define three states
const PENDING = 'PENDING';      // have in hand
const FULFILLED = 'FULFILLED';  // Succeeded
const REJECTED = 'REJECTED';    // Failed

class Promise {
  constructor(exector) {
    // Initialization status
    this.status = PENDING;
    // Put the success and failure results on this to facilitate access by then and catch
    this.value = undefined;
    this.reason = undefined;
    // Successful callback function queue
    this.onFulfilledCallbacks = [];
    // Failed callback function queue
    this.onRejectedCallbacks = [];

    const resolve = value => {
      // The status can only be changed if the status is in progress
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        // Successful state functions are executed in sequence
        this.onFulfilledCallbacks.forEach(fn => fn(this.value));
      }
    }
    const reject = reason => {
      // The status can only be changed if the status is in progress
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        // Failed state functions are executed in sequence
        this.onRejectedCallbacks.forEach(fn => fn(this.reason))
      }
    }
    try {
      // Execute executor now
      // Pass the internal resolve and reject into the executor, and the user can call resolve and reject
      exector(resolve, reject);
    } catch(e) {
      // The executor executes an error and throws out the error content reject
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function'? onRejected :
      reason => { throw new Error(reason instanceof Error ? reason.message : reason) }
    // Save this
    const self = this;
    return new Promise((resolve, reject) => {
      if (self.status === PENDING) {
        self.onFulfilledCallbacks.push(() => {
          // try capture error
          try {
            // Simulated micro task
            setTimeout(() => {
              const result = onFulfilled(self.value);
              // There are two cases:
              // 1. The return value of the callback function is Promise. Execute the then operation
              // 2. If it is not Promise, call the resolve function of the new Promise
              result instanceof Promise ? result.then(resolve, reject) : resolve(result);
            })
          } catch(e) {
            reject(e);
          }
        });
        self.onRejectedCallbacks.push(() => {
          // The same is true below
          try {
            setTimeout(() => {
              const result = onRejected(self.reason);
              // Difference: reject
              result instanceof Promise ? result.then(resolve, reject) : resolve(result);
            })
          } catch(e) {
            reject(e);
          }
        })
      } else if (self.status === FULFILLED) {
        try {
          setTimeout(() => {
            const result = onFulfilled(self.value);
            result instanceof Promise ? result.then(resolve, reject) : resolve(result);
          });
        } catch(e) {
          reject(e);
        }
      } else if (self.status === REJECTED) {
        try {
          setTimeout(() => {
            const result = onRejected(self.reason);
            result instanceof Promise ? result.then(resolve, reject) : resolve(result);
          })
        } catch(e) {
          reject(e);
        }
      }
    });
  }
  catch(onRejected) {
    return this.then(null, onRejected);
  }
  static resolve(value) {
    if (value instanceof Promise) {
      // If it is a Promise instance, return directly
      return value;
    } else {
      // If it is not a Promise instance, a new Promise object is returned with the status of full
      return new Promise((resolve, reject) => resolve(value));
    }
  }
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    })
  }
  static all(promiseArr) {
    const len = promiseArr.length;
    const values = new Array(len);
    // Record the number of promise s that have been successfully executed
    let count = 0;
    return new Promise((resolve, reject) => {
      for (let i = 0; i < len; i++) {
        // Promise.resolve() to ensure that each is a promise instance
        Promise.resolve(promiseArr[i]).then(
          val => {
            values[i] = val;
            count++;
            // If all execution is completed, the status of return promise can be changed
            if (count === len) resolve(values);
          },
          err => reject(err),
        );
      }
    })
  }
  static race(promiseArr) {
    return new Promise((resolve, reject) => {
      promiseArr.forEach(p => {
        Promise.resolve(p).then(
          val => resolve(val),
          err => reject(err),
        )
      })
    })
  }
}
Copy code

21.Promise.all

Promise.all supports chain calls. In essence, it returns a promise instance and changes the instance state through resolve and reject.

Promise.myAll = function(promiseArr) {
  return new Promise((resolve, reject) => {
    const ans = [];
    let index = 0;
    for (let i = 0; i < promiseArr.length; i++) {
      promiseArr[i]
      .then(res => {
        ans[i] = res;
        index++;
        if (index === promiseArr.length) {
          resolve(ans);
        }
      })
      .catch(err => reject(err));
    }
  })
}
Copy code

22.Promise.race

Promise.race = function(promiseArr) {
  return new Promise((resolve, reject) => {
    promiseArr.forEach(p => {
      // If it is not a Promise instance, it needs to be converted to a Promise instance
      Promise.resolve(p).then(
        val => resolve(val),
        err => reject(err),
      )
    })
  })
}
Copy code

23.Promise parallelism limitation

This is the problem of implementing Promise scheduler with parallel constraints.

Detailed implementation ideas: Original question of a high-frequency interview: implementing Promise scheduler with parallel constraints

class Scheduler {
  constructor() {
    this.queue = [];
    this.maxCount = 2;
    this.runCounts = 0;
  }
  add(promiseCreator) {
    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 timeout = time => new Promise(resolve => {
  setTimeout(resolve, time);
})
  
const scheduler = new Scheduler();
  
const addTask = (time,order) => {
  scheduler.add(() => timeout(time).then(()=>console.log(order)))
}
  
  
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
scheduler.taskStart()
// 2
// 3
// 1
// 4
 Copy code

24.JSONP

The script tag does not follow the same origin protocol and can be used for cross domain requests. The advantage is good compatibility, but it is limited to GET requests

const jsonp = ({ url, params, callbackName }) => {
  const generateUrl = () => {
    let dataSrc = '';
    for (let key in params) {
      if (Object.prototype.hasOwnProperty.call(params, key)) {
        dataSrc += `${key}=${params[key]}&`;
      }
    }
    dataSrc += `callback=${callbackName}`;
    return `${url}?${dataSrc}`;
  }
  return new Promise((resolve, reject) => {
    const scriptEle = document.createElement('script');
    scriptEle.src = generateUrl();
    document.body.appendChild(scriptEle);
    window[callbackName] = data => {
      resolve(data);
      document.removeChild(scriptEle);
    }
  })
}
Copy code

25.AJAX

const getJSON = function(url) {
  return new Promise((resolve, reject) => {
    const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp');
    xhr.open('GET', url, false);
    xhr.setRequestHeader('Accept', '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

26.event module

The mechanism of implementing the callback function in node. The callback function in node actually uses the observer mode internally.

Observer mode: defines a one to many dependency between objects. When the target object Subject changes, all object observers that depend on it will be notified.

function EventEmitter() {
  this.events = new Map();
}

// Some methods to be implemented:
// addListener,removeListener,once,removeAllListeners,emit

// Simulation Implementation of addlistener method
const wrapCallback = (fn, once = false) => ({ callback: fn, once });
EventEmitter.prototype.addListener = function(type, fn, once = false) {
  const hanlder = this.events.get(type);
  if (!hanlder) {
    // No type binding event
    this.events.set(type, wrapCallback(fn, once));
  } else if (hanlder && typeof hanlder.callback === 'function') {
    // Currently, there is only one callback for the type event
    this.events.set(type, [hanlder, wrapCallback(fn, once)]);
  } else {
    // Current type events > = 2
    hanlder.push(wrapCallback(fn, once));
  }
}
// Simulate the implementation of removeListener
EventEmitter.prototype.removeListener = function(type, listener) {
  const hanlder = this.events.get(type);
  if (!hanlder) return;
  if (!Array.isArray(this.events)) {
    if (hanlder.callback === listener.callback) this.events.delete(type);
    else return;
  }
  for (let i = 0; i < hanlder.length; i++) {
    const item = hanlder[i];
    if (item.callback === listener.callback) {
      hanlder.splice(i, 1);
      i--;
      if (hanlder.length === 1) {
        this.events.set(type, hanlder[0]);
      }
    }
  }
}
// Simulation Implementation of once method
EventEmitter.prototype.once = function(type, listener) {
  this.addListener(type, listener, true);
}
// Simulation Implementation of emit
EventEmitter.prototype.emit = function(type, ...args) {
  const hanlder = this.events.get(type);
  if (!hanlder) return;
  if (Array.isArray(hanlder)) {
    hanlder.forEach(item => {
      item.callback.apply(this, args);
      if (item.once) {
        this.removeListener(type, item);
      }
    })
  } else {
    hanlder.callback.apply(this, args);
    if (hanlder.once) {
      this.events.delete(type);
    }
  }
  return true;
}
EventEmitter.prototype.removeAllListeners = function(type) {
  const hanlder = this.events.get(type);
  if (!hanlder) return;
  this.events.delete(type);
}
Copy code

27. Picture loading

You can give img tags a unified custom attribute data src ='default Png ', when it is detected that the picture appears in the window, add the src attribute, and then the picture resource will be loaded.

function lazyload() {
  const imgs = document.getElementsByTagName('img');
  const len = imgs.length;
  // The height of the viewport
  const viewHeight = document.documentElement.clientHeight;
  // Scroll bar height
  const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop;
  for (let i = 0; i < len; i++) {
    const offsetHeight = imgs[i].offsetTop;
    if (offsetHeight < viewHeight + scrollHeight) {
      const src = imgs[i].dataset.src;
      imgs[i].src = src;
    }
  }
}

// You can use throttling to optimize it
window.addEventListener('scroll', lazyload);
Copy code

28. Rolling load

The principle is to monitor page scrolling events and analyze the attribute relationship of clientHeight, scrollTop and scrollHeight.

window.addEventListener('scroll', function() {
  const clientHeight = document.documentElement.clientHeight;
  const scrollTop = document.documentElement.scrollTop;
  const scrollHeight = document.documentElement.scrollHeight;
  if (clientHeight + scrollTop >= scrollHeight) {
    // Scroll to the bottom of the page if detected for subsequent operations
    // ...
  }
}, false);
Copy code

A Demo: Page scrolling loaded Demo

29. Rendering tens of thousands of data does not block the page

When rendering big data, reasonably use createDocumentFragment and requestAnimationFrame to cut the operation into small segments for execution.

setTimeout(() => {
  // Insert 100000 pieces of data
  const total = 100000;
  // Data inserted at one time
  const once = 20;
  // Number of times required to insert data
  const loopCount = Math.ceil(total / once);
  let countOfRender = 0;
  const ul = document.querySelector('ul');
  // Method of adding data
  function add() {
    const fragment = document.createDocumentFragment();
    for(let i = 0; i < once; i++) {
      const li = document.createElement('li');
      li.innerText = Math.floor(Math.random() * total);
      fragment.appendChild(li);
    }
    ul.appendChild(fragment);
    countOfRender += 1;
    loop();
  }
  function loop() {
    if(countOfRender < loopCount) {
      window.requestAnimationFrame(add);
    }
  }
  loop();
}, 0)
Copy code

30. Print out how many HTML elements are used in the current web page

One line of code can solve:

const fn = () => {
  return [...new Set([...document.querySelectorAll('*')].map(el => el.tagName))].length;
}
Copy code

It is worth noting that the DOM operation returns an array of classes, which can only be called after being converted to an array.

31. Transform VirtualDom into real DOM structure

This is one of the core concepts of current SPA application

// vnode structure:
// {
//   tag,
//   attrs,
//   children,
// }

//Virtual DOM => DOM
function render(vnode, container) {
  container.appendChild(_render(vnode));
}
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 => render(child, dom));
  return dom;
}
Copy code

32. String parsing

var a = {
	b: 123,
	c: '456',
	e: '789',
}
var str=`a{a.b}aa{a.c}aa {a.d}aaaa`;
// => 'a123aa456aa {a.d}aaaa'
Copy code

Implement the function to replace the variable in {} in str string. If the attribute does not exist, keep it as it is (such as {a.d})

It is similar to template string, but there is a difference. In fact, the principle is very different

const fn1 = (str, obj) => {
	let res = '';
    // Flag bit, whether there is in front of the flag{
	let flag = false;
	let start;
	for (let i = 0; i < str.length; i++) {
		if (str[i] === '{') {
			flag = true;
			start = i + 1;
			continue;
		}
		if (!flag) res += str[i];
		else {
			if (str[i] === '}') {
				flag = false;
				res += match(str.slice(start, i), obj);
			}
		}
	}
	return res;
}
// Object matching operation
const match = (str, obj) => {
	const keys = str.split('.').slice(1);
	let index = 0;
	let o = obj;
	while (index < keys.length) {
		const key = keys[index];
		if (!o[key]) {
			return `{${str}}`;
		} else {
			o = o[key];
		}
		index++;
	}
	return o;
}

//Traversal properties
Object.keys(vnode.attrs).forEach(key => {
const value = vnode.attrs[key];
dom.setAttribute(key, value);
})
}
//Recursive operation of subarray
vnode.children.forEach(child => render(child, dom));
return dom;
}
Copy code

## 32. String parsing

```javascript
var a = {
	b: 123,
	c: '456',
	e: '789',
}
var str=`a{a.b}aa{a.c}aa {a.d}aaaa`;
// => 'a123aa456aa {a.d}aaaa'
Copy code

Implement the function to replace the variable in {} in str string. If the attribute does not exist, keep it as it is (such as {a.d})

It is similar to template string, but there is a difference. In fact, the principle is very different

const fn1 = (str, obj) => {
	let res = '';
    // Flag bit, whether there is in front of the flag{
	let flag = false;
	let start;
	for (let i = 0; i < str.length; i++) {
		if (str[i] === '{') {
			flag = true;
			start = i + 1;
			continue;
		}
		if (!flag) res += str[i];
		else {
			if (str[i] === '}') {
				flag = false;
				res += match(str.slice(start, i), obj);
			}
		}
	}
	return res;
}
// Object matching operation
const match = (str, obj) => {
	const keys = str.split('.').slice(1);
	let index = 0;
	let o = obj;
	while (index < keys.length) {
		const key = keys[index];
		if (!o[key]) {
			return `{${str}}`;
		} else {
			o = o[key];
		}
		index++;
	}
	return o;
}

Topics: Javascript