var VERSION = '1.13.6';
Underscore.js 1.13.6
https://underscorejs.node.org.cn
(c) 2009-2022 Jeremy Ashkenas, Julian Gonggrijp, and DocumentCloud and Investigative Reporters & Editors
Underscore may be freely distributed under the MIT license.
当前版本。
var VERSION = '1.13.6';
建立根对象,即浏览器中的 `window`(`self`)、服务器上的 `global`,或某些虚拟机中的 `this`。我们使用 `self` 而不是 `window` 来支持 `WebWorker`。
var root = (typeof self == 'object' && self.self === self && self) ||
(typeof global == 'object' && global.global === global && global) ||
Function('return this')() ||
{};
在缩小(但未经 gzip 压缩)的版本中节省字节
var ArrayProto = Array.prototype, ObjProto = Object.prototype;
var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;
创建快速引用变量,以快速访问核心原型。
var push = ArrayProto.push,
slice = ArrayProto.slice,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
现代功能检测。
var supportsArrayBuffer = typeof ArrayBuffer !== 'undefined',
supportsDataView = typeof DataView !== 'undefined';
我们希望使用的所有 **ECMAScript 5+** 原生函数实现都已在此声明。
var nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeCreate = Object.create,
nativeIsView = supportsArrayBuffer && ArrayBuffer.isView;
创建对这些内置函数的引用,因为我们重写了它们。
var _isNaN = isNaN,
_isFinite = isFinite;
IE < 9 中不会通过 `for key in ...` 迭代的键,因此会遗漏。
var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
可以精确表示的最大整数。
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
某些函数采用可变数量的参数,或在开头采用几个预期参数,然后采用可变数量的值进行操作。此帮助器将函数的参数长度(或显式 `startIndex`)之后的所有剩余参数累积到一个数组中,该数组成为最后一个参数。类似于 ES6 的“rest 参数”。
function restArguments(func, startIndex) {
startIndex = startIndex == null ? func.length - 1 : +startIndex;
return function() {
var length = Math.max(arguments.length - startIndex, 0),
rest = Array(length),
index = 0;
for (; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
switch (startIndex) {
case 0: return func.call(this, rest);
case 1: return func.call(this, arguments[0], rest);
case 2: return func.call(this, arguments[0], arguments[1], rest);
}
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
args[startIndex] = rest;
return func.apply(this, args);
};
}
给定变量是否为对象?
function isObject(obj) {
var type = typeof obj;
return type === 'function' || (type === 'object' && !!obj);
}
给定值是否等于 null?
function isNull(obj) {
return obj === null;
}
给定变量是否未定义?
function isUndefined(obj) {
return obj === void 0;
}
给定值是否为布尔值?
function isBoolean(obj) {
return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
}
给定值是否为 DOM 元素?
function isElement(obj) {
return !!(obj && obj.nodeType === 1);
}
用于创建基于 `toString` 的类型测试器的内部函数。
function tagTester(name) {
var tag = '[object ' + name + ']';
return function(obj) {
return toString.call(obj) === tag;
};
}
var isString = tagTester('String');
var isNumber = tagTester('Number');
var isDate = tagTester('Date');
var isRegExp = tagTester('RegExp');
var isError = tagTester('Error');
var isSymbol = tagTester('Symbol');
var isArrayBuffer = tagTester('ArrayBuffer');
var isFunction = tagTester('Function');
在适当的情况下优化 `isFunction`。解决旧版 v8、IE 11(#1621)、Safari 8(#1929)和 PhantomJS(#2236)中的一些 `typeof` 错误。
var nodelist = root.document && root.document.childNodes;
if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {
isFunction = function(obj) {
return typeof obj == 'function' || false;
};
}
var isFunction$1 = isFunction;
var hasObjectTag = tagTester('Object');
在 IE 10 - Edge 13 中,`DataView` 的字符串标记为 `'[object Object]'`。在其中最常见的 IE 11 中,此问题也适用于 `Map`、`WeakMap` 和 `Set`。
var hasStringTagBug = (
supportsDataView && hasObjectTag(new DataView(new ArrayBuffer(8)))
),
isIE11 = (typeof Map !== 'undefined' && hasObjectTag(new Map));
var isDataView = tagTester('DataView');
在 IE 10 - Edge 13 中,我们需要一种不同的启发式方法来确定对象是否为 `DataView`。
function ie10IsDataView(obj) {
return obj != null && isFunction$1(obj.getInt8) && isArrayBuffer(obj.buffer);
}
var isDataView$1 = (hasStringTagBug ? ie10IsDataView : isDataView);
给定值是否为数组?委托给 ECMA5 的原生 `Array.isArray`。
var isArray = nativeIsArray || tagTester('Array');
用于检查 `key` 是否为 `obj` 的自有属性名的内部函数。
function has$1(obj, key) {
return obj != null && hasOwnProperty.call(obj, key);
}
var isArguments = tagTester('Arguments');
在浏览器(比如 IE < 9)中定义该方法的备用版本,在这些浏览器中没有可检查的“Arguments”类型。
(function() {
if (!isArguments(arguments)) {
isArguments = function(obj) {
return has$1(obj, 'callee');
};
}
}());
var isArguments$1 = isArguments;
给定对象是否为有限数字?
function isFinite$1(obj) {
return !isSymbol(obj) && _isFinite(obj) && !isNaN(parseFloat(obj));
}
给定值是否为 `NaN`?
function isNaN$1(obj) {
return isNumber(obj) && _isNaN(obj);
}
谓词生成函数。通常在 Underscore 外部有用。
function constant(value) {
return function() {
return value;
};
}
用于 `isArrayLike` 和 `isBufferLike` 的常见内部逻辑。
function createSizePropertyCheck(getSizeProperty) {
return function(collection) {
var sizeProperty = getSizeProperty(collection);
return typeof sizeProperty == 'number' && sizeProperty >= 0 && sizeProperty <= MAX_ARRAY_INDEX;
}
}
用于从 `obj` 获取属性 `key` 的函数的内部帮助器。
function shallowProperty(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
}
用于获取对象的 `byteLength` 属性的内部帮助器。
var getByteLength = shallowProperty('byteLength');
用于确定是否应该对 `ArrayBuffer` 等进行广泛检查的内部帮助器。
var isBufferLike = createSizePropertyCheck(getByteLength);
给定值是否为类型化数组?
var typedArrayPattern = /\[object ((I|Ui)nt(8|16|32)|Float(32|64)|Uint8Clamped|Big(I|Ui)nt64)Array\]/;
function isTypedArray(obj) {
`ArrayBuffer.isView` 最具前瞻性,因此在可用时使用它。否则,退回到上述正则表达式。
return nativeIsView ? (nativeIsView(obj) && !isDataView$1(obj)) :
isBufferLike(obj) && typedArrayPattern.test(toString.call(obj));
}
var isTypedArray$1 = supportsArrayBuffer ? isTypedArray : constant(false);
用于获取对象的 `length` 属性的内部帮助器。
var getLength = shallowProperty('length');
用于创建简单查找结构的内部帮助器。`collectNonEnumProps` 过去依赖于 `_.contains`,但这导致了循环导入。`emulatedSet` 是一种一次性解决方案,仅适用于字符串数组。
function emulatedSet(keys) {
var hash = {};
for (var l = keys.length, i = 0; i < l; ++i) hash[keys[i]] = true;
return {
contains: function(key) { return hash[key] === true; },
push: function(key) {
hash[key] = true;
return keys.push(key);
}
};
}
内部帮助器。检查 `keys` 中是否存在 IE < 9 中不会通过 `for key in ...` 迭代的键,因此会遗漏。如果需要,则就地扩展 `keys`。
function collectNonEnumProps(obj, keys) {
keys = emulatedSet(keys);
var nonEnumIdx = nonEnumerableProps.length;
var constructor = obj.constructor;
var proto = (isFunction$1(constructor) && constructor.prototype) || ObjProto;
构造函数是一个特例。
var prop = 'constructor';
if (has$1(obj, prop) && !keys.contains(prop)) keys.push(prop);
while (nonEnumIdx--) {
prop = nonEnumerableProps[nonEnumIdx];
if (prop in obj && obj[prop] !== proto[prop] && !keys.contains(prop)) {
keys.push(prop);
}
}
}
检索对象的自有属性的名称。委托给 **ECMAScript 5** 的原生 `Object.keys`。
function keys(obj) {
if (!isObject(obj)) return [];
if (nativeKeys) return nativeKeys(obj);
var keys = [];
for (var key in obj) if (has$1(obj, key)) keys.push(key);
比如 IE < 9。
if (hasEnumBug) collectNonEnumProps(obj, keys);
return keys;
}
给定的数组、字符串或对象是否为空?“空”对象没有可枚举的自有属性。
function isEmpty(obj) {
if (obj == null) return true;
如果 `obj` 没有 `length`,则跳过更昂贵的基于 `toString` 的类型检查。
var length = getLength(obj);
if (typeof length == 'number' && (
isArray(obj) || isString(obj) || isArguments$1(obj)
)) return length === 0;
return getLength(keys(obj)) === 0;
}
返回对象是否具有给定的 `key:value` 对。
function isMatch(object, attrs) {
var _keys = keys(attrs), length = _keys.length;
if (object == null) return !length;
var obj = Object(object);
for (var i = 0; i < length; i++) {
var key = _keys[i];
if (attrs[key] !== obj[key] || !(key in obj)) return false;
}
return true;
}
如果 Underscore 被调用为一个函数,它将返回一个包装对象,该对象可以使用 OO 风格。此包装器包含通过 _.mixin
添加的所有函数的已更改版本。包装对象可以链接。
function _$1(obj) {
if (obj instanceof _$1) return obj;
if (!(this instanceof _$1)) return new _$1(obj);
this._wrapped = obj;
}
_$1.VERSION = VERSION;
从包装和链接对象中提取结果。
_$1.prototype.value = function() {
return this._wrapped;
};
提供一些方法的解包代理,这些方法用于引擎操作,例如算术和 JSON 字符串化。
_$1.prototype.valueOf = _$1.prototype.toJSON = _$1.prototype.value;
_$1.prototype.toString = function() {
return String(this._wrapped);
};
用于将 ArrayBuffer、类型化数组或 DataView 包装或浅拷贝到新视图的内部函数,重用缓冲区。
function toBufferView(bufferSource) {
return new Uint8Array(
bufferSource.buffer || bufferSource,
bufferSource.byteOffset || 0,
getByteLength(bufferSource)
);
}
我们使用此字符串两次,因此为其提供一个名称以进行缩小。
var tagDataView = '[object DataView]';
用于 _.isEqual
的内部递归比较函数。
function eq(a, b, aStack, bStack) {
相同对象相等。0 === -0
,但它们并不相同。请参阅 Harmony egal
提议。
if (a === b) return a !== 0 || 1 / a === 1 / b;
null
或 undefined
仅等于其自身(严格比较)。
if (a == null || b == null) return false;
NaN
相等,但非自反。
if (a !== a) return b !== b;
穷举原始检查
var type = typeof a;
if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
return deepEq(a, b, aStack, bStack);
}
用于 _.isEqual
的内部递归比较函数。
function deepEq(a, b, aStack, bStack) {
解包任何包装对象。
if (a instanceof _$1) a = a._wrapped;
if (b instanceof _$1) b = b._wrapped;
比较 [[Class]]
名称。
var className = toString.call(a);
if (className !== toString.call(b)) return false;
解决 IE 10 - Edge 13 中的一个错误。
if (hasStringTagBug && className == '[object Object]' && isDataView$1(a)) {
if (!isDataView$1(b)) return false;
className = tagDataView;
}
switch (className) {
这些类型按值比较。
case '[object RegExp]':
将正则表达式强制转换为字符串以进行比较(注意:‘’ + /a/i === ‘/a/i’)
case '[object String]':
基元及其对应的对象包装器是等效的;因此,"5"
等效于 new String("5")
。
return '' + a === '' + b;
case '[object Number]':
NaN
相等,但非自反。Object(NaN) 等效于 NaN。
if (+a !== +a) return +b !== +b;
对其他数字值执行 egal
比较。
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
case '[object Date]':
case '[object Boolean]':
将日期和布尔值强制转换为数字基元值。日期按其毫秒表示形式进行比较。请注意,毫秒表示形式为 NaN
的无效日期并不相等。
return +a === +b;
case '[object Symbol]':
return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
case '[object ArrayBuffer]':
case tagDataView:
强制转换为类型化数组,以便我们可以继续执行。
return deepEq(toBufferView(a), toBufferView(b), aStack, bStack);
}
var areArrays = className === '[object Array]';
if (!areArrays && isTypedArray$1(a)) {
var byteLength = getByteLength(a);
if (byteLength !== getByteLength(b)) return false;
if (a.buffer === b.buffer && a.byteOffset === b.byteOffset) return true;
areArrays = true;
}
if (!areArrays) {
if (typeof a != 'object' || typeof b != 'object') return false;
具有不同构造函数的对象不相等,但来自不同帧的 Object
或 Array
相等。
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(isFunction$1(aCtor) && aCtor instanceof aCtor &&
isFunction$1(bCtor) && bCtor instanceof bCtor)
&& ('constructor' in a && 'constructor' in b)) {
return false;
}
}
对循环结构假设相等。用于检测循环结构的算法改编自 ES 5.1 第 15.12.3 节,抽象操作 JO
。
初始化遍历对象堆栈。在此处完成,因为我们仅需要它们进行对象和数组比较。
aStack = aStack || [];
bStack = bStack || [];
var length = aStack.length;
while (length--) {
线性搜索。性能与唯一嵌套结构的数量成反比。
if (aStack[length] === a) return bStack[length] === b;
}
将第一个对象添加到遍历对象堆栈。
aStack.push(a);
bStack.push(b);
递归比较对象和数组。
if (areArrays) {
比较数组长度以确定是否需要深度比较。
length = a.length;
if (length !== b.length) return false;
深度比较内容,忽略非数字属性。
while (length--) {
if (!eq(a[length], b[length], aStack, bStack)) return false;
}
} else {
深度比较对象。
var _keys = keys(a), key;
length = _keys.length;
在比较深度相等性之前,确保两个对象包含相同数量的属性。
if (keys(b).length !== length) return false;
while (length--) {
深度比较每个成员
key = _keys[length];
if (!(has$1(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
}
}
从遍历对象堆栈中移除第一个对象。
aStack.pop();
bStack.pop();
return true;
}
执行深度比较以检查两个对象是否相等。
function isEqual(a, b) {
return eq(a, b);
}
检索对象的全部可枚举属性名称。
function allKeys(obj) {
if (!isObject(obj)) return [];
var keys = [];
for (var key in obj) keys.push(key);
比如 IE < 9。
if (hasEnumBug) collectNonEnumProps(obj, keys);
return keys;
}
由于常规的 Object.prototype.toString
类型测试不适用于 IE 11 中的某些类型,因此我们改用基于方法的指纹启发式方法。它不是很好,但这是我们能做到的最好方法。指纹方法列表定义如下。
function ie11fingerprint(methods) {
var length = getLength(methods);
return function(obj) {
if (obj == null) return false;
Map
、WeakMap
和 Set
没有可枚举键。
var keys = allKeys(obj);
if (getLength(keys)) return false;
for (var i = 0; i < length; i++) {
if (!isFunction$1(obj[methods[i]])) return false;
}
如果我们针对 WeakMap
进行测试,我们需要确保 obj
没有 forEach
方法,以便将其与常规 Map
区分开来。
return methods !== weakMapMethods || !isFunction$1(obj[forEachName]);
};
}
为了紧凑缩小,我们仅在指纹中编写每个字符串一次。
var forEachName = 'forEach',
hasName = 'has',
commonInit = ['clear', 'delete'],
mapTail = ['get', hasName, 'set'];
Map
、WeakMap
和 Set
各有上述子列表的略微不同的组合。
var mapMethods = commonInit.concat(forEachName, mapTail),
weakMapMethods = commonInit.concat(mapTail),
setMethods = ['add'].concat(commonInit, forEachName, hasName);
var isMap = isIE11 ? ie11fingerprint(mapMethods) : tagTester('Map');
var isWeakMap = isIE11 ? ie11fingerprint(weakMapMethods) : tagTester('WeakMap');
var isSet = isIE11 ? ie11fingerprint(setMethods) : tagTester('Set');
var isWeakSet = tagTester('WeakSet');
检索对象属性的值。
function values(obj) {
var _keys = keys(obj);
var length = _keys.length;
var values = Array(length);
for (var i = 0; i < length; i++) {
values[i] = obj[_keys[i]];
}
return values;
}
将一个对象转换为一个 [key, value]
对的列表。与只有一个参数的 _.object
相反。
function pairs(obj) {
var _keys = keys(obj);
var length = _keys.length;
var pairs = Array(length);
for (var i = 0; i < length; i++) {
pairs[i] = [_keys[i], obj[_keys[i]]];
}
return pairs;
}
反转一个对象的键和值。这些值必须是可序列化的。
function invert(obj) {
var result = {};
var _keys = keys(obj);
for (var i = 0, length = _keys.length; i < length; i++) {
result[obj[_keys[i]]] = _keys[i];
}
return result;
}
返回一个对象上可用函数名称的有序列表。
function functions(obj) {
var names = [];
for (var key in obj) {
if (isFunction$1(obj[key])) names.push(key);
}
return names.sort();
}
用于创建赋值函数的内部函数。
function createAssigner(keysFunc, defaults) {
return function(obj) {
var length = arguments.length;
if (defaults) obj = Object(obj);
if (length < 2 || obj == null) return obj;
for (var index = 1; index < length; index++) {
var source = arguments[index],
keys = keysFunc(source),
l = keys.length;
for (var i = 0; i < l; i++) {
var key = keys[i];
if (!defaults || obj[key] === void 0) obj[key] = source[key];
}
}
return obj;
};
}
使用传入对象中的所有属性扩展一个给定的对象。
var extend = createAssigner(allKeys);
使用传入对象中的所有自身属性赋值给一个给定的对象。(https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
var extendOwn = createAssigner(keys);
使用默认属性填充一个给定的对象。
var defaults = createAssigner(allKeys, true);
创建一个裸函数引用,用于代理原型交换。
function ctor() {
return function(){};
}
用于创建从另一个对象继承的新对象的内部函数。
function baseCreate(prototype) {
if (!isObject(prototype)) return {};
if (nativeCreate) return nativeCreate(prototype);
var Ctor = ctor();
Ctor.prototype = prototype;
var result = new Ctor;
Ctor.prototype = null;
return result;
}
创建一个从给定原型对象继承的对象。如果提供了其他属性,则将它们添加到创建的对象中。
function create(prototype, props) {
var result = baseCreate(prototype);
if (props) extendOwn(result, props);
return result;
}
创建一个对象的(浅克隆)副本。
function clone(obj) {
if (!isObject(obj)) return obj;
return isArray(obj) ? obj.slice() : extend({}, obj);
}
使用 obj
调用 interceptor
,然后返回 obj
。此方法的主要目的是“进入”方法链,以便对链中的中间结果执行操作。
function tap(obj, interceptor) {
interceptor(obj);
return obj;
}
将(深层)属性 path
标准化为数组。与 _.iteratee
类似,此函数可以自定义。
function toPath$1(path) {
return isArray(path) ? path : [path];
}
_$1.toPath = toPath$1;
_.toPath
的内部包装器,用于启用缩小。类似于 _.iteratee
的 cb
。
function toPath(path) {
return _$1.toPath(path);
}
在 obj
中沿 path
获取嵌套属性的内部函数。
function deepGet(obj, path) {
var length = path.length;
for (var i = 0; i < length; i++) {
if (obj == null) return void 0;
obj = obj[path[i]];
}
return length ? obj : void 0;
}
从 object
中获取 path
上的(深层)属性值。如果 path
中的任何属性不存在或值是 undefined
,则返回 defaultValue
。path
通过 _.toPath
标准化。
function get(object, path, defaultValue) {
var value = deepGet(object, toPath(path));
return isUndefined(value) ? defaultValue : value;
}
用于检查对象是否直接具有给定属性的快捷函数(换句话说,不在原型上)。与内部 has
函数不同,此公开版本还可以遍历嵌套属性。
function has(obj, path) {
path = toPath(path);
var length = path.length;
for (var i = 0; i < length; i++) {
var key = path[i];
if (!has$1(obj, key)) return false;
obj = obj[key];
}
return !!length;
}
保留标识函数,以用于默认迭代器。
function identity(value) {
return value;
}
返回一个谓词,用于检查对象是否具有给定的 key:value
对集。
function matcher(attrs) {
attrs = extendOwn({}, attrs);
return function(obj) {
return isMatch(obj, attrs);
};
}
创建一个函数,当传递一个对象时,将遍历该对象的属性,向下遍历给定的 path
,指定为键或索引的数组。
function property(path) {
path = toPath(path);
return function(obj) {
return deepGet(obj, path);
};
}
返回传入回调的有效(对于当前引擎)版本的内部函数,在其他 Underscore 函数中重复应用。
function optimizeCb(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
return func.call(context, value);
};
省略 2 参数的情况,因为我们不使用它。
case 3: return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
}
生成可应用于集合中每个元素的回调的内部函数,返回所需的结果——_.identity
、任意回调、属性匹配器或属性访问器。
function baseIteratee(value, context, argCount) {
if (value == null) return identity;
if (isFunction$1(value)) return optimizeCb(value, context, argCount);
if (isObject(value) && !isArray(value)) return matcher(value);
return property(value);
}
我们回调生成器的外部包装器。如果用户想要其他谓词/迭代器简写样式,他们可以自定义 _.iteratee
。此抽象隐藏了仅供内部使用的 argCount
参数。
function iteratee(value, context) {
return baseIteratee(value, context, Infinity);
}
_$1.iteratee = iteratee;
我们在内部调用的函数,用于生成回调。如果被覆盖,它将调用 _.iteratee
,否则调用 baseIteratee
。
function cb(value, context, argCount) {
if (_$1.iteratee !== iteratee) return _$1.iteratee(value, context);
return baseIteratee(value, context, argCount);
}
返回将 iteratee
应用于 obj
的每个元素的结果。与 _.map
相反,它返回一个对象。
function mapObject(obj, iteratee, context) {
iteratee = cb(iteratee, context);
var _keys = keys(obj),
length = _keys.length,
results = {};
for (var index = 0; index < length; index++) {
var currentKey = _keys[index];
results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
}
谓词生成函数。通常在 Underscore 外部有用。
function noop(){}
为给定对象生成一个返回给定属性的函数。
function propertyOf(obj) {
if (obj == null) return noop;
return function(path) {
return get(obj, path);
};
}
运行一个函数 n 次。
function times(n, iteratee, context) {
var accum = Array(Math.max(0, n));
iteratee = optimizeCb(iteratee, context, 1);
for (var i = 0; i < n; i++) accum[i] = iteratee(i);
return accum;
}
返回 min
和 max
(包括)之间的随机整数。
function random(min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + Math.floor(Math.random() * (max - min + 1));
}
一种(可能更快)的方法,以整数形式获取当前时间戳。
var now = Date.now || function() {
return new Date().getTime();
};
生成用于转义和取消转义字符串到/从 HTML 插值函数的内部帮助器。
function createEscaper(map) {
var escaper = function(match) {
return map[match];
};
用于识别需要转义的键的正则表达式。
var source = '(?:' + keys(map).join('|') + ')';
var testRegexp = RegExp(source);
var replaceRegexp = RegExp(source, 'g');
return function(string) {
string = string == null ? '' : '' + string;
return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
};
}
用于转义的 HTML 实体的内部列表。
var escapeMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'`': '`'
};
用于将字符串转义到 HTML 插值中的函数。
var _escape = createEscaper(escapeMap);
用于取消转义 HTML 插值中字符串的内部列表。
var unescapeMap = invert(escapeMap);
用于取消转义 HTML 插值中字符串的函数。
var _unescape = createEscaper(unescapeMap);
默认情况下,Underscore 使用 ERB 样式的模板分隔符。更改以下模板设置以使用备用分隔符。
var templateSettings = _$1.templateSettings = {
evaluate: /<%([\s\S]+?)%>/g,
interpolate: /<%=([\s\S]+?)%>/g,
escape: /<%-([\s\S]+?)%>/g
};
自定义 _.templateSettings
时,如果您不想定义内插、求值或转义正则表达式,我们需要一个保证不匹配的正则表达式。
var noMatch = /(.)^/;
某些字符需要转义,以便可以将其放入字符串文字中。
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
function escapeChar(match) {
return '\\' + escapes[match];
}
为了防止通过 _.templateSettings.variable
注入第三方代码,我们对其进行以下正则表达式的测试。它故意比仅匹配有效标识符宽松一些,但仍然可以通过默认值或解构赋值来防止可能的漏洞。
var bareIdentifier = /^\s*(\w|\$)+\s*$/;
类似于 John Resig 实现的 JavaScript 微模板。Underscore 模板处理任意分隔符,保留空白,并正确转义内插代码中的引号。注意:oldSettings
仅出于向后兼容性的考虑而存在。
function template(text, settings, oldSettings) {
if (!settings && oldSettings) settings = oldSettings;
settings = defaults({}, settings, _$1.templateSettings);
通过交替将分隔符组合成一个正则表达式。
var matcher = RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
编译模板源,适当地转义字符串文字。
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
index = offset + match.length;
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
} else if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
} else if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
Adobe VM 需要返回匹配项以生成正确的偏移量。
return match;
});
source += "';\n";
var argument = settings.variable;
if (argument) {
确保不注入第三方代码。(CVE-2021-23358)
if (!bareIdentifier.test(argument)) throw new Error(
'variable is not a bare identifier: ' + argument
);
} else {
如果未指定变量,请将数据值放入本地作用域。
source = 'with(obj||{}){\n' + source + '}\n';
argument = 'obj';
}
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + 'return __p;\n';
var render;
try {
render = new Function(argument, '_', source);
} catch (e) {
e.source = source;
throw e;
}
var template = function(data) {
return render.call(this, data, _$1);
};
提供已编译的源代码,以方便预编译。
template.source = 'function(' + argument + '){\n' + source + '}';
return template;
}
沿着 path
遍历 obj
的子项。如果子项是函数,则使用其父项作为上下文调用它。返回最后一个子项的值,或者如果任何子项未定义,则返回 fallback
。
function result(obj, path, fallback) {
path = toPath(path);
var length = path.length;
if (!length) {
return isFunction$1(fallback) ? fallback.call(obj) : fallback;
}
for (var i = 0; i < length; i++) {
var prop = obj == null ? void 0 : obj[path[i]];
if (prop === void 0) {
prop = fallback;
i = length; // Ensure we don't continue iterating.
}
obj = isFunction$1(prop) ? prop.call(obj) : prop;
}
return obj;
}
生成唯一的整数 ID(在整个客户端会话中唯一)。对于临时 DOM ID 很有用。
var idCounter = 0;
function uniqueId(prefix) {
var id = ++idCounter + '';
return prefix ? prefix + id : id;
}
开始链接一个包装的 Underscore 对象。
function chain(obj) {
var instance = _$1(obj);
instance._chain = true;
return instance;
}
内部函数,用于执行绑定到 context
的 sourceFunc
,并带有可选的 args
。确定是将函数作为构造函数执行还是作为普通函数执行。
function executeBound(sourceFunc, boundFunc, context, callingContext, args) {
if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
var self = baseCreate(sourceFunc.prototype);
var result = sourceFunc.apply(self, args);
if (isObject(result)) return result;
return self;
}
通过创建已预先填充了部分参数的版本,对函数进行部分应用,而无需更改其动态 this
上下文。默认情况下,_
充当占位符,允许预先填充任意组合的参数。为自定义占位符参数设置 _.partial.placeholder
。
var partial = restArguments(function(func, boundArgs) {
var placeholder = partial.placeholder;
var bound = function() {
var position = 0, length = boundArgs.length;
var args = Array(length);
for (var i = 0; i < length; i++) {
args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];
}
while (position < arguments.length) args.push(arguments[position++]);
return executeBound(func, bound, this, this, args);
};
return bound;
});
partial.placeholder = _$1;
创建绑定到给定对象(可选地分配 this
和参数)的函数。
var bind = restArguments(function(func, context, args) {
if (!isFunction$1(func)) throw new TypeError('Bind must be called on a function');
var bound = restArguments(function(callArgs) {
return executeBound(func, bound, context, this, args.concat(callArgs));
});
return bound;
});
用于集合方法的内部帮助器,以确定是否应将集合作为数组或对象进行迭代。相关信息:https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength 避免在 ARM-64 上出现非常讨厌的 iOS 8 JIT 错误。#2094
var isArrayLike = createSizePropertyCheck(getLength);
递归 flatten
函数的内部实现。
function flatten$1(input, depth, strict, output) {
output = output || [];
if (!depth && depth !== 0) {
depth = Infinity;
} else if (depth <= 0) {
return output.concat(input);
}
var idx = output.length;
for (var i = 0, length = getLength(input); i < length; i++) {
var value = input[i];
if (isArrayLike(value) && (isArray(value) || isArguments$1(value))) {
展平数组或参数对象的当前级别。
if (depth > 1) {
flatten$1(value, depth - 1, strict, output);
idx = output.length;
} else {
var j = 0, len = value.length;
while (j < len) output[idx++] = value[j++];
}
} else if (!strict) {
output[idx++] = value;
}
}
return output;
}
将对象的多个方法绑定到该对象。其余参数是要绑定的方法名称。对于确保对象上定义的所有回调都属于该对象非常有用。
var bindAll = restArguments(function(obj, keys) {
keys = flatten$1(keys, false, false);
var index = keys.length;
if (index < 1) throw new Error('bindAll must be passed function names');
while (index--) {
var key = keys[index];
obj[key] = bind(obj[key], obj);
}
return obj;
});
通过存储结果来记忆昂贵的函数。
function memoize(func, hasher) {
var memoize = function(key) {
var cache = memoize.cache;
var address = '' + (hasher ? hasher.apply(this, arguments) : key);
if (!has$1(cache, address)) cache[address] = func.apply(this, arguments);
return cache[address];
};
memoize.cache = {};
return memoize;
}
将函数延迟给定的毫秒数,然后使用提供的参数调用它。
var delay = restArguments(function(func, wait, args) {
return setTimeout(function() {
return func.apply(null, args);
}, wait);
});
推迟函数,安排在当前调用堆栈清除后运行它。
var defer = partial(delay, _$1, 1);
返回一个函数,当调用时,在给定的时间窗口内最多只触发一次。通常,受限函数将尽可能多地运行,但每次持续时间不得超过 wait
;但如果你想禁用前沿执行,请传递 {leading: false}
。要禁用后沿执行,请重复上述操作。
function throttle(func, wait, options) {
var timeout, context, args, result;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
var throttled = function() {
var _now = now();
if (!previous && options.leading === false) previous = _now;
var remaining = wait - (_now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = _now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
throttled.cancel = function() {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};
return throttled;
}
当返回函数的调用序列结束时,将触发参数函数。序列的结束由 wait
参数定义。如果传递了 immediate
,则参数函数将在序列的开头而不是结尾处触发。
function debounce(func, wait, immediate) {
var timeout, previous, args, result, context;
var later = function() {
var passed = now() - previous;
if (wait > passed) {
timeout = setTimeout(later, wait - passed);
} else {
timeout = null;
if (!immediate) result = func.apply(context, args);
需要此检查,因为 func
可以递归调用 debounced
。
if (!timeout) args = context = null;
}
};
var debounced = restArguments(function(_args) {
context = this;
args = _args;
previous = now();
if (!timeout) {
timeout = setTimeout(later, wait);
if (immediate) result = func.apply(context, args);
}
return result;
});
debounced.cancel = function() {
clearTimeout(timeout);
timeout = args = context = null;
};
return debounced;
}
返回作为第二个参数传递给第一个函数的函数,允许你调整参数,在前后运行代码,并有条件地执行原始函数。
function wrap(func, wrapper) {
return partial(wrapper, func);
}
返回传入谓词的否定版本。
function negate(predicate) {
return function() {
return !predicate.apply(this, arguments);
};
}
返回一个函数,它是函数列表的组合,每个函数使用后续函数的返回值。
function compose() {
var args = arguments;
var start = args.length - 1;
return function() {
var i = start;
var result = args[start].apply(this, arguments);
while (i--) result = args[i].call(this, result);
return result;
};
}
返回一个函数,该函数仅在第 N 次调用及以后执行。
function after(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
}
返回一个函数,该函数仅执行到第 N 次调用(但不包括第 N 次调用)。
function before(times, func) {
var memo;
return function() {
if (--times > 0) {
memo = func.apply(this, arguments);
}
if (times <= 1) func = null;
return memo;
};
}
返回一个函数,该函数最多执行一次,无论你调用它多少次。对延迟初始化很有用。
var once = partial(before, 2);
返回对象上通过真值测试的第一个键。
function findKey(obj, predicate, context) {
predicate = cb(predicate, context);
var _keys = keys(obj), key;
for (var i = 0, length = _keys.length; i < length; i++) {
key = _keys[i];
if (predicate(obj[key], key, obj)) return key;
}
}
生成 _.findIndex
和 _.findLastIndex
的内部函数。
function createPredicateIndexFinder(dir) {
return function(array, predicate, context) {
predicate = cb(predicate, context);
var length = getLength(array);
var index = dir > 0 ? 0 : length - 1;
for (; index >= 0 && index < length; index += dir) {
if (predicate(array[index], index, array)) return index;
}
return -1;
};
}
返回类似数组中通过真值测试的第一个索引。
var findIndex = createPredicateIndexFinder(1);
返回类似数组中通过真值测试的最后一个索引。
var findLastIndex = createPredicateIndexFinder(-1);
使用比较器函数找出应插入对象的最小索引,以维持顺序。使用二分查找。
function sortedIndex(array, obj, iteratee, context) {
iteratee = cb(iteratee, context, 1);
var value = iteratee(obj);
var low = 0, high = getLength(array);
while (low < high) {
var mid = Math.floor((low + high) / 2);
if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
}
return low;
}
生成 _.indexOf
和 _.lastIndexOf
函数的内部函数。
function createIndexFinder(dir, predicateFind, sortedIndex) {
return function(array, item, idx) {
var i = 0, length = getLength(array);
if (typeof idx == 'number') {
if (dir > 0) {
i = idx >= 0 ? idx : Math.max(idx + length, i);
} else {
length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
}
} else if (sortedIndex && idx && length) {
idx = sortedIndex(array, item);
return array[idx] === item ? idx : -1;
}
if (item !== item) {
idx = predicateFind(slice.call(array, i, length), isNaN$1);
return idx >= 0 ? idx + i : -1;
}
for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
if (array[idx] === item) return idx;
}
return -1;
};
}
返回数组中某个项目首次出现的索引,如果数组中不包含该项目,则返回 -1。如果数组很大并且已经按排序顺序排列,请传递 true
以使用二分查找。
var indexOf = createIndexFinder(1, findIndex, sortedIndex);
返回数组中某个项目最后一次出现的索引,如果数组中不包含该项目,则返回 -1。
var lastIndexOf = createIndexFinder(-1, findLastIndex);
返回通过真值测试的第一个值。
function find(obj, predicate, context) {
var keyFinder = isArrayLike(obj) ? findIndex : findKey;
var key = keyFinder(obj, predicate, context);
if (key !== void 0 && key !== -1) return obj[key];
}
_.find
的常见用例的便捷版本:获取包含特定 key:value
对的第一个对象。
function findWhere(obj, attrs) {
return find(obj, matcher(attrs));
}
集合函数的基石,一个 each
实现,又名 forEach
。除了类似数组之外,还处理原始对象。将所有稀疏数组类似物视为密集数组类似物。
function each(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context);
var i, length;
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
var _keys = keys(obj);
for (i = 0, length = _keys.length; i < length; i++) {
iteratee(obj[_keys[i]], _keys[i], obj);
}
}
return obj;
}
返回对每个元素应用迭代器的结果。
function map(obj, iteratee, context) {
iteratee = cb(iteratee, context);
var _keys = !isArrayLike(obj) && keys(obj),
length = (_keys || obj).length,
results = Array(length);
for (var index = 0; index < length; index++) {
var currentKey = _keys ? _keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
}
创建缩减函数的内部帮助器,向左或向右迭代。
function createReduce(dir) {
将重新分配参数变量的代码包装在与访问 arguments.length
不同的函数中,以避免性能下降。(#1991)
var reducer = function(obj, iteratee, memo, initial) {
var _keys = !isArrayLike(obj) && keys(obj),
length = (_keys || obj).length,
index = dir > 0 ? 0 : length - 1;
if (!initial) {
memo = obj[_keys ? _keys[index] : index];
index += dir;
}
for (; index >= 0 && index < length; index += dir) {
var currentKey = _keys ? _keys[index] : index;
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
};
return function(obj, iteratee, memo, context) {
var initial = arguments.length >= 3;
return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
};
}
Reduce 从值列表中构建单个结果,又名 inject
或 foldl
。
var reduce = createReduce(1);
reduce 的右结合版本,也称为 foldr
。
var reduceRight = createReduce(-1);
返回通过真值测试的所有元素。
function filter(obj, predicate, context) {
var results = [];
predicate = cb(predicate, context);
each(obj, function(value, index, list) {
if (predicate(value, index, list)) results.push(value);
});
return results;
}
返回真值测试失败的所有元素。
function reject(obj, predicate, context) {
return filter(obj, negate(cb(predicate)), context);
}
确定所有元素是否通过真值测试。
function every(obj, predicate, context) {
predicate = cb(predicate, context);
var _keys = !isArrayLike(obj) && keys(obj),
length = (_keys || obj).length;
for (var index = 0; index < length; index++) {
var currentKey = _keys ? _keys[index] : index;
if (!predicate(obj[currentKey], currentKey, obj)) return false;
}
return true;
}
确定对象中至少有一个元素通过真值测试。
function some(obj, predicate, context) {
predicate = cb(predicate, context);
var _keys = !isArrayLike(obj) && keys(obj),
length = (_keys || obj).length;
for (var index = 0; index < length; index++) {
var currentKey = _keys ? _keys[index] : index;
if (predicate(obj[currentKey], currentKey, obj)) return true;
}
return false;
}
确定数组或对象是否包含给定的项(使用 ===
)。
function contains(obj, item, fromIndex, guard) {
if (!isArrayLike(obj)) obj = values(obj);
if (typeof fromIndex != 'number' || guard) fromIndex = 0;
return indexOf(obj, item, fromIndex) >= 0;
}
对集合中的每个项调用一个方法(带参数)。
var invoke = restArguments(function(obj, path, args) {
var contextPath, func;
if (isFunction$1(path)) {
func = path;
} else {
path = toPath(path);
contextPath = path.slice(0, -1);
path = path[path.length - 1];
}
return map(obj, function(context) {
var method = func;
if (!method) {
if (contextPath && contextPath.length) {
context = deepGet(context, contextPath);
}
if (context == null) return void 0;
method = context[path];
}
return method == null ? method : method.apply(context, args);
});
});
_.map
常用用例的便捷版本:获取属性。
function pluck(obj, key) {
return map(obj, property(key));
}
_.filter
常用用例的便捷版本:仅选择包含特定 key:value
对的对象。
function where(obj, attrs) {
return filter(obj, matcher(attrs));
}
返回最大元素(或基于元素的计算)。
function max(obj, iteratee, context) {
var result = -Infinity, lastComputed = -Infinity,
value, computed;
if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null)) {
obj = isArrayLike(obj) ? obj : values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value != null && value > result) {
result = value;
}
}
} else {
iteratee = cb(iteratee, context);
each(obj, function(v, index, list) {
computed = iteratee(v, index, list);
if (computed > lastComputed || (computed === -Infinity && result === -Infinity)) {
result = v;
lastComputed = computed;
}
});
}
return result;
}
返回最小元素(或基于元素的计算)。
function min(obj, iteratee, context) {
var result = Infinity, lastComputed = Infinity,
value, computed;
if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null)) {
obj = isArrayLike(obj) ? obj : values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value != null && value < result) {
result = value;
}
}
} else {
iteratee = cb(iteratee, context);
each(obj, function(v, index, list) {
computed = iteratee(v, index, list);
if (computed < lastComputed || (computed === Infinity && result === Infinity)) {
result = v;
lastComputed = computed;
}
});
}
return result;
}
安全地从任何可迭代对象创建真实的实时数组。
var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
function toArray(obj) {
if (!obj) return [];
if (isArray(obj)) return slice.call(obj);
if (isString(obj)) {
保持代理对字符在一起。
return obj.match(reStrSymbol);
}
if (isArrayLike(obj)) return map(obj, identity);
return values(obj);
}
使用 Fisher-Yates 洗牌 的现代版本从集合中抽取 n 个随机值。如果未指定 n,则返回单个随机元素。内部 guard
参数允许它与 _.map
一起使用。
function sample(obj, n, guard) {
if (n == null || guard) {
if (!isArrayLike(obj)) obj = values(obj);
return obj[random(obj.length - 1)];
}
var sample = toArray(obj);
var length = getLength(sample);
n = Math.max(Math.min(n, length), 0);
var last = length - 1;
for (var index = 0; index < n; index++) {
var rand = random(index, last);
var temp = sample[index];
sample[index] = sample[rand];
sample[rand] = temp;
}
return sample.slice(0, n);
}
洗牌集合。
function shuffle(obj) {
return sample(obj, Infinity);
}
按迭代器产生的条件对对象的数值进行排序。
function sortBy(obj, iteratee, context) {
var index = 0;
iteratee = cb(iteratee, context);
return pluck(map(obj, function(value, key, list) {
return {
value: value,
index: index++,
criteria: iteratee(value, key, list)
};
}).sort(function(left, right) {
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index - right.index;
}), 'value');
}
用于聚合“按组”操作的内部函数。
function group(behavior, partition) {
return function(obj, iteratee, context) {
var result = partition ? [[], []] : {};
iteratee = cb(iteratee, context);
each(obj, function(value, index) {
var key = iteratee(value, index, obj);
behavior(result, value, key);
});
return result;
};
}
按条件对对象的数值进行分组。传递要按其分组的字符串属性,或返回条件的函数。
var groupBy = group(function(result, value, key) {
if (has$1(result, key)) result[key].push(value); else result[key] = [value];
});
按条件对对象的数值进行索引,类似于 _.groupBy
,但用于已知索引值唯一的情况。
var indexBy = group(function(result, value, key) {
result[key] = value;
});
按特定条件对对象进行分组,并统计每个组的实例数。传递要按其计数的字符串属性,或返回条件的函数。
var countBy = group(function(result, value, key) {
if (has$1(result, key)) result[key]++; else result[key] = 1;
});
将集合拆分为两个数组:一个数组中的元素全部通过给定的真值测试,另一个数组中的元素全部未通过真值测试。
var partition = group(function(result, value, pass) {
result[pass ? 0 : 1].push(value);
}, true);
返回集合中的元素数量。
function size(obj) {
if (obj == null) return 0;
return isArrayLike(obj) ? obj.length : keys(obj).length;
}
内部 _.pick
帮助函数,用于确定 key
是否是 obj
的可枚举属性名称。
function keyInObj(value, key, obj) {
return key in obj;
}
仅返回包含允许属性的对象副本。
var pick = restArguments(function(obj, keys) {
var result = {}, iteratee = keys[0];
if (obj == null) return result;
if (isFunction$1(iteratee)) {
if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);
keys = allKeys(obj);
} else {
iteratee = keyInObj;
keys = flatten$1(keys, false, false);
obj = Object(obj);
}
for (var i = 0, length = keys.length; i < length; i++) {
var key = keys[i];
var value = obj[key];
if (iteratee(value, key, obj)) result[key] = value;
}
return result;
});
返回不包含不允许属性的对象副本。
var omit = restArguments(function(obj, keys) {
var iteratee = keys[0], context;
if (isFunction$1(iteratee)) {
iteratee = negate(iteratee);
if (keys.length > 1) context = keys[1];
} else {
keys = map(flatten$1(keys, false, false), String);
iteratee = function(value, key) {
return !contains(keys, key);
};
}
return pick(obj, iteratee, context);
});
返回数组中除最后一个条目之外的所有条目。在参数对象上尤其有用。传递 n 将返回数组中的所有值,但不包括最后 N 个值。
function initial(array, n, guard) {
return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
}
获取数组的第一个元素。传递 n 将返回数组中的前 N 个值。guard 检查允许它与 _.map
一起使用。
function first(array, n, guard) {
if (array == null || array.length < 1) return n == null || guard ? void 0 : [];
if (n == null || guard) return array[0];
return initial(array, array.length - n);
}
返回 array
中除第一个条目之外的所有条目。在 arguments
对象上尤其有用。传递一个 n 将返回 array
中其余 N 个值。
function rest(array, n, guard) {
return slice.call(array, n == null || guard ? 1 : n);
}
获取数组的最后一个元素。传递 n 将返回数组中的最后 N 个值。
function last(array, n, guard) {
if (array == null || array.length < 1) return n == null || guard ? void 0 : [];
if (n == null || guard) return array[array.length - 1];
return rest(array, Math.max(0, array.length - n));
}
从数组中剔除所有假值。
function compact(array) {
return filter(array, Boolean);
}
展平数组,可以递归(默认)或展平到 depth
。将 true
或 false
作为 depth
传递分别表示 1
或 Infinity
。
function flatten(array, depth) {
return flatten$1(array, depth, false);
}
取一个数组与其他多个数组之间的差值。只有第一个数组中存在的元素才会保留。
var difference = restArguments(function(array, rest) {
rest = flatten$1(rest, true, true);
return filter(array, function(value){
return !contains(rest, value);
});
});
返回不包含指定值(值)的数组版本。
var without = restArguments(function(array, otherArrays) {
return difference(array, otherArrays);
});
生成数组的无重复版本。如果数组已经过排序,你可以选择使用更快的算法。如果迭代器不是一对一函数,则更快的算法将无法与迭代器一起使用,因此提供迭代器将禁用更快的算法。
function uniq(array, isSorted, iteratee, context) {
if (!isBoolean(isSorted)) {
context = iteratee;
iteratee = isSorted;
isSorted = false;
}
if (iteratee != null) iteratee = cb(iteratee, context);
var result = [];
var seen = [];
for (var i = 0, length = getLength(array); i < length; i++) {
var value = array[i],
computed = iteratee ? iteratee(value, i, array) : value;
if (isSorted && !iteratee) {
if (!i || seen !== computed) result.push(value);
seen = computed;
} else if (iteratee) {
if (!contains(seen, computed)) {
seen.push(computed);
result.push(value);
}
} else if (!contains(result, value)) {
result.push(value);
}
}
return result;
}
生成包含并集的数组:所有传入数组中的每个不同元素。
var union = restArguments(function(arrays) {
return uniq(flatten$1(arrays, true, true));
});
生成一个数组,其中包含所有传入数组中共享的每个项目。
function intersection(array) {
var result = [];
var argsLength = arguments.length;
for (var i = 0, length = getLength(array); i < length; i++) {
var item = array[i];
if (contains(result, item)) continue;
var j;
for (j = 1; j < argsLength; j++) {
if (!contains(arguments[j], item)) break;
}
if (j === argsLength) result.push(item);
}
return result;
}
zip 的补语。Unzip 接受一个数组数组,并按共享索引对每个数组的元素进行分组。
function unzip(array) {
var length = (array && max(array, getLength).length) || 0;
var result = Array(length);
for (var index = 0; index < length; index++) {
result[index] = pluck(array, index);
}
return result;
}
将多个列表压缩到一个数组中——共享索引的元素放在一起。
var zip = restArguments(unzip);
将列表转换为对象。传递一个包含 [key, value]
对的数组,或两个长度相同的并行数组——一个用于键,另一个用于相应的值。按对传递是 _.pairs
的逆运算。
function object(list, values) {
var result = {};
for (var i = 0, length = getLength(list); i < length; i++) {
if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
}
function range(start, stop, step) {
if (stop == null) {
stop = start || 0;
start = 0;
}
if (!step) {
step = stop < start ? -1 : 1;
}
var length = Math.max(Math.ceil((stop - start) / step), 0);
var range = Array(length);
for (var idx = 0; idx < length; idx++, start += step) {
range[idx] = start;
}
return range;
}
将单个数组分块为多个数组,每个数组包含 count
个或更少的项目。
function chunk(array, count) {
if (count == null || count < 1) return [];
var result = [];
var i = 0, length = array.length;
while (i < length) {
result.push(slice.call(array, i, i += count));
}
return result;
}
帮助函数,用于继续链接中间结果。
function chainResult(instance, obj) {
return instance._chain ? _$1(obj).chain() : obj;
}
将您自己的自定义函数添加到 Underscore 对象。
function mixin(obj) {
each(functions(obj), function(name) {
var func = _$1[name] = obj[name];
_$1.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return chainResult(this, func.apply(_$1, args));
};
});
return _$1;
}
将所有变异器 Array
函数添加到包装器。
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
_$1.prototype[name] = function() {
var obj = this._wrapped;
if (obj != null) {
method.apply(obj, arguments);
if ((name === 'shift' || name === 'splice') && obj.length === 0) {
delete obj[0];
}
}
return chainResult(this, obj);
};
});
将所有访问器 Array
函数添加到包装器。
each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_$1.prototype[name] = function() {
var obj = this._wrapped;
if (obj != null) obj = method.apply(obj, arguments);
return chainResult(this, obj);
};
});
命名导出
var allExports = {
__proto__: null,
VERSION: VERSION,
restArguments: restArguments,
isObject: isObject,
isNull: isNull,
isUndefined: isUndefined,
isBoolean: isBoolean,
isElement: isElement,
isString: isString,
isNumber: isNumber,
isDate: isDate,
isRegExp: isRegExp,
isError: isError,
isSymbol: isSymbol,
isArrayBuffer: isArrayBuffer,
isDataView: isDataView$1,
isArray: isArray,
isFunction: isFunction$1,
isArguments: isArguments$1,
isFinite: isFinite$1,
isNaN: isNaN$1,
isTypedArray: isTypedArray$1,
isEmpty: isEmpty,
isMatch: isMatch,
isEqual: isEqual,
isMap: isMap,
isWeakMap: isWeakMap,
isSet: isSet,
isWeakSet: isWeakSet,
keys: keys,
allKeys: allKeys,
values: values,
pairs: pairs,
invert: invert,
functions: functions,
methods: functions,
extend: extend,
extendOwn: extendOwn,
assign: extendOwn,
defaults: defaults,
create: create,
clone: clone,
tap: tap,
get: get,
has: has,
mapObject: mapObject,
identity: identity,
constant: constant,
noop: noop,
toPath: toPath$1,
property: property,
propertyOf: propertyOf,
matcher: matcher,
matches: matcher,
times: times,
random: random,
now: now,
escape: _escape,
unescape: _unescape,
templateSettings: templateSettings,
template: template,
result: result,
uniqueId: uniqueId,
chain: chain,
iteratee: iteratee,
partial: partial,
bind: bind,
bindAll: bindAll,
memoize: memoize,
delay: delay,
defer: defer,
throttle: throttle,
debounce: debounce,
wrap: wrap,
negate: negate,
compose: compose,
after: after,
before: before,
once: once,
findKey: findKey,
findIndex: findIndex,
findLastIndex: findLastIndex,
sortedIndex: sortedIndex,
indexOf: indexOf,
lastIndexOf: lastIndexOf,
find: find,
detect: find,
findWhere: findWhere,
each: each,
forEach: each,
map: map,
collect: map,
reduce: reduce,
foldl: reduce,
inject: reduce,
reduceRight: reduceRight,
foldr: reduceRight,
filter: filter,
select: filter,
reject: reject,
every: every,
all: every,
some: some,
any: some,
contains: contains,
includes: contains,
include: contains,
invoke: invoke,
pluck: pluck,
where: where,
max: max,
min: min,
shuffle: shuffle,
sample: sample,
sortBy: sortBy,
groupBy: groupBy,
indexBy: indexBy,
countBy: countBy,
partition: partition,
toArray: toArray,
size: size,
pick: pick,
omit: omit,
first: first,
head: first,
take: first,
initial: initial,
last: last,
rest: rest,
tail: rest,
drop: rest,
compact: compact,
flatten: flatten,
without: without,
uniq: uniq,
unique: uniq,
union: union,
intersection: intersection,
difference: difference,
unzip: unzip,
transpose: unzip,
zip: zip,
object: object,
range: range,
chunk: chunk,
mixin: mixin,
'default': _$1
};
默认导出
将所有 Underscore 函数添加到包装器对象。
var _ = mixin(allExports);
旧版 Node.js API。
_._ = _;
ESM 导出
export default _;
export { VERSION, after, every as all, allKeys, some as any, extendOwn as assign, before, bind, bindAll, chain, chunk, clone, map as collect, compact, compose, constant, contains, countBy, create, debounce, defaults, defer, delay, find as detect, difference, rest as drop, each, _escape as escape, every, extend, extendOwn, filter, find, findIndex, findKey, findLastIndex, findWhere, first, flatten, reduce as foldl, reduceRight as foldr, each as forEach, functions, get, groupBy, has, first as head, identity, contains as include, contains as includes, indexBy, indexOf, initial, reduce as inject, intersection, invert, invoke, isArguments$1 as isArguments, isArray, isArrayBuffer, isBoolean, isDataView$1 as isDataView, isDate, isElement, isEmpty, isEqual, isError, isFinite$1 as isFinite, isFunction$1 as isFunction, isMap, isMatch, isNaN$1 as isNaN, isNull, isNumber, isObject, isRegExp, isSet, isString, isSymbol, isTypedArray$1 as isTypedArray, isUndefined, isWeakMap, isWeakSet, iteratee, keys, last, lastIndexOf, map, mapObject, matcher, matcher as matches, max, memoize, functions as methods, min, mixin, negate, noop, now, object, omit, once, pairs, partial, partition, pick, pluck, property, propertyOf, random, range, reduce, reduceRight, reject, rest, restArguments, result, sample, filter as select, shuffle, size, some, sortBy, sortedIndex, rest as tail, first as take, tap, template, templateSettings, throttle, times, toArray, toPath$1 as toPath, unzip as transpose, _unescape as unescape, union, uniq, uniq as unique, uniqueId, unzip, values, where, without, wrap, zip };