JavaScript Written Section

Click to follow this official account to get the latest updates, and you can get the “Front Interview Manual” and the most standard resume template.

Implement the anti-skid function (debounce)

The mechanism of the anti-shake function is that the callback is performed n seconds after the event is triggered. If the event is triggered again within n seconds, the timer is re-timed.

The difference with the throttling function can be directly seen in this animation implementation.

Handwritten simplified version:

// Anti-shaking function
const debounce = (fn, delay) = > {
  let timer = null;
  return (. args) = > {
    clearTimeout(timer);
    timer = setTimeout((a)= > {
      fn.apply(this, args);
    }, delay);
  };
};
Copy the code

Application scenario:

  • Button submit scenario: Prevent multiple button submissions and execute only the last one
  • Server validation scenario: Form validation requires server cooperation, only the last time of a series of input events are performed, and search for associative words is similar

For survival, use lodash. Debounce

Implement throttling functions

Anti – shaking function principle: in a unit of time, can only trigger a function. If this function fires multiple times per unit time, only one will take effect.

// Write a simplified version

// Throttling function
const throttle = (fn, delay = 500) = > {
  let flag = true;
  return (. args) = > {
    if(! flag)return;
    flag = false;
    setTimeout((a)= > {
      fn.apply(this, args);
      flag = true;
    }, delay);
  };
};
Copy the code

Application scenario:

  • Drag and drop scenario: Only once in a fixed time to prevent uHF trigger position changes
  • Zoom scenario: Monitor the browser resize
  • Animated scenes: Avoid triggering animations multiple times in a short period of time to cause performance problems

DeepClone

Simple version:

const newObj = JSON.parse(JSON.stringify(oldObj));
Copy the code

Limitations:

  1. He cannot clone special objects such as functions, regexps, etc

  2. Discard constructor for objects; all constructors will point to objects

  3. Object with a circular reference

Interview version:

/** * deep Clone * @param {[type]} Parent Object Object to be cloned * @return {[type]} Deep clone object */
const clone = parent= > {
  // Determine the type
  const isType = (obj, type) = > {
    if (typeofobj ! = ="object") return false;
    const typeString = Object.prototype.toString.call(obj);
    let flag;
    switch (type) {
      case "Array":
        flag = typeString === "[object Array]";
        break;
      case "Date":
        flag = typeString === "[object Date]";
        break;
      case "RegExp":
        flag = typeString === "[object RegExp]";
        break;
      default:
        flag = false;
    }
    return flag;
  };

  // Process the re
  const getRegExp = re= > {
    var flags = "";
    if (re.global) flags += "g";
    if (re.ignoreCase) flags += "i";
    if (re.multiline) flags += "m";
    return flags;
  };
  // Maintain two arrays that store circular references
  const parents = [];
  const children = [];

  const _clone = parent= > {
    if (parent === null) return null;
    if (typeofparent ! = ="object") return parent;

    let child, proto;

    if (isType(parent, "Array")) {
      // Do something special to the array
      child = [];
    } else if (isType(parent, "RegExp")) {
      // Do special treatment for regular objects
      child = new RegExp(parent.source, getRegExp(parent));
      if (parent.lastIndex) child.lastIndex = parent.lastIndex;
    } else if (isType(parent, "Date")) {
      // Do something special for the Date object
      child = new Date(parent.getTime());
    } else {
      // Handle the object prototype
      proto = Object.getPrototypeOf(parent);
      // Break the prototype chain with object.create
      child = Object.create(proto);
    }

    // Handle circular references
    const index = parents.indexOf(parent);

    if(index ! =- 1) {
      // If the parent array has this object, it means it has been referenced before, so return this object directly
      return children[index];
    }
    parents.push(parent);
    children.push(child);

    for (let i in parent) {
      / / recursion
      child[i] = _clone(parent[i]);
    }

    return child;
  };
  return _clone(parent);
};

