preface

Hi, I’m Wakawa. This is the third article in the learning the overall architecture of the source code series. Overall architecture this word seems to be a bit big, let’s say it is the overall structure of the source code, the main is to learn the overall structure of the code, do not go into other is not the main line of the specific function implementation. This article is about code packaged and integrated, not code broken down in the actual repository.

Learn the overall architecture of the source code series articles as follows:

1. Learn the overall architecture of jQuery source code, and create your own JS class library 2. Learn to use the whole framework of the source code to create their own functional programming library 3. Learning loDASH source code overall architecture, to create their own functional programming class library 4. Learn sentry source code overall architecture, build their own front-end exception monitoring SDK 5. Learn vuEX source code overall architecture, to create their own state management library 6. Learning axiOS source code overall architecture, to create their own request library 7. Learning koA source code overall architecture, a brief analysis of KOA onion model principle and CO principle 8. Learn redux source code overall architecture, in-depth understanding of Redux and its middleware principles

Interested readers can click to read.

Underscore source analysis has more articles than Lodash source analysis. One reason may be the number of lines of lodash source code. The comments add up to more than 10,000 lines.

There are few articles that analyze the overall structure of Lodash code. I did a search on Google, Bing, Github, etc., but I didn’t find it. So I decided to write one myself. Most people in general development use Lodash, and they all know, to some extent, that lodash performs better than underscore primarily because of the lazy evaluation feature that is used.

The version of Lodash studied in this article is V4.17.15. Unpkg.com address https://unpkg.com/[email protected]/lodash.js

The length of the article may be relatively long, which can be collected first and then read, so the author uses the form of expansion and contraction.

Takeaway:

RunInContext () export _ lodash function using baseCreate method prototype LodashWrapper and LazyWrapper, Lodash.prototype. value(wrapperValue) and lazy.prototype. value(lazyValue) are instantiated with examples.

Anonymous function execution

; (function() {

}.call(this));
Copy the code

Exposed lodash

var _ = runInContext();
Copy the code

RunInContext function

The simplified version of the source code here focuses only on function entry and return values.

