Step by step, the more difficult it is!!

Implementing shallow copy

If you assign an object to a variable, both values will be the same reference, and if one changes, the other will change accordingly. For reference types we need to copy the data.

  1. with.implementation
constcopy = {... {x:1}}
Copy the code
  1. withObject.assignimplementation
const copy = Object.assign({}, {x:1})
Copy the code
  1. withsliceimplementation
let arr = [1.3, {
  x: 1
}];

let copy = arr.slice();
Copy the code

Deep copy

Usually a shallow copy solves most of the problems, but only at the first level. If there are objects in the values that follow, then we need to use a deep copy.

  1. Through JSON transformation

Disadvantages:

  • For function and undefined, these attributes are lost.
  • For RegExp and Error objects, only empty objects are returned
  • For date objects, the result is a string, not a date object
  • Becomes null for NaN, Infinity, and -infinity
  • Unable to handle circular references
const obj = {a: 1.b: {x: 3}}
const copy = JSON.parse(JSON.stringify(obj))
Copy the code
  1. Beggar recursion

Parse (json.stringify (oldObj)) solves most of json.parse (json.stringify (oldObj)) problems, but it still doesn’t solve the problem of circular references.

function deepClone(obj) {
  let copy = obj instanceof Array ? [] : {}
  for (let i in obj) {
    if (obj.hasOwnProperty(i)) {
      copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
    }
  }
  return copy
}
Copy the code
  1. Improved deep copy

Refer to the vuex deepCopy source code to solve the circular reference

function deepCopy (obj, cache = []) {
  // typeof [] => 'object'
  // typeof {} => 'object'
  if (obj === null || typeofobj ! = ='object') {
    return obj
  }
  // If the object passed is equal to the cached object, the recursion ends, preventing loops
  Var a = {b:1} * a.c = a * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value */
  const hit = cache.filter(c= > c.original === obj)[0]
  if (hit) {
    return hit.copy
  }

  const copy = Array.isArray(obj) ?  [] :   {}
  // Put copy in cache first because we need to reference it when we recursively deepCopy
  cache.push({
    original: obj,
    copy
  })
  Object.keys(obj).forEach(key= > {
    copy[key] = deepCopy(obj[key], cache)
  })

  return copy
}
Copy the code

Of course: Cache can be replaced with new WeakMap()

  1. Deep copy reoptimization
  • Deep copy addMapSetRelated, of course you can add moreDateAnd so on
  • useWeakMapInstead of[]
function clone(target, map = new WeakMap()) {
  // Clone the original type
  if(! isObject(target)) {return target;
  }

  / / initialization
  const type = getType(target);
  let cloneTarget;
  if (deepTag.includes(type)) {
    cloneTarget = getInit(target, type);
  }

  // Prevent circular references
  if (map.get(target)) {
    return map.get(target);
  }
  map.set(target, cloneTarget);

  / / clone set
  if (type === setTag) {
    target.forEach(value= > {
      cloneTarget.add(clone(value,map));
    });
    return cloneTarget;
  }

  / / clone map
  if (type === mapTag) {
    target.forEach((value, key) = > {
      cloneTarget.set(key, clone(value,map));
    });
    return cloneTarget;
  }

  // Clone objects and arrays
  const keys = type === arrayTag ? undefined : Object.keys(target);
  forEach(keys || target, (value, key) => {
    if (keys) {
      key = value;
    }
    cloneTarget[key] = clone(target[key], map);
  });

  return cloneTarget;
}
Copy the code
  1. Deep copy of various compatible versions

Compatible with objects, arrays, Symbol, re, Error, Date, base types.

const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const argsTag = '[object Arguments]';

const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]';

const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];

function forEach(array, iteratee) {
  let index = - 1;
  const length = array.length;
  while (++index < length) {
    iteratee(array[index], index);
  }
  return array;
}

function isObject(target) {
  const type = typeof target;
  returntarget ! = =null && (type === 'object' || type === 'function');
}

function getType(target) {
  return Object.prototype.toString.call(target);
}

