Wang's blog Wang's blog
首页
  • 前端文章

    • HTML教程
    • CSS
    • JavaScript
  • 前端框架

    • Vue
    • React
    • VuePress
    • Electron
  • 后端技术

    • Npm
    • Node
    • TypeScript
  • 编程规范

    • 规范
  • 我的笔记
  • Git
  • GitHub
  • VSCode
  • Mac工具
  • 数据库
  • Google
  • 服务器
  • Python爬虫
  • 前端教程
更多
收藏
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Wang Mings

跟随大神,成为大神!
首页
  • 前端文章

    • HTML教程
    • CSS
    • JavaScript
  • 前端框架

    • Vue
    • React
    • VuePress
    • Electron
  • 后端技术

    • Npm
    • Node
    • TypeScript
  • 编程规范

    • 规范
  • 我的笔记
  • Git
  • GitHub
  • VSCode
  • Mac工具
  • 数据库
  • Google
  • 服务器
  • Python爬虫
  • 前端教程
更多
收藏
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Python爬虫

  • 前端教程

    • 团队规范

    • Project

    • JS

      • Canvas基础
      • 数据结构
      • 树的深度优先遍历与广度优先遍历
      • for in和for of区别
      • ES6-新增特性一览
      • ES6-解构赋值及原理
      • ES6-Object
      • ES6-模块详解
      • ES6-Class
      • ES6-ECMAScript特性汇总
      • 输入URL背后的技术步骤
      • JavaScript与浏览器 - 线程与引擎
      • HTTP跨域解决方案
      • Http 2与Http 1.x比较
      • JavaScript原型
      • JavaScript继承
      • JavaScript事件循环
      • 动手实现Promise
      • JS设计模式
      • JS 经典面试题
      • 排序算法
      • 正则表达式
      • MVC、MVP、MVVM区别
      • Array API与V8源码解析
        • [#](#array-reduce-callback-initialvalue) array.reduce\(callback\[, initialValue\]\)
        • [#](#array-slice-begin-end) array.slice\(\[begin\[, end\]\]\)
        • [#](#array-splice-start-deletecount-item1-item2) array.splice\(start\[, deleteCount\[, item1\[, item2\[, ...\]\]\]\]\)
      • 从V8 sort源码看插入排序
    • NodeJS

    • Vue

    • React

    • 效率工具

    • 读书笔记

  • 教程
  • 前端教程
  • JS
wangmings
2022-07-19
目录

Array API与V8源码解析

# # Array API与V8源码解析

在阅读You-Dont-Need-Lodash-Underscore (opens new window) (opens new window)源码时,发现很多关于array数组方法的技巧,由此了解之前对array数组方法有很多细节点没有深入应用。故重新翻开V8 array源码 (opens new window) (opens new window),记录下一些array高级API以及其源码。

# # array.reduce(callback[, initialValue])

对每项执行reducer函数,返回迭代结果。

  • callback(accumulator, currentValue, currentIndex, array)
  • initialValue
    • 如果有设置,则作为初始值循环
    • 如果没有设置,则使用array第一项作为初始值
    // 无初始值
    [1, 2, 3].reduce((pre, cur, index) => {
        l(pre,cur)
        return pre + cur
    })
    // first loop: 1 2
    // second loop: 3 3
    
    // 有初始值
    [1, 2, 3].reduce((pre, cur, index) => {
        l(pre,cur)
        return pre + cur
    }, 0)
    // first loop: 0 1
    // second loop: 1 2
    // third loop: 3 3
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

源码在1223行 (opens new window) (opens new window)

    function ArrayReduce(callback, current) {
      CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduce");
    
      // Pull out the length so that modifications to the length in the
      // loop will not affect the looping and side effects are visible.
      var array = TO_OBJECT(this);
      var length = TO_LENGTH(array.length);
      return InnerArrayReduce(callback, current, array, length,
                              arguments.length);
    }
    
    function InnerArrayReduce(callback, current, array, length, argumentsLength) {
      if (!IS_CALLABLE(callback)) {
        throw %make_type_error(kCalledNonCallable, callback);
      }
    
      var i = 0;
      // 当只有callback参数时,设置current为第一个参数
      find_initial: if (argumentsLength < 2) {
        for (; i < length; i++) {
          if (i in array) {
            current = array[i++]; // 修改current值
            break find_initial;
          }
        }
        throw %make_type_error(kReduceNoInitial);
      }
    
      // callback迭代
      for (; i < length; i++) {
        if (i in array) {
          var element = array[i];
          // 每次return返回的值作为current值
          current = callback(current, element, i, array);
        }
      }
      return current;
    }
    
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# # array.slice([begin[, end]])

复制数组。返回数组从下标begin到end(不包含end)的新数组,原数组不变。

  • begin
    • 如果begin省略,则从0开始
    • 如果begin超过数组长度,则直接返回[]空数组
    • begin为负数,一般等同于begin+arr.length。如果读String.prototype.slice源码,也是这种处理。
  • end
    • 如果end省略,则为arr.length
    • 如果end超过数组长度,则为arr.length
    • end为负数,一般等同于end+arr.length
    // base
    var arr = [1, 2, 3, 4, 5]
    
    console.log(arr.slice()) // [1, 2, 3, 4, 5]
    console.log(arr.slice(2)) // [3, 4, 5]
    console.log(arr.slice(-2, 4)) // [4] 等于 arr.slice(-2+5, 4)
    console.log(arr.slice(-2)) // [4, 5] 等于 arr.slice(-2+5, +5)
    
1
2
3
4
5
6
7
8

V8源码在587行 (opens new window) (opens new window):

    function ArraySlice(start, end) {
      CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
    
      var array = TO_OBJECT(this);
      var len = TO_LENGTH(array.length);
      var start_i = TO_INTEGER(start); // 默认是0
      var end_i = len; // 默认是array.length
    
    // 定义end则赋值
      if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);
    
    // 处理start为负数或大于数组长度处理
      if (start_i < 0) {
        start_i += len;
        if (start_i < 0) start_i = 0;
      } else {
        if (start_i > len) start_i = len;
      }
    
    // 处理end为负数或大于数组长度
      if (end_i < 0) {
        end_i += len;
        if (end_i < 0) end_i = 0;
      } else {
        if (end_i > len) end_i = len;
      }
    
    // 创建指定长度的array数组
      var result = ArraySpeciesCreate(array, MaxSimple(end_i - start_i, 0));
    
    // start超过end,直接返回[]
      if (end_i < start_i) return result;
    
      if (UseSparseVariant(array, len, IS_ARRAY(array), end_i - start_i)) {
        // 应对array的变种时,进行处理
        %NormalizeElements(array);
        if (IS_ARRAY(result)) %NormalizeElements(result);
        SparseSlice(array, start_i, end_i - start_i, len, result);
      } else {
        SimpleSlice(array, start_i, end_i - start_i, len, result);
      }
    
      result.length = end_i - start_i;
    
      return result;
    }
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

以上源码也给我们解释了[].slice.call(object)返回对应数组。

    let arrayLike = {
     '0':'a',
     '1':'b',
     '2':'c',
     length: 3 // 长度不符合则超出的长度对象都是undefined
    }
    console.log([].slice.call(arrayLike)) //[a,b,c]
    
1
2
3
4
5
6
7
8

# # array.splice(start[, deleteCount[, item1[, item2[, ...]]]])

增加/删除数组。通过add/remove改变最终数组

  • start
    • 当大于数组长度时,则认为是array.length
  • deleteCount
    • 当这个值省略或者大于array.length - start时,start后面的都会删除
    var arr = [1, 2, 3, 4, 5]
    
    // splice会改变原数组,以下注视都是单独console.log
    arr.splice(1, 0, 10); // [ 1, 10, 2, 3, 4, 5 ]
    arr.splice(1, 1, 10); // [ 1, 10, 3, 4, 5 ]
    arr.splice(100, 1, 10); // [ 1, 2, 3, 4, 5, 10 ]
    arr.splice(1) // [ 1 ]
    arr.splice(1, 100) // [ 1 ]
    arr.splice(1, 100, ['new1', 'new2']) // [ 1, [ 'new1', 'new2' ] ]
    arr.splice(1, 100, 'new1', 'new2') // [ 1, 'new1', 'new2' ]
    
1
2
3
4
5
6
7
8
9
10
11
    function ArraySplice(start, delete_count) {
      CHECK_OBJECT_COERCIBLE(this, "Array.prototype.splice");
    
      var num_arguments = arguments.length;
      var array = TO_OBJECT(this);
      var len = TO_LENGTH(array.length);
      // start处理,超过长度取array.length
      var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
      // deleteCount处理
      var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
                                               start_i);
      var deleted_elements = ArraySpeciesCreate(array, del_count);
      deleted_elements.length = del_count;
      var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
    
      if (del_count != num_elements_to_add && %object_is_sealed(array)) {
        throw %make_type_error(kArrayFunctionsOnSealed);
      } else if (del_count > 0 && %object_is_frozen(array)) {
        throw %make_type_error(kArrayFunctionsOnFrozen);
      }
    
      var changed_elements = del_count;
      if (num_elements_to_add != del_count) {
        // If the slice needs to do a actually move elements after the insertion
        // point, then include those in the estimate of changed elements.
        changed_elements += len - start_i - del_count;
      }
    
      // 删除先slice,再move
      if (UseSparseVariant(array, len, IS_ARRAY(array), changed_elements)) {
        %NormalizeElements(array);
        if (IS_ARRAY(deleted_elements)) %NormalizeElements(deleted_elements);
        SparseSlice(array, start_i, del_count, len, deleted_elements);
        SparseMove(array, start_i, del_count, len, num_elements_to_add);
      } else {
        SimpleSlice(array, start_i, del_count, len, deleted_elements);
        SimpleMove(array, start_i, del_count, len, num_elements_to_add);
      }
    
      // 如果有第三、四...数据,从start处开始插入数据
      // Insert the arguments into the resulting array in
      // place of the deleted elements.
      var i = start_i;
      var arguments_index = 2;
      var arguments_length = arguments.length;
      while (arguments_index < arguments_length) {
        array[i++] = arguments[arguments_index++];
      }
      array.length = len - del_count + num_elements_to_add;
    
      // Return the deleted elements.
      return deleted_elements;
    }
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
编辑 (opens new window)
MVC、MVP、MVVM区别
从V8 sort源码看插入排序

← MVC、MVP、MVVM区别 从V8 sort源码看插入排序→

最近更新
01
theme-vdoing-blog博客静态编译问题
09-16
02
搜索引擎
07-19
03
友情链接
07-19
更多文章>
Theme by Vdoing | Copyright © 2019-2022 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式