var runInContext = (function runInContext(context) {
 // The browser processes context as window
 // ...
 function lodash(value) {} {  // ...
 return new LodashWrapper(value);  }  // ...  return lodash; }); Copy the code

You can see that a runInContext function is declared. There’s a lodash function in there, and the final processing returns this lodash function.

Look at the return value of new LodashWrapper(value) in the lodash function.

LodashWrapper function

function LodashWrapper(value, chainAll) {
 this.__wrapped__ = value;
 this.__actions__ = [];
 this.__chain__ = !! chainAll; this.__index__ = 0;
 this.__values__ = undefined; } Copy the code

Set these properties:

__wrapped__ : stores parameter value.

__actions__ : holds the function body func, the function argument args, and the function execution of this pointing to thisArg.

__chain__ and undefined are reversed to false Boolean values. Chained calls are not supported. As with underscore, chained calls are not supported by default.

__index__ : The default index value is 0.

__values__ : Used for primary Clone.

Then search down the source code, LodashWrapper, and you’ll find these two lines of code.

LodashWrapper.prototype = baseCreate(baseLodash.prototype);
LodashWrapper.prototype.constructor = LodashWrapper;
Copy the code

BaseCreate: baseLodash: baseLodash: baseCreate: baseLodash

BaseCreate Prototype inheritance

// Execute the anonymous function immediately
// Return a function that sets the prototype, which can be understood as __proto__
var baseCreate = (function() {
 BaseCreate: baseCreate: baseCreate: baseCreate: baseCreate: baseCreate: baseCreate: baseCreate
 // Underscore (underscore) declare an empty function 'Ctor'
 function object() {}  return function(proto) {  // Null if the argument passed is not object or function  // Returns an empty object.  if(! isObject(proto)) { return {};  }  // Return object.create if the object.create method is supported  if (objectCreate) {  // Object.create  return objectCreate(proto);  }  // If Object. Create is not supported, use ployFill New  object.prototype = proto;  var result = new object;  / / restore the prototype  object.prototype = undefined;  return result;  }; } ()); / / empty function function baseLodash() {  // No operation performed. }  // Ensure wrappers are instances of `baseLodash`. lodash.prototype = baseLodash.prototype; // Why is this sentence? Because at the lodash. Prototype. Construtor set to the Object. This sentence corrects Constructor lodash.prototype.constructor = lodash;  LodashWrapper.prototype = baseCreate(baseLodash.prototype); LodashWrapper.prototype.constructor = LodashWrapper; Copy the code

The author drew a diagram showing this relationship.

Lodash prototype diagram

The derived isObject function

Verify that typeof value is not null and is object or function.

function isObject(value) {
 var type = typeof value;
 returnvalue ! =null && (type == 'object' || type == 'function');
}
Copy the code

Object.create() Example usage

Interviewer: is it possible to simulate the implementation of the JS new operator?

Click to see an example of object.create () usage

As I mentioned in a previous article, you can look at all the API parsing of JavaScript objects

MDN Object.create()

The object.create (proto, [propertiesObject]) method creates a new Object, using an existing Object to provide the __proto__ of the newly created Object. It takes two arguments, but the second optional argument is the property descriptor (less commonly used; the default is undefined).

var anotherObject = {
    name: 'if the sichuan'
};
var myObject = Object.create(anotherObject, {
    age: {
Value:18. }, }); // Get a prototype of it Object.getPrototypeOf(anotherObject) === Object.prototype; // True indicates that the prototype of anotherObject is object.prototype Object.getPrototypeOf(myObject); // {name: "wakawa "} myObject.hasOwnProperty('name'); // false; Note that name is on the prototype. myObject.hasOwnProperty('age'); // True indicates that age is self-contained myObject.name; // 'rho' myObject.age; / / 18; Copy the code

For browsers that do not support ES5, the PloyFill scheme is provided on the MDN.

if (typeof Object.create ! = ="function") {
    Object.create = function (proto, propertiesObject) {
        if (typeofproto ! = ='object' && typeofproto ! = ='function') {
            throw new TypeError('Object prototype may only be an Object: ' + proto);
        } else if (proto === null) {
 throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");  }   if (typeofpropertiesObject ! ='undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");   function F() {}  F.prototype = proto;  return new F();  }; } Copy the code

There are many methods and properties on Lodash, but lodash.prototype also has many of the same methods as on Lodash. Definitely not a rewrite on lodash.prototype. It’s mounted through mixins.

mixin

Mixin Specific usage

_.mixin([object=lodash], source, [options={}])
Copy the code

Adds all enumerable function properties from the source object itself to the target object. If object is a function, then the function method is added to the prototype chain.

Note: Use _.runinContext to create the original lodash function to avoid conflicts caused by modification.

Add the version

0.1.0 from

parameter

[object = lodash] (Function | object) : the target object.

Source (Object): indicates the source Object.

[options={}] (Object): Option Object.

[Options. Chain =true] (Boolean): Whether to enable the chain operation.

return

(*) : returns object.

Mixins source

Click here to expand the source code for Mixin and parse the comments
function mixin(object, source, options) {
 var props = keys(source),
  methodNames = baseFunctions(source, props);

 if (options == null &&
! (isObject(source) && (methodNames.length || ! props.length))) { options = source;  source = object;  object = this;  methodNames = baseFunctions(source, keys(source));  }  varchain = ! (isObject(options) &&'chain' inoptions) || !! options.chain, isFunc = isFunction(object);   arrayEach(methodNames, function(methodName) {  var func = source[methodName];  object[methodName] = func;  if (isFunc) {  object.prototype[methodName] = function() {  var chainAll = this.__chain__;  if (chain || chainAll) {  var result = object(this.__wrapped__),  actions = result.__actions__ = copyArray(this.__actions__);   actions.push({ 'func': func, 'args': arguments.'thisArg': object });  result.__chain__ = chainAll;  return result;  }  return func.apply(object, arrayPush([this.value()], arguments));  };  }  });   return object; } Copy the code

In fact, when you look at the code that defines the function, you get a sense of what the function does. In order not to affect the main line, resulting in a long article. The specific source code is not expanded here.

Interested readers can see for themselves the source code for other functions derived from these functions.

Mixin derived function keys

In the mixin function, the Object. Keys is called

function keys(object) {
 return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
}
Copy the code

BaseFunctions derived from mixins

Returns a collection of function arrays

function baseFunctions(object, props) {
 return arrayFilter(props, function(key) {
  return isFunction(object[key]);
 });
}
Copy the code

Mixin derived function isFunction

Determine whether the parameter is a function

function isFunction(value) {
 if(! isObject(value)) {  return false;
 }
 // The use of `Object#toString` avoids issues with the `typeof` operator
 // in Safari 9 which returns 'object' for typed arrays and other constructors.  var tag = baseGetTag(value);  return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; } Copy the code

ArrayEach, a function derived from mixin

Similar to []. ForEarch

function arrayEach(array, iteratee) {
 var index = - 1.  length = array == null ? 0 : array.length;

 while (++index < length) {
 if (iteratee(array[index], index, array) === false) {  break;  }  }  return array; } Copy the code

ArrayPush function derived from mixin

Similar to []. Push

function arrayPush(array, values) {
 var index = - 1.  length = values.length,
  offset = array.length;

 while (++index < length) {  array[offset + index] = values[index];  }  return array; } Copy the code

Mixin derived function copyArray

Copy an array

function copyArray(source, array) {
 var index = - 1.  length = source.length;

 array || (array = Array(length));
 while (++index < length) {  array[index] = source[index];  }  return array; } Copy the code

Mixin source code parsing

The lodash source code calls mixin twice

// Add methods that return wrapped values in chain sequences.
lodash.after = after;
// code ... And 153 methods that support chained calls

// Add methods to `lodash.prototype`.
// Assign static methods on lodash to lodash.prototype mixin(lodash, lodash);  // Add methods that return unwrapped values in chain sequences. lodash.add = add; // code ... And 152 methods that do not support chained calls   // Let's filter after and add add to lodash.prototype. mixin(lodash, (function() {  var source = {};  // baseForOwn: iterates through the static methods on lodash and executes the callback function  baseForOwn(lodash, function(func, methodName) {  // Lodash.prototype is assigned to the first mixin call  // So here we use object.hasownProperty to exclude methods that are not on Lodash.prototype. That is 152 methods such as Add that do not support chained calls.  if(! hasOwnProperty.call(lodash.prototype, methodName)) { source[methodName] = func;  }  });  return source; // The last parameter, options, expressly states that chained calls are not supported , {} ())'chain': false }); Copy the code

Combining two calls to the mixin substitution into the source code is resolved as follows

Click here to expand the mixin source code and comments
function mixin(object, source, options) {
 // Enumerable properties in the source object
 var props = keys(source),
  // Array of method names in the source object
  methodNames = baseFunctions(source, props);
  if (options == null && ! (isObject(source) && (methodNames.length || ! props.length))) { // If options are not passed to undefined, undefined == null is true  // If source is not an object or not a function  // And the length of the function or property of the source object is not 0  // Set options to source  options = source;  // Assign source to object  source = object;  // Assign object to this which is _ (lodash)  object = this;  // Get the array of all method names  methodNames = baseFunctions(source, keys(source));  }  // Whether to support chained calls  // Options is not an object or a function, but null or some other value  // Check whether options is an object or a function. If not, 'chain' in options will not be executed  // And the chain is in the options object or prototype chain  / / knowledge in [MDN in: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/in  // The in operator returns true if the specified property is in the specified object or its prototype chain.   // Or options.chain to Boolean values  varchain = ! (isObject(options) &&'chain' inoptions) || !! options.chain, // Object is a function  isFunc = isFunction(object);   Loop through the array of method names  arrayEach(methodNames, function(methodName) {  // The function itself  var func = source[methodName];  // Object usually lodash is also assigned to this function.  object[methodName] = func;  if (isFunc) {  // If object is a function assigned to object Prototype, it is usually lodash  object.prototype[methodName] = function() {  // Whether the __chain__ attribute on the instance supports chained calls  // Where this is a new LodashWrapper instance like the following  / * * {  __actions__: [],  __chain__: true  __index__: 0  __values__: undefined  __wrapped__: []  } * * /   var chainAll = this.__chain__;  // Whether the chain property in options supports chained calls  // Both have a chaining call that executes the following code  if (chain || chainAll) {  // Usually lodash  var result = object(this.__wrapped__),  // Copy __action__ on the instance to result.__action__ and action  actions = result.__actions__ = copyArray(this.__actions__);   // Action adds the function and the args and this points, delaying the calculation of the call.  actions.push({ 'func': func, 'args': arguments.'thisArg': object });  // The __chain__ attribute on the instance is assigned to the __chain__ attribute of result  result.__chain__ = chainAll;  // Finally return the instance  return result;  }   // Neither supports chained calls. Direct call  // Call func with the value and arguments objects of the current instance. Returns the result of the call.  return func.apply(object, arrayPush([this.value()], arguments));  };  }  });   // Finally, return object object  return object; } Copy the code

Summary: In short, assign static methods on lodash to lodash.prototype. There are two ways to support chained calls (153 methods such as lodash.After) and two ways to not support chained calls (152 methods such as lodash.add).

How many methods and properties lodash has mounted in _ and _.prototype

Take a look at how many static methods and properties lodash has mounted on the _ function object and how many methods and properties lodash has mounted on the _.prototype.

Try it out using the for in loop. Look at this code:

var staticMethods = [];
var staticProperty = [];
for(var name in_) { if(typeof _[name] === 'function') {  staticMethods.push(name);
 }  else{  staticProperty.push(name);  } } console.log(staticProperty); // ["templateSettings", "VERSION"] 2 console.log(staticMethods); // ["after", "ary", "assign", "assignIn", "assignInWith", ...] 305 Copy the code

In fact, it is the above mentioned lodash.after and 153 functions that support chained calls, and lodash.add and 152 functions that do not support chained calls.

var prototypeMethods = [];
var prototypeProperty = [];
for(var name in _.prototype){
 if(typeof _.prototype[name] === 'function') {  prototypeMethods.push(name);
 }  else{  prototypeProperty.push(name);  } } console.log(prototypeProperty); / / [] console.log(prototypeMethods); // ["after", "all", "allKeys", "any", "assign", ...] 317 Copy the code

This is 12 more than the static method on Lodash, indicating that there are 12 other forms of assignment in addition to mixins.

Methods that support chained calls finally return the instance object to get the result value of the final processing, and then need to call the value method.

I have drawn a method and attribute mount diagram representing Lodash.

lodashMethod and property mount relationship

Give a simple example that runs through the rest of this article

var result = _.chain([1.2.3.4.5])
.map(el= > {
 console.log(el); / / 1, 2, 3
 return el + 1;
})
.take(3) .value(); // The 'map' here in lodash is executed only '3' times. // The function is simple: add one to the array 1-5, and get three of the values. console.log('result:', result); Copy the code

In other words, lodash is smart enough to know how many values are needed at the end, so it executes the map loop a few times, which is a great performance boost for large arrays. Underscore executes this code where map executes 5 times. If it is normal to implement this function is also simple.

var result = [1.2.3.4.5].map(el= > el + 1).slice(0.3);
console.log('result:', result);
Copy the code

Compared to lodash, where the map is executed five times.

// Do not use map, slice
var result = [];
var arr = [1.2.3.4.5];
for (var i = 0; i < 3; i++){
 result[i] = arr[i] + 1;
} console.log(result, 'result'); Copy the code

To simplify the map method here, add the LazyWrapper method to lodash.prototype’s store and call it at the end of the value call. See the following source code implementation.

addLazyWrapperMethods tolodash.prototype

The following methods are added to the lodash.prototype prototype.

// "constructor"
["drop"."dropRight"."take"."takeRight"."filter"."map"."takeWhile"."head"."last"."initial"."tail"."compact"."find"."findLast"."invokeMap"."reject"."slice"."takeRightWhile"."toArray"."clone"."reverse"."value"]
Copy the code
Click here to expand the specific source code and comments
// Add `LazyWrapper` methods to `lodash.prototype`.
Lazywrapper.prototype (); // baseForOwn ()
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
 // Check whether the function name is an iterator, i.e., a loop
 var checkIteratee = / ^ (? :filter|find|map|reject)|While$/.test(methodName),
 // Check whether the name of the function is head and last  // By the way () this is the capture group and add? : is a non-capture group, that is, not used for other operations  isTaker = / ^ (? :head|last)$/.test(methodName),  // lodashFunc is the isTaker combination takeRight take methodName  lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : ' ')) : methodName],  // Determine whether the result is packaged according to isTaker and is find  retUnwrapped = isTaker || /^find/.test(methodName);   // If this function does not exist, it is not executed  if(! lodashFunc) { return;  }  // Assign the lodash.prototype method to lodash.prototype  lodash.prototype[methodName] = function() {  __wrapped__ = [1,2,3,4,5]  var value = this.__wrapped__,  // isTaker returns [1] if head and last are methods, otherwise arguments object  args = isTaker ? [1] : arguments. // If value is an instance of LayeWrapper  isLazy = value instanceof LazyWrapper,  // Iterator loop  iteratee = args[0]. // Use useLazy isLazy value or an array  useLazy = isLazy || isArray(value);   var interceptor = function(value) {  // The function executes value args as an array parameter  var result = lodashFunc.apply(lodash, arrayPush([value], args));  // If head and last (isTaker) support chaining calls that return the first argument of the result; otherwise return result  return (isTaker && chainAll) ? result[0] : result;  };   // useLazy true and the function checkIteratee is a function and the number of iterator arguments is not equal to 1  if (useLazy && checkIteratee && typeof iteratee == 'function'&& iteratee.length ! =1) {  // Avoid lazy use if the iteratee has a "length" value other than `1`.  // useLazy is set to false  // Set isLazy to false  isLazy = useLazy = false;  }  // Take __chain__ on the instance  var chainAll = this.__chain__,  // The stored function __actions__ is a Boolean value that is equal to zero or greater than zero  isHybrid = !!this.__actions__.length,  // Unwrapped is the result unwrapped and does not support chained calls isUnwrapped = retUnwrapped && ! chainAll, // Whether only Lazy uses isLazy and stored functions onlyLazy = isLazy && ! isHybrid;  // The result is unwrapped and useLazy is true  if(! retUnwrapped && useLazy) { // Example new LazyWrapper where this is new LodashWrapper()  value = onlyLazy ? value : new LazyWrapper(this);  // result Specifies the result of the function  var result = func.apply(value, args);   / * *  // _.thru(value, interceptor) // This method is similar to _.tap, except that it returns the result of interceptor. The purpose of the method is to "pass" the value into a sequence of method chains to replace the intermediate result._ ([1, 2, 3]) .tap(function(array) { // Change the array passed in array.pop();  })  .reverse()  .value(); / / = > [2, 1)* /   // thisArg points to undefined or null. In non-strict mode, it points to window. In strict mode, it points to undefined or NLL  result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });  // Return instance lodashWrapper  return new LodashWrapper(result, chainAll);  }  // Unwrapped and onlyLazy is true  if (isUnwrapped && onlyLazy) {  // Execute the function  return func.apply(this, args);  }  // This is the end of the line  // Execute the thru function. The callback function is interceptor  result = this.thru(interceptor);  return isUnwrapped ? (isTaker ? result.value()[0] : result.value()) : result;  }; }); Copy the code

Lazywrapper. prototype overwrites lodash.prototype to determine if the function needs lazy evaluation and then calls it if it needs to.

The reader can debug the breakpoint, make good use of the breakpoint entry function, and look at the comments, it may be more clear.

Click to see partial screenshots of breakpoint debugging
A screenshot of the debugger after the example chain and Map execution
A screenshot of the example chain and map execution results

The chain call returns the instance object at the end, the actual function that handles the data is not called, it is stored, and the value method is called at the end, and these functions are executed.

WrapperValue lodash. Prototype. The value

function baseWrapperValue(value, actions) {
 var result = value;
 / / if it is lazyWrapper instance, call the lazyWrapper. Prototype. Value method, namely lazyValue method
 if (result instanceof LazyWrapper) {
  result = result.value();
 }  // Similar to [].reduce(), pass the result returned by the previous function as an argument to the next function  return arrayReduce(actions, function(result, action) {  return action.func.apply(action.thisArg, arrayPush([result], action.args));  }, result); } function wrapperValue() {  return baseWrapperValue(this.__wrapped__, this.__actions__); } lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue; Copy the code

If it is inert, is called LazyWrapper. The prototype. The value namely lazyValue.

LazyWrapper. Prototype. The value is lazyValue inert evaluated

Click here to expand the lazyValue source code and comments
function LazyWrapper(value) {
 / / parameter value
 this.__wrapped__ = value;
 // The function to execute
 this.__actions__ = [];
 this.__dir__ = 1;  / / filter  this.__filtered__ = false;  // Store iterator functions  this.__iteratees__ = [];  // The default maximum number of values  this.__takeCount__ = MAX_ARRAY_LENGTH;  // The number of specific values, store functions and types  this.__views__ = []; } / * ** Extracts the unwrapped value from its lazy wrapper. * * @private * @name value * @memberOf LazyWrapper * @returns {*} Returns the unwrapped value. * / function lazyValue() {  __wrapped__ is a new LodashWrapper instance so execute.value to get the original value  var array = this.__wrapped__.value(),  //  dir = this.__dir__,  // Whether it is a function  isArr = isArray(array),  // Whether to start from the right  isRight = dir < 0. // The length of the array. If it's not an array, it's 0  arrLength = isArr ? array.length : 0. Start: 0, end: 3  view = getView(0, arrLength, this.__views__),  start = view.start,  end = view.end,  The length of / / 3  length = end - start,  // Start from the right  index = isRight ? end : (start - 1),  // An array of stored iterators  iteratees = this.__iteratees__,  // The length of the iterator array  iterLength = iteratees.length,  / / resIndex results  resIndex = 0. // Finally get some values, which is 3  takeCount = nativeMin(length, this.__takeCount__);   // If it's not an array, or it's not starting from the right and the length of the argument array is equal to the length of the take, takeCount is equal to the length  // Call baseWrapperValue directly  if(! isArr || (! isRight && arrLength == length && takeCount == length)) { return baseWrapperValue(array, this.__actions__);  }  var result = [];   // Label statement  // MDN label link  // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/label  // Tag statements can be used with break or continue statements. A tag is a statement preceded by an identifier that can be referenced.  outer:  while (length-- && resIndex < takeCount) {  index += dir;   var iterIndex = - 1. // The first item in the array  value = array[index];   while (++iterIndex < iterLength) {  {iteratee: function{}, typy: 2}  var data = iteratees[iterIndex],  iteratee = data.iteratee,  type = data.type,  // The result iterator executes the result  computed = iteratee(value);   if (type == LAZY_MAP_FLAG) {  // If type is map, computed is assigned to value  value = computed;  } else if(! computed) { if (type == LAZY_FILTER_FLAG) {  // Exit the current loop and proceed to the next loop  continue outer;  } else {  // Exit the entire loop  break outer;  }  }  }  // The final array  result[resIndex++] = value;  }  // Return an array like [2, 3, 4]  return result; } // Ensure `LazyWrapper` is an instance of `baseLodash`. LazyWrapper.prototype = baseCreate(baseLodash.prototype); LazyWrapper.prototype.constructor = LazyWrapper;  LazyWrapper.prototype.value = lazyValue; Copy the code

The author drew a diagram of the relationship between Lodash and LazyWrapper.

lodashand
LazyWrapperThe diagram of

Summary: The function of lazyValue is to execute the previous record several times and the record storage function several times. It will not execute many times according to the number of items, but according to the need to execute several items. In this example, the map function is executed only three times. If lazy evaluation is not used, the map function is executed five times.

var result = _.chain([1.2.3.4.5])
.map(el= > el + 1)
.take(3)
.value();
Copy the code

conclusion

At this point, basically close to the end, the last summary.

RunInContext () export _ lodash function using baseCreate method prototype LodashWrapper and LazyWrapper, Lodash.prototype. value(wrapperValue) and lazy.prototype. value(lazyValue) are instantiated with examples.

Use Ctrl + P to locate the location of a function declaration. Enter @functionname to locate the exact location of the functionName in the source file. If you know where to call, press Alt + left mouse button to jump to the position of the function declaration.

If the reader finds something wrong or can be improved, or if there is anything that is not clear, feel free to comment. In addition, I think it is well written, which is of some help to you. I can like it, comment on it and retweet it, which is also a kind of support for the author. Thanks a million.

Recommended reading

Lodash warehouses | | lodash official document lodash Chinese document to build a similar lodash front-end tool library inert interpretation evaluation – lodash source luobo tang: Lazy. js lazy evaluation implementations analyze the lazy.js Github repository for this article’s version of Lodash v4.17.15 unpkg.com link

The author’s previous article

Q: Can you simulate the JS call and apply method? Q: Can you simulate the JS bind method? Can simulate the implementation of the JS new operator front end using puppeteer crawler to generate the React. JS small book PDF and merge

about

Author: often with the name of Ruochuan mixed in rivers and lakes. The front road lovers | | PPT know little, only good study. Personal blog – if the use of vuepress reconstruction, reading experience may be better dig gold column, welcome to pay attention to ~ segmentfault front view column, welcome to pay attention to ~ zhihu front view column, welcome to pay attention to ~ github blog, related source code and resources are put here, seek a star^_^~

Welcome to add wechat communication wechat public account

May be more interesting wechat public number, long press scan code concern. Welcome to add the author’s wechat ruochuan12 (specify the source, basic users do not refuse), pull you into [front view communication group], long-term communication and learning ~

If sichuan vision

This article was typeset using MDNICE