function getInit(target) {
  const Ctor = target.constructor;
  return new Ctor();
}

function cloneSymbol(targe) {
  return Object(Symbol.prototype.valueOf.call(targe));
}

function cloneReg(targe) {
  const reFlags = /\w*$/;
  const result = new targe.constructor(targe.source, reFlags.exec(targe));
  result.lastIndex = targe.lastIndex;
  return result;
}

function cloneFunction(func) {
  const bodyReg = / (? <={)(.|\n)+(? =})/m;
  const paramReg = / (? < = \ () + (? =\)\s+{)/;
  const funcString = func.toString();

  if (func.prototype) {
    const param = paramReg.exec(funcString);
    const body = bodyReg.exec(funcString);
    if (body) {
      if (param) {
        const paramArr = param[0].split(', ');
        return new Function(... paramArr, body[0]);
      } else {
        return new Function(body[0]); }}else {
      return null; }}else {
    return eval(funcString); }}function cloneOtherType(targe, type) {
  const Ctor = targe.constructor;
  switch (type) {
    case boolTag:
    case numberTag:
    case stringTag:
    case errorTag:
    case dateTag:
      return new Ctor(targe);
    case regexpTag:
      return cloneReg(targe);
    case symbolTag:
      return cloneSymbol(targe);
    case funcTag:
      return cloneFunction(targe);
    default:
      return null; }}function clone(target, map = new WeakMap()) {

  // Clone the original type
  if(! isObject(target)) {return target;
  }

  / / initialization
  const type = getType(target);
  let cloneTarget;
  if (deepTag.includes(type)) {
    cloneTarget = getInit(target, type);
  } else {
    return cloneOtherType(target, type);
  }

  // Prevent circular references
  if (map.get(target)) {
    return map.get(target);
  }
  map.set(target, cloneTarget);

  / / clone set
  if (type === setTag) {
    target.forEach(value= > {
      cloneTarget.add(clone(value, map));
    });
    return cloneTarget;
  }

  / / clone map
  if (type === mapTag) {
    target.forEach((value, key) = > {
      cloneTarget.set(key, clone(value, map));
    });
    return cloneTarget;
  }

  // Clone objects and arrays
  const keys = type === arrayTag ? undefined : Object.keys(target);
  forEach(keys || target, (value, key) => {
    if (keys) {
      key = value;
    }
    cloneTarget[key] = clone(target[key], map);
  });

  return cloneTarget;
}
Copy the code

Implement a bind function

Through the closure call call | | apply can realize the bind function.

  1. The beggar version
Function.prototype.myapply = function (context, ... preArgs) {
    return (. args) = > this.call(context, ... preArgs, ... args) }Copy the code
  1. Advanced version, do some exception handling
Function.prototype.mybind = function (context) {
  if (typeof this! = ='function') {
    throw new TypeError('Error')}let _this = this
  let arg = [...arguments].slice(1)
  return function F() {
    // Handle the case where the function uses new
    if (this instanceof F) {
      return new_this(... arg, ... arguments) }else {
      return_this.apply(context, arg.concat(... arguments)) } } }Copy the code

Implement an apply function

Implement the bind you need to call the apply | | call, then how to do?

Thread: Attach the method this points to to the target this and return

Function.prototype.myapply = function (context) {
  if (typeof this! = ='function') {
    throw new TypeError('not funciton')
  }

  context = context || window
  context.fn = this
  let result

  if (arguments[1]) { result = context.fn(... arguments[1])}else {
    result = context.fn()
  }

  delete context.fn

  return result
}
Copy the code

Realize the anti – shake function

Scroll events, resize events, input events, etc. : need to do a complex calculation or to implement a button against second click operation. These requirements can be achieved through function jitter prevention.

  1. The beggar version

Cons: This stabilization can only be called at the end. Generally, anti-shake has the immediate option, which indicates whether to invoke it immediately.

const debounce = (func, wait = 50) = > {
  // Cache a timer ID
  let timer = 0
  // The function returned here is the anti-shake function that the user actually calls each time
  // Clear the last timer if it has already been set
  // Start a new timer to delay the execution of the user-passed method
  return function(. args) {
    if (timer) clearTimeout(timer)
    timer = setTimeout((a)= > {
      func.apply(this, args)
    }, wait)
  }
}
Copy the code
  1. modified
// This is used to get the current timestamp
function now() {
  return +new Date()}/ * * * image stabilization function, return function calls in a row, idle time must be greater than or equal to wait, * * @param {function} * @param {number} wait specifies the interval between time Windows * @param {Boolean} immediate Whether to call the function immediately * @return {function} returns the client called function */
function debounce (func, wait = 50, immediate = true) {
  let timer, context, args

  // Delay execution of the function
  const later = (a)= > setTimeout((a)= > {
    // Clear the cache timer sequence number when the delay function is completed
    timer = null
    // In the case of delayed execution, the function is executed in the delayed function
    // Use the previously cached parameters and context
    if(! immediate) { func.apply(context, args) context = args =null
    }
  }, wait)

  // The function returned here is the function actually called each time
  return function(. params) {
    // If no deferred execution function is created (later), create one
    if(! timer) { timer = later()// If execute immediately, call the function
      // Otherwise cache parameters and call context
      if (immediate) {
        func.apply(this, params)
      } else {
        context = this
        args = params
      }
    // If the function is already delayed (later), the call clears the original and resets one
    // This will reset the delay function
    } else {
      clearTimeout(timer)
      timer = later()
    }
  }
}
Copy the code

Implement a throttling function

Throttling is essentially the same as shaking prevention.

  1. The beggar version

Disadvantages: This throttling function lacks a start-end call switch

/** ** Function throttling method * @param Function fn Delay Function * @param Number delay * @param Number atleast How long to trigger a Function * @return Function delays the execution of the method */
var throttle = function (fn, delay, atleast) {
  var timer = null;
  var previous = null;

  return function () {
    var now = Date.now();

    if ( !previous ) previous = now;

    if ( now - previous > atleast ) {
      fn();
      // Reset the last start time to the current end time
      previous = now;
    } else {
      clearTimeout(timer);
      timer = setTimeout(function() { fn(); }, delay); }}};Copy the code
  1. Modified version: ReferencelodashClassic throttling function
/** * underscore / wait * * @param function Func callback * @param number wait indicates the interval of time window * @param object options If you want to ignore the start function call, pass {leading: false}. * If you want to ignore the trailing function call, pass {trailing: false} * the two cannot coexist, otherwise the function cannot be executed * @return function returns the client called function */
const throttle = function(func, wait, options) {
  var context, args, result;
  var timeout = null;
  // The previous timestamp
  var previous = 0;
  // Set to empty if options are not passed
  if(! options) options = {};// Timer callback function
  var later = function() {
    // Set previous to 0 if leading is set
    // used for the first if judgment of the following function
    previous = options.leading === false ? 0 : Date.now();
    // Empty to prevent memory leaks, and for the following timer judgment
    timeout = null;
    result = func.apply(context, args);
    if(! timeout) context = args =null;
  };

  return function() {
    // Get the current timestamp
    var now = Date.now();
    // The first entry into the former must be true
    // Do not execute the function the first time if needed
    // Set the last timestamp to the current one
    // The value of remaining will then be greater than 0
    if(! previous && options.leading ===false) previous = now;
    // Calculate the remaining time
    var remaining = wait - (now - previous);
    context = this;
    args = arguments;

    // If the current call is longer than the last call + wait
    // Or the user manually adjusts the time
    // If trailing is set, only this condition will be entered
    // if not set to leading, the condition will be entered for the first time
    // One more thing, you might think that if you start the timer, you should not enter the if condition
    // It will actually enter because of the timer delay
    // Not the exact time, you probably set it to 2 seconds
    // But it takes 2.2 seconds to trigger, and then the condition will enter
    if (remaining <= 0 || remaining > wait) {
      // Clear the timer if it exists otherwise a second callback will be called
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      previous = now;
      result = func.apply(context, args);
      if(! timeout) context = args =null;
    } else if(! timeout && options.trailing ! = =false) {
      // Check whether the timer and trailing are set
    // If not, start a timer
      // Do not set leading and trailing at the same time
      timeout = setTimeout(later, remaining);
    }
    return result;
  };
};
Copy the code

Implementation of currization functions

Corey:

  • Transform a function that takes multiple arguments into a function that takes a single argument, the first argument of the original function
  • And returns a new function that takes the remaining arguments and returns the result

Implement creefaction

  1. Fixed passing arguments until sufficient arguments are passed
/** * Implementation note: When a function receives enough arguments, it executes the original function, so how do we determine when enough arguments are reached? * The currization function needs to remember the arguments you gave it, if not, it defaults to an empty array. * On each subsequent call, we need to check that the arguments are sufficient, and if so, we will execute fn, and if not, we will return a new curry function, stuffing it with the existing arguments. * * /
// The function to be currified
let sum = (a, b, c, d) = > {
  return a + b + c + d
}

// The currified function returns a processed function
let curry = (fn, ... arr) = > {  // arR records existing parameters
  return (. args) = > {   // args receives new parameters
    if(fn.length <= (... arr,... args)) {// If the parameters are sufficient, the execution is triggered
      returnfn(... arr, ... args) }else {  // Continue adding parameters
      return curry(fn, [...arr, ...args])
    }
  }
}

var sumPlus = curry(sum)
sumPlus(1) (2) (3) (4)
sumPlus(1.2) (3) (4)
sumPlus(1.2.3) (4)
Copy the code
  1. Do not fix the incoming parameter, execute at any time
/** * The main function of the Currization is to delay execution. The execution condition is not necessarily equal to the number of arguments, but can also be other conditions. * For example, if the parameter is 0, we need to modify the above curry function slightly */
// The function to be currified
let sum = arr= > {
  return arr.reduce((a, b) = > {
    return a + b
  })
}

// The currified function returns a processed function
let curry = (fn, ... arr) = > { // arR records existing parameters
  var len = fn.length; // Calculate the desired function parameter length
  return (. args) = > { // args receives new parameters
    const combArg = [...arr, ...args];
    if (len <= combArg.length) { // If the parameters are sufficient, the execution is triggered
	  returnfn(... combArg) }else { // Continue adding parameters
	  returncurry(fn, ... combArg) } } }var sumPlus = curry(sum)
sumPlus(1) (2) (3) (4)()
sumPlus(1.2) (3) (4)()
sumPlus(1.2.3) (4) ()Copy the code

Array flattening

const flattenDeep = (arr) = > Array.isArray(arr)
  ? arr.reduce( (a, b) = > [...a, ...flattenDeep(b)] , [])
  : [arr]

flattenDeep([1The [[2], [3[4]], 5]])
Copy the code

Implement a new operator

The new operator does these things:

  1. A brand new object is created.
  2. The [[Prototype]] (A.K.A. __proto__) link is executed.
  3. Make this point to the newly created object.
  4. Each object created by new will eventually be linked to the function’s Prototype object by [[Prototype]].
  5. If the function does not return the Object type Object(including Functoin, Array, Date, RegExg, Error), the function call in the new expression will return the Object reference.
function New(func) {
  var res = {};
  if(func.prototype ! = =null) {
    res.__proto__ = func.prototype;
  }
  var ret = func.apply(res, Array.prototype.slice.call(arguments.1));
  if ((typeof ret === "object" || typeof ret === "function") && ret ! = =null) {
    return ret;
  }
  return res;
}

var obj = New(A, 1.2);
Copy the code

Implement an instanceOf

InstanceOf’s internal mechanism is to determine whether an object’s prototype can be found in its prototype chain

function instanceOf(left,right) {
  let proto = left.__proto__;
  let prototype = right.prototype
  while(true) {
    if(proto === null) return false
    if(proto === prototype) return trueproto = proto.__proto__; }}Copy the code

Promise to realize

A Promise has three states, Pending, resolve, and Reject. Promise results are reliable because of the certainty of the state.

Promise also solves the problem of callback hell.

  1. The beggar version

Implements the main Promise features, but lacks asynchronous processing and other conditions

class Promise {
  constructor (fn) {
    // Three states
    this.state = 'pending'
    this.value = undefined
    this.reason = undefined

    let resolve = value= > {
      if (this.state === 'pending') {
        this.state = 'fulfilled'
        this.value = value
      }
    }
    let reject = value= > {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.reason = value
      }
    }
    // Automatically execute the function
    try {
      fn(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }

  // then
  then(onFulfilled, onRejected) {
    switch (this.state) {
      case 'fulfilled':
        onFulfilled(this.value)
        break
      case 'rejected':
        onRejected(this.value)
        break
      default:}}}Copy the code
  1. Improved version: The yCK booklet implements the main features of Promise (no catch, finally, static calls, etc.)
// Three states
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";

// Promise receives a function argument that executes immediately
function MyPromise(fn) {
  let _this = this;
  _this.currentState = PENDING;
  _this.value = undefined;
  // Used to hold callbacks in THEN, only when a promise
  // Cache only when the state is pending, and no more than one cache per instance
  _this.resolvedCallbacks = [];
  _this.rejectedCallbacks = [];

  _this.resolve = function (value) {
    if (value instanceof MyPromise) {
      // If value is a Promise, execute recursively
      return value.then(_this.resolve, _this.reject)
    }
    setTimeout((a)= > { // Execute asynchronously to ensure the execution sequence
      if (_this.currentState === PENDING) {
        _this.currentState = RESOLVED;
        _this.value = value;
        _this.resolvedCallbacks.forEach(cb= >cb()); }})}; _this.reject =function (reason) {
    setTimeout((a)= > { // Execute asynchronously to ensure the execution sequence
      if (_this.currentState === PENDING) {
        _this.currentState = REJECTED;
        _this.value = reason;
        _this.rejectedCallbacks.forEach(cb= >cb()); }})}// Used to solve the following problems
  // new Promise(() => throw Error('error))
  try {
    fn(_this.resolve, _this.reject);
  } catch (e) {
    _this.reject(e);
  }
}

MyPromise.prototype.then = function (onResolved, onRejected) {
  var self = this;
  // Specification 2.2.7, then must return a new promise
  var promise2;
  // Specification 2.2. OnResolved and onRejected are optional
  // Ignore the type if it is not a function, and pass through is also implemented
  // Promise.resolve(4).then().then((value) => console.log(value))
  onResolved = typeof onResolved === 'function' ? onResolved : v= > v;
  onRejected = typeof onRejected === 'function' ? onRejected : r= > {throw r};

  if (self.currentState === RESOLVED) {
    return (promise2 = new MyPromise(function (resolve, reject) {
      // Specification 2.2.4 ensures ondepressing, onRjected asynchronously
      // Use setTimeout
      setTimeout(function () {
        try {
          var x = onResolved(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(reason) { reject(reason); }}); })); }if (self.currentState === REJECTED) {
    return (promise2 = new MyPromise(function (resolve, reject) {
      setTimeout(function () {
        // Execute onRejected asynchronously
        try {
          var x = onRejected(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(reason) { reject(reason); }}); })); }if (self.currentState === PENDING) {
    return (promise2 = new MyPromise(function (resolve, reject) {
      self.resolvedCallbacks.push(function () {
        // We use a try/catch package to allow for possible errors
        try {
          var x = onResolved(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(r) { reject(r); }}); self.rejectedCallbacks.push(function () {
        try {
          var x = onRejected(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(r) { reject(r); }}); })); }};/ / specification 2.3
function resolutionProcedure(promise2, x, resolve, reject) {
  // Spec 2.3.1, x cannot be the same as promise2, avoid circular references
  if (promise2 === x) {
    return reject(new TypeError("Error"));
  }
  / / specification 2.3.2
  // If x is a Promise and the state is pending, continue to wait or execute
  if (x instanceof MyPromise) {
    if (x.currentState === PENDING) {
      x.then(function (value) {
        // This function is called again to confirm x resolve
        // What type is the argument? If it is a basic type, resolve again
        // Pass the value to the next then
        resolutionProcedure(promise2, value, resolve, reject);
      }, reject);
    } else {
      x.then(resolve, reject);
    }
    return;
  }
  / / specification 2.3.3.3.3
  // reject or resolve if one executes, ignore the others
  let called = false;
  // Specification 2.3.3, determine whether x is an object or a function
  if(x ! = =null && (typeof x === "object" || typeof x === "function")) {
    Reject (reject); reject (reject)
    try {
      2.3.3.1 / / specification
      let then = x.then;
      // If then is a function, call x.teng
      if (typeof then === "function") {
        2.3.3.3 / / specification
        then.call(
          x,
          y => {
            if (called) return;
            called = true;
            / / specification 2.3.3.3.1
            resolutionProcedure(promise2, y, resolve, reject);
          },
          e => {
            if (called) return;
            called = true; reject(e); }); }else {
        2.3.3.4 / / specificationresolve(x); }}catch (e) {
      if (called) return;
      called = true; reject(e); }}else {
    // Specification 2.3.4, x is the basic typeresolve(x); }}Copy the code
  1. Added version
// Three states
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";

function MyPromise(fn) {
  let _this = this;
  _this.currentState = PENDING;
  _this.value = undefined;
  _this.resolvedCallbacks = [];
  _this.rejectedCallbacks = [];

  _this.resolve = function (value) {
    if (value instanceof MyPromise) {
      return value.then(_this.resolve, _this.reject)
    }
    setTimeout((a)= > {
      if (_this.currentState === PENDING) {
        _this.currentState = RESOLVED;
        _this.value = value;
        _this.resolvedCallbacks.forEach(cb= >cb()); }})}; _this.reject =function (reason) {
    setTimeout((a)= > {
      if (_this.currentState === PENDING) {
        _this.currentState = REJECTED;
        _this.value = reason;
        _this.rejectedCallbacks.forEach(cb= >cb()); }})}try {
    fn(_this.resolve, _this.reject);
  } catch (e) {
    _this.reject(e);
  }
}

MyPromise.prototype.then = function (onResolved, onRejected) {
  var self = this;
  var promise2;
  onResolved = typeof onResolved === 'function' ? onResolved : v= > v;
  onRejected = typeof onRejected === 'function' ? onRejected : r= > {throw r};

  if (self.currentState === RESOLVED) {
    return (promise2 = new MyPromise(function (resolve, reject) {
      setTimeout(function () {
        try {
          var x = onResolved(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(reason) { reject(reason); }}); })); }if (self.currentState === REJECTED) {
    return (promise2 = new MyPromise(function (resolve, reject) {
      setTimeout(function () {
        try {
          var x = onRejected(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(reason) { reject(reason); }}); })); }if (self.currentState === PENDING) {
    return (promise2 = new MyPromise(function (resolve, reject) {
      self.resolvedCallbacks.push(function () {
        try {
          var x = onResolved(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(r) { reject(r); }}); self.rejectedCallbacks.push(function () {
        try {
          var x = onRejected(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch(r) { reject(r); }}); })); }};/ / specification 2.3
function resolutionProcedure(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError("Error"));
  }
  if (x instanceof MyPromise) {
    if (x.currentState === PENDING) {
      x.then(function (value) {
        resolutionProcedure(promise2, value, resolve, reject);
      }, reject);
    } else {
      x.then(resolve, reject);
    }
    return;
  }
  let called = false;
  if(x ! = =null && (typeof x === "object" || typeof x === "function")) {
    try {
      let then = x.then;
      if (typeof then === "function") {
        then.call(
          x,
          y => {
            if (called) return;
            called = true;
            resolutionProcedure(promise2, y, resolve, reject);
          },
          e => {
            if (called) return;
            called = true; reject(e); }); }else{ resolve(x); }}catch (e) {
      if (called) return;
      called = true; reject(e); }}else{ resolve(x); }}/ / catch method
MyPromise.prototype.catch = function (rejectFn) {
  return this.then(undefined, rejectFn)
}

/ / finally
MyPromise.prototype.finally = function (callback) {
  return this.then(
    value= > MyPromise.resolve(callback()).then((a)= > value),
    reason => MyPromise.resolve(callback()).then((a)= > { throw reason })
  )
}

/* Static methods add */

/ / resolve method
MyPromise.resolve = function(val){
  return new MyPromise((resolve,reject) = >{
    resolve(val)
  });
}

/ / reject method
MyPromise.reject = function(val){
  return new MyPromise((resolve,reject) = >{
    reject(val)
  });
}

/ / race method
MyPromise.race = function(promises){
  return new MyPromise((resolve,reject) = >{
    for(let i=0; i<promises.length; i++){ promises[i].then(resolve, reject) }; })}//all (get all promises, execute then, place the results in an array, and return them together)
MyPromise.all = function(promises){
  let arr = [];
  let i = 0;

  function processData(index,data){
    arr[index] = data;
    i++;
    if(i == promises.length){
      resolve(arr);
    };
  };

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

Realize the EventBus

  1. The beggar version

The main functions are implemented, but the exception scenarios are not handled, and remove is not implemented

class EventEmitter {
  constructor () {
    // Store events
    this.events = this.events || new Map()}// Listen on events
  addListener (type, fn) {
    if (!this.events.get(type)) {
      this.events.set(type, fn)
    }
  }

  // Trigger the event
  emit (type) {
    let handle = this.events.get(type)
    handle.apply(this, [...arguments].slice(1))}}Copy the code
  1. premium
class EventEmitter{
  constructor() {if(this._events === undefined) {this._events = Object.create(null);// Define the event object
      this._eventsCount = 0; } } emit(type,... args){const events=this._events;
    const handler=events[type];
    // Determine whether the function executing the corresponding type is a function or an array
    if(typeof handler==='function') {Reflect.apply(handler,this,args);
    }else{
      const len=handler.length;
      for(var i=0; li<len; i++){Reflect.apply(handler[i],this,args); }}return true;
  }

  on(type,listener,prepend){
    var m;
    var events;
    var existing;
    events=this._events;
    // Add event
    if(events.newListener! = =undefined) {this.emit('namelessListener',type,listener);
       events=target._events;
    }
    existing=events[type];
    // Check whether the corresponding type method exists
    if(existing===undefined) {// If a method of the corresponding type does not exist, an event of the corresponding type is added
      existing=events[type]=listener;
      ++this._eventsCount;
    }else{
      // If there is a method of type, determine whether the method of type is an array or just a method
      // If only
      if(typeof existing==='function') {// If it is only a method, addexisting=events[type]=prepend? [listener,existing]:[existing,listener]; }else if(prepend){
        existing.unshift(listener);
      }else{ existing.push(listener); }}// chain call
    return this;
  }

  removeListener(type,listener){
    var list,events,position,i,originalListener;
    events=this._events;
    list=events[type];
    // If the corresponding event object's attribute value is a function, that is, the event is only listened for by a function
    if(list===listener){
      if(--this._eventsCount===0) {this._events=Object.create(null);
      }else{
        delete events[type];
        RemoveListener is triggered if there is a listener for the removeListener event
        if(events.removeListener)
          this.emit('removeListener',type,listener); }}else if(typeoflist! = ='function') {If the corresponding event object attribute value is an array of functions
      // Iterate through the array to find the position of the listener function in the array
      for(i=list.length- 1; i>=0; i--){if(list[i]===listener){
          position=i;
          break; }}// If this function is not found, the object is returned unchanged
      if(position){
        return this;
      }
      // If the first function in the array is the corresponding listener function, remove it
      if(position===0){
        list.shift();
      }else{
        list.splice(position,1);
      }
      if(list.length===1)
        events[type]=list[0];
      if(events.removeListener! = =undefined)
        this.emit('removeListener',type,listener);
    }
    return this; }}Copy the code

The last

  1. Please give a thumbs up if you find it helpful
  2. Welcome to pay attention to the public number “front-end advanced class” seriously learn the front end, step up together.