Copy the code

Limitations:

  1. Some special cases are not handled, such as buffers, promises, sets, and maps
  2. In addition, for objects that ensure there are no circular references, we can eliminate the special handling of circular references, which can be time-consuming

Detailed principle to achieve deep cloning

Implement the Event (Event bus)

Event Bus is not only the cornerstone of each module in node, but also one of the dependent means of communication of front-end components. It also involves the subscription-publish design pattern, which is a very important foundation.

Simple version:

class EventEmeitter {
  constructor() {
    this._events = this._events || new Map(a);// Store event/callback key-value pairs
    this._maxListeners = this._maxListeners || 10; // Set the listening limit}}// Fires an event named type
EventEmeitter.prototype.emit = function(type, ... args) {
  let handler;
  // Retrieve the event callback function from this._events where the event key-value pair is stored
  handler = this._events.get(type);
  if (args.length > 0) {
    handler.apply(this, args);
  } else {
    handler.call(this);
  }
  return true;
};

// Listen for events named type
EventEmeitter.prototype.addListener = function(type, fn) {
  // Store the type event and the corresponding fn function in this._events
  if (!this._events.get(type)) {
    this._events.set(type, fn); }};Copy the code

Interview version:

class EventEmeitter {
  constructor() {
    this._events = this._events || new Map(a);// Store event/callback key-value pairs
    this._maxListeners = this._maxListeners || 10; // Set the listening limit}}// Fires an event named type
EventEmeitter.prototype.emit = function(type, ... args) {
  let handler;
  // Retrieve the event callback function from this._events where the event key-value pair is stored
  handler = this._events.get(type);
  if (args.length > 0) {
    handler.apply(this, args);
  } else {
    handler.call(this);
  }
  return true;
};

// Listen for events named type
EventEmeitter.prototype.addListener = function(type, fn) {
  // Store the type event and the corresponding fn function in this._events
  if (!this._events.get(type)) {
    this._events.set(type, fn); }};// Fires an event named type
EventEmeitter.prototype.emit = function(type, ... args) {
  let handler;
  handler = this._events.get(type);
  if (Array.isArray(handler)) {
    // If it is an array, it means that there are more than one listener
    for (let i = 0; i < handler.length; i++) {
      if (args.length > 0) {
        handler[i].apply(this, args);
      } else {
        handler[i].call(this); }}}else {
    // In the case of a single function, we can trigger it directly
    if (args.length > 0) {
      handler.apply(this, args);
    } else {
      handler.call(this); }}return true;
};

// Listen for events named type
EventEmeitter.prototype.addListener = function(type, fn) {
  const handler = this._events.get(type); // Get a list of functions corresponding to the event name
  if(! handler) {this._events.set(type, fn);
  } else if (handler && typeof handler === "function") {
    // If handler is a function, there is only one listener
    this._events.set(type, [handler, fn]); // We need to store multiple listeners in an array
  } else {
    handler.push(fn); // There are already multiple listeners, so push the function directly into the array}}; EventEmeitter.prototype.removeListener =function(type, fn) {
  const handler = this._events.get(type); // Get a list of functions corresponding to the event name

  // If it is a function, it is listened only once
  if (handler && typeof handler === "function") {
    this._events.delete(type, fn);
  } else {
    let postion;
    // If handler is an array, it means that the handler has been listened to many times to find the corresponding function
    for (let i = 0; i < handler.length; i++) {
      if (handler[i] === fn) {
        postion = i;
      } else {
        postion = - 1; }}// If a matching function is found, clear it from the array
    if(postion ! = =- 1) {
      // Find the corresponding position of the array and clear the callback directly
      handler.splice(postion, 1);
      // If only one function is cleared, then cancel the array and save it as a function
      if (handler.length === 1) {
        this._events.set(type, handler[0]); }}else {
      return this; }}};Copy the code

Realize the specific process and ideas see the implementation event

Implement instanceOf

/ / simulation instanceof
function instance_of(L, R) {
  //L represents the left expression and R represents the right expression
  var O = R.prototype; // Take the display prototype of R
  L = L.__proto__; // Take the implicit prototype of L
  while (true) {
    if (L === null) return false;
    if (O === L)
      Return true if O is strictly equal to L
      return true; L = L.__proto__; }}Copy the code

Simulation of the new

The new operator does these things:

  • It creates an entirely new object
  • It executes the [[Prototype]] (that is, __proto__) link
  • It makes this point to the newly created object
  • Each object created with new will eventually be linked to the function’s Prototype object by [[Prototype]]
  • If the function does not return the Object type Object(including Functoin, Array, Date, RegExg, Error), then the function call in the new expression returns a reference to that Object
// objectFactory(name, 'cxk', '18')
function objectFactory() {
  const obj = new Object(a);const Constructor = [].shift.call(arguments);

  obj.__proto__ = Constructor.prototype;

  const ret = Constructor.apply(obj, arguments);

  return typeof ret === "object" ? ret : obj;
}
Copy the code

Implementing a call

What did Call do:

  • Sets a function as a property of an object
  • Execute & to remove the function
  • Specify this to the function and pass the given arguments to execute the function
  • If no argument is passed, the default point is window
// Simulate call bar.myCall (null);
// Implement a call method:
Function.prototype.myCall = function(context) {
  // The context is not an object
  context.fn = this;
  let args = [];
  for (let i = 1, len = arguments.length; i < len; i++) {
    args.push(arguments[i]); } context.fn(... args);letresult = context.fn(... args);delete context.fn;
  return result;
};
Copy the code

Specific implementation reference JavaScript in-depth call and apply simulation implementation

Implement the Apply method

The apply principle is similar to call and will not go into detail

/ / simulate the apply
Function.prototype.myapply = function(context, arr) {
  var context = Object(context) || window;
  context.fn = this;

  var result;
  if(! arr) { result = context.fn(); }else {
    var args = [];
    for (var i = 0, len = arr.length; i < len; i++) {
      args.push("arr[" + i + "]");
    }
    result = eval("context.fn(" + args + ")");
  }

  delete context.fn;
  return result;
};
Copy the code

To realize the bind

What does a bind implementation do

  • Return a function, bound to this, passing preset arguments
  • The function returned by bind can be used as a constructor. So the constructor should invalidate this, but the arguments passed in are still valid
// MDN implementation
if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this! = ='function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

    var aArgs   = Array.prototype.slice.call(arguments.1),
        fToBind = this,
        fNOP    = function() {},
        fBound  = function() {
          // This instanceof fBound === true specifies that the returned fBound is called as the new constructor
          return fToBind.apply(this instanceof fBound
                 ? this
                 : oThis,
                 // Get the arguments passed in the call (fBound). This is the usual way to pass arguments to a function returned by bind
                 aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    // Maintain the prototype relationship
    if (this.prototype) {
      // Function.prototype doesn't have a prototype property
      fNOP.prototype = this.prototype; 
    }
    // The downstream code makes fbound. prototype an instance of fNOP, therefore
    // If the returned fBound is used as the constructor for new, the new generated object is passed as this. The __proto__ of the new object is an instance of fNOP
    fBound.prototype = new fNOP();

    return fBound;
  };
}
Copy the code

For more details, go to JavaScript in-depth bind’s simulation implementation #12

Simulation Object. The create

The object.create () method creates a new Object, using an existing Object to provide the __proto__ of the newly created Object.

/ / simulation Object. The create

function create(proto) {
  function F() {}
  F.prototype = proto;

  return new F();
}
Copy the code

Implement class inheritance

Class inheritance in a few years ago is the focus of the content, there are n kinds of inheritance methods have their advantages and disadvantages, ES6 after the popularity of less and less important, then a variety of writing a little “back to the word has four kinds of writing method” meaning, if you also want to in-depth understanding of the little Red Book can be, we only achieve one of the most ideal inheritance.

function Parent(name) {
    this.parent = name
}
Parent.prototype.say = function() {
    console.log(`The ${this.parent}You play basketball like Kunkun)}function Child(name, parent) {
    // Bind the superclass constructor to the subclass
    Parent.call(this, parent)
    this.child = name
}

/** 1. If you want to change the prototype of the Parent class, you can change the prototype of the Parent class. 3. Object. Create is a copy of the Parent prototype that is completely isolated from the Parent prototype */
Child.prototype = Object.create(Parent.prototype);
Child.prototype.say = function() {
    console.log(`The ${this.parent}Well, I've been practicing for two and a half yearsThe ${this.child}`);
}

// Remember to point the construction of the subclass to the subclass itself
Child.prototype.constructor = Child;

var parent = new Parent('father');
parent.say() // Father: You play basketball like Kunkun

var child = new Child('cxk'.'father');
child.say() // Good father, I am CXK for two and a half years
Copy the code

Realize the JSON parse

var json = '{"name":"cxk", "age":25}';
var obj = eval("(" + json + ")");
Copy the code

This method is dark magic and is easily attacked by XSS. There is also a new Function that is similar.

A simple tutorial on implementing a JSON parser in half an hour

Realize the Promise

I implemented A version A long time ago, and there were A lot of notes, but I couldn’t find it. I found an annotated version on the Internet, and there was no big problem visually. The specific process can be seen in this full implementation of Promise/A+, which is the most easy to read in the history

var PromisePolyfill = (function () {
  // Resolve, unlike reject, attempts to expand the thenable object
  function tryToResolve (value) {
    if (this === value) {
    // The main reason is to prevent the following situation
    // let y = new Promise(res => setTimeout(res(y)))
      throw TypeError('Chaining cycle detected for promise! ')}// Try to expand an object or function according to 2.32 and 2.33
    // Ensure that pre-S6 polyfills can also be mixed with ES6 native Promises
    if(value ! = =null &&
      (typeof value === 'object' || typeof value === 'function')) {
      try {
      // This is where the value of the then is recorded and the try is wrapped
      // The main reason is that then can be a getter, i.e
      // 1.value. Then error may be reported
      // 2.value. Then may cause side effects (for example, multiple executions may result in different results)
        var then = value.then

        // On the other hand, there is no guarantee that then will indeed call onFullfilled/onRejected as expected
        // So a flag is added to prevent resolveOrReject from being called more than once
        var thenAlreadyCalledOrThrow = false
        if (typeof then === 'function') {
        // Is thenable then try to expand
        // And the state of this object does not change until the thenable state changes
          then.bind(value)(
          // onFullfilled
            function (value2) {
              if (thenAlreadyCalledOrThrow) return
              thenAlreadyCalledOrThrow = true
              tryToResolve.bind(this, value2)()
            }.bind(this),

            // onRejected
            function (reason2) {
              if (thenAlreadyCalledOrThrow) return
              thenAlreadyCalledOrThrow = true
              resolveOrReject.bind(this.'rejected', reason2)()
            }.bind(this))}else {
        // Has then but then is not a function and therefore is not thenable
          resolveOrReject.bind(this.'resolved', value)()
        }
      } catch (e) {
        if (thenAlreadyCalledOrThrow) return
        thenAlreadyCalledOrThrow = true
        resolveOrReject.bind(this.'rejected', e)()
      }
    } else {
    // The basic type is returned directly
      resolveOrReject.bind(this.'resolved', value)()
    }
  }

  function resolveOrReject (status, data) {
    if (this.status ! = ='pending') return
    this.status = status
    this.data = data
    if (status === 'resolved') {
      for (var i = 0; i < this.resolveList.length; ++i) {
        this.resolveList[i]()
      }
    } else {
      for (i = 0; i < this.rejectList.length; ++i) {
        this.rejectList[i]()
      }
    }
  }

  function Promise (executor) {
    if(! (this instanceof Promise)) {
      throw Error('Promise can not be called without new ! ')}if (typeofexecutor ! = ='function') {
    // Non-standard but consistent with Chrome Google
      throw TypeError('Promise resolver ' + executor + ' is not a function')}this.status = 'pending'
    this.resolveList = []
    this.rejectList = []

    try {
      executor(tryToResolve.bind(this), resolveOrReject.bind(this.'rejected'))}catch (e) {
      resolveOrReject.bind(this.'rejected', e)()
    }
  }

  Promise.prototype.then = function (onFullfilled, onRejected) {
  // Return value penetration and error penetration. Note that error penetration is thrown instead of return, otherwise
  // The promise state returned by this THEN will become resolved, which means onFullfilled in the following THEN
  // it will be called, but we want to call onRejected
    if (typeofonFullfilled ! = ='function') {
      onFullfilled = function (data) {
        return data
      }
    }
    if (typeofonRejected ! = ='function') {
      onRejected = function (reason) {
        throw reason
      }
    }

    var executor = function (resolve, reject) {
      setTimeout(function () {
        try {
        // Get the corresponding handle function to handle this.data
        // This is the new Promise
          var value = this.status === 'resolved'
            ? onFullfilled(this.data)
            : onRejected(this.data)
          resolve(value)
        } catch (e) {
          reject(e)
        }
      }.bind(this))}// Then accepts two functions to return a new Promise
    // Then its execution is always asynchronous with onFullfilled/onRejected
    if (this.status ! = ='pending') {
      return new Promise(executor.bind(this))}else {
    // pending
      return new Promise(function (resolve, reject) {
        this.resolveList.push(executor.bind(this, resolve, reject))
        this.rejectList.push(executor.bind(this, resolve, reject))
      }.bind(this))}}// for prmise A+ test
  Promise.deferred = Promise.defer = function () {
    var dfd = {}
    dfd.promise = new Promise(function (resolve, reject) {
      dfd.resolve = resolve
      dfd.reject = reject
    })
    return dfd
  }

  // for prmise A+ test
  if (typeof module! = ='undefined') {
    module.exports = Promise
  }

  return Promise
})()

PromisePolyfill.all = function (promises) {
  return new Promise((resolve, reject) = > {
    const result = []
    let cnt = 0
    for (let i = 0; i < promises.length; ++i) {
      promises[i].then(value= > {
        cnt++
        result[i] = value
        if (cnt === promises.length) resolve(result)
      }, reject)
    }
  })
}

PromisePolyfill.race = function (promises) {
  return new Promise((resolve, reject) = > {
    for (let i = 0; i < promises.length; ++i) {
      promises[i].then(resolve, reject)
    }
  })
}
Copy the code

Parse URL Params into objects

let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* Result {user: 'anonymous', id: [123, 456], // Result {user: 'anonymous', id: [123, 456], // Result {user: 'anonymous', id: [123, 456], True, // not specifying worthy key convention is true} */

Copy the code
function parseParam(url) {
  const paramsStr = /. + \? (. +) $/.exec(url)[1]; / / will be? I'm going to take the strings that follow
  const paramsArr = paramsStr.split('&'); // Store the string in an array, split by &
  let paramsObj = {};
  // Save params to an object
  paramsArr.forEach(param= > {
    if (/ = /.test(param)) { // Process parameters with value
      let [key, val] = param.split('='); // Separate key and value
      val = decodeURIComponent(val); / / decoding
      val = /^\d+$/.test(val) ? parseFloat(val) : val; // Determine whether to convert to a number

      if (paramsObj.hasOwnProperty(key)) { // Add a value if the object has a key
        paramsObj[key] = [].concat(paramsObj[key], val);
      } else { // If the object does not have this key, create the key and set the valueparamsObj[key] = val; }}else { // Process parameters without value
      paramsObj[param] = true; }})return paramsObj;
}

Copy the code

Template engine implementation

let template = 'I'm {{name}}, age {{age}}, sex {{sex}}';
let data = {
  name: 'name'.age: 18
}
render(template, data); // My name is name, age 18, gender undefined

Copy the code
function render(template, data) {
  const reg = /\{\{(\w+)\}\}/; // The template string is regular
  if (reg.test(template)) { // Check whether there is a template string in the template
    const name = reg.exec(template)[1]; // Find the field of the first string in the current template
    template = template.replace(reg, data[name]); // Render the first template string
    return render(template, data); // Render recursively and return the rendered structure
  }
  return template; // If the template does not have a template string, return it directly
}

Copy the code

Convert to a hump name

var s1 = "get-element-by-id"

// Convert to getElementById
Copy the code
var f = function(s) {
    return s.replace(/-\w/g.function(x) {
        return x.slice(1).toUpperCase(); })}Copy the code

Finds the characters and number of characters that occur most frequently in the string

Example: abbcccDDDDD -> the most common character is d, which occurs 5 times

let str = "abcabcabcbbccccc";
let num = 0;
let char = ' ';

 // Arrange them in a certain order
str = str.split(' ').sort().join(' ');
// "aaabbbbbcccccccc"

// Define the regular expression
let re = /(\w)\1+/g;
str.replace(re,($0, $1) = > {if(num < $0.length){
        num = $0.length;
        char = $1; }});console.log(The most 'characters are${char}Appeared,${num}Time `);

Copy the code

String lookup

Use a basic traversal implementation to determine whether string A is contained in string B and return the first occurrence (-1 if not found).

a='34'; b='1234567'; / / return 2
a='35'; b='1234567'; / / return 1
a='355'; b='12354355'; / / return 5
isContain(a,b);

Copy the code
function isContain(a, b) {
  for (let i in b) {
    if (a[0] === b[i]) {
      let tmp = true;
      for (let j in a) {
        if(a[j] ! == b[~~i + ~~j]) { tmp =false; }}if (tmp) {
        returni; }}}return - 1;
}

Copy the code

Implement the thousands separator

// Keep three decimal places
parseToMoney(1234.56); // return '1,234.56'
parseToMoney(123456789); / / return '123456789'
parseToMoney(1087654.321); / / return '1087654321'

Copy the code
function parseToMoney(num) {
  num = parseFloat(num.toFixed(3));
  let [integer, decimal] = String.prototype.split.call(num, '. ');
  integer = integer.replace(/\d(? =(\d{3})+$)/g.'$&,);
  return integer + '. ' + (decimal ? decimal : ' ');
}

Copy the code

Regular expressions (using regular forward and backward declarations):

function parseToMoney(str){
    // Only match the position
    let re = / (? = (? ! \b)(\d{3})+$)/g; 
   return str.replace(re,', '); 
}
Copy the code

Determine if it is a phone number

function isPhone(tel) {
    var regx = /^1[34578]\d{9}$/;
    return regx.test(tel);
}

Copy the code

Verify whether it is a mailbox

function isEmail(email) {
    var regx = /^([a-zA-Z0-9_\-])+@([a-zA-Z0-9_\-])+(\.[a-zA-Z0-9_\-])+$/;
    return regx.test(email);
}

Copy the code

Verifying identity

function isCardNo(number) {
    var regx = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
    return regx.test(number);
}

Copy the code

The public,

If you want to follow the author’s latest articles and the latest document updates in real time, please follow the programmer interview officer of the public account, and the subsequent articles will be updated in the public account first.

Resume template: follow the public number reply “template” to obtain

“Front-end Interview Manual” : a surprise manual attached to this guide. Follow the official account “FED” to get it