JS中从Array.slice()与Array.splice()的底层实现原理分析区别

本文详细分析了JavaScript中Array.prototype.slice()和Array.prototype.splice()两个方法的工作原理和区别。slice()返回数组的一个浅拷贝而不改变原数组,支持负数索引,而splice()直接修改原数组,可以用于删除、插入元素,其效率相对较低。两者在应用场景和返回值上也有显著差异。

1.Array.prototype.slice()
slice() 方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对像,原始数组不会改变。

arrayObject.slice(start,end)
  • start (可选,如果start不写则从0开始)从该索引处开始提取原数组中的元素。
    如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2)表示提取原数组的倒数第二个元素到最后一个元素(包含最后一个元素)。
  • end(可选)在该索引处结束提取原数组元素(从0开始)。slice会提取原数组中索引从 begin 到 end 的所有元素(包含begin,但不包含end)。
    *1.如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。 slice(-2,-1)表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。
    2.如果 end 被省略,则slice 会一直提取到原数组末尾。
    3.如果 end 大于数组长度,slice 也会一直提取到原数组末尾。*
    原理:
    (应该都能看懂)
// This will work for genuine arrays, array-like objects, 
    // NamedNodeMap (attributes, entities, notations),
    // NodeList (e.g., getElementsByTagName), HTMLCollection (e.g., childNodes),
    // and will not fail on other DOM objects (as do DOM elements in IE < 9)
    Array.prototype.slice = function(begin, end) {
      // IE < 9 gets unhappy with an undefined end argument
      end = (typeof end !== 'undefined') ? end : this.length;

      // For native Array objects, we use the native slice function
      if (Object.prototype.toString.call(this) === '[object Array]'){
        return _slice.call(this, begin, end); 
      }

      // For array like object we handle it ourselves.
      var i, cloned = [],
        size, len = this.length;

      // Handle negative value for "begin"
      var start = begin || 0;
      start = (start >= 0) ? start : Math.max(0, len + start);

      // Handle negative value for "end"
      var upTo = (typeof end == 'number') ? Math.min(end, len) : len;
      if (end < 0) {
        upTo = len + end;
      }

      // Actual expected size of the slice
      size = upTo - start;

      if (size > 0) {
        cloned = new Array(size);
        if (this.charAt) {
          for (i = 0; i < size; i++) {
            cloned[i] = this.charAt(start + i);
          }
        } else {
          for (i = 0; i < size; i++) {
            cloned[i] = this[start + i];
          }
        }
      }

      return cloned;
    };

还可以查看V8源码第587行:直达

2.Array.prototype.splice()
splice()方法通过删除现有元素和/或添加新元素来更改一个数组的内容。
原型:Array.prototype.splice (start, deleteCount, item1, item2, … )

  • start
    指定修改的开始位置(从0计数)。如果超出了数组的长度,则从数组末尾开始添加内容;如果是负值,则表示从数组末位开始的第几位(从1计数)。
  • deleteCount (可选)
    整数,表示要移除的数组元素的个数。如果 deleteCount 是 0,则不移除元素。这种情况下,至少应添加一个新元素。如果 deleteCount 大于start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。
  • item1, item2, …(可选)
    要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。
    源码:
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);
  var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
  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;
  }
  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);
  }

  // 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;
}

还可以查看V8源码第660行:直达
或者开发文档:ECMA5.1文档


请注意
1.splice() 方法会直接对数组进行修改。slice() 不对数组进行修改,原数组不会改变。
2.splice() 返回值为被操作的值。slice() 以新的字符串返回被提取的部分。
3.
[14, 3, 77].slice(1, 2) // [3]
[14, 3, 77].splice(1, 2) // [3, 77]

4.splice先执行删除操作,删除指定个数的元素,然后再插入elements(元素或数组),他的每次删除都涉及大量元素的重新排列,而在插入元素时引入队列来管理。所以splice()的效率不高

<think> 我们被要求解释JavaScriptarray.slice()方法的使用 根据引用[3]:Array.slice()方法返回指定数组的一个片段或子数组。它的两个参数分别指定了片段的开始和结束的位置。 引用[2]和[1]则提到了Array.prototype.slice.call()的用法,用于将类数组对象(如arguments)转换为数组并调用slice方法。 我们将从以下几个方面进行说明: 1. slice方法的基本用法 2. 参数说明(包括负数索引) 3. 不会修改原数组 4. 使用slice将类数组对象转换为数组 根据引用[3]和引用[4]的内容,我们可以详细说明。 注意:用户要求尽可能参考引用,并且在回答中引用段落末尾自然地添加引用标识。 我们将这样组织回答: 一、基本语法 二、参数详解(含示例) 三、注意事项 四、扩展应用(包括将类数组对象转为数组) 最后生成相关问题。 </think>### JavaScript 中 `array.slice()` 方法详解 `array.slice()` 是 JavaScript 数组的核心方法,用于提取数组片段而不修改原数组。以下是关键使用要点: #### 1. **基础用法** - **语法**:`array.slice(start, end)` - **参数**: - `start`:提取起始索引(包含) - `end`:提取结束索引(不包含,可省略) - **特性**: - 返回新数组,原数组不变 - 负索引表示从末尾倒数(如 `-1` 表示最后一个元素)[^3] ```javascript const fruits = ['🍎', '🍌', '🍊', '🍇', '🍓']; // 提取索引1到3(不含3) console.log(fruits.slice(1, 3)); // ['🍌', '🍊'] // 仅指定起始索引 console.log(fruits.slice(2)); // ['🍊', '🍇', '🍓'] // 负索引用法 console.log(fruits.slice(-3, -1)); // ['🍊', '🍇'] ``` #### 2. **特殊场景应用** - **复制整个数组**: ```javascript const copy = fruits.slice(); // ['🍎', '🍌', '🍊', '🍇', '🍓'] ``` - **获取数组首尾元素**: ```javascript const firstTwo = fruits.slice(0, 2); // ['🍎', '🍌'] const lastTwo = fruits.slice(-2); // ['🍇', '🍓'] ``` - **移除首个元素**: ```javascript const remaining = fruits.slice(1); // ['🍌', '🍊', '🍇', '🍓'] ``` #### 3. **类数组对象转换** 使用 `Array.prototype.slice.call()` 可将类数组对象(如 `arguments`)转为真实数组: ```javascript function listArguments() { // arguments 是类数组对象 const argsArray = Array.prototype.slice.call(arguments); console.log(argsArray); // ['A', 'B', 'C'] } listArguments('A', 'B', 'C'); ``` > 直接调用 `arguments.slice()` 会报错,必须通过原型链调用[^1][^2] #### 4. **注意事项** 1. **索引边界**: - 起始索引超范围 → 返回空数组 - 结束索引超范围 → 提取到数组末尾 ```javascript console.log(fruits.slice(10)); // [] console.log(fruits.slice(1, 10)); // ['🍌', '🍊', '🍇', '🍓'] ``` 2. **浅拷贝特性**: ```javascript const items = [{id: 1}, {id: 2}]; const sliced = items.slice(0, 1); sliced[0].id = 99; // 修改会影响原数组 console.log(items); // [{id: 99}, {id: 2}] ``` 3. **对比 `splice()`**: | 方法 | 返回值 | 是否修改原数组 | 参数含义 | |------------|-------------|---------------|-----------------------| | `slice()` | 新数组 | ❌ 否 | (起始索引, 结束索引) | | `splice()` | 被删除的元素 | ✔️ 是 | (起始索引, 删除数量) | --- ###
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值