深入了解Array.prototype.slice.call(arguments)
- 对于Array.prototype.slice.call(arguments),我网上一搜,大部分都只是讲解了作用
“把类数组对象转为数组对象,而并没有分析其原理 - 下面让我来谈谈我对它的理解
1.首先介绍它的两种写法
- Array.prototype.slice.call(arguments) 或者 [].shift.call(arguments ) 这两种写法都能把 伪数组arguments转换成真正的数组
- es6语法中新增了Array.from(),所以上述类型的对象可以Array.from(obj)就直接转化成数组
//第一种调用call只改变this指向为 arguments
function add() {
console.log(arguments) // {0: 7, 1:8, 2:9, length: 2 其他....}
var _args = Array.prototype.slice.call(arguments)
console.log(_args) // [7,8,9]
}
add(7,8,9)
// 第二种调用call改变this指向为 arguments,并且传入start,end
function add1() {
console.log(arguments) // {0: 7, 1:8, 2:9, length: 2 其他....}
var _args1 = Array.prototype.slice.call(arguments,0,1) // this = rguments ,从第一位开始剪切,剪切一个
console.log(_args1) // [7]
}
add1(7,8,9)
2.call()
- call()不多介绍,想必大家也知道,它在这里只是起到改变slice()里面的this指向,使它指向arguments(不懂call,或忘记了的话,请到这里学习call
3.arr.slice()方法用法
- 作用: 剪切数组,并返回一个新的数组,不会改变原来的数组
- 语法: arr.slice(start,end)
-
当然,讲到这还是不能解释Array.prototype.slice.call(arguments)如何将arguments变成真正的数组,start : 必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。 也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。 end: 可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。 如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。 如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。
让我们接着看下面arr.slice()源码分析
4.arr.slice()源码分析
v8引擎中slice源码地址第587行
function ArraySlice(start, end) {
// 对this进行检查,判断其是否可以具象化为对象变量,不行则抛出异常
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
// this具象化为对象变量,也就是this指向的数组本身
var array = TO_OBJECT(this);
// length合法性处理, 它必须是一个有效范围内的整数。 转换数组长度
var len = TO_LENGTH(array.length);
// start转换为整数 start_i (从哪里开始剪切 => 开始值)
var start_i = TO_INTEGER(start);
// (剪切多少位=>结束值) 默认是array.length
var end_i = len;
// 原本 end_i = len 如果形参end不等于undefined, 则end_i = end
if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);
// 如果传入的start为负数,则从尾部开始推算
if (start_i < 0) {
start_i += len;
// 绝对值大于len(数组长度) 则start_i赋值为0
if (start_i < 0) start_i = 0;
} else {
// start_i大于len(数组长度) 则start_i = len(数组长度)
if (start_i > len) start_i = len;
}
// 以上总结 start_i为负数值从尾部开始推算,最小值是0,最大值是数组的长度.
// end_i(结束值)为负数,从尾部往前推算
if (end_i < 0) {
end_i += len;
// end_i(结束值)最小值为0
if (end_i < 0) end_i = 0;
} else {
// end_i(结束值)最大值为len(数组长度)
if (end_i > len) end_i = len;
}
// 创建指定长度新数组result // MaxSimple(a,b) 取其中最大的值
var result = ArraySpeciesCreate(array, MaxSimple(end_i - start_i, 0));
// 返回空数组
if (end_i < start_i) return result;
// array是数组
if (UseSparseVariant(array, len, IS_ARRAY(array), end_i - start_i)) {
%NormalizeElements(array);
if (IS_ARRAY(result)) %NormalizeElements(result);
SparseSlice(array, start_i, end_i - start_i, len, result);
} else {
// array不是数组
SimpleSlice(array, start_i, end_i - start_i, len, result);
}
result.length = end_i - start_i;
return result;
}
/*
* array 具体操作的非数组
* start_i 开始位置
* del_count 新数组esult.length
* len array.length
* deleted_elements 新数组result
*/
// 处理array不是数组(伪数组=>具有length属性)
function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
for (var i = 0; i < del_count; i++) {
var index = start_i + i;
if (index in array) {
var current = array[index];
// CreateDataProperty接受参数O(一个对象)、P(一个属性键)和V(一个ECMAScript语言值)
%CreateDataProperty(deleted_elements, i, current);
}
}
}
5.总结
- Array.prototype.slice.call(arguments)
1.首先调用call(arguments),使得slice()方法中的this指向了arguments,
2.然后通过4.arr.slice()源码分析,我们就能知道slice()是如何将伪数组arguments转为真正的数组.