[已解决]Lodash源码之isFlattenable的一个疑问

业务背景

看他这个源码,感觉是个bug,但是这个库怎么会有bug呢,肯定是自己哪里搞错了,但实在是不知道错在哪儿

疑问

  • baseFlatten函数的一个参数predicate默认值是一个函数isFlattenable
  • isFlattenable通过判断数组/类数组/Symbol.isConcatSpreadable来确定对象是否可以被解构...

但是

复现

var b = [2, 3];
console.log([].push(...b)); // => [2, 3];
b[Symbol.isConcatSpreadable] = false;
console.log([].push(...b)); // => [2, 3]

源代码
https://github.com/ranwawa/lo...

isFlattenable

import isArguments from '../isArguments.js';
const spreadableSymbol = Symbol.isConcatSpreadable;

function isFlattenable(value) {
  return Array.isArray(value) || isArguments(
    value) || !!(value && value[spreadableSymbol]);
}

baseFlatten
注意倒数第7行



// 深度合并数组的操作
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 {
        // 即使value的Symbol.isConcatSpreadable为false,也可以被解构啊
        result.push(...value)
      }
    } else if (!isStrict) {
      result[result.length] = value
    }
  }
  return result
}

问题

我是直接看的master分支,难道是因为我看的分支出错的原因?

阅读 568
评论
    2 个回答
    • 46.9k

    因为 isFlattenable 是给 flatten 族函数用的。而 flatten 的实现是按照 tc39/flat 提案实现的,在规范中 flat 中并没有使用 @@iterator

    有一个非常简单的解释:因为字符串也是可迭代的。比如下面代码:

    ['hello', 'world'].flat()

    如果输出的结果是 ['h', 'e', 'l', 'l', ...] 是不是很怪异。

    因为 Array 的大多数方法的出现都比 @@iterator 要早,所以如果你去看数组函数,Array.from 可能是目前唯一一个是用了 @@iterator 的函数。

    所以,这个函数没有 bug 吗?并不是。

    这个函数的实现和规范并不兼容,但是问题不是在于 @@iterator,而是在于 @@isConcatSpreadable。根据规范,flat 函数是不检查 @@isConcatSpreadable 的。

    官方的意见是,我们是按照 tc39/flat 提案实现的这个函数,但是后来提案变了。

    如果 lodash 改变了这个函数,就会导致一个 break change。

    如果你真想研究规范是如何用 js 实现的,推荐看看 core-js 源码,真是教科书式的源码。而 lodash 毕竟是第三方工具库,看 lodash 源码就先忽略规范,只关注他的奇技淫巧就够了。

      能解构不代表一定要展开。策略问题,默认仅展开数组拼接(concat)时会展开的那些元素。如果要把所有能展开的全部展开,可以自定义判断函数。

        撰写回答

        登录后参与交流、获取后续更新提醒