JavaScript 严格模式下 arguments 的区别
标签:#前端 #JavaScript #严格模式 #arguments #学习笔记
一、arguments 是什么?
arguments 是函数内部的类数组对象(Array-like),包含了函数调用时传入的所有实参。
function foo() {
console.log(arguments); // [1, 2, 3]
console.log(arguments[0]); // 1
console.log(arguments.length); // 3
}
foo(1, 2, 3);
⚠️ 它不是真正的数组,没有
push、map、forEach等方法(除非用Array.from()转换)。
二、如何开启严格模式
// 全局严格模式
'use strict';
// 函数级严格模式
function bar() {
'use strict';
// 此函数内启用严格模式
}
三、严格模式 vs 非严格模式的核心区别
🔴 区别一:arguments 不可被修改来影响命名参数(最重要)
非严格模式(二者联动)
function foo(a, b) {
console.log(a, b); // 1, 2
arguments[0] = 100;
console.log(a, b); // 100, 2 ← a 被改变了!
}
foo(1, 2);
非严格模式下,修改
arguments[n]会同步修改对应的命名参数。
严格模式(二者独立)
'use strict';
function foo(a, b) {
console.log(a, b); // 1, 2
arguments[0] = 100;
console.log(a, b); // 1, 2 ← a 没变!
}
foo(1, 2);
严格模式下,
arguments和命名参数完全独立,互不影响。
🔴 区别二:arguments.callee 被禁用
非严格模式(可用)
function factorial(n) {
if (n <= 1) return 1;
return n * arguments.callee(n - 1); // ✅ 正常执行
}
console.log(factorial(5)); // 120
arguments.callee指向当前正在执行的函数本身,常用于匿名函数递归。
严格模式(报错)
'use strict';
function factorial(n) {
if (n <= 1) return 1;
return n * arguments.callee(n - 1); // ❌ TypeError!
}
严格模式下访问
arguments.callee会直接抛出TypeError。
✅ 替代方案
// 方案1:命名函数直接调用自身
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
// 方案2:使用箭头函数 + 函数名(尾递归友好)
const factorial = (n) => {
if (n <= 1) return 1;
return n * factorial(n - 1);
};
// 方案3:用函数表达式赋给变量
const factorial = function f(n) {
if (n <= 1) return 1;
return n * f(n - 1);
};
🔴 区别三:arguments.caller 被禁用
非严格模式
function inner() {
console.log(arguments.caller); // 返回调用 inner 的外部函数
}
function outer() {
inner();
}
outer();
严格模式
'use strict';
function inner() {
console.log(arguments.caller); // ❌ TypeError!
}
arguments.caller和arguments.callee在严格模式下都被禁止,原因是它们存在安全隐患(可以访问调用栈)。
🟡 区别四:arguments 不会追踪剩余参数(Spread Rest)
严格模式引入了 ...rest 语法,它与 arguments 的行为完全不同:
'use strict';
function foo(a, ...rest) {
console.log(arguments.length); // 实参个数
console.log(rest.length); // 剩余参数个数
}
foo(1, 2, 3, 4);
// arguments.length → 4(所有实参)
// rest.length → 3(去掉 a 之后的部分:[2, 3, 4])
关键区别:
arguments:类数组,包含所有实参...rest:真正的数组,只包含未匹配命名参数的部分
四、完整对比表
特性
非严格模式
严格模式
修改 arguments[n] 影响命名参数
✅ 会影响
❌ 不影响
arguments.callee
✅ 可用
❌ TypeError
arguments.caller
✅ 可用
❌ TypeError
arguments 与 rest 参数共存
✅ 可共存
✅ 可共存(但行为独立)
五、为什么严格模式要限制 arguments?
1. 性能优化
非严格模式下,JS 引擎必须维护 arguments 和参数之间的双向绑定关系,这导致无法对函数参数进行某些优化。严格模式下二者独立,引擎可以更高效地处理参数。
2. 安全性
arguments.callee 和 arguments.caller 允许访问调用栈,存在被利用来进行安全攻击的风险。
3. 代码可读性
现代 JS 推荐使用命名函数或rest 参数替代 arguments 的各种黑魔法,代码更清晰。
六、现代推荐写法
'use strict';
// ❌ 旧写法:依赖 arguments
function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
// ✅ 新写法:使用 rest 参数
function sum(...nums) {
return nums.reduce((acc, n) => acc + n, 0);
}
// ✅ 新写法:rest + 命名参数结合
function log(prefix, ...messages) {
messages.forEach(msg => console.log(prefix, msg));
}
log('[INFO]', 'hello', 'world');
总结
严格模式的核心改变:将
arguments从一个"神奇的对象"变成了一个普通的类数组,切断了它与命名参数的隐式绑定,并禁用了不安全的callee/caller属性。在现代 JS 开发中,推荐使用...rest参数替代arguments。