preface

The ancients learned without exhaustion, young time old beginning. The paper come zhongjue shallow, and must know this to practice. It’s quick to understand an algorithm, but we have to sort out the idea of the problem and write it out by hand. The idea of a problem in coding, we can come to the dead knock 36 JS handwriting questions.

Three JS handwritten ideas and code implementation

Array flattening

Demonstration effect

Change [1, [1, 2], [1, [2]]] into [1, 1, 2, 1, 2]

One: just use.flat

console.log([1[1.2], [1[2]]].flat(3));
Copy the code
  • You can reduce the dimensions of a multidimensional array by whatever parameters you pass in
  • General direct transmission parameter is Infinity(simple and crude)

Second: recursive method method + borrow array API completion

(1)

function flattten(arr) {
    var result = [];
    for(var i = 0, len = arr.length; i < len; i++) {
        if(Array.isArray(arr[i])) {  // array. isArray Checks whether it is an Array
            result = result.concat(flattten(arr[i]))  The concat() method is used to join two or more arrays.
        } else {
            result.push(arr[i])
        }
    }
    return result;
}
Copy the code

(2)

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

Some +… (Extension operator) +.concat

function flattten(arr) {
    The // some() method is used to check whether elements in an array meet the specified criteria (provided by the function).
    The // some() method executes each element of the array in turn:
    // If one element meets the criteria, the expression returns true, and the rest of the elements are not tested.
    // If there is no element that meets the condition, return false.

    while(arr.some(item= > Array.isArray(item))) {
        console.log(arr) arr = [].concat(... arr)/ /... Reduces the dimension of the multidimensional array by one level
    }
    return arr
}
Copy the code

Fifth: the multidimensional array into a string, in the operation

(1)

function flatten(arr) {
    let str = arr.toString();
    str = str.replace(/(\[|\])/g.' ').split(', ').map(Number)
    return str;
}
Copy the code
  • Regular expression / / |)/g () represents a grouping, \ is escape character (because the regular expression rules of syntax, [and] using \ can make rules ignore [and])/g for the whole match, as long as met [and], ‘this is used in place of the.
  • The replace() method is used to replace some characters in a string with other characters, or to replace a substring that matches a regular expression.

(2)


function flatten(arr) {
    let result = arr.toString();
    result = result.replace(/(\[|\])/g.' ');
    result =  '[' + result + '] ';
    result = JSON.parse(result);
    Parse () converts strings of JSON rules to JsonObjects
    return result;
}
Copy the code

Depth copy

Shallow copy implementation

  1. Understand the limitations of shallow copying: only one layer of objects can be copied. If there is nesting of objects, shallow copies are useless
  2. Make a basic copy of the underlying data type
  3. Create a new store for reference types and copy a layer of object properties
function deepClone(target) {
  if(typeof target === 'object'&& target ! =null) {
      // Check whether it is an array or an object
      const targetclone = Array.isArray(target)? [] : {}// Whether the key exists
      for(let prop in target) {
        if(target.hasOwnProperty(prop)) {
        // The hasOwnProperty() method does not detect the object's prototype chain,
        // Checks only the current object itself, and returns true only if the current object itself has this property.
            targetclone[prop] = (typeof target[prop] === 'object')?
            deepClone(target[prop]):target[prop]
        }
      }
      return targetclone;
  } else {
      returntarget; }}let arr1 = [ 1.2, { val: 4.xdm: { dd: 99}}];let str = shallowerClone(arr1)
 console.log(arr1, 'arr1')
 console.log(str, 'str')
 str.push({mo: 'Brothers'})
 console.log('str.push-----------')
 console.log(arr1, 'arr1')
 console.log(str, 'str + push')
Copy the code

The final version of deep copy

Deep copy:

  1. For dates and re types, handle new a new
  2. When a: {val: a} is used for circular reference, weakMap is used for clever processing
  3. Use reflect. ownKeys to return an array of the target object’s own property keys,
  4. For the remaining copies of type Object and function but not NULL,
  5. In addition to the above type directly “key” assignment operation.

Details:

  1. Using getOwnPropertyDescriptors returns the specified all its attributes (not inherit property) a description of the object
  2. The resulting properties are inherited from the prototype chain using Object.create
  3. For a: {val: a} circular reference, weakMap.set and GET are used for processing.

The implementation code

const isComplexDataType = obj= > (typeof obj === 'object' 
    || typeof obj === 'function') && (obj ! = =null)
