JavaScript 数组遍历动态增长问题(V8源码解析)

2021/5/10 22:25:12

本文主要是介绍JavaScript 数组遍历动态增长问题(V8源码解析),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

数组 arr 在遍历同时动态增长会发生什么呢

let arr = [1,2]
arr.forEach(item => arr.push(...arr))
console.log(arr)
// 输出: [ 1, 2, 1, 2, 1, 2, 1, 2 ]

并未输出 [1,2] 或 [1,2,1,2...] 无限循环下去

JavaScript引擎:V8源码git地址:https://github.com/v8/v8


vscode打开源码文件夹
暂无图片

V8-MASTER/src/builtins-collections-gen.cc

   数组遍历的循环由 Goto 与 BIND 搭配完成,Goto(adress) 指跳转的目标adress , BIND(adress) 指对应接受的目的地adress

  • 记录数组初始信息:所以遍历时Label loop(this, {&var_index, &var_table}), done_loop(this);
    • var_index:已遍历的长度 var_table:可遍历的容量(长度)done_loop循环完成时的地址
    • 在遍历时 push 并未改变 var_table 指向的值
  • Goto(&loop) 跳到 BIND(&loop) Goto(&done_loop)跳到BIND(&done_loop)
  • callback_not_callable 函数不可调用的话也会跳出去
TF_BUILTIN(MapPrototypeForEach, CollectionsBuiltinsAssembler) {
  const char* const kMethodName = "Map.prototype.forEach"; // Array 继承自 MapCollections
  ......
  // Ensure that {callback} is actually callable.
  Label callback_not_callable(this, Label::kDeferred);
  GotoIf(TaggedIsSmi(callback), &callback_not_callable);
  GotoIfNot(IsCallable(CAST(callback)), &callback_not_callable);

  TVARIABLE(IntPtrT, var_index, IntPtrConstant(0));
  TVARIABLE(OrderedHashMap, var_table,
            CAST(LoadObjectField(CAST(receiver), JSMap::kTableOffset)));
  Label loop(this, {&var_index, &var_table}), done_loop(this); // 记录了数组的初始信息,后期判断是否遍历结束的标志
  Goto(&loop);
  BIND(&loop);
  {
    // 中括号内的依然会执行,只是产生了一个块作用域
    // 代码快内部执行完后,会被回收资源
    // Transition {table} and {index} if there was any modification to
    // the {receiver} while we're iterating.
    TNode<IntPtrT> index = var_index.value();
    TNode<OrderedHashMap> table = var_table.value();
    std::tie(table, index) = Transition<OrderedHashMap>(
        table, index, [](const TNode<OrderedHashMap>, const TNode<IntPtrT>) {});

    // Read the next entry from the {table}, skipping holes.
    TNode<Object> entry_key;
    TNode<IntPtrT> entry_start_position;
    std::tie(entry_key, entry_start_position, index) =
        NextSkipHoles<OrderedHashMap>(table, index, &done_loop);

    // Load the entry value as well.
    TNode<Object> entry_value = LoadFixedArrayElement(
        table, entry_start_position,
        (OrderedHashMap::HashTableStartIndex() + OrderedHashMap::kValueOffset) *
            kTaggedSize);

    // Invoke the {callback} passing the {entry_key}, {entry_value} and the
    // {receiver}.
    Call(context, callback, this_arg, entry_value, entry_key, receiver);

    // Continue with the next entry.
    var_index = index;
    var_table = table;
    Goto(&loop);
  }
  BIND(&done_loop);
  args.PopAndReturn(UndefinedConstant());
  BIND(&callback_not_callable);
  ......
}
暂无图片
  • Goto(&loop) 进入 {中括号循环代码块,运行完后会清除资源} 循环

  • NextSkipHoles<OrderedHashMap>(table, index, &done_loop); 判断是否结束,传入了 &done_loop

  • NextSkipHoles 函数找一下源码

    • GotoIfNot 根据比较条件判断是否 Goto(if_end)
    • done_loop 的值传给了 if_end
暂无图片
CollectionsBuiltinsAssembler::NextSkipHoles(TNode<TableType> table,
                                            TNode<IntPtrT> index,
                                            Label* if_end) {
  // 比较已使用的容量与总容量
  // Compute the used capacity for the {table}.
  TNode<IntPtrT> number_of_buckets =
      LoadAndUntagObjectField(table, TableType::NumberOfBucketsOffset());
  TNode<IntPtrT> number_of_elements =
      LoadAndUntagObjectField(table, TableType::NumberOfElementsOffset());
  TNode<IntPtrT> number_of_deleted_elements = LoadAndUntagObjectField(
      table, TableType::NumberOfDeletedElementsOffset());
  TNode<IntPtrT> used_capacity =
      IntPtrAdd(number_of_elements, number_of_deleted_elements);

  TNode<Object> entry_key;
  TNode<IntPtrT> entry_start_position;
  TVARIABLE(IntPtrT, var_index, index);
  Label loop(this, &var_index), done_loop(this);
  Goto(&loop);
  BIND(&loop);
  {
    GotoIfNot(IntPtrLessThan(var_index.value(), used_capacity), if_end);  //判断是否跳出
    entry_start_position = IntPtrAdd(
        IntPtrMul(var_index.value(), IntPtrConstant(TableType::kEntrySize)),
        number_of_buckets);
    entry_key = UnsafeLoadFixedArrayElement(
        table, entry_start_position,
        TableType::HashTableStartIndex() * kTaggedSize);
    Increment(&var_index);
    Branch(IsTheHole(entry_key), &loop, &done_loop);
}


这篇关于JavaScript 数组遍历动态增长问题(V8源码解析)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程