arguments是一个对应于传递给函数参数的类数组(array-like)对象。它长得像数组,但是并不是数组,本质上是个对象。
function foo(num1, num2, num3) {
console.log(arguments);
}
foo(10, 20, 30, 40, 50)
我们在浏览器中运行看到的结果。我们可以看到这个对象中有Symbol(Symbol.iterator),我们后续进行介绍。
它不是一个数组类型,而是一个对象类型,但是它却拥有数组的一些特性,例如获取参数的长度,根据索引值获取某一个参数。但是它没有数组的方法,例如forEach、map等。
function foo(num1, num2, num3) {
console.log(arguments);
console.log(arguments.length);
console.log(arguments[1]);
console.log(arguments[2]);
}
另外,我们可以通过callee获取当前arguments所在的函数。
function foo(num1, num2, num3) {
console.log(arguments);
console.log(arguments.callee);
}
foo(10, 20, 30, 40, 50)
注意:我们不能在函数内部再去调用arguments.callee,会进入死循环。
arguments转成数组
我们说arguments是个伪数组,那么怎么把它变成真的数组呢?
- Array.prototype.slice.call(arguments)
- Array.from(arguments)
- 展开运算符[...arguments]
方式一: 自己遍历arguments中的所有元素
function foo(num1, num2) {
var newArr = []
for(let i = 0; i <= arguments.length; i++) {
newArr.push(arguments[i] * 10)
}
console.log(newArr);
}
foo(10, 20, 30, 40)
方式二: Array.prototype.slice.call将arguments转成array
function foo(num1, num2) {
var newArr2 = Array.prototype.slice.call(arguments)
console.log(newArr2); // [ 10, 20, 30, 40 ]
}
foo(10, 20, 30, 40)
我们知道数组有slice方法,这是因为数组的原型Array.prototype上有slice方法,Array.prototype.slice()。 例如我们现在有一个数组,我们可以直接调用slice方法,这是因为Array的原型上有slice方法。
var names = [1, 2, 3]
names.slice()
如果我们没有像names这样的具体的数组,还是想调用slice方法,那么我们就直接这样调用Array.prototype.slice(),那为什么还需要使用call或者apply呢? 如果直接进行调用Array.prototpye.slice(),那么slice方法里面的this指向的是调用者—调用这个slice方法的数组原型。
Array.prototype.slice = function() {
console.log(this);
}
Array.prototype.slice()
但是我们拿到原型是没有意义的,我们想要拿到的是可遍历的对象。如果我们进行call调用,那么现在slice方法中的this进行了显示绑定,指向的是['aaa', 'bbb', 'ccc']这个数组。
Array.prototype.slice.call(['aaa', 'bbb', 'ccc'])
那么有什么用呢?其实Array.prototype.slice方法里面是这样实现的。
Array.prototype.slice = function () {
console.log(this);
var arr = this // 拿到['aaa', 'bbb', 'ccc']这个数组
var newArr = []
for (var i = 0; i < arr.length; i++) {
newArr.push(arr[i])
}
return newArr
}
// 接收到新的数组
var newArray = Array.prototype.slice.call(['aaa', 'bbb', 'ccc'])
我们知道slice是可以传参数的。
var names = [1, 2, 3]
names.slice(1, 2)
Array.prototype.slice方法方法也会接收参数。
Array.prototype.slice = function (start, end) {
console.log(this);
var arr = this
// 不传参数
start = start || 0
end = end || arr.length
var newArr = []
for (var i = start; i < end; i++) {
newArr.push(arr[i])
}
return newArr
}
我们来试验下。
Array.prototype.mySlice = function (start, end) {
// console.log(this);
var arr = this
var newArr = []
for (var i = start; i < end; i++) {
newArr.push(arr[i])
}
return newArr
}
var newArray = Array.prototype.mySlice.call(['aaa', 'bbb', 'ccc'], 1, 3)
console.log(newArray);
现在我们传入的是数组,那么我们传入不是数组的arguments会怎么样呢?不会影响。为什么?因为slice内部是进行了遍历,而arguments是可以进行遍历的。
无论是数组还是arguments,Array.prototype.slice.call()都可以传入,因为它的内部无非是进行遍历,然后返回一个新的数组中,不传索引,默认返回整个长度。
所以我们也可以直接通过[].slice.call(arguments)获取新的数组。[].slice()直接调用slice方法内部的this指向的是[],因为进行了隐式绑定。而通过[].slice.call(arguments)调用,this指向了arguments,对arguments进行遍历返回新的数组。
function foo(num1, num2) {
/* var newArr1 = Array.prototype.mySlice.call(arguments)
console.log(newArr1); */
var newArr2 = [].slice.call(arguments)
console.log(newArr2);
}
方式三:Array.from(arguments)
在ES6中,还提供了Array.from方法,里面还可以传入可迭代的东西或者ArrayLike伪数组。它的内部也进行了遍历并返回一个数组。
function foo(num1, num2) {
var newArr1 = Array.prototype.slice.call(arguments)
console.log(newArr1);
var newArr2 = [].slice.call(arguments)
console.log(newArr2);
var newArr3 = Array.from(arguments)
console.log(newArr3);
}
方式四:展开运算符
展开运算符相当于遍历整个arguments里面的所有元素,然后挨个放到新的数组中。
function foo(num1, num2) {
var newArr1 = Array.prototype.slice.call(arguments)
console.log(newArr1);
var newArr2 = [].slice.call(arguments)
console.log(newArr2);
var newArr3 = Array.from(arguments)
console.log(newArr3);
var newArr4 = [...arguments]
console.log(newArr4);
}
箭头函数中没有arguments
箭头函数没有arguments,它会去上层作用域查找arguments,如果没有,一层层往上查找,直到全局。在浏览器中是没有arguments的,而在node中是有arguments的。
案例一
var foo = () => {
console.log(arguments);
}
foo()
箭头函数中没有arguments,它会找到全局。
在浏览器中运行。
在node中运行。
案例二
function foo () {
var bar = () => {
console.log(arguments); // [Arguments] { '0': 123 }
}
return bar
}
var fn = foo(123)
fn()
箭头函数找到上层作用域foo中的arguments。
案例三
如果想要传递额外参数并且获取额外参数,可以通过剩余参数获取。
var foo = (num1, num2, ...args) => {
console.log(args); // [ 30, 40, 50 ]
}
foo(10, 20, 30, 40, 50)