前置
本篇隨筆包含 _.compact
和 _.concat
及其依賴的工具函式。
你可能需要一些 JavaScript 基礎知識才能看懂一些沒有註釋的細節。
compact
_.compact(array)
建立一個新陣列,包含原陣列中所有的非假值元素。例如 false
, null
, 0
, ""
, undefined
, 和 NaN
都是被認為是“假值”。
/**
* Creates an array with all falsey values removed. The values `false`, `null`,
* `0`, `""`, `undefined`, and `NaN` are falsey.
*
* @since 0.1.0
* @category Array
* @param {Array} array The array to compact.
* @returns {Array} Returns the new array of filtered values.
* @example
*
* compact([0, 1, false, 2, '', 3])
* // => [1, 2, 3]
*/
function compact(array) {
let resIndex = 0
const result = []
if (array == null) {
return result
}
// for of 迴圈 array
// resIndex 自增賦值符合條件的陣列
for (const value of array) {
if (value) {
result[resIndex++] = value
}
}
return result
}
export default compact
arrayPush
/**
* Appends the elements of `values` to `array`.
*
* @private
* @param {Array} array The array to modify.
* @param {Array} values The values to append.
* @returns {Array} Returns `array`.
*/
function arrayPush(array, values) {
var index = -1,
length = values.length,
offset = array.length;
// 迴圈往array結尾追加values的元素
while (++index < length) {
array[offset + index] = values[index];
}
return array;
}
module.exports = arrayPush;
對比 v8 的 push
實現:
// Appends the arguments to the end of the array and returns the new
// length of the array. See ECMA-262, section 15.4.4.7.
function ArrayPush() {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push");
var array = TO_OBJECT(this);
var n = TO_LENGTH(array.length); // 被push的array的length
var m = arguments.length;
// Subtract n from kMaxSafeInteger rather than testing m + n >
// kMaxSafeInteger. n may already be kMaxSafeInteger. In that case adding
// e.g., 1 would not be safe.
if (m > kMaxSafeInteger - n) throw %make_type_error(kPushPastSafeLength, m, n);
for (var i = 0; i < m; i++) {
array[i+n] = arguments[i]; // 複製元素
}
var new_length = n + m; // // 修正length屬性的值
array.length = new_length;
return new_length;
}
isObjectLike
檢查 value
是否是類物件。如果一個值不是 null
,並且 typeof
結果為 object
,則該值為類物件。
/**
* Checks if `value` is object-like. A value is object-like if it's not `null`
* and has a `typeof` result of "object".
*
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
* @example
*
* isObjectLike({})
* // => true
*
* isObjectLike([1, 2, 3])
* // => true
*
* isObjectLike(Function)
* // => false
*
* isObjectLike(null)
* // => false
*/
function isObjectLike(value) {
return typeof value === 'object' && value !== null
}
export default isObjectLike
isArguments
import getTag from './.internal/getTag.js' // 在上一篇文章中解釋了這個方法
import isObjectLike from './isObjectLike.js'
/**
* Checks if `value` is likely an `arguments` object.
*
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an `arguments` object, else `false`.
* @example
*
* isArguments(function() { return arguments }())
* // => true
*
* isArguments([1, 2, 3])
* // => false
*/
function isArguments(value) {
// 已經解釋過 toStringTag
return isObjectLike(value) && getTag(value) == '[object Arguments]'
}
export default isArguments
isFlattenable
檢查 value
是否為可扁平化的 arguments
物件或陣列。
import isArguments from '../isArguments.js'
/** Built-in value reference. */
const spreadableSymbol = Symbol.isConcatSpreadable
/**
* Checks if `value` is a flattenable `arguments` object or array.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
*/
function isFlattenable(value) {
// Symbol.isConcatSpreadable 用於配置某物件作為 Array.prototype.concat() 方法的引數時是否展開其陣列元素。
// 參考 https://segmentfault.com/q/1010000021953491 的採納答案
return Array.isArray(value) || isArguments(value) ||
!!(value && value[spreadableSymbol])
}
export default isFlattenable
baseFlatten
import isFlattenable from './isFlattenable.js'
/**
* The base implementation of `flatten` with support for restricting flattening.
*
* @private
* @param {Array} array The array to flatten. // 要扁平化的陣列。
* @param {number} depth The maximum recursion depth. // 最大遞迴深度。
* @param {boolean} [predicate=isFlattenable] The function invoked per iteration. // 每次迭代呼叫的函式。
* @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. // 只限於通過 "predicate"檢查的數值。
* @param {Array} [result=[]] The initial result value. // 初始結果值
* @returns {Array} Returns the new flattened array. // 返回新的扁平化陣列。
*/
function baseFlatten(array, depth, predicate, isStrict, result) {
predicate || (predicate = isFlattenable)
result || (result = [])
if (array == null) {
return result
}
for (const value of array) {
if (depth > 0 && predicate(value)) {
if (depth > 1) {
// Recursively flatten arrays (susceptible to call stack limits).
// 遞迴扁平化陣列(易受呼叫棧限制)。
baseFlatten(value, depth - 1, predicate, isStrict, result)
} else {
result.push(...value)
}
} else if (!isStrict) {
result[result.length] = value
}
}
return result
}
export default baseFlatten
copyArray
/**
* Copies the values of `source` to `array`.
*
* @private
* @param {Array} source The array to copy values from.
* @param {Array} [array=[]] The array to copy values to.
* @returns {Array} Returns `array`.
*/
function copyArray(source, array) {
let index = -1
const length = source.length
array || (array = new Array(length))
while (++index < length) {
array[index] = source[index]
}
return array
}
export default copyArray
isArray
/**
* Checks if `value` is classified as an `Array` object.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an array, else `false`.
* @example
*
* _.isArray([1, 2, 3]);
* // => true
*
* _.isArray(document.body.children);
* // => false
*
* _.isArray('abc');
* // => false
*
* _.isArray(_.noop);
* // => false
*/
var isArray = Array.isArray;
module.exports = isArray;
concat
_.concat(array, [values])
建立一個新陣列,將 array 與任何陣列或值連線在一起。
var arrayPush = require('./_arrayPush'),
baseFlatten = require('./_baseFlatten'), // 扁平化陣列
copyArray = require('./_copyArray'),
isArray = require('./isArray');
/**
* Creates a new array concatenating `array` with any additional arrays
* and/or values.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Array
* @param {Array} array The array to concatenate.
* @param {...*} [values] The values to concatenate.
* @returns {Array} Returns the new concatenated array.
* @example
*
* var array = [1];
* var other = _.concat(array, 2, [3], [[4]]);
*
* console.log(other);
* // => [1, 2, 3, [4]]
*
* console.log(array);
* // => [1]
*/
function concat() {
var length = arguments.length; // 引數數量
if (!length) {
return [];
}
var args = Array(length - 1), // [empty]
array = arguments[0], // 原陣列
index = length;
// args 加入除原陣列以外的引數其他引數的陣列或值
while (index--) {
args[index - 1] = arguments[index];
}
// 若原陣列是 Array 型別,拷貝一遍陣列返回,反之建立一個陣列
// 將 args 扁平化並 push 到原陣列
return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));
}
module.exports = concat;