技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTJS

JavaScript 的 arguments 详解

所属馆藏
JavaScript
文件格式
Markdown
原始路径
JavaScript/47-js的arguments详解
本文目录10 个章节
  1. 一、arguments 是什么
  2. 二、哪些函数拥有 arguments
  3. 三、它是类数组对象,不是数组
  4. 四、arguments 与 rest 参数
  5. 五、映射的 arguments 和非映射的 arguments
  6. 六、arguments 的常用场景
  7. 七、不要依赖 arguments.callee 和 arguments.caller
  8. 八、常见误区
  9. 九、总结
  10. 参考资料

JavaScript 的 arguments 详解

Category(分类): JavaScript Status: 已整理(2026)

原文只有标题和来源链接。这里补齐 arguments 的规范行为、常见误区和现代替代写法,重点保留它在面试题、可变参数函数和参数转发中的使用价值。

原文:js 的 arguments 详解

一、arguments 是什么

arguments 是普通函数内部可用的一个类数组对象,表示本次调用实际传入的所有参数。它有从 0 开始的数字索引和 length 属性,但它不是 Array 实例:

function inspect(first, second) {
  console.log(arguments[0]) // first
  console.log(arguments[1]) // second
  console.log(arguments[2]) // 即使没有形参,也能访问额外参数
  console.log(arguments.length) // 实际传入的参数数量
  console.log(Array.isArray(arguments)) // false
}

inspect('a', 'b', 'c')

它表示“调用者传入了什么”,而函数的 length 属性表示“函数声明中第一个带默认值参数之前有多少个形参”:

function example(a, b, c = 3, ...rest) {}

console.log(example.length) // 2:只计算默认参数之前的形参

function count(a, b) {
  return arguments.length
}

console.log(count(1)) // 1
console.log(count(1, 2, 3)) // 3

arguments.lengthfunction.length 解决的是不同问题,不能混用。

二、哪些函数拥有 arguments

arguments 是函数调用上下文中的局部绑定,普通函数、方法、构造函数和 class 构造器中都可以使用;箭头函数没有自己的 arguments

function normal() {
  return arguments.length
}

const object = {
  method() {
    return arguments[0]
  }
}

class Example {
  constructor() {
    this.count = arguments.length
  }
}

console.log(normal(1, 2)) // 2
console.log(object.method('value')) // value
console.log(new Example(1, 2).count) // 2

箭头函数如果直接放在模块或脚本顶层,不能凭空获得一个函数参数列表;如果它嵌套在普通函数中,则会捕获外层普通函数的 arguments

function outer(first, second) {
  const readArguments = () => arguments[1]
  return readArguments()
}

console.log(outer('a', 'b')) // b

不要把“箭头函数没有自己的 arguments”理解为“箭头函数永远不能访问名为 arguments 的变量”;它可以通过词法作用域读取外层绑定。

三、它是类数组对象,不是数组

arguments 有数字索引和 length,但没有 mapfilterforEach 等数组实例方法:

function listArguments() {
  console.log(typeof arguments) // object
  console.log(arguments.map) // undefined

  const array = Array.from(arguments)
  return array.map(value => String(value).toUpperCase())
}

console.log(listArguments('a', 'b')) // ['A', 'B']

现代 JavaScript 可以直接使用以下方式转换:

function toArray() {
  const byFrom = Array.from(arguments)
  const bySpread = [...arguments]
  const bySlice = Array.prototype.slice.call(arguments)

  return { byFrom, bySpread, bySlice }
}

console.log(toArray(1, 2, 3))

arguments 在现代 ECMAScript 中是可迭代的,因此展开语法可用;非常老的运行环境可能只支持 slice.call。新代码更推荐 rest 参数,因为它直接创建真正的数组。

四、arguments 与 rest 参数

历史代码经常这样写:

function sum() {
  return Array.from(arguments).reduce((total, value) => total + value, 0)
}

console.log(sum(1, 2, 3)) // 6

现代写法是 rest 参数:

function sumModern(...numbers) {
  return numbers.reduce((total, value) => total + value, 0)
}

console.log(sumModern(1, 2, 3)) // 6

二者的主要区别:

特性argumentsrest 参数
类型类数组对象真正的数组
是否有自己的绑定普通函数有,箭头函数没有由参数声明直接提供
只收集部分参数需要索引或 slicefunction f(first, ...rest)
与形参联动非严格简单参数可能联动是独立数组值
可读性适合维护旧代码新代码首选
可用于箭头函数不能创建自己的 arguments可以
function logFirstAndRest(first, ...rest) {
  console.log(first) // 第一个参数
  console.log(rest) // 其余参数组成的新数组
}

logFirstAndRest('first', 'second', 'third')

如果只需要转发全部参数,rest 与 spread 的组合通常最清楚:

function callWithLogging(fn, ...args) {
  console.log('calling with', args)
  return fn(...args)
}

console.log(callWithLogging(Math.max, 3, 1, 5)) // 5

五、映射的 arguments 和非映射的 arguments

这是 arguments 最容易被忽略的历史细节。

5.1 非严格模式的简单参数

非严格模式、且函数使用简单参数列表时,具名形参与 arguments 的对应索引可能共享同一参数绑定:

function sloppy(a) {
  arguments[0] = 99
  console.log(a) // 99

  a = 100
  console.log(arguments[0]) // 100
}

sloppy(1)

