Open tiantingrui opened 3 years ago
JS中一直存在一种类数组的对象,他们不能直接调用数组的方法,但是又和数组比较类似,在某些特定的编程场景中会出现。
最常见的类数组对象就是函数中的arguments
先来重点讲讲 arguments 对象,我们在日常开发中经常会遇到各种类数组对象,最常见的便是在函数中使用的 arguments,它的对象只定义在函数体中,包括了函数的参数和其他属性。我们通过一段代码来看下 arguments 的使用方法,如下所示。
function foo(name, age, sex) {
console.log(arguments);
console.log(typeof arguments);
console.log(Object.prototype.toString.call(arguments));
}
foo('jack', '18', 'male');
从结果中可以看到,typeof 这个 arguments 返回的是 object,通过 Object.prototype.toString.call 返回的结果是 '[object arguments]',可以看出来返回的不是 '[object array]',说明 arguments 和数组还是有区别的。
length 属性很好理解,它就是函数参数的长度,我们从打印出的代码也可以看得出来。另外可以看到 arguments 不仅仅有一个 length 属性,还有一个 callee 属性,如果在函数内部直接执行调用 callee 的话,那它就会不停地执行当前函数,直到执行到内存溢出。
HTMLCollection 简单来说是 HTML DOM 对象的一个接口,这个接口包含了获取到的 DOM 元素集合,返回的类型是类数组对象,如果用 typeof 来判断的话,它返回的是 'object'。它是及时更新的,当文档中的 DOM 变化时,它也会随之变化。
NodeList 对象是节点的集合,通常是由 querySelector 返回的。
NodeList 可以使用 for of 来迭代。
我们在函数内部可以直接获取 arguments 这个类数组的值,那么也可以对于参数进行一些操作,比如下面这段代码,我们可以将函数的参数默认进行求和操作
function add() {
var sum =0,
len = arguments.length;
for(var i = 0; i < len; i++){
sum += arguments[i];
}
return sum;
}
add() // 0
add(1) // 1
add(1,2) // 3
add(1,2,3,4); // 10
我们可以通过 arguments 这个例子定义一个函数来连接字符串。这个函数唯一正式声明了的参数是一个字符串,该参数指定一个字符作为衔接点来连接字符串。该函数定义如下。
function myConcat(separa) {
var args = Array.prototype.slice.call(arguments, 1);
return args.join(separa);
}
myConcat(", ", "red", "orange", "blue");
// "red, orange, blue"
myConcat("; ", "elephant", "lion", "snake");
// "elephant; lion; snake"
myConcat(". ", "one", "two", "three", "four", "five");
// "one. two. three. four. five"
这段代码说明了,你可以传递任意数量的参数到该函数,并使用每个参数作为列表中的项创建列表进行拼接。从这个例子中也可以看出,我们可以在日常编码中采用这样的代码抽象方式,把需要解决的这一类问题,都抽象成通用的方法,来提升代码的可复用性。
借助 arguments 将参数从一个函数传递到另一个函数,请看下面这个例子。
// 使用 apply 将 foo 的参数传递给 bar
function foo() {
bar.apply(this, arguments);
}
function bar(a, b, c) {
console.log(a, b, c);
}
foo(1, 2, 3) //1 2 3
上述代码中,通过在 foo 函数内部调用 apply 方法,用 foo 函数的参数传递给 bar 函数,这样就实现了借用参数的妙用。
function sum(a, b) {
let args = Array.prototype.slice.call(arguments);
// let args = [].slice.call(arguments); // 这样写也是一样效果
console.log(args.reduce((sum, cur) => sum + cur));
}
sum(1, 2); // 3
function sum(a, b) {
let args = Array.prototype.concat.apply([], arguments);
console.log(args.reduce((sum, cur) => sum + cur));
}
sum(1, 2); // 3
<!DOCTYPE html>
对于类数组转换成数组的方式,我们还可以采用 ES6 新增的 Array.from 方法以及展开运算符的方法。
function sum(a, b) {
let args = Array.from(arguments);
console.log(args.reduce((sum, cur) => sum + cur));
}
sum(1, 2); // 3
function sum(a, b) {
let args = [arguments];
console.log(args.reduce((sum, cur) => sum + cur));
}
sum(1, 2); // 3
function sum(args) {
console.log(args.reduce((sum, cur) => sum + cur));
}
sum(1, 2); // 3
从代码中可以看出,Array.from 和 ES6 的展开运算符,都可以把 arguments 这个类数组转换成数组 args,从而实现调用 reduce 方法对参数进行累加操作。其中第二种和第三种都是用 ES6 的展开运算符,虽然写法不一样,但是基本都可以满足多个参数实现累加的效果。
方法/特征 | 数组 | 类数组 |
---|---|---|
自带方法 | 多个方法 | 无 |
length属性 | 有 | 有 |
callee 属性 | 无 | 有 |
var arr = [1, [2, [3, 4]]];
// 1. 普通递归实现
const flatten = (arr) => {
let rst = [];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
rst = rst.concat(flatten(arr[i]));
} else {
rst.push(arr[i]);
}
}
return rst;
};
// 2. split 和 toString
const flattent = (arr) => {
return arr.toString().split(",");
};
// 3. 扩展运算符实现
const flatten = (arr) => {
while (arr.some((item) => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
};
// 4. ES6 flat
const flatten = (arr) => {
return arr.flat(Infinity);
};
深入JS数组