JavaScript 严格模式下 arguments 的区别

标签:#前端 #JavaScript #严格模式 #arguments #学习笔记

一、arguments 是什么?

arguments 是函数内部的类数组对象(Array-like),包含了函数调用时传入的所有实参。

javascript 复制代码
function foo() {
  console.log(arguments);       // [1, 2, 3]
  console.log(arguments[0]);    // 1
  console.log(arguments.length); // 3
}
foo(1, 2, 3);

⚠️ 它不是真正的数组 ,没有 pushmapforEach 等方法(除非用 Array.from() 转换)。

二、如何开启严格模式

javascript 复制代码
// 全局严格模式
'use strict';

// 函数级严格模式
function bar() {
  'use strict';
  // 此函数内启用严格模式
}

三、严格模式 vs 非严格模式的核心区别

🔴 区别一:arguments 不可被修改来影响命名参数(最重要)

非严格模式(二者联动)

scss 复制代码
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] 会同步修改对应的命名参数。

严格模式(二者独立)

javascript 复制代码
'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 被禁用

非严格模式(可用)

javascript 复制代码
function factorial(n) {
  if (n <= 1) return 1;
  return n * arguments.callee(n - 1);  // ✅ 正常执行
}
console.log(factorial(5)); // 120

arguments.callee 指向当前正在执行的函数本身,常用于匿名函数递归。

严格模式(报错)

javascript 复制代码
'use strict';
function factorial(n) {
  if (n <= 1) return 1;
  return n * arguments.callee(n - 1);  // ❌ TypeError!
}

严格模式下访问 arguments.callee 会直接抛出 TypeError

✅ 替代方案

kotlin 复制代码
// 方案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 被禁用

非严格模式

scss 复制代码
function inner() {
  console.log(arguments.caller);  // 返回调用 inner 的外部函数
}
function outer() {
  inner();
}
outer();

严格模式

javascript 复制代码
'use strict';
function inner() {
  console.log(arguments.caller);  // ❌ TypeError!
}

arguments.callerarguments.callee 在严格模式下都被禁止,原因是它们存在安全隐患(可以访问调用栈)。

🟡 区别四:arguments 不会追踪剩余参数(Spread Rest)

严格模式引入了 ...rest 语法,它与 arguments 的行为完全不同:

javascript 复制代码
'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.calleearguments.caller 允许访问调用栈,存在被利用来进行安全攻击的风险。

3. 代码可读性

现代 JS 推荐使用命名函数rest 参数 替代 arguments 的各种黑魔法,代码更清晰。

六、现代推荐写法

javascript 复制代码
'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

相关推荐
whuhewei24 分钟前
为什么客户端不存在跨域问题
前端·安全
妮妮喔妮38 分钟前
supabase的webhook报错
开发语言·前端·javascript
qq_12084093711 小时前
Three.js 大场景分块加载实战:从全量渲染到可视集调度
开发语言·javascript·数码相机
yivifu1 小时前
手搓HTML双行夹批效果
前端·html·html双行夹注
奔跑的卡卡2 小时前
Web开发与AI融合-第一篇:Web开发与AI融合的时代序幕
前端·人工智能
IT_陈寒2 小时前
Redis批量删除的大坑,差点让我加班到天亮
前端·人工智能·后端
帆张芳显2 小时前
智表ZCELL产品V3.6 版发布,新增系统预置右键菜单操作、页签栏操作等功能
前端·canva可画·excel插件
漂流瓶jz2 小时前
运行时vs编译时:CSS in JS四种主流方案介绍和对比
前端·javascript·css
Asmewill2 小时前
uv包管理命令
前端
发现一只大呆瓜2 小时前
深入浅出 Tree Shaking:Rollup 是如何“摇”掉死代码的?
前端·性能优化·vite