As the front-end development, JS is the top priority. Recently, the peak of the interview has ended, and basically the offer has been settled, waiting for the lottery. Taking this time to summarize 32 handwritten JS questions, these are high-frequency interview questions, I hope to help you.

About the source code are closely compliance with the specification, can run through the MDN example, the rest will mostly involve some JS application problems and my interview process

01. Array flattening

Array flattening is the process of turning a multidimensional array into a one-dimensional array

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

Method 1: Use flat()

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

Method two: use regularity

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

But the data type will always be a string

Method three: regular improved version

const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g.' ') + '] ');
Copy the 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 the 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 the code

02. Array deduplication

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

Method 1: Use Set

const res1 = Array.from(new Set(arr));
Copy the 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 is guaranteed to remain the same. At the same time, len-- reduces the number of cycles to improve performancelen--; j--; }}}return arr;
}
Copy the code

Method 3: Use 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 the code

Of course, you can also use include, filter, the idea is 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 the code

Method 5: Use filter

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

Method 6: Use Map

const unique5 = arr= > {
  const map = new Map(a);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 the code

03. Class array to array

Class arrays have the length attribute, but no methods on the array prototype. Common arrays of classes include arguments, the results returned by DOM manipulation methods.

Method 1: array. from

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

Method 2: Array. Prototype. Slice. The call ()

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

Method three: Extend the operator

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

Method 4: Use concat

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

04.Array.prototype.filter()

Array.prototype.filter = function(callback, thisArg) {
  if (this= =undefined) {
    throw new TypeError('this is null or not undefined');
  }
  if (typeofcallback ! = ='function') {
    throw new TypeError(callback + 'is not a function');
  }
  const res = [];
  // make O an object pass for the callback function (cast object)
  const O = Object(this);
  // >>>0 Ensure that len is number and a positive integer
  const len = O.length >>> 0;
  for (let i = 0; i < len; i++) {
    // Check if I is an attribute of O (prototype chain is checked)
    if (i in O) {
      // The callback function call passes arguments
      if(callback.call(thisArg, O[i], i, O)) { res.push(O[i]); }}}return res;
}
Copy the code

For >>>0 in doubt: explain what >>>0 does

05.Array.prototype.map()

Array.prototype.map = function(callback, thisArg) {
  if (this= =undefined) {
    throw new TypeError('this is null or not defined');
  }
  if (typeofcallback ! = ='function') {
    throw new TypeError(callback + ' is not a function');
  }
  const res = [];
  / / in the same way
  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 the code

06.Array.prototype.forEach()

forEachIt’s similar to MAP, except thatforEachThere is no return value.

Array.prototype.forEach = function(callback, thisArg) {
  if (this= =null) {
    throw new TypeError('this is null or not defined');
  }
  if (typeofcallback ! = ="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 inO) { callback.call(thisArg, O[k], k, O); } k++; }}Copy the code

07.Array.prototype.reduce()

Array.prototype.reduce = function(callback, initialValue) {
  if (this= =undefined) {
    throw new TypeError('this is null or not defined');
  }
  if (typeofcallback ! = ='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 the initial value of the accumulator
  if (accumulator === undefined) {
    while(k < len && ! (kin O)) {
      k++;
    }
    // If the array bounds are exceeded and the initial value of the accumulator is not found, TypeError is used
    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 the code

08.Function.prototype.apply()

The first argument is the bound this, which defaults to Window, and the second argument is an 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;

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

09.Function.prototype.call

The only difference with call is that the call() method takes a list of arguments

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

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

10.Function.prototype.bind

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

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

11. Debounce

The function is executed only once within n seconds after the high frequency time is triggered. If the high frequency time is triggered again within n seconds, the time is recalculated.

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

The anti – shake function is usually used to save resources when the user enters a search request. The anti – shake function is triggered only once when the Window triggers the REsize event.

You are throttle.

High frequency time triggers, but only executes once in n seconds, so throttling dilutes 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 the code

Throttling is often used to listen for scrolling events triggered by repeated mouse clicks.

13. Creatization of the function

Refers to changing a function that takes multiple arguments into a fixed form that takes one argument and returns one function, so that it can be called again, e.g. F (1)(2)

Add (1)(2)(3)(4)=10; , the add (1) (1, 2, 3) and (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 the code

14. Simulate the new operation

Three steps:

  1. In order toctor.prototypeCreate an object for the prototype.
  2. Executes the constructor and binds this to the newly created object.
  3. Determines whether the result returned by the constructor execution is a reference data type, if so, or the object created otherwise.
function newOperator(ctor, ... args) {
  if (typeofctor ! = ='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 the code

15.instanceof

The instanceof operator is used to check whether the constructor’s prototype property appears on the prototype chain of an instance object.

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

16. Prototype inheritance

I’m just going to say that there’s a parasitic combination inheritance, and there’s a couple of descendents in between but they all have some flaws

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

17.Object.is

Object. Is mainly solves these two problems:

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

18.Object.assign

The object.assign () method is used to copy the values of all enumerable properties from one or more source objects to target objects. 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 must always refer to the data type, otherwise 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) {
        / / used for... In and hasOwnProperty double check to ensure that only its own properties, methods (not inherited) are retrieved
        for (const nextKey in nextSource) {
          if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; }}}}return to;
  },
  // Not enumerable
  enumerable: false.writable: true.configurable: true,})Copy the code

19. Deep copy

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

const cloneDeep1 = (target, hash = new WeakMap(a)) = > {
  // Handle incoming arguments
  if (typeoftarget ! = ='object' || target === null) {
    return target;
  }
  // There are direct returns in the hash table
  if (hash.has(target)) return hash.get(target);

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

  // For the Symbol attribute
  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 the code

20.Promise

Implementation idea: Promise source code implementation

// Simulate the implementation of Promise
// Promise solves callback hell with three tools:
// 1. Callback function delay binding
// 2. Return value through
// 3. Error bubbling

// Define three states
const PENDING = 'PENDING';      / /
const FULFILLED = 'FULFILLED';  / / has been successful
const REJECTED = 'REJECTED';    / / has failed

class Promise {
  constructor(exector) {
    // Initialization state
    this.status = PENDING;
    // Place success and failure results on this for easy access to then and catch
    this.value = undefined;
    this.reason = undefined;
    // Success callback function queue
    this.onFulfilledCallbacks = [];
    // Failed state callback function queue
    this.onRejectedCallbacks = [];

    const resolve = value= > {
      // You can change a state only if it is in progress
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        // The success state functions are executed in sequence
        this.onFulfilledCallbacks.forEach(fn= > fn(this.value)); }}const reject = reason= > {
      // You can change a state only if it is in progress
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        // The failed state functions are executed in sequence
        this.onRejectedCallbacks.forEach(fn= > fn(this.reason))
      }
    }
    try {
      Execute executor immediately
      // Pass internal resolve and reject to the executor. The user can call resolve and reject
      exector(resolve, reject);
    } catch(e) {
      // The executor executes an error, throwing out the error content rejectreject(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 to catch an error
          try {
            // Simulate microtasks
            setTimeout(() = > {
              const result = onFulfilled(self.value);
              // There are two kinds of cases:
              // 1. The return value of the callback is Promise
              // 2. If it is not a Promise, call the new Promise's resolve function
              result instanceof Promise? result.then(resolve, reject) : resolve(result); })}catch(e) { reject(e); }}); self.onRejectedCallbacks.push(() = > {
          // Do the following
          try {
            setTimeout(() = > {
              const result = onRejected(self.reason);
              // Reject: 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 it directly
      return value;
    } else {
      // If it is not a Promise instance, return a new Promise object, which is FULFILLED
      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 successfully implemented promises
    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++;
            // The state of the return promise can be changed if all execution is completed
            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 the code

21.Promise.all

Promise.all supports chained calls, essentially returning a Promise instance that changes its 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 the 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 the code

23.Promise parallel limit

Is the problem of implementing the Promise scheduler with parallel limitations.

Implement the Promise scheduler with parallel limitations

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 the code

24.JSONP

Script tags do not follow the same origin protocol and can be used for cross-domain requests, with the advantage of compatibility but only for 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 the 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 the code

26. The event module

Implement the mechanism of node callback function, node callback function is actually internal use observer mode.

Observer pattern: Defines a one-to-many dependency between objects in which all dependent object Observers are notified when the target object Subject changes.

function EventEmitter() {
  this.events = new Map(a); }// Some methods need to be implemented:
// addListener, removeListener, once, removeAllListeners, and listeners

// Simulate the 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) {// There is 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 type events
    this.events.set(type, [hanlder, wrapCallback(fn, once)]);
  } else {
    // Current number of type events >=2hanlder.push(wrapCallback(fn, once)); }}// Implement 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]); }}}}// Implement the once method
EventEmitter.prototype.once = function(type, listener) {
  this.addListener(type, listener, true);
}
// Simulate the emit method
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 the code

27. Lazy loading of images

You can customize the img tag attribute data-src=’default.png’. Add the SRC attribute only when the image is detected to appear in the window.

function lazyload() {
  const imgs = document.getElementsByTagName('img');
  const len = imgs.length;
  // Height of 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) {
      constsrc = imgs[i].dataset.src; imgs[i].src = src; }}}// Use throttling to optimize
window.addEventListener('scroll', lazyload);
Copy the code

28. Scroll loading

The principle is to listen to page scrolling events and analyze the property relations 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 detected, proceed with subsequent operations
    // ...}},false);
Copy the code

A Demo: a scrolling page loading Demo

Render tens of thousands of data without jamming the page

When rendering large data, use createDocumentFragment and requestAnimationFrame properly to split operations into small segments for execution.

setTimeout(() = > {
  // Insert 100,000 pieces of data
  const total = 100000;
  // Insert data once
  const once = 20;
  // The number of times needed to insert data
  const loopCount = Math.ceil(total / once);
  let countOfRender = 0;
  const ul = document.querySelector('ul');
  // The method to add 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 the code

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

A line of code can solve this:

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

It is worth noting that DOM operations return an array of classes and need to be converted to an array before they can call the array’s methods.

31. Convert VirtualDom to a real DOM structure

This is one of the core concepts of current SPA applications

// 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 directly a text node
  if (typeof vnode === 'string') {
    return document.createTextNode(vnode);
  }
  / / common DOM
  const dom = document.createElement(vnode.tag);
  if (vnode.attrs) {
    // Iterate over attributes
    Object.keys(vnode.attrs).forEach(key= > {
      constvalue = vnode.attrs[key]; dom.setAttribute(key, value); })}// Subarrays operate recursively
  vnode.children.forEach(child= > render(child, dom));
  return dom;
}
Copy the code

String parsing problem

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 the code

Implement functions to replace variables in {} in STR strings, leaving them as they are if attributes do not exist (e.g. {a.d})

Similar to template strings, but a little different, in fact, the principle is very much the same

const fn1 = (str, obj) = > {
	let res = ' ';
    // flag bit, flag before whether {
	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;
}
Copy the code

Welcome to pay attention to the “front-end Luo Xia” wechat for more learning materials, continue to update the front-end related articles ~

We are the front-end architecture team of Bytedance interactive Entertainment research and development. At present, we have positions for internships in Beijing, Hangzhou, Shenzhen and social recruitment. Welcome to add my resume to wechat FE-Luoxia for harassment ~