const deepClone = function (obj, hash = new WeakMap(a)) {
  if (obj.constructor === Date) 
    return new Date(obj)       // The date object returns a new date object
  if (obj.constructor === RegExp)
    return new RegExp(obj)     The regular object returns a new regular object directly
  // If the loop is referenced, use weakMap
  if (hash.has(obj)) return hash.get(obj)
  let allDesc = Object.getOwnPropertyDescriptors(obj)
  // Walks through the properties of all keys of the passed argument
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
  // Inherit the prototype chain
  hash.set(obj, cloneObj)
  for (let key of Reflect.ownKeys(obj)) { 
  // We can use reflect.ownkeys for non-enumerable properties that iterate over objects and for Symbol types
    cloneObj[key] = (isComplexDataType(obj[key]) && 
    typeofobj[key] ! = ='function')? deepClone(obj[key], hash) : obj[key]//  typeof obj[key] !== 'function')
  }
  return cloneObj
}
Copy the code

Test code

let obj = {
  num: 0.str: ' '.boolean: true.unf: undefined.nul: null.obj: { name: 'I am an object'.id: 1 },
  arr: [0.1.2].func: function () { console.log(I'm a function.)},date: new Date(0),
  reg: new RegExp('/ I'm a regular /ig'),Symbol('1')]: 1};Object.defineProperty(obj, 'innumerable', {
  enumerable: false.value: 'Non-enumerable properties'}); obj =Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj    // Set loop to a property referenced in a loop
let cloneObj = deepClone(obj)
cloneObj.arr.push(4)
console.log('obj', obj)
console.log('cloneObj', cloneObj)
console.log(cloneObj.func)
Copy the code

Realizes the object cyclic application copy

To illustrate the above code:

Object. GetOwnPropertyDescriptors returns the specified all its attributes (not inherit property) a description of the Object. You can learn more about the API here

  • The object.create () method creates a new Object, using an existing Object to provide the __proto__ of the newly created Object,
  • Object.create If this parameter is specified and not undefined, the incoming Object’s own enumerable properties (that is, those defined by the Object itself, rather than enumerated properties in its stereotype chain) add the specified property value and corresponding property descriptor to the newly created Object.
const person = {
    isHuman: false};const me = Object.create(person);
console.log(me.__proto__ === person);  // true
Copy the code

The object.getProtoTypeof method returns the inherited Prototype chain of the specified Object’s Prototype (the value of the internal [[Prototype]] property)

A WeakMap object is a collection of key-value pairs, where the keys are weak reference objects and the values can be arbitrary. Because WeakMap is a weak reference type, it can effectively prevent memory leaks and is helpful as a detection loop reference. If there is a loop, the reference directly returns the value that WeakMap stores. Read more about the differences between a Map and a Map here

  1. Reflect. OwnKeys = = Object. GetOwnPropertyNames (target) contact (Object. GetOwnPropertySymbols (target).
  2. Object. GetOwnPropertyNames () method returns a specified by the Object of all its attributes of the attribute name (including not enumerated attribute but does not include Symbol value as the name of the attribute) consisting of an array.
  3. Object. GetOwnPropertySymbols () method returns a given Object itself all the attributes of the Symbol of the array

Event Bus (publish subscribe mode)

Principle:

Event bus

Is an implementation of a publish/subscribe pattern in which publishers publish data and subscribers can listen to and act on it. This loosens the coupling between publisher and subscriber. Publishers publish data events to the event bus, which is responsible for sending them to subscribers

On or addListener (event, listenr)

Adds a listener to the end of the listener array for the specified event.

Off or removeListener (event, listenr)

Removes a listener for the specified event. the listener must be a registered listener for the event.

emit(event, [arg1], [arg2] …)

Each listener is executed in the order of its arguments, returning true if the event has a registered listener, false otherwise. Use Node.js to learn about the event bus

var events = require('events');
var eventEmitter = new events.EventEmitter();
eventEmitter.on('say'.function(name) {
    console.log('Hello', name);
})
eventEmitter.emit('say'.'Ruoli Teacher');
function helloA(name) {
    console.log("helloAAAAAAA", name)
}

function helloB(name) {
    console.log("helloBBBBBBB", name)
}

eventEmitter.on('say', helloA)
eventEmitter.on('say', helloB)
eventEmitter.emit('say'.'Ruoli Teacher')
eventEmitter.off('say', helloB);
eventEmitter.emit('say'.'Ruoli Teacher')
Copy the code

EventEmitter is an instance of eventEmitter receiving the eventEmitter module new. The EMIT method of eventEmitter emits a SAY event, which is monitored by eventEmitter’s on method. To execute the corresponding function. When off is triggered, the response function on the SAY event is removed.

On implementation code:

The realization idea of ON

Add a listener for the specified event on: {“say”: [{listener:(function), once:(false or true)}, {}, {}]}

  1. The parameter has two (name, fn). Name is the specified event, and fn is a callback function
  2. Check whether fn does not exist, whether it is valid (function), and whether events cannot be added repeatedly

The following code for ON

function EventEmitter() {
    this.__events = {}
}

// Check whether the listener is valid
function isValidListener(listener) {
   if (typeof listener === 'function') {
       return true;
   } else if (listener && typeof listener === 'object') {
    // The listener, as a callback to a custom event, must be a function,
    // Check whether the object is recursive and whether there are functions in the object.
    // Custom events will not work without a callback
       return isValidListener(listener.listener);
   } else {
       return false; }}// As the name implies, check whether the new custom event exists
function indexOf(array, item) {
   var result = -1
   item = typeof item === 'object' ? item.listener : item;
   for (var i = 0, len = array.length; i < len; i++) {
       if (array[i].listener === item) {
           result = i;
           break; }}return result;
}
EventEmitter.prototype.on = function(eventName, listener){
    if(! eventName || ! listener)return;
    // Determine whether the listener for the callback is a function
    if(! isValidListener(listener)) {throw new TypeError('listener must be a function');
    }
     let events = this.__events;
     console.log(events)
       // var listeners = events[eventName] = events[eventName] = events[eventName] || [];
     events[eventName] = events[eventName] || [];
     let listeners = events[eventName]
     ListenerIsWrapped {listener: listener,once: false}
     let listenerIsWrapped = (typeof listener === 'object');
     // Do not add events repeatedly, check whether there is the same
     if (indexOf(listeners, listener) === -1) {
         listeners.push(listenerIsWrapped ? listener : {
             listener: listener,
             once: false
         });
     }
     return this;
     // This points to EventEmitter, which returns the instantiation object that actually called the method
};
Copy the code

A = B=C where the order of execution is B=C A = B emit code implementation

The thinking of emit

Take the corresponding listener event from this._events and execute it (note the execution of multiple events)

The following code for emit

EventEmitter.prototype.emit = function(eventName,... args) {
    // Get the corresponding custom event callback directly from the internal object
    let listeners = this.__events[eventName];
    if(! listeners)return;
    // Multiple listeners need to be considered
    for (let i = 0; i < listeners.length; i++) {   
        let listener = listeners[i];
        if (listener) {
            listener.listener.call(this. args || []);// Give special treatment to the listener whose once is true
            if (listener.once) {
                this.off(eventName, listener.listener)
            }
        }
    }
    return this;
};
Copy the code

listener.listener.call(this, … args || []); Bind this to listener.listener and execute the corresponding function. For example, when executing fn1, fn1.call(this, name, age). Equivalent to executing the function fn1(). Off code implementation

Off the train of thought

Delete the corresponding function on the listening event

The code for off is as follows

EventEmitter.prototype.off = function(eventName, listener) {
    // Make basic judgments
    let listeners = this.__events[eventName];
    let index = -1;
    if(! listeners)return;
    for(let i = 0; i < listeners.length; i++) {
        if(listeners[i] && listeners[i].listener === listener) {
            index = i;
            break; }}if(index ! = = -1) {
        listeners.splice(index, 1.null);
    }
    return this;

}
Copy the code

Publish subscribe mode detection code:

let eventBus = new EventEmitter()
let fn1 = function(name, age) {
	console.log(`${name} ${age}`)}let fn2 = function(name, age) {
	console.log(`hello, ${name} ${age}`)}let fn3 = function(name, age) {
	console.log(`hello myname is, ${name} ${age}`)
}
eventBus.on('say', fn1)
eventBus.on('say', fn2)
eventBus.on('say', fn3)
eventBus.emit('say'.'布兰'.12)
eventBus.off('say', fn1)
console.log('Use off to remove fn1 function on say event -------')
eventBus.emit('say'.'布兰'.12)
Copy the code

The end of the

I am just a front-end chicken, is learning JS handwriting 36 topics. If there is a problem with the code, please point it out.

This paper summarizes the reference:

36 js handwritten questions