предисловие
Здравствуйте, яВакагава. Это
学习源码整体架构系列
Третья статья. Слово «общая архитектура» кажется немного большим, скажем так, это общая структура исходного кода. В этой статье рассказывается об упакованном и интегрированном коде, а не о разделенном коде на реальном складе.
学习源码整体架构系列
Статья выглядит следующим образом:
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
Диаграмма отношения монтирования метода и свойства.
Пожалуйста, приведите простые примеры во всем следующем
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
представлена диаграммой отношений.
резюме: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набор текста