Learn the overall structure of lodash source code and build its own functional programming class library

Preface

This is the third in a series on learning the overall architecture of source code. The term "overall architecture" seems to be a bit big, even if it is the overall structure of the source code, the main thing is to learn the overall structure of the code, not to delve into the realization of other specific functions that are not the main line. This article is about packaged and integrated code, not split code in the actual warehouse.

The last article wrote about the overall architecture of jQuery source code. Learn the overall architecture of underscore source code and build its own functional programming class library

The last article wrote about the overall architecture of underscore source code. Learn the overall architecture of jQuery source code and build your own js class library

Interested readers can click to read.

There are many articles about underscore source code analysis, but few articles about lodash source code analysis. One reason may be that there are too many lines of lodash source code. The notes add up to more than 10,000 lines.

There are few articles analyzing the overall code structure of lodash. The author has not found any articles using Google, Bing, github and other searches, which may be the wrong way to find them. So I planned to write one by myself. Most developers usually use lodash, and they know more or less that lodash has better performance than underscore. The main reason for its good performance is the use of inert evaluation.

The version of lodash studied in this article is v4.17.15. unpkg.com address https://unpkg.com/lodash@4.17...

The length of the article may be relatively long, you can first collect and then read, so the author used the form of expansion and contraction.

Introduction:

This paper mainly studies the source code implementation of the runInContext() export lodash function, which inherits LodashWrapper and LazyWrapper using baseCreate method prototype, mixin mounting method to lodash.prototype, and later explains lodash.prototype.value(wrapperValue) and Lazy.prototype.value(lazyValue) inert evaluation with examples.

Anonymous function execution

;(function() {

}.call(this));

Exposure to lodash

var _ = runInContext();

runInContext function

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

var runInContext = (function runInContext(context) {
    // Processing context as window in browser
    // ...
    function lodash(value) {}{
        // ...
        return new LodashWrapper(value);
    }
    // ...
    return lodash;
});

You can see that a runInContext function is declared. There is a lodash function in it, and the final processing returns the lodash function.

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

LodashWrapper function

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

These properties are set:

_ wrapped_: Store parameter value.

_ Action_: Store the function body func to be executed, function parameter args, function execution this points to this Arg.

_ Chain_ and undefined are inverted to Boolean value false twice, and chain invocation is not supported. Like underscore, chain invocation is not supported by default.

_ index_: index value defaults to 0.

_ values_: Used mainly for clone.

Next, search for the source code, LodashWrapper.
You will find these two lines of code.

LodashWrapper.prototype = baseCreate(baseLodash.prototype);
LodashWrapper.prototype.constructor = LodashWrapper;

Next, look up for the baseCreate and baseLodash functions.

baseCreate prototype inheritance

//  Execute anonymous functions immediately
// Returns a function to set the prototype, which can be understood as _proto__
var baseCreate = (function() {
    // This sentence is placed outside the function so that the object is not repeated every time the baseCreate is called.
    // In underscore source code, putting this sentence at the beginning declares an empty function `Ctor'.`
    function object() {}
    return function(proto) {
        // If the incoming parameter is neither object nor function is null
        // Returns an empty object.
        if (!isObject(proto)) {
            return {};
        }
        // If the Object.create method is supported, Object.create is returned.
        if (objectCreate) {
            // Object.create
            return objectCreate(proto);
        }
        // If Object.create is not supported, ployfill new
        object.prototype = proto;
        var result = new object;
        // Restore 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 there such a sentence? Because the last sentence set lodash.prototype.construtor to Object. This sentence amends constructor
lodash.prototype.constructor = lodash;

LodashWrapper.prototype = baseCreate(baseLodash.prototype);
LodashWrapper.prototype.constructor = LodashWrapper;

The author draws a picture to show this relationship.

Derived isObject functions

Judge that typeof value is not equal to null, and is an object or function.

function isObject(value) {
    var type = typeof value;
    return value != null && (type == 'object' || type == 'function');
}

Examples of Object.create() usage

Interviewer asked: Can you simulate the new operator that implements JS? This article has written a paragraph before, so it's contracted here.
The author has also talked about it in a previous article, you can read it. All API parsing of JavaScript objects

MDN Object.create()

Object.create(proto, [propertiesObject])
Method creates a new object, using the existing object to provide the _proto_ of the newly created object.
It takes two parameters, but the second optional parameter is the attribute descriptor (not commonly used, undefined by default).

var anotherObject = {
    name: 'If Chuan'
};
var myObject = Object.create(anotherObject, {
    age: {
        value: 18,
    },
});
// Get its prototype
Object.getPrototypeOf(anotherObject) === Object.prototype; // true indicates that the prototype of anotherObject is Object.prototype
Object.getPrototypeOf(myObject); // {name: "Ruochuan"}// Explains that the prototype of myObject is {name: "Ruochuan"}
myObject.hasOwnProperty('name'); // false; indicates that name is prototype.
myObject.hasOwnProperty('age'); // true indicates that age is its own
myObject.name; // 'Ruochuan'
myObject.age; // 18;

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

if (typeof Object.create !== "function") {
    Object.create = function (proto, propertiesObject) {
        if (typeof proto !== 'object' && typeof proto !== '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 (typeof propertiesObject != '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();
    };
}

Lodash has many methods and attributes, but lodash.prototype has many of the same methods as lodash. It's certainly not a rewrite on lodash.prototype. It's mounted through mixin.

mixin

Specific usage of mixin

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

Add all enumerable function attributes of the source object itself to the target object. If object is a function, then function methods will be added to the prototype chain.

Note: Use. runInContext to create the original lodash function to avoid conflicts caused by modifications.

Added version

0.1.0

parameter

[object=lodash] (Function|Object): Target object.

source (Object): Source object.

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

[options.chain=true] (boolean): Whether to open the chain operation.

Return

(*): Returns object.

mixin source code

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));
    }
    var chain = !(isObject(options) && 'chain' in options) || !!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;
}