这里的“简单参数列表”不包含默认参数、rest 参数或解构参数,例如 function f(a, b) 是简单的,function f(a = 1) 不是。

5.2 严格模式中不再联动

严格模式下,形参和 arguments 索引互相独立:

function strictExample(a) {
  'use strict'

  arguments[0] = 99
  console.log(a) // 1

  a = 100
  console.log(arguments[0]) // 99
}

strictExample(1)

ES modules 和 class 方法默认使用严格模式。

5.3 非简单参数列表也不联动

即使没有显式写 'use strict',默认参数、rest 或解构参数也会让 arguments 与具名参数保持独立:

function withDefault(value = 10) {
  arguments[0] = 99
  console.log(value) // 1

  value = 100
  console.log(arguments[0]) // 99
}

withDefault(1)

另外,带默认参数、rest 或解构参数的函数体不能再使用函数级的 'use strict' 指令:

// SyntaxError:非简单参数列表的函数体不能写函数级 use strict 指令
// function invalid(value = 1) {
//   'use strict'
// }

如果需要严格模式,可以让整个脚本或模块处于严格模式,而不是在该函数体中单独写指令。

六、arguments 的常用场景

6.1 实现可变参数函数

function longestString() {
  if (arguments.length === 0) {
    throw new TypeError('至少传入一个字符串')
  }

  let longest = ''
  for (const value of arguments) {
    if (value.length > longest.length) longest = value
  }
  return longest
}

console.log(longestString('a', 'hello', 'js')) // hello

新代码可以使用 rest:

function longestStringModern(...values) {
  if (values.length === 0) throw new TypeError('至少传入一个字符串')
  return values.reduce((longest, value) =>
    value.length > longest.length ? value : longest,
    ''
  )
}

6.2 保留第一个形参,再处理剩余参数

function concat(separator) {
  const values = Array.prototype.slice.call(arguments, 1)
  return values.join(separator)
}

console.log(concat(', ', 'red', 'green', 'blue')) // red, green, blue

使用 rest 参数更直观:

function concatModern(separator, ...values) {
  return values.join(separator)
}

6.3 通过 apply 转发类数组参数

Function.prototype.apply 接受数组或类数组对象,所以旧代码可以直接把 arguments 传给它:

function midpoint() {
  return (
    (Math.min.apply(null, arguments) + Math.max.apply(null, arguments)) / 2
  )
}

console.log(midpoint(3, 1, 4, 1, 5)) // 3

现代写法:

function midpointModern(...values) {
  return (Math.min(...values) + Math.max(...values)) / 2
}

如果要保留 this,使用 Reflect.applyfn.apply(context, args)

function invoke(fn, context, argsLike) {
  return Reflect.apply(fn, context, argsLike)
}

function add(first, second) {
  return this.base + first + second
}

console.log(invoke(add, { base: 10 }, [1, 2])) // 13

七、不要依赖 arguments.calleearguments.caller

历史代码有时通过 arguments.callee 获得当前函数:

function factorial(n) {
  if (n <= 1) return 1
  return n * arguments.callee(n - 1)
}

这种写法会阻碍优化、破坏可读性,并且在严格模式中访问 arguments.callee 会抛出 TypeError。直接给函数命名即可:

const factorial = function factorial(n) {
  if (n <= 1) return 1
  return n * factorial(n - 1)
}

console.log(factorial(5)) // 120

arguments.caller 也不是可靠的标准调用栈 API。需要调试调用栈时使用开发者工具、Error().stack(注意它是宿主相关信息)或显式传递上下文。

八、常见误区

8.1 arguments 不会自动深拷贝参数

function mutate(first) {
  first.value = 2
}

const object = { value: 1 }
mutate(object)
console.log(object.value) // 2:传入的是对象引用

arguments[0] 保存的是传入值;对象值仍然指向原对象。是否复制对象与 arguments 无关。

8.2 arguments 只属于当前函数

function outer() {
  console.log(arguments.length) // 2

  function inner() {
    console.log(arguments.length) // 1:这是 inner 的 arguments
  }

  inner('inner value')
}

outer('a', 'b')

如果内部使用箭头函数,则会词法捕获外层的 arguments;如果使用普通函数,则拥有自己的 arguments

8.3 形参没有赋值时也可能有索引

function optional(first, second) {
  console.log(arguments.length)
  console.log(arguments[1])
}

optional('only first') // 1,undefined

arguments[1]undefined 不代表调用者实际传入了第二个参数。需要判断是否真的传入,可以结合 arguments.lengthObject.hasOwn(arguments, '1')

function wasProvided(value) {
  return arguments.length > 0
}

console.log(wasProvided(undefined)) // true
console.log(wasProvided()) // false

九、总结

  • arguments 是普通函数内部的类数组对象,记录实际传参;
  • arguments.length 是实际参数数量,Function.length 是声明参数数量的另一种统计;
  • 箭头函数没有自己的 arguments,但可以捕获外层普通函数的 arguments
  • 非严格模式的简单参数可能与 arguments 索引联动,严格模式和非简单参数不会;
  • arguments 没有数组方法,使用 Array.from、展开语法或 slice.call 转换;
  • 新代码优先使用 rest 参数;维护旧代码时要注意 arguments.callee 在严格模式中不可用;
  • 参数对象不会改变对象引用语义,也不会自动复制或验证参数。

参考资料

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS