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.
- with
.
implementation
constcopy = {... {x:1}}
Copy the code
- with
Object.assign
implementation
const copy = Object.assign({}, {x:1})
Copy the code
- with
slice
implementation
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.
- 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
- 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
- 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()
- Deep copy reoptimization
- Deep copy add
Map
和Set
Related, of course you can add moreDate
And so on - use
WeakMap
Instead 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
- 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.
- The beggar version
Function.prototype.myapply = function (context, ... preArgs) {
return (. args) = > this.call(context, ... preArgs, ... args) }Copy the code
- 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.
- 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
- 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.
- 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
- Modified version: Reference
lodash
Classic 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
- 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
- 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:
- A brand new object is created.
- The [[Prototype]] (A.K.A. __proto__) link is executed.
- Make this point to the newly created object.
- Each object created by 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), 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.
- 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
- 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
- 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
- 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
- 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
- Please give a thumbs up if you find it helpful
- Welcome to pay attention to the public number “front-end advanced class” seriously learn the front end, step up together.