1. 程式人生 > 實用技巧 >硬剛 lodash 原始碼之路,compact & concat

硬剛 lodash 原始碼之路,compact & concat

前置

本篇隨筆包含 _.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;