New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lodash源码研析-数组分久必合concat #4

Open
littlewin-wang opened this Issue Apr 17, 2018 · 0 comments

Comments

1 participant
@littlewin-wang
Copy link
Owner

littlewin-wang commented Apr 17, 2018

我愿在心与心之间架起一道小桥,哪怕只是几块垫脚的青石板也好,将我们琐屑凌乱的生命连接。
----<破解幸福密码>

介绍

concat 函数类似于 ES 规范中的函数作用,用于进行数组拼接。
只是 lodash 在实现的过程中,不断践行自建基石的方针,也做了一些判断处理。

依赖

数组拷贝 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) {
  var index = -1,
      length = source.length;

  // 对array的存在性做一下判断
  array || (array = Array(length));

  while (++index < length) {
    array[index] = source[index];
  }
  return array;
}

module.exports = copyArray;

数组后推入 arrayPush

继续自创push操作,通过操作数组下标进行赋值。

/**
 * 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;

  while (++index < length) {
    array[offset + index] = values[index];
  }
  return array;
}

module.exports = arrayPush;

基础的数组扁平化操作 baseFlatten 👈 (划重点啦)

明显数组和类数组的 arguments 对象,可以通过遍历来展平。
另外在 ES6 中,可以设置 Symbol.isConcatSpreadable 的属性来表示该对象是否可以被展平。 Symbol.isConcatSpreadable 的值如果被设置为真值时,该对象是可以被展平的。

let b = {0: 1, 1: 2, length: 2}

b[Symbol.isConcatSpreadable]
// undefined, 默认无定义
console.log([].concat(b))
// [{…}],没取出其中元素

b[Symbol.isConcatSpreadable] = true
console.log([].concat(b))
// [1, 2],可以被展平
var arrayPush = require('./_arrayPush'),
    // 可扁平化判断
    isFlattenable = require('./_isFlattenable');

/**
 * 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.
 * @param {Array} [result=[]] The initial result value.  
 * @returns {Array} Returns the new flattened array.
 */
function baseFlatten(array, depth, predicate, isStrict, result) {
  var index = -1,
      length = array.length;

  predicate || (predicate = isFlattenable);  // 预检查函数,不传指定为 isFlattenable
  result || (result = []);  // 期望放到指定的数组里,不传默认为一个空数组

  while (++index < length) {
    var value = array[index];
    if (depth > 0 && predicate(value)) {
      if (depth > 1) {
        // 递归扁平化操作
        // Recursively flatten arrays (susceptible to call stack limits).
        baseFlatten(value, depth - 1, predicate, isStrict, result);
      } else {
        arrayPush(result, value);
      }
    } else if (!isStrict) {
      // 非严格模式下,只做一层扁平化
      result[result.length] = value;
    }
  }
  return result;
}

module.exports = baseFlatten;

源码

/**
 * 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),
      array = arguments[0],
      index = length;

  while (index--) {
    args[index - 1] = arguments[index];
  }

  // 对参数列表做了一级扁平化处理
  return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));
}

module.exports = concat;

总结

  • lodash 工具函数充分考虑了对传入参数的保护,尽可能小的影响参数。
  • lodash 通过一些选项判断赋予函数更高的自由度和容错率。

引用

本文使用「 署名 4.0 国际」创作共享协议。
本文同步发布于Littlewin's Blog,欢迎多多交流。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment