Lodash 源码阅读-initCloneArray
概述
initCloneArray
是 Lodash 内部用于初始化数组克隆的函数,主要用于创建一个与原数组相同长度的新数组,并且当原数组具有特定属性(如通过正则表达式匹配产生的数组属性)时,会将这些特定属性也复制到新数组中。
前置学习
依赖函数
- 无直接依赖函数
相关技术知识
- JavaScript 数组构造器(Array Constructor)
- RegExp.exec() 方法产生的结果数组的特殊属性
- JavaScript 对象属性的检测与复制
- hasOwnProperty 方法的使用
源码实现
js
/**
* Initializes an array clone.
*
* @private
* @param {Array} array The array to clone.
* @returns {Array} Returns the initialized clone.
*/
function initCloneArray(array) {
var length = array.length,
result = new array.constructor(length);
// Add properties assigned by `RegExp#exec`.
if (
length &&
typeof array[0] == "string" &&
hasOwnProperty.call(array, "index")
) {
result.index = array.index;
result.input = array.input;
}
return result;
}
实现思路
initCloneArray
函数的实现思路比较简单:
- 首先获取原数组的长度
- 使用原数组的构造器创建一个相同长度的新数组(这样可以保证克隆结果与原数组类型一致)
- 然后检查是否需要复制 RegExp.exec() 方法产生的特殊属性:
- 检查数组长度是否大于 0
- 检查数组第一个元素是否为字符串类型
- 检查数组是否有自己的 'index' 属性(RegExp.exec() 结果特有)
- 如果满足上述条件,则将原数组的 index 和 input 属性复制到新数组中
- 最后返回初始化后的克隆数组
源码解析
获取原数组长度并创建新数组
js
var length = array.length,
result = new array.constructor(length);
这里使用 array.constructor
而不是直接使用 Array
构造函数,是为了保证创建的新数组与原数组具有相同的类型。这在处理类数组对象或特殊数组(如 TypedArray)时特别重要。
示例:
js
// 普通数组
const arr1 = [1, 2, 3];
const newArr1 = new arr1.constructor(arr1.length); // 等同于 new Array(3)
// 类型化数组
const arr2 = new Uint8Array([1, 2, 3]);
const newArr2 = new arr2.constructor(arr2.length); // 创建 Uint8Array 而非普通数组
复制 RegExp.exec() 的特殊属性
js
if (
length &&
typeof array[0] == "string" &&
hasOwnProperty.call(array, "index")
) {
result.index = array.index;
result.input = array.input;
}
这段代码处理的是当数组来自 RegExp#exec()
方法调用的特殊情况。
当使用正则表达式的 exec 方法匹配字符串时,返回的数组会带有两个特殊属性:
index
: 表示匹配的文本在原字符串中的位置input
: 表示原始的字符串
下面是一个例子:
js
const regex = /c/;
const result = regex.exec("abcde");
console.log(result); // ['c', index: 2, input: 'abcde', groups: undefined]
console.log(result.index); // 2
console.log(result.input); // 'abcde'
函数通过三个条件来判断是否为 exec 结果:
- 数组长度大于 0(
length
) - 第一个元素是字符串(
typeof array[0] == 'string'
) - 数组拥有自己的
index
属性(hasOwnProperty.call(array, 'index')
)
如果满足这些条件,就将原数组的 index
和 input
属性复制到新数组中,以保持这些特殊属性。
应用场景
在 Lodash 内部的应用
initCloneArray
主要用在 Lodash 的 clone
和 cloneDeep
方法内部,是 baseClone
函数的一部分。当需要克隆数组类型的值时,会使用该函数初始化克隆结果:
js
// 在 baseClone 函数中的应用
if (isArr) {
result = initCloneArray(value);
if (!isDeep) {
return copyArray(value, result);
}
}
如果不是进行深度克隆,会在初始化数组后通过 copyArray
函数复制数组元素。如果是深度克隆,则会在后续代码中递归处理每个数组元素。
总结
initCloneArray
函数虽然短小,但体现了 Lodash 源码的几个核心设计思想:
- 类型保持:通过使用原对象的构造器而不是硬编码的 Array 构造函数,确保克隆结果与原始数据类型一致
- 特殊情况处理:识别并处理 RegExp.exec() 结果这种特殊情况,保证克隆的完整性
- 职责单一:该函数只负责初始化数组克隆,不负责复制数组元素内容,符合单一职责原则
这种精细的类型处理和特殊情况的考虑,是 Lodash 库稳定性和适用性广泛的重要原因之一。在我们自己实现工具函数时,也应当学习这种严谨和全面的思考方式。