每天一个lodash方法-concat

腱鞘炎犯了。第三天就没坚持住,罪过罪过。

concat源码链接

功能

创建一个用任何数组 或 值连接的新数组。

使用方法

 * var array = [1];
 * var other = _.concat(array, 2, [3], [[4]]);
 *
 * console.log(other);
 * // => [1, 2, 3, [4]]
 *
 * console.log(array);
 * // => [1]

注意,会返回一个新数组,并且不改变传入的数组。

源码分析

源码里有100多行,显着有点乱,但是读下来,还是有很多值得思考的地方。

var MAX_SAFE_INTEGER = 9007199254740991;

JS中Number类型的最大值,2的32次幂指-1


/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
    funcTag = '[object Function]',
    genTag = '[object GeneratorFunction]';

在js中判断类型我们可以借用Object.prototype.toString方法来判断类型。 下边是demo

var toString =Object.prototype.toString
var a = function(){console.log(toString.call(arguments))}
// [object Arguments]

link

var root = freeGlobal || freeSelf || Function('return this')();

这里我比较注意的Function('return this')(),我在chrome里执行这段代码,返回的是全局对象。

通过google查到了原因。

Functions created with the Function constructor do not create closures to their creation contexts; they always are created in the global scope. When running them, they will only be able to access their own local variables and global ones, not the ones from the scope in which the Function constructor was called. This is different from using eval with code for a function expression.

使用Function构造函数创建的函数不会创建对其创建上下文的闭包;他们总是在全局创建。执行时,它们只能访问它们自己的局部变量和全局变量,而不能访问函数构造函数调用的范围。这与使用eval解析函数表达式的代码不同。

link


arrayPushconcat中用到的工具方法。他接收两个数组,返回一个新的数组。

function arrayPush(array, values) {
  var index = -1,
      length = values.length, //传入的数组长度
      offset = array.length; //偏离值,values并入array,当然要从array的最后一位往里传。

  while (++index < length) { // loop 操作。
    array[offset + index] = values[index];
  }
  return array;
}

arrayPush([1,2,3],[4,5,6])

//[1, 2, 3, 4, 5, 6]

var objectProto = Object.prototype; //保留Object.prototype的引用。

var hasOwnProperty = objectProto.hasOwnProperty; // 这个方法判断是否是该对象上的自建属性,而不是prototype上的。

var objectToString = objectProto.toString; //保留引用。加快访问该方法的速度

/**
 * The base implementation of `_.flatten` with support for restricting flattening.
 *  _.flatten的基本实现方法
 * @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.//返回一个新的被扁平化的数组
 */
// 传入的方式 baseFlatten([[1,2,3,4,],1],1,false) =>

function baseFlatten(array, depth, predicate, isStrict, result) {
  var index = -1, //初使下标
      length = array.length;//需要被扁平的长度 => 2

  predicate || (predicate = isFlattenable);// 这里貌似只能传false,或者一个方法  => isFlattenable
  result || (result = []); // result => []

  while (++index < length) {
    var value = array[index]; // 0: [1,2,3,4]  depth:1   1: 1, 从1的位置就走到else if里了。
    if (depth > 0 && predicate(value)) {//predicate(value)  返回true or false,默认情况下是用来判断value是否是可被打平的。  depath 如果是2的话,就会将更高阶的数组打平,然后递归调用而传入result。
      if (depth > 1) {
        // Recursively flatten arrays (susceptible to call stack limits).
        baseFlatten(value, depth - 1, predicate, isStrict, result);
      } else {
        arrayPush(result, value); //arrayPush([],[1,2,3,4]) => result=[1,2,3,4]
      }
    } else if (!isStrict) {
      result[result.length] = value;
    }
  }
  return result;
}

/**
 * Checks if `value` is a flattenable `arguments` object or array.
 * 返回传入的value 是否是一个可以被打平的 arguments 对象或者数组
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
 */
function isFlattenable(value) {
  return isArray(value) || isArguments(value) ||
    !!(spreadableSymbol && value && value[spreadableSymbol]);
}

正主concat来了

//concat([1,2,3,4],[5,6,7,8])
function concat() { 
  var length = arguments.length, // 2
      args = Array(length ? length - 1 : 0),//arg = [empty]
      array = arguments[0],// [1,2,3,4]
      index = length;//2

  while (index--) {
    // 1
    args[index - 1] = arguments[index];   }
  // args[0]= [1,2,3,4] args[-1]=[5,6,7,8]
  return length
    ? arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1))
    : [];
}
//isArray(array) ? copyArray(array) : [array] =>   [1,2,3,4]
//baseFlatten(args, 1) => [5,6,7,8]
// return这里就成了 arrayPush([1,2,3,4],[5,6,7,8]) => [1,2,3,4,5,6,7,8]

/**
 * 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(length)); // array 不存在就创建一个和source长度一样的数组
  while (++index < length) {
    array[index] = source[index]; // 赋值进去,返回copy的数组,不改变原有的数组
  }
  return array;
}

你可能感兴趣的