Next, let's look at the derived functions.

In fact, you probably know the function of this function when you see the function code defined in detail. In order not to affect the main line, the article length is too long. Specific source code does not expand here.

Interested readers can look at the source code of other functions derived from these functions.

Function keys derived from mixin

In fact, the final call in the mixin function is Object.keys.

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

baseFunctions Derived from Mixing

Return function array set

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

The function isFunction derived from mixin

Judging whether a 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;
}

ArayEach 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;
}

ArayPush, a 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;
}

The function copyArray derived from mixin

Copy array

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

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

mixin source code parsing

mixin is called twice in lodash source code

// Add methods that return wrapped values in chain sequences.
lodash.after = after;
// 153 methods supporting chain invocation

// 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... etc. 152 methods that do not support chain invocation


// In fact, this is to filter after and other methods to support chain invocation, get adds on lodash and add them to lodash.prototype.
mixin(lodash, (function() {
    var source = {};
    // baseForOwn here is actually a static method that traverses the lodash to execute callback functions
    baseForOwn(lodash, function(func, methodName) {
        // The first mixin calls, so it's assigned to lodash.prototype
        // So we use Object.hasOwnProperty to exclude methods that are not on lodash.prototype. That is to say, add s and other 152 methods do not support chain invocation.
        if (!hasOwnProperty.call(lodash.prototype, methodName)) {
            source[methodName] = func;
        }
    });
    return source;
// The last parameter options specifically indicates that chain calls are not supported
}()), { 'chain': false });

Combining two calls to mixin substitution to source code parses as follows

function mixin(object, source, options) {
    // Enumerable properties in source objects
    var props = keys(source),
        // Array of method names in source objects
        methodNames = baseFunctions(source, props);

    if (options == null &&
        !(isObject(source) && (methodNames.length || !props.length))) {
        // If options are not passed as undefined undefined == null is true
        // And if source is not an object or a function
        // And the function function length of the source object or the attribute length of the source object is not zero.
        // Assign options to source
        options = source;
        // Assign source to object
        source = object;
        // Assigning object to this is (lodash)
        object = this;
        // Gets an array of all method names
        methodNames = baseFunctions(source, keys(source));
    }
    // Whether Chain Call is Supported
    // options are not objects or functions, null or other values
    // Determine whether options are objects or functions, and if they are not, they will not execute'chain'in options and will not report errors.
    //  And chain is in the object or prototype chain of options
    // Knowledge Points in [MDN in: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/in]
    // If the specified property is in the specified object or its prototype chain, the in operator returns true.

    // Or options.chain to Boolean
    var chain = !(isObject(options) && 'chain' in options) || !!options.chain,
        // object is a function
        isFunc = isFunction(object);

    // Loop method name array
    arrayEach(methodNames, function(methodName) {
        // Function itself
        var func = source[methodName];
        // Objects are usually lodash assigns this function as well.
        object[methodName] = func;
        if (isFunc) {
            // If the object is a function assigned to the object prototype, it is usually lodash
            object.prototype[methodName] = function() {
                // Does the _chain_ attribute on the instance support chain invocation
                // Here this is a new LodashWrapper example similar to the following
                /**
                 {
                    __actions__: [],
                    __chain__: true
                    __index__: 0
                    __values__: undefined
                    __wrapped__: []
                 }
                 **/

                var chainAll = this.__chain__;
                // Does the chain attribute in options support chain invocation
                // Both have a chain-like call to execute the following code
                if (chain || chainAll) {
                    // Usually lodash
                    var result = object(this.__wrapped__),
                    // Replicate _action_ on the instance to result. _action_ and action
                    actions = result.__actions__ = copyArray(this.__actions__);

                    // action adds functions and args and this pointers, delaying computation calls.
                    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 to this instance.
                    return result;
                }

                // Chain calls are not supported. Direct call
                // Pass the value and arguments objects of the current instance to the func function as parameter calls. Returns the result of the call.
                return func.apply(object, arrayPush([this.value()], arguments));
            };
        }
    });

    // Finally, the object is returned
    return object;
}

Summary: Simply put, the static method on lodash is assigned to lodash.prototype. The first is to support chain invocation (153 methods supporting chain invocation such as lodash.after), and the second is not to support chain invocation (152 methods such as lodash.add do not support chain invocation).

How many methods and attributes are mounted by lodash in and. prototype

Let's see how many static methods and attributes lodash mounts on function objects and how many methods and attributes lodash mounts on. prototype.

Use the for in loop to try it out. Look at the following code:

var staticMethods = [];
var staticProperty = [];
for(var name in _){
    if(typeof _[name] === 'function'){
        staticMethods.push(name);
    }
    else{
        staticProperty.push(name);
    }
}
console.log(staticProperty); // ["template settings", "VERSION"] 2
console.log(staticMethods); // ["After", "ary", "assign", "assign In", "assign In With",...] 305

In fact, 153 functions such as lodash.after mentioned above, 152 functions such as lodash.add and so on, do not support the assignment of functions called by chain.

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,""all Keys,""any,""assign,...] 317

Compared with the static method on lodash, there are 12 other forms of assignment besides mixin.

The method supporting chain invocation finally returns to the instance object, gets the result value of the final processing, and finally calls the value method.

The author draws a method and attribute mounting diagram for lodash.

Please give a simple example that runs through the following

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 has been executed only `3' times.
// The specific function is also simple array 1-5 plus one, and finally get three values.
console.log('result:', result);

That is to say, lodash knows smartly that the last few values are needed, and then executes several map loops, which is very helpful for large arrays and improves performance.

Uncore executes this code, where map executes five times.
It's also easy to implement this function if it's normal.

var result = [1, 2, 3, 4, 5].map(el => el + 1).slice(0, 3);
console.log('result:', result);

In contrast to lodash, the map was 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');

Simply put the map method here, add LazyWrapper method to lodash.prototype to store, and then call value when the last call.
See the source code implementation below.

Add Lazy Wrapper method to lodash.prototype

The main method is to add it to the prototype of lodash.prototype.

// "constructor"
["drop", "dropRight", "take", "takeRight", "filter", "map", "takeWhile", "head", "last", "initial", "tail", "compact", "find", "findLast", "invokeMap", "reject", "slice", "takeRightWhile", "toArray", "clone", "reverse", "value"]
// Add `LazyWrapper` methods to `lodash.prototype`.
// baseForOwn here is actually a way to traverse LazyWrapper.prototype and execute callback functions
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
    // Check whether the function name is an iterator or a loop
    var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
        // Check whether the function name is head and last
        // By the way () this is a captured packet plus?: a non-captured packet, that is, it is not used for other operations.
        isTaker = /^(?:head|last)$/.test(methodName),
        // lodashFunc is based on isTaker combination takeRight take methodName
        lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
        // Determine whether the result is packaged based on isTaker sum and find
        retUnwrapped = isTaker || /^find/.test(methodName);

    // If this function does not exist, it will not be executed down.
    if (!lodashFunc) {
        return;
    }
    // Assign the lodash.prototype method to lodash.prototype
    lodash.prototype[methodName] = function() {
        // In the case of _wrapped_ value in the example, [1,2,3,4,5]
        var value = this.__wrapped__,
            // If the head and last methods isTaker returns [1], otherwise they are arguments objects
            args = isTaker ? [1] : arguments,
            // If value is an example of LayeWrapper
            isLazy = value instanceof LazyWrapper,
            // Iterator loop
            iteratee = args[0],
            // Use useLazy isLazy value or array
            useLazy = isLazy || isArray(value);

        var interceptor = function(value) {
            // Function execution value args combined into array parameters
            var result = lodashFunc.apply(lodash, arrayPush([value], args));
            // If head and last (isTaker) support the first parameter of the chain call returning the result, otherwise return the result
            return (isTaker && chainAll) ? result[0] : result;
        };

        // useLazy true and function checkIteratee and the iterator is a function, and the number of iterator parameters 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 assigns false
            // isLazy assignment is false
            isLazy = useLazy = false;
        }
        // Take the _chain on the instance__
        var chainAll = this.__chain__,
            // Stored function _actions quadratic inversion is Boolean value which is equal to or greater than 0.
            isHybrid = !!this.__actions__.length,
            // Whether unpackaged results are unpackaged and do not support chain calls
            isUnwrapped = retUnwrapped && !chainAll,
            // Whether only Lazy uses isLazy and stored functions
            onlyLazy = isLazy && !isHybrid;

        // The result is unpackaged and useLazy is true
        if (!retUnwrapped && useLazy) {
            // Instance new Lazy Wrapper where this is new LodashWrapper()
            value = onlyLazy ? value : new LazyWrapper(this);
            // result Execution Function result
            var result = func.apply(value, args);

            /*
            *
            // _.thru(value, interceptor)
            // This method is similar to. tap except that it returns the result of interceptor. The aim of this method is to "pass" the value to a method chain sequence to replace the intermediate result.
            _([1, 2, 3])
            .tap(function(array) {
                // Changing the incoming array
                array.pop();
            })
            .reverse()
            .value();
            // => [2, 1]
            */

            // This Arg pointing to undefined or null is pointing to window in non-strict mode and undefined or nll in strict mode.
            result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
            // Return instance lodashWrapper
            return new LodashWrapper(result, chainAll);
        }
        // Unpackaged and onlyLazy is true
        if (isUnwrapped && onlyLazy) {
            // Execution function
            return func.apply(this, args);
        }
        // None of the above has been executed. It's here.
        // Execute thru function, callback function is interceptor
        result = this.thru(interceptor);
        return isUnwrapped ? (isTaker ? result.value()[0] : result.value()) : result;
    };
});

To sum up, I have written so many comments, simply say: in fact, I use LazyWrapper.prototype to rewrite the original function in lodash.prototype, to determine whether the function needs to use inert evaluation, and then call it when needed.

Readers can debug breakpoints, make good use of breakpoints to enter function functions, and look at comments, it may be more clear.

Chain calls end up returning instance objects. The actual functions that process data are not called, but stored. Finally, the value method is called to execute these functions.

lodash.prototype.value is wrapperValue

function baseWrapperValue(value, actions) {
    var result = value;
    // If it is an instance of lazyWrapper, the LazyWrapper.prototype.value method, or lazyValue method, is called.
    if (result instanceof LazyWrapper) {
        result = result.value();
    }
    // Similar to []. reduce(), the result returned by the previous function is passed as a parameter 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;

If it is lazy evaluation, it calls LazyWrapper.prototype.value, or lazyValue.

LazyWrapper.prototype.value is lazyValue inert evaluation

function LazyWrapper(value) {
    // Parameter value
    this.__wrapped__ = value;
    // Executing functions
    this.__actions__ = [];
    this.__dir__ = 1;
    // filter
    this.__filtered__ = false;
    // Storage iterator function
    this.__iteratees__ = [];
    // Default maximum number of values
    this.__takeCount__ = MAX_ARRAY_LENGTH;
    // How many specific values, storage 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() {
    // This. _wrapped_ is a new LodashWrapper instance, so execute. value to get the original value
    var array = this.__wrapped__.value(),
        //
        dir = this.__dir__,
        // Is it a function?
        isArr = isArray(array),
        // Do you start on the right?
        isRight = dir < 0,
        // The length of the array. If it's not an array, it's zero.
        arrLength = isArr ? array.length : 0,
        // Get take(3). In the above example, start: 0, end: 3
        view = getView(0, arrLength, this.__views__),
        start = view.start,
        end = view.end,
        // Length 3
        length = end - start,
        // If it starts from the right
        index = isRight ? end : (start - 1),
        // Stored iterator array
        iteratees = this.__iteratees__,
        // Iterator array length
        iterLength = iteratees.length,
        // Result resIndex
        resIndex = 0,
        // Finally, get a few values, that is, 3
        takeCount = nativeMin(length, this.__takeCount__);

    // If it's not an array, or if it's not starting from the right and the length of the parameter array is equal to the length of take, takeCount is equal to the length.
    // It is not necessary to call baseWrapperValue directly
    if (!isArr || (!isRight && arrLength == length && takeCount == length)) {
        return baseWrapperValue(array, this.__actions__);
    }
    var result = [];

    // Label statement label
    // MDN label link
    // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/label
    // Markup statements can be used with break or continue statements. A tag is an identifier that can be referenced before a statement.
    outer:
    while (length-- && resIndex < takeCount) {
        index += dir;

        var iterIndex = -1,
            // First item of array
            value = array[index];

        while (++iterIndex < iterLength) {
            // Iterator array {iteratee: function {}, type: 2}
            var data = iteratees[iterIndex],
                iteratee = data.iteratee,
                type = data.type,
                // Result Iterator Execution Result
                computed = iteratee(value);

            if (type == LAZY_MAP_FLAG) {
                // If type is map type, the result computed is assigned to value
                value = computed;
            } else if (!computed) {
                if (type == LAZY_FILTER_FLAG) {
                    // Exit the current cycle and proceed to the next one
                    continue outer;
                } else {
                    // Exit the whole cycle
                    break outer;
                }
            }
        }
        // Final array
        result[resIndex++] = value;
    }
    // In the example of returning arrays, [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;

The author draws a graph of the relationship between lodash and Lazy Wrapper.

Summary: lazyValue simply says that the function is to execute the previous record several times, and the function stored in the record several times, not how many times the data will be executed, but according to the needs of several items, several items will be executed.
That is to say, in the following example, the map function will only execute three times. If no evaluation is done with inertia, the map function executes five times.

var result = _.chain([1, 2, 3, 4, 5])
.map(el => el + 1)
.take(3)
.value();

summary

So far, the text is basically close to the end, and finally sum up.

This paper mainly studies the source code implementation of the runInContext() export lodash function, which inherits LodashWrapper and LazyWrapper using baseCreate method prototype, mixin mounting method to lodash.prototype, and later explains lodash.prototype.value(wrapperValue) and Lazy.prototype.value(lazyValue) inert evaluation with examples.

Share a VSCode trick: Ctrl + p, which only knows the name of the function to find the location of the source location function declaration. Enter the @functionName location function, the specific location of functionName in the source file. If you know the location of the call, you can jump to the position declared by the function by pressing alt + left mouse button directly.

Readers are welcome to comment on any inadequacies or improvements they find, or where they do not write clearly. In addition, I think it's well written. It's a little helpful for you. It can be praised, commented, forwarded and shared. It's also a kind of support for the author. Thank you very much.

Recommended reading

lodash github warehouse
lodash official document
lodash Chinese Documents
Create a front-end tool library similar to lodash
Inert Evaluation-lodash Source Code Interpretation
Implementation analysis of luobo tang: lazy.js inert evaluation
lazy.js github warehouse
The link to version v4.17.15 unpkg.com of lodash studied in this article

The author's previous articles

Learn the overall architecture of underscore source code and build its own functional programming class library
Learn the overall architecture of jQuery source code and build your own js class library
Interviewer asked: JS inheritance
Interviewer asked: JS points to this
Interviewer asked: Can you simulate the call and apply method of JS?
Interviewer asked: Can we simulate the bind method of JS implementation?
Interviewer asked: Can you simulate the new operator that implements JS?
The front end uses the puppeteer crawler to generate the PDF of React.js Books and merge them.

about

Author: Often in the name of Ruochuan mixed in rivers and lakes. On the front road | PPT enthusiasts | know little, only good at learning.
Personal Blog - Ruochuan With vuepress refactoring, the reading experience may be better
Nuggets column Welcome your attention~
segmentfault Front-end Vision Column Welcome your attention~
Knowing Front-end Vision Column Welcome your attention~
github blog The relevant source code and resources are all put here. Find a star.^^~

Wechat Public Signal Ruochuan Vision

Possibly more interesting Wechat Public Number, long-term scanning attention. You can also add Weixin lxchuan12, indicating the source, pulling you into the front-end vision communication group.

Tags: Javascript Attribute github JQuery

Posted on Tue, 10 Sep 2019 03:16:41 -0700 by dannyluked