предисловие
Здравствуйте, яВакагава. Это
学习源码整体架构系列Третья статья. Слово «общая архитектура» кажется немного большим, скажем так, это общая структура исходного кода. В этой статье рассказывается об упакованном и интегрированном коде, а не о разделенном коде на реальном складе.
学习源码整体架构系列Статья выглядит следующим образом:
1.Изучите общую архитектуру исходного кода jQuery и создайте собственную библиотеку js.
2.Изучите общую архитектуру исходного кода подчеркивания и создайте собственную библиотеку классов функционального программирования.
3.Изучите общую архитектуру исходного кода lodash и создайте собственную библиотеку классов функционального программирования.
4.Изучите общую архитектуру исходного кода sentry и создайте собственный SDK для мониторинга исключений переднего плана.
5.Изучите общую архитектуру исходного кода vuex и создайте собственную библиотеку управления состоянием.
6.Изучите общую архитектуру исходного кода axios и создайте собственную библиотеку запросов.
7.Изучите общую архитектуру исходного кода koa, проанализируйте принцип луковой модели koa и принцип совместной работы.
8.Изучите общую архитектуру исходного кода Redux и глубоко поймите принципы Redux и его промежуточного программного обеспечения.
Заинтересованные читатели могут нажать, чтобы прочитать.
underscoreСуществует множество статей по анализу исходного кода, иlodashСтатей по анализу исходного кода сравнительно немного. Одна из причин может заключаться вlodashСлишком много исходных строк. Комментарии составляют более 10 000 строк.
анализироватьlodashСтатей по общей структуре кода относительно немного, автор использует Google, Bing,githubЯ не могу найти его после поиска, может быть, я ищу его неправильно. Поэтому я решил написать один сам. Большинство людей будут использовать его в нормальном развитииlodash, и все знают более-менее,lodashСравниватьunderscoreХорошая производительность, основной причиной хорошей производительности является использование функции отложенной оценки.
узнал из этой статьиlodashВерсия:v4.17.15.unpkg.comАдрес https://unpkg.com/lodash@4.17.15/lodash.js
Длина статьи может быть относительно большой, ее можно сначала собрать, а потом прочитать, поэтому автор использует форму расширения и сокращения.
Гид:
Статья в основном учит
runInContext()экспорт_lodashиспользование функцииbaseCreateметод прототипного наследованияLodashWrapperа такжеLazyWrapper,mixinметод монтирования наlodash.prototypeПосле объяснения в сочетании с примерамиlodash.prototype.value(wrapperValue)а такжеLazy.prototype.value(lazyValue)Исходный код реализации ленивых вычислений.
Анонимное выполнение функции
;(function() {
}.call(this));
разоблачение лодаша
var _ = runInContext();
функция runInContext
Упрощенная версия исходного кода здесь фокусируется только на записи функции и возвращаемом значении.
var runInContext = (function runInContext(context) {
// 浏览器中处理context为window
// ...
function lodash(value) {}{
// ...
return new LodashWrapper(value);
}
// ...
return lodash;
});
Видно, что декларацияrunInContextфункция. Eстьlodashфункция, окончательная обработка возвращает этоlodashфункция.
посмотри сноваlodashвозвращаемое значение в функцииnew LodashWrapper(value).
Функция LodashWrapper
function LodashWrapper(value, chainAll) {
this.__wrapped__ = value;
this.__actions__ = [];
this.__chain__ = !!chainAll;
this.__index__ = 0;
this.__values__ = undefined;
}
Эти свойства устанавливаются:
__wrapped__: сохранить параметрыvalue.
__actions__: Сохраняет тело функции, которая должна быть выполнена.func, параметр функцииargs, функция выполняетсяthisнаправлениеthisArg.
__chain__,undefinedДважды инвертировать в логическое значениеfalse, связанные вызовы не поддерживаются. а такжеunderscoreАналогично, связанные вызовы по умолчанию не поддерживаются.
__index__: значение индекса по умолчанию равно 0.
__values__:основнойcloneпри использовании.
Затем найдите исходный код,LodashWrapper,
Вы найдете эти две строки кода.
LodashWrapper.prototype = baseCreate(baseLodash.prototype);
LodashWrapper.prototype.constructor = LodashWrapper;
Тогда посмотрите вверхbaseCreate、baseLodashэти две функции.
baseCreate прототипное наследование
// 立即执行匿名函数
// 返回一个函数,用于设置原型 可以理解为是 __proto__
var baseCreate = (function() {
// 这句放在函数外,是为了不用每次调用baseCreate都重复申明 object
// underscore 源码中,把这句放在开头就申明了一个空函数 `Ctor`
function object() {}
return function(proto) {
// 如果传入的参数不是object也不是function 是null
// 则返回空对象。
if (!isObject(proto)) {
return {};
}
// 如果支持Object.create方法,则返回 Object.create
if (objectCreate) {
// Object.create
return objectCreate(proto);
}
// 如果不支持Object.create 用 ployfill new
object.prototype = proto;
var result = new object;
// 还原 prototype
object.prototype = undefined;
return result;
};
}());
// 空函数
function baseLodash() {
// No operation performed.
}
// Ensure wrappers are instances of `baseLodash`.
lodash.prototype = baseLodash.prototype;
// 为什么会有这一句?因为上一句把lodash.prototype.construtor 设置为Object了。这一句修正constructor
lodash.prototype.constructor = lodash;
LodashWrapper.prototype = baseCreate(baseLodash.prototype);
LodashWrapper.prototype.constructor = LodashWrapper;
Автор нарисовал картину, чтобы показать эту связь.
Производная функция isObject
судитьtypeof valueне равноnull, и являетсяobjectилиfunction.
function isObject(value) {
var type = typeof value;
return value != null && (type == 'object' || type == 'function');
}
Пример использования Object.create()
Интервьюер спросил: Могу ли я смоделировать новый оператор, реализующий JS?Абзац написан в предыдущей статье, поэтому здесь он сокращен.
Нажмите, чтобы увидеть пример использования Object.create()
Я также упомянул об этом в статье, которую я составил ранее, вы можете прочитать ееВесь API-анализ объектов JavaScript
Object.create(proto, [propertiesObject])метод создает новый объект, используя существующий объект для предоставления __proto__ вновь созданного объекта.
Он принимает два параметра, но вторым необязательным параметром является дескриптор атрибута (обычно не используется, по умолчаниюundefined).
var anotherObject = {
name: '若川'
};
var myObject = Object.create(anotherObject, {
age: {
value:18,
},
});
// 获得它的原型
Object.getPrototypeOf(anotherObject) === Object.prototype; // true 说明anotherObject的原型是Object.prototype
Object.getPrototypeOf(myObject); // {name: "若川"} // 说明myObject的原型是{name: "若川"}
myObject.hasOwnProperty('name'); // false; 说明name是原型上的。
myObject.hasOwnProperty('age'); // true 说明age是自身的
myObject.name; // '若川'
myObject.age; // 18;
для не поддерживаемыхES5браузер,MDNпредоставляется наployfillстроить планы.
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Существует множество методов и свойств наlodash.prototypeЕсть также многоlodashтот же метод, что и выше. Конечно нетlodash.prototypeПерепишите его. но черезmixinустановлен.
mixin
конкретное использование миксина
_.mixin([object=lodash], source, [options={}])
Добавляет все перечисляемые свойства функции самого исходного объекта к целевому объекту. Если объект является функцией, метод функции будет добавлен в цепочку прототипов.
Примечание. Используйте _.runInContext для создания исходной функции lodash, чтобы избежать конфликтов, вызванных модификациями.
добавить версию
0.1.0
параметр
[object=lodash] (Функция|Объект): целевой объект.
источник (объект): исходный объект.
[options={}] (Object): Объект опций.
[options.chain=true] (логическое значение): следует ли включать цепочку операций.
вернуть
(*): Возвращаемый объект.
исходный код примеси
Нажмите здесь, чтобы развернуть исходный код миксина, а комментарии будут проанализированы позже.
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;
}
На самом деле, когда вы видите код функции конкретного определения, вы, вероятно, знаете назначение этой функции. Чтобы не затрагивать основную линию, длина статьи слишком велика. Конкретный исходный код здесь не раскрывается.
Заинтересованные читатели могут посмотреть исходный код других функций, производных от этих функций.
функциональные клавиши, полученные из миксина
существуетmixinВ функции последний вызов фактическиObject.keys
function keys(object) {
return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
}
производные функции примеси baseFunctions
Возвращает коллекцию массивов функций
function baseFunctions(object, props) {
return arrayFilter(props, function(key) {
return isFunction(object[key]);
});
}
функция isFunction, полученная из миксина
Определить, является ли аргумент функцией
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;
}
производная функция миксина arrayEach
Похоже на [].forEach
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;
}
производная функция миксина arrayPush
Похоже на [].push
function arrayPush(array, values) {
var index = -1,
length = values.length,
offset = array.length;
while (++index < length) {
array[offset + index] = values[index];
}
return array;
}
функция, производная от примеси, copyArray
копировать массив
function copyArray(source, array) {
var index = -1,
length = source.length;
array || (array = Array(length));
while (++index < length) {
array[index] = source[index];
}
return array;
}
анализ исходного кода миксина
lodashДва вызова в исходном кодеmixin
// Add methods that return wrapped values in chain sequences.
lodash.after = after;
// code ... 等 153 个支持链式调用的方法
// Add methods to `lodash.prototype`.
// 把lodash上的静态方法赋值到 lodash.prototype 上
mixin(lodash, lodash);
// Add methods that return unwrapped values in chain sequences.
lodash.add = add;
// code ... 等 152 个不支持链式调用的方法
// 这里其实就是过滤 after 等支持链式调用的方法,获取到 lodash 上的 add 等 添加到lodash.prototype 上。
mixin(lodash, (function() {
var source = {};
// baseForOwn 这里其实就是遍历lodash上的静态方法,执行回调函数
baseForOwn(lodash, function(func, methodName) {
// 第一次 mixin 调用了所以赋值到了lodash.prototype
// 所以这里用 Object.hasOwnProperty 排除不在lodash.prototype 上的方法。也就是 add 等 152 个不支持链式调用的方法。
if (!hasOwnProperty.call(lodash.prototype, methodName)) {
source[methodName] = func;
}
});
return source;
// 最后一个参数options 特意注明不支持链式调用
}()), { 'chain': false });
объединить два звонкаmixinЗамените в анализе исходного кода следующим образом
Нажмите здесь, чтобы развернуть исходный код миксина и комментарии
function mixin(object, source, options) {
// source 对象中可以枚举的属性
var props = keys(source),
// source 对象中的方法名称数组
methodNames = baseFunctions(source, props);
if (options == null &&
!(isObject(source) && (methodNames.length || !props.length))) {
// 如果 options 没传为 undefined undefined == null 为true
// 且 如果source 不为 对象或者不是函数
// 且 source对象的函数函数长度 或者 source 对象的属性长度不为0
// 把 options 赋值为 source
options = source;
// 把 source 赋值为 object
source = object;
// 把 object 赋值为 this 也就是 _ (lodash)
object = this;
// 获取到所有的方法名称数组
methodNames = baseFunctions(source, keys(source));
}
// 是否支持 链式调用
// options 不是对象或者不是函数,是null或者其他值
// 判断options是否是对象或者函数,如果不是或者函数则不会执行 'chain' in options 也就不会报错
// 且 chain 在 options的对象或者原型链中
// 知识点 in [MDN in : https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/in
// 如果指定的属性在指定的对象或其原型链中,则in 运算符返回true。
// 或者 options.chain 转布尔值
var chain = !(isObject(options) && 'chain' in options) || !!options.chain,
// object 是函数
isFunc = isFunction(object);
// 循环 方法名称数组
arrayEach(methodNames, function(methodName) {
// 函数本身
var func = source[methodName];
// object 通常是 lodash 也赋值这个函数。
object[methodName] = func;
if (isFunc) {
// 如果object是函数 赋值到 object prototype 上,通常是lodash
object.prototype[methodName] = function() {
// 实例上的__chain__ 属性 是否支持链式调用
// 这里的 this 是 new LodashWrapper 实例 类似如下
/**
{
__actions__: [],
__chain__: true
__index__: 0
__values__: undefined
__wrapped__: []
}
**/
var chainAll = this.__chain__;
// options 中的 chain 属性 是否支持链式调用
// 两者有一个符合链式调用 执行下面的代码
if (chain || chainAll) {
// 通常是 lodash
var result = object(this.__wrapped__),
// 复制 实例上的 __action__ 到 result.__action__ 和 action 上
actions = result.__actions__ = copyArray(this.__actions__);
// action 添加 函数 和 args 和 this 指向,延迟计算调用。
actions.push({ 'func': func, 'args': arguments, 'thisArg': object });
//实例上的__chain__ 属性 赋值给 result 的 属性 __chain__
result.__chain__ = chainAll;
// 最后返回这个实例
return result;
}
// 都不支持链式调用。直接调用
// 把当前实例的 value 和 arguments 对象 传递给 func 函数作为参数调用。返回调用结果。
return func.apply(object, arrayPush([this.value()], arguments));
};
}
});
// 最后返回对象 object
return object;
}
Резюме: Проще говоряlodashСтатический метод присваиванияlodash.prototypeначальство. Первый раз для поддержки связанных вызовов (lodash.afterЖдать153метод, поддерживающий цепочку вызовов), второй — метод, не поддерживающий цепочку вызовов (lodash.addЖдать152метод, который не поддерживает цепочку).
Сколько методов и свойств монтирует lodash в _ и _.prototype
посмотри сноваlodashточно установлен на_Сколько статических методов и свойств находится в объекте функции и монтирует_.prototypeСколько существует методов и свойств.
использоватьfor inПросто попробуйте цикл. См. следующий код:
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个
На самом деле упомянутое вышеlodash.afterЖдать153функция, которая поддерживает цепочку,lodash.addЖдать152Назначение из функции, которая не поддерживает цепочку.
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个
в сравнении сlodashЕсть больше статических методов на12, что свидетельствует о том, что в дополнение кmixinКроме того, есть12другие формы поручения.
Метод, который поддерживает связанные вызовы, наконец, возвращает объект экземпляра, получает значение результата окончательной обработки и, наконец, должен вызватьvalueметод.
Автор нарисовалlodashДиаграмма отношения монтирования метода и свойства.
lodashОтношение монтирования метода и атрибутаПожалуйста, приведите простые примеры во всем следующем
var result = _.chain([1, 2, 3, 4, 5])
.map(el => {
console.log(el); // 1, 2, 3
return el + 1;
})
.take(3)
.value();
// lodash中这里的`map`仅执行了`3`次。
// 具体功能也很简单 数组 1-5 加一,最后获取其中三个值。
console.log('result:', result);
То есть здесьlodashУмело знайте, сколько значений нужно в конце, и выполняйте несколько разmapДля циклов, для очень больших массивов очень полезно повысить производительность.
а такжеunderscoreвыполнить этот код, которыйmapВыполнено 5 раз.
Если это нормально, добиться этой функции также просто.
var result = [1, 2, 3, 4, 5].map(el => el + 1).slice(0, 3);
console.log('result:', result);
в сравнении сlodashздесьmapказнен5Второсортный.
// 不使用 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');
просто скажи здесьmapметод, добавитьLazyWrapperПуть кlodash.prototypeхранится и, наконец, называетсяvalueпозвони снова.
Подробности смотрите в реализации исходного кода ниже.
Добавить кLazyWrapperспособlodash.prototype
В основном следующие методы добавляются к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 这里其实就是遍历LazyWrapper.prototype上的方法,执行回调函数
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
// 检测函数名称是否是迭代器也就是循环
var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
// 检测函数名称是否head和last
// 顺便提一下 ()这个是捕获分组 而加上 ?: 则是非捕获分组 也就是说不用于其他操作
isTaker = /^(?:head|last)$/.test(methodName),
// lodashFunc 是 根据 isTaker 组合 takeRight take methodName
lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
// 根据isTaker 和 是 find 判断结果是否 包装
retUnwrapped = isTaker || /^find/.test(methodName);
// 如果不存在这个函数,就不往下执行
if (!lodashFunc) {
return;
}
// 把 lodash.prototype 方法赋值到lodash.prototype
lodash.prototype[methodName] = function() {
// 取实例中的__wrapped__ 值 例子中则是 [1,2,3,4,5]
var value = this.__wrapped__,
// 如果是head和last 方法 isTaker 返回 [1], 否则是arguments对象
args = isTaker ? [1] : arguments,
// 如果value 是LayeWrapper的实例
isLazy = value instanceof LazyWrapper,
// 迭代器 循环
iteratee = args[0],
// 使用useLazy isLazy value或者是数组
useLazy = isLazy || isArray(value);
var interceptor = function(value) {
// 函数执行 value args 组合成数组参数
var result = lodashFunc.apply(lodash, arrayPush([value], args));
// 如果是 head 和 last (isTaker) 支持链式调用 返回结果的第一个参数 否则 返回result
return (isTaker && chainAll) ? result[0] : result;
};
// useLazy true 并且 函数checkIteratee 且迭代器是函数,且迭代器参数个数不等于1
if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
// Avoid lazy use if the iteratee has a "length" value other than `1`.
// useLazy 赋值为 false
// isLazy 赋值为 false
isLazy = useLazy = false;
}
// 取实例上的 __chain__
var chainAll = this.__chain__,
// 存储的待执行的函数 __actions__ 二次取反是布尔值 也就是等于0或者大于0两种结果
isHybrid = !!this.__actions__.length,
// 是否不包装 用结果是否不包装 且 不支持链式调用
isUnwrapped = retUnwrapped && !chainAll,
// 是否仅Lazy 用isLazy 和 存储的函数
onlyLazy = isLazy && !isHybrid;
// 结果不包装 且 useLazy 为 true
if (!retUnwrapped && useLazy) {
// 实例 new LazyWrapper 这里的this 是 new LodashWrapper()
value = onlyLazy ? value : new LazyWrapper(this);
// result 执行函数结果
var result = func.apply(value, args);
/*
*
// _.thru(value, interceptor)
// 这个方法类似 _.tap, 除了它返回 interceptor 的返回结果。该方法的目的是"传递" 值到一个方法链序列以取代中间结果。
_([1, 2, 3])
.tap(function(array) {
// 改变传入的数组
array.pop();
})
.reverse()
.value();
// => [2, 1]
*/
// thisArg 指向undefined 或者null 非严格模式下是指向window,严格模式是undefined 或者nll
result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
// 返回实例 lodashWrapper
return new LodashWrapper(result, chainAll);
}
// 不包装 且 onlyLazy 为 true
if (isUnwrapped && onlyLazy) {
// 执行函数
return func.apply(this, args);
}
// 上面都没有执行,执行到这里了
// 执行 thru 函数,回调函数 是 interceptor
result = this.thru(interceptor);
return isUnwrapped ? (isTaker ? result.value()[0] : result.value()) : result;
};
});
Подводя итог, я написал так много заметок, проще говоря: на самом деле это используетсяLazyWrapper.prototypeпереписать оригиналlodash.prototypeФункция, которая определяет, должна ли функция использовать ленивое вычисление, а затем вызывает ее при необходимости.
Читатели могут выполнять отладку с помощью точек останова, эффективно использовать точки останова для входа в функции и просматривать комментарии, которые могут быть более понятными.
Нажмите, чтобы просмотреть скриншоты отладки точки останова.
Цепной вызов, наконец, возвращает объект экземпляра.Фактическая функция обработки данных не вызывается, но сохраняется и сохраняется, и, наконец, вызываетсяvalueспособ выполнения этих функций.
lodash.prototype.value — это wrapperValue
function baseWrapperValue(value, actions) {
var result = value;
// 如果是lazyWrapper的实例,则调用LazyWrapper.prototype.value 方法,也就是 lazyValue 方法
if (result instanceof LazyWrapper) {
result = result.value();
}
// 类似 [].reduce(),把上一个函数返回结果作为参数传递给下一个函数
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;
В случае ленивой оценки вызовLazyWrapper.prototype.valueкоторыйlazyValue.
LazyWrapper.prototype.value — это ленивая оценка lazyValue.
Нажмите здесь, чтобы развернуть исходный код lazyValue и комментарии
function LazyWrapper(value) {
// 参数 value
this.__wrapped__ = value;
// 执行的函数
this.__actions__ = [];
this.__dir__ = 1;
// 过滤
this.__filtered__ = false;
// 存储迭代器函数
this.__iteratees__ = [];
// 默认最大取值个数
this.__takeCount__ = MAX_ARRAY_LENGTH;
// 具体取值多少个,存储函数和类型
this.__views__ = [];
}
/**
* Extracts the unwrapped value from its lazy wrapper.
*
* @private
* @name value
* @memberOf LazyWrapper
* @returns {*} Returns the unwrapped value.
*/
function lazyValue() {
// this.__wrapped__ 是 new LodashWrapper 实例 所以执行.value 获取原始值
var array = this.__wrapped__.value(),
//
dir = this.__dir__,
// 是否是函数
isArr = isArray(array),
// 是否从右边开始
isRight = dir < 0,
// 数组的长度。如果不是数组,则是0
arrLength = isArr ? array.length : 0,
// 获取 take(3) 上述例子中 则是 start: 0,end: 3
view = getView(0, arrLength, this.__views__),
start = view.start,
end = view.end,
// 长度 3
length = end - start,
// 如果是是从右开始
index = isRight ? end : (start - 1),
// 存储的迭代器数组
iteratees = this.__iteratees__,
// 迭代器数组长度
iterLength = iteratees.length,
// 结果resIndex
resIndex = 0,
// 最后获取几个值,也就是 3
takeCount = nativeMin(length, this.__takeCount__);
// 如果不是数组,或者 不是从右开始 并且 参数数组长度等于take的长度 takeCount等于长度
// 则直接调用 baseWrapperValue 不需要
if (!isArr || (!isRight && arrLength == length && takeCount == length)) {
return baseWrapperValue(array, this.__actions__);
}
var result = [];
// 标签语句 label
// MDN label 链接
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/label
// 标记语句可以和 break 或 continue 语句一起使用。标记就是在一条语句前面加个可以引用的标识符(identifier)。
outer:
while (length-- && resIndex < takeCount) {
index += dir;
var iterIndex = -1,
// 数组第一项
value = array[index];
while (++iterIndex < iterLength) {
// 迭代器数组 {iteratee: function{}, typy: 2}
var data = iteratees[iterIndex],
iteratee = data.iteratee,
type = data.type,
// 结果 迭代器执行结果
computed = iteratee(value);
if (type == LAZY_MAP_FLAG) {
// 如果 type 是 map 类型,结果 computed 赋值给value
value = computed;
} else if (!computed) {
if (type == LAZY_FILTER_FLAG) {
// 退出当前这次循环,进行下一次循环
continue outer;
} else {
// 退出整个循环
break outer;
}
}
}
// 最终数组
result[resIndex++] = value;
}
// 返回数组 例子中则是 [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;
Автор нарисовалlodashа такжеLazyWrapperпредставлена диаграммой отношений.
lodashа такжеLazyWrapperдиаграмма отношенийрезюме:lazyValueПроще говоря, реализованная функция состоит в том, чтобы несколько раз выполнить ранее записанную функцию и несколько раз выполнить функцию, хранящуюся в записи.
То есть в следующем примереmapфункция будет выполняться только3Второсортный. Если ленивые вычисления не используются, тоmapфункция будет выполняться5Второсортный.
var result = _.chain([1, 2, 3, 4, 5])
.map(el => el + 1)
.take(3)
.value();
Суммировать
К этому моменту текст в основном подходит к концу и, наконец, подводится итог.
Статья в основном учит
runInContext()экспорт_lodashиспользование функцииbaseCreateметод прототипного наследованияLodashWrapperа такжеLazyWrapper,mixinметод монтирования наlodash.prototype, объяснено позже с комбинацией примеровlodash.prototype.value(wrapperValue)а такжеLazy.prototype.value(lazyValue)Исходный код реализации ленивых вычислений.
Поделитесь методом, который знает только имя функции, чтобы найти местоположение объявления функции местоположения исходного кода.VSCodeНавык:Ctrl + p. войти@functionNameфункция позиционированияfunctionNameКонкретное место в исходном файле. Если вы знаете место вызова, просто нажмитеalt+鼠标左键Вы можете перейти к месту объявления функции.
Если читатель обнаружит что-то неправильное или что-то, что можно улучшить, или где это неясно написано, пожалуйста, прокомментируйте и укажите. Кроме того, я думаю, что написание хорошее, и это немного полезно для вас.Вы можете ставить лайки, комментировать, пересылать и делиться, что также является своего рода поддержкой для автора. Большое спасибо.
Рекомендуемое чтение
репозиторий lodash | официальная документация lodash | lodash китайская документация
Создайте библиотеку интерфейсных инструментов, похожую на lodash.
Ленивая оценка - интерпретация исходного кода lodash
luobo tang: анализ реализации ленивой оценки lazy.js
репозиторий lazy.js на гитхабе
узнал из этой статьиlodashверсияv4.17.15 unpkg.comСвязь
Предыдущие статьи автора
Интервьюер спросил: наследование JS
Интервьюер спросил: этот пункт JS
Интервьюер спросил: Могу ли я смоделировать вызов и применить методы JS?
Интервьюер спросил: Могу ли я смоделировать метод привязки, реализующий JS?
Интервьюер спросил: Могу ли я смоделировать новый оператор, реализующий JS?
Внешний интерфейс использует сканер puppeteer для создания PDF-файла «React.js Book» и его слияния.
о
Автор: Чанг ИВакагаваНазвание смешано в реках и озерах. По дороге на фронт | Энтузиасты РРТ | Знаю очень мало, только хорошо учусь.
Личный блог-Вакагава,использоватьvuepressРефакторинг, опыт чтения может быть лучше
Колонка самородков, добро пожаловать, обратите внимание~
segmentfaultПередняя колонка обзора, добро пожаловать, обратите внимание~
Знайте переднюю колонку видения, добро пожаловать, обратите внимание~
github blog, соответствующий исходный код и ресурсы размещены здесь, попросите одинstar^_^~
Добро пожаловать, чтобы добавить общедоступную учетную запись WeChat для общения в WeChat
Может быть более интересным общедоступный аккаунт WeChat, нажмите и удерживайте, чтобы отсканировать код, чтобы следовать. Добро пожаловать, чтобы добавить автора WeChatruochuan12(Укажите источник, в основном никто не будет отклонен), пригласите вас в [Группу обмена видением переднего плана] для долгосрочного обмена и обучения ~
В этой статье используетсяmdniceнабор текста