var VERSION = '1.13.6';
Underscore.js 1.13.6
https://underscorejs.dev.org.tw
(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]':
RegExps 會強制轉換為字串進行比較(注意:‘’ + /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 插補或從 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 插補中將字串轉譯回來的 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*$/;
JavaScript 微型範本,類似於 John Resig 的實作。底線範本處理任意分隔符號,保留空白,並正確跳脫插補程式碼中的引號。注意: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;
}
產生一個唯一的整數識別碼(在整個用戶端工作階段中唯一)。對於暫時性 DOM 識別碼很有用。
var idCounter = 0;
function uniqueId(prefix) {
var id = ++idCounter + '';
return prefix ? prefix + id : id;
}
開始連結一個包裝的底線物件。
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 次呼叫(不包含)。
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
給 isSorted 以使用二元搜尋。
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);
};
}
簡化 從一個值清單建立一個單一結果,又名 inject
或 foldl
。
var reduce = createReduce(1);
簡化的右結合版本,又名 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);
}
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;
}
將所有 mutator 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);
};
});
將所有 accessor 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 };