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
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
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
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
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
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
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
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)