1. 什么是 arguments
MDN 上诠释:
arguments 是一个类数组对象。代表传给一个function的参数列表。
我们先用一个例子直观相识下 JavaScript 中的 arguments 长什么模样。
function printArgs() {
console.log(arguments);
}
printArgs("A", "a", 0, { foo: "Hello, arguments" });
实行效果是:
["A", "a", 0, Object]
乍一看,效果是个数组,但并非真正的数组,所以说 arguments 是一个类数组的对象(想相识真正数组与类数组对象的区分能够一向翻到末了)。
再看看 arguments 示意的内容,其示意了函数实行时传入函数的一切参数。在上面的例子中,代表了传入 printArgs
函数中的四个参数,能够分别用 arguments[0]
、 arguments[1]
… 来猎取单个的参数。
2. arguments 操纵
2.1 arguments length
arguments 是个类数组对象,其包括一个 length
属性,能够用 arguments.length
来获得传入函数的参数个数。
function func() {
console.log("The number of parameters is " + arguments.length);
}
func();
func(1, 2);
func(1, 2, 3);
实行效果以下:
The number of parameters is 0
The number of parameters is 2
The number of parameters is 3
2.2 arguments 转数组
平常运用下面的要领来将 arguments 转换成数组:
Array.prototype.slice.call(arguments);
另有一个更简短的写法:
[].slice.call(arguments);
在这里,只是简朴地挪用了空数组的 slice 要领,而没有从 Array 的原型层面挪用。
为何上面两种要领能够转换呢?
起首,slice 要领获得的效果是一个数组,参数就是 arguments。事实上,满足肯定前提的对象都能被 slice 要领转换成数组。看个例子:
const obj = { 0: "A", 1: "B", length: 2 };
const result = [].slice.call(obj);
console.log(Array.isArray(result), result);
实行效果是:
true ["A", "B"]
从上面例子能够看出,前提就是: 1) 属性为 0,1,2…;2) 具有 length 属性;
别的,有一个须要注重的处所就是,不能将函数的 arguments 泄漏或许通报出去。什么意思呢?看下面的几个泄漏 arguments 的例子:
// Leaking arguments example1:
function getArgs() {
return arguments;
}
// Leaking arguments example2:
function getArgs() {
const args = [].slice.call(arguments);
return args;
}
// Leaking arguments example3:
function getArgs() {
const args = arguments;
return function() {
return args;
};
}
上面的做法就直接将函数的 arguments 对象泄漏出去了,终究的效果就是 V8 引擎将会跳过优化,致使相当大的机能丧失。
你能够这么做:
function getArgs() {
const args = new Array(arguments.length);
for(let i = 0; i < args.length; ++i) {
args[i] = arguments[i];
}
return args;
}
那就很猎奇了,我们每次运用 arguments 时平常第一步都邑将其转换为数组,同时 arguments 运用不当还轻易致使机能丧失,那末为何不将 arguments 直接设想成数组对象呢?
这须要从这门言语的一开始提及。arguments 在言语的初期就引入了,当时的 Array 对象具有 4 个要领: toString、 join、 reverse 和 sort。arguments 继续于 Object 的很大原因是不须要这四个要领。而如今,Array 添加了许多壮大的要领,比方 forEach、map、filter 等等。那为何如今不在新的版本里让 arguments 从新继续自 Array呢?实在 ES5 的草案中就包括这一点,但为了向前兼容,终究照样被委员会反对了。
2.3 修正 arguments 值
在严厉形式与非严厉形式下,修正函数参数值表现的效果不一样。看下面的两个例子:
function foo(a) {
"use strict";
console.log(a, arguments[0]);
a = 10;
console.log(a, arguments[0]);
arguments[0] = 20;
console.log(a, arguments[0]);
}
foo(1);
输出:
1 1
10 1
10 20
另一个非严厉形式的例子:
function foo(a) {
console.log(a, arguments[0]);
a = 10;
console.log(a, arguments[0]);
arguments[0] = 20;
console.log(a, arguments[0]);
}
foo(1);
输出效果为:
1 1
10 10
20 20
从上面的两个例子中能够看出,在严厉形式下,函数中的参数与 arguments 对象没有联络,修正一个值不会转变另一个值。而在非严厉形式下,两个会相互影响。
2.4 将参数从一个函数通报到另一个函数
下面是将参数从一个函数通报到另一个函数的引荐做法。
function foo() {
bar.apply(this, arguments);
}
function bar(a, b, c) {
// logic
}
2.5 arguments 与重载
许多言语中都有重载,但 JavaScript 中没有。先看个例子:
function add(num1, num2) {
console.log("Method one");
return num1 + num2;
}
function add(num1, num2, num3) {
console.log("Method two");
return num1 + num2 + num3;
}
add(1, 2);
add(1, 2, 3);
实行效果为:
Method two
Method two
所以,JavaScript 中,函数并没有依据参数的差别而发生差别的挪用。
是否是 JavaScript 中就没有重载了呢?并非,我们能够应用 arguments 模仿重载。照样上面的例子。
function add(num1, num2, num3) {
if (arguments.length === 2) {
console.log("Result is " + (num1 + num2));
}
else if (arguments.length === 3) {
console.log("Result is " + (num1 + num2 + num3));
}
}
add(1, 2);
add(1, 2, 3)
实行效果以下:
Result is 3
Result is 6
3. ES6 中的 arguments
3.1 扩大操纵符
直接上栗子:
function func() {
console.log(...arguments);
}
func(1, 2, 3);
实行效果是:
1 2 3
简约地讲,扩大操纵符能够将 arguments 展开成自力的参数。
3.2 Rest 参数
照样上栗子:
function func(firstArg, ...restArgs) {
console.log(Array.isArray(restArgs));
console.log(firstArg, restArgs);
}
func(1, 2, 3);
实行效果是:
true
1 [2, 3]
从上面的效果能够看出,Rest 参数示意除了明白指定剩下的参数鸠合,范例是 Array。
3.3 默许参数
栗子:
function func(firstArg = 0, secondArg = 1) {
console.log(arguments[0], arguments[1]);
console.log(firstArg, secondArg);
}
func(99);
实行效果是:
99 undefined
99 1
可见,默许参数对 arguments 没有影响,arguments 照样仅仅示意挪用函数时所传入的一切参数。
3.4 arguments 转数组
Array.from()
是个异常引荐的要领,其能够将一切类数组对象转换成数组。
4. 数组与类数组对象
数组具有一个基本特征:索引。这是平常对象所没有的。
const obj = { 0: "a", 1: "b" };
const arr = [ "a", "b" ];
我们应用 obj[0]
、arr[0]
都能获得本身想要的数据,但获得数据的体式格局确切差别的。obj[0]
是应用对象的键值对存取数据,而 arr[0]
倒是应用数组的索引。事实上,Object 与 Array 的唯一区分就是 Object 的属性是 string,而 Array 的索引是 number。
下面看看类数组对象。
伪数组的特征就是长得像数组,包括一组数据以及具有一个 length 属性,然则没有任何 Array 的要领。再详细的说,length 属性是个非负整数,上限是 JavaScript 中能准确表达的最大数字;别的,类数组对象的 length 值没法自动转变。
怎样本身建立一个类数组对象?
function Foo() {}
Foo.prototype = Object.create(Array.prototype);
const foo = new Foo();
foo.push('A');
console.log(foo, foo.length);
console.log("foo is an array? " + Array.isArray(foo));
实行效果是:
["A"] 1
foo is an array? false
也就是说 Foo 的示例具有 Array 的一切要领,但范例不是 Array。
假如不须要 Array 的一切要领,只须要部份怎么办呢?
function Bar() {}
Bar.prototype.push = Array.prototype.push;
const bar = new Bar();
bar.push('A');
bar.push('B');
console.log(bar);
实行效果是:
Bar {0: "A", 1: "B", length: 2}
参考: