别再搞混了!arguments对象和rest参数到底有啥不一样?
- 别再搞混了!arguments对象和rest参数到底有啥不一样?
- 开场白:当年我把面试官聊懵了
- 先给俩货拍个证件照,省得脸盲
- 老古董 arguments
- 新生代 rest
- 现场翻车实录:一行代码看出亲妈和保姆的区别
- 箭头函数里根本没有 arguments?对,但故事得补全
- 类数组转真数组的三板斧,面试常问!
- 性能深坑:别再在热路径里访问 arguments
- 递归场景:rest 爽到飞起,但爆栈别怪我
- arguments.callee 被禁之后,匿名递归怎么苟?
- 动态获取参数个数:length 行为大不同
- 模拟函数重载?可以,但真的值得吗
- TypeScript 写 rest:不加类型让你怀疑人生
- class 方法里别乱用 arguments,this 可能让你哭
- 实战小技巧:rest + 解构,前几个我要,剩下的打包
- 非严格模式下改 arguments,会反向污染实参?
- 把 arguments 当普通对象遍历?隐藏属性在盯着你
- 兼容老浏览器又想用 rest?Babel 教你做人
- 调试小贴士:控制台自动展开 arguments 别慌
- 谣言终结:arguments 真的被废弃了吗?
- 一张脑图总结(语音转文字,脑图在脑子里)
- 彩蛋:用 rest 写个“任意参数管道”玩玩
- 收尾:别再傻傻分不清
别再搞混了!arguments对象和rest参数到底有啥不一样?
——深夜语音转文字版,边吐槽边把坑填平
开场白:当年我把面试官聊懵了
先交代背景,免得你以为我在吹牛逼。
三年前我拎着半桶水的 JS 去面试,对面小哥邪魅一笑:“说说 arguments 和 rest 区别。”
我张嘴就来:“不都是收参数的吗?rest 就是语法糖吧!”
小哥没说话,默默在纸上写了两行代码,推到我面前。十秒后,我社死。
今天把那段社死经历翻出来,揉碎喂给你们,能救一个是一个。
先给俩货拍个证件照,省得脸盲
老古董 arguments
出生年份:上古(ES1)
身份:类数组对象,长得跟数组撞脸,但没护照(原型链上没 Array)
脾气:怪异,非严格模式下还能改成员,一不留神就把实参带沟里
隐藏技能:arguments.callee指向自己,可惜被 strict 模式拉黑了
新生代 rest
出生年份:ES6,带光环
身份:真 · 数组,全家桶方法都能用
脾气:佛系,只读,不乱动外层变量
隐藏技能:想拿几个就拿几个,剩余的全打包,搭配解构食用更佳
现场翻车实录:一行代码看出亲妈和保姆的区别
// 场景:老板让把收到的参数翻倍后求和functionoldWay(){// 1. arguments 先冒充数组constnums=Array.prototype.slice.call(arguments);// 老手艺,又叫“借刀杀人”returnnums.reduce((s,n)=>s+n*2,0);}functionnewWay(...nums){// 2. rest 天生就是数组,直接开干returnnums.reduce((s,n)=>s+n*2,0);}console.log(oldWay(1,2,3));// 12console.log(newWay(1,2,3));// 12看着结果一样,但老Way里那句slice.call就是脱裤子放屁——多此一举。
而且slice在 V8 里会触发一次完整的对象复制,参数一多,性能肉眼可见往下掉。
面试官当初就给我甩了 1w 个数字,老Way直接卡成 PPT,newWay 还丝滑得像个渣男。
箭头函数里根本没有 arguments?对,但故事得补全
constfoo=()=>console.log(arguments);// 报错:arguments is not defined箭头函数没自己的arguments,它用的是外层函数(或全局)的。
想偷懒?直接 rest:
constfoo=(...args)=>console.log(args);// 完美,args 是真数组记住:箭头函数 + rest 参数,才是现代前端人的基本修养,别再写const self = this那种考古代码了。
类数组转真数组的三板斧,面试常问!
老派刀法:
Array.prototype.slice.call(arguments)
兼容 IE 6,但慢,且 strict 下 callee 拿不到。ES6 语法糖:
const arr = [...arguments]
一句话,颜值高,性能比 slice 好一丢丢。官方外挂:
Array.from(arguments)
专治各种类数组,还能顺便 map:Array.from({length: 5}, (_, i) => i * i) // [0, 1, 4, 9, 16]
性能深坑:别再在热路径里访问 arguments
V8 的“优化杀手”名单里,arguments高挂榜首。
只要你在函数里对arguments有“不规矩”操作——比如arguments[i]、.length、或者把它当对象塞到另一个函数——TurboFan 直接躺平,回退到字节码解释器,性能雪崩。
rest 参数因为天生数组,JIT 能提前做类型推导,反而一路绿灯。
口说无凭,跑个分:
// benchmark:累加 100 万个数字functionwithArguments(){lets=0;for(leti=0;i<arguments.length;i++)s+=arguments[i];returns;}functionwithRest(...args){lets=0;for(constnofargs)s+=n;returns;}constdata=Array.from({length:1e6},()=>Math.random());console.time('arg');withArguments(...data);console.timeEnd('arg');console.time('rest');withRest(...data);console.timeEnd('rest');Node 20 下差距 3~5 倍,自己玩去吧。
递归场景:rest 爽到飞起,但爆栈别怪我
写个累加递归感受一下:
// rest 版,颜值 100 分constsum=(first,...rest)=>rest.length===0?first:first+sum(...rest);console.log(sum(1,2,3,4));// 10优雅是优雅,可 JS 没有尾递归优化,1w 个数字直接Maximum call stack size exceeded。
真要在生产里玩递归,老老实实改循环,或者上 trampoline 蹦床模式,别拿业务数据开玩笑。
arguments.callee 被禁之后,匿名递归怎么苟?
strict 模式里arguments.callee被枪毙,-named 匿名函数表达式了解一下:
constfactorial=functionf(n){returnn<=1?1:n*f(n-1);};给函数起个内部名f,作用域内永远指向自己,就算外部变量被覆盖也稳如老狗。
不想起名?那就别写匿名递归,代码可读性第一。
动态获取参数个数:length 行为大不同
functionfoo(a,b,c){}console.log(foo.length);// 3 形参个数functionbar(...args){}console.log(bar.length);// 0 rest 不参与计数arguments.length是实际传了几个,func.length是定义时写了几人,
rest 参数在定义阶段不算人头,面试常考,记住喽。
模拟函数重载?可以,但真的值得吗
Java 党最爱重载,JS 党没有,于是有人拿arguments.length硬拗:
functionload(){if(arguments.length===1)returnloadUser(arguments[0]);if(arguments.length===2)returnloadUserWithRole(arguments[0],arguments[1]);thrownewTypeError('参数不对');}功能能跑,但维护起来想打人。
TypeScript 时代,直接上联合类型 + 泛型,编译期就把错误拍死,不香吗?
TypeScript 写 rest:不加类型让你怀疑人生
functionpush(...args){// 报错:Parameter implicitly has an 'any' typeconsole.log(args);}正解:
functionpush<T>(...args:T[]):T[]{returnargs;}一行泛型,世界清静。
别偷懒写any[],那样还不如回 JS 放羊。
class 方法里别乱用 arguments,this 可能让你哭
classCounter{count=0;inc(){// 这里如果写成 [...arguments],this 指向没问题// 但你要是手贱把 arguments 传给外部函数,this 就丢了[].slice.call(arguments).forEach(n=>this.count+=n);}}正确姿势:直接 rest,少碰瓷this:
inc(...nums){nums.forEach(n=>this.count+=n);}实战小技巧:rest + 解构,前几个我要,剩下的打包
functionlogFirstTwoAndRest(first,second,...others){console.log('老大:',first);console.log('老二:',second);console.log('小弟们:',others.join(' | '));}logFirstTwoAndRest('a','b','c','d','e');// 老大:a// 老二:b// 小弟们:c | d | e写中间件、拦截器时这招超香,前面拿配置,后面传可变参数,代码干净得像刚洗的袜子。
非严格模式下改 arguments,会反向污染实参?
真的,童叟无欺:
functionsneaky(a,b){arguments[0]=100;console.log(a);// 100}sneaky(1,2);strict 模式下相安无事,非严格模式直接改原变量,老项目里这种黑魔法一抓一把,升级 TS 记得加"strict": true,不然半夜被坑哭。
把 arguments 当普通对象遍历?隐藏属性在盯着你
functionshowHidden(){for(constkeyinarguments){console.log(key);// 0 1 2 ...}console.log(Object.keys(arguments));// ['0', '1', '2']console.log(arguments.hasOwnProperty('callee'));// true,非 strict}callee、caller这些属性平时隐身,一for...in就蹦出来,很容易踩雷。Object.keys不会拿不可枚举属性,但for...in会,写工具库时别混用。
兼容老浏览器又想用 rest?Babel 教你做人
安装一条龙:
npmi -D @babel/preset-env.babelrc 里写:
{"presets":[["@babel/preset-env",{"targets":{"ie":"11"}}]]}写最潮的语法,让 Babel 帮你翻译成 IE 能看懂的古代汉语。
打包体积会大一点,但业务要兼容,没办法。
调试小贴士:控制台自动展开 arguments 别慌
Chrome DevTools 里console.log(arguments)会直接展开成数组样式,那是浏览器 UI 在帮你,不等于它是真数组。
想看庐山真面目:
console.dir(arguments);隐藏属性、原型链一目了然,面试时现场演示,对面小姐姐直呼专业。
谣言终结:arguments 真的被废弃了吗?
ECMA 规范里从没写“deprecated”,只是不推荐使用。
某些极端场景,比如高性能模板引擎、代码混淆器,仍然靠arguments省字节。
再说一遍:不是不能用,而是“大部分场景你有更好的选择”。
一张脑图总结(语音转文字,脑图在脑子里)
- 新代码默认 rest,真数组、无性能坑、结合解构爽歪歪。
- 老库维护、强依赖 callee、要跑 IE8?再请 arguments 出山。
- 箭头函数里没得选,只能 rest。
- 递归、重载、动态参数个数,想清楚再下手,别为了装逼把栈爆掉。
- 非严格模式改 arguments 会反噬,记得开 strict 或用 TS。
彩蛋:用 rest 写个“任意参数管道”玩玩
constpipe=(first,...fns)=>fns.reduce((acc,fn)=>fn(acc),first);constadd2=x=>x+2;constmul3=x=>x*3;constpow=x=>x**2;console.log(pipe(3,add2,mul3,pow));// (3+2)*3 然后平方 -> 225一行代码把函数式玩出花,rest 参数就是灵魂。
收尾:别再傻傻分不清
当年我面试挂得冤,今天把话都摊开了:
arguments 是老臣,rest 是新王;老臣还没死透,新王已登基。
写业务——优先 rest;
抠性能——测了再说;
维护屎山——看懂 arguments 才能不踩雷。
把这篇文章甩进微信群,下次谁再跟你说“这俩差不多”,直接把性能测试链接糊他脸上。
好了,我去撸串,你们慢慢消化,记得多写代码多console.log,坑踩多了,自然就胖……哦不,就强了。
欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
推荐:DTcode7的博客首页。
一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,一边打入敌人内部一边持续提升自己,为我们广大开发同胞谋福祉,坚决抵制睿智产品折磨我们码农兄弟!
| 专栏系列(点击解锁) | 学习路线(点击解锁) | 知识定位 |
|---|---|---|
| 《微信小程序相关博客》 | 持续更新中~ | 结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等 |
| 《AIGC相关博客》 | 持续更新中~ | AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结 |
| 《HTML网站开发相关》 | 《前端基础入门三大核心之html相关博客》 | 前端基础入门三大核心之html板块的内容,入坑前端或者辅助学习的必看知识 |
| 《前端基础入门三大核心之JS相关博客》 | 前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。 通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心 | |
| 《前端基础入门三大核心之CSS相关博客》 | 介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页 | |
| 《canvas绘图相关博客》 | Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化 | |
| 《Vue实战相关博客》 | 持续更新中~ | 详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅 |
| 《python相关博客》 | 持续更新中~ | Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具 |
| 《sql数据库相关博客》 | 持续更新中~ | SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能 |
| 《算法系列相关博客》 | 持续更新中~ | 算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维 |
| 《IT信息技术相关博客》 | 持续更新中~ | 作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识 |
| 《信息化人员基础技能知识相关博客》 | 无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方 | |
| 《信息化技能面试宝典相关博客》 | 涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面 | |
| 《前端开发习惯与小技巧相关博客》 | 持续更新中~ | 罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等 |
| 《photoshop相关博客》 | 持续更新中~ | 基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结 |
| 日常开发&办公&生产【实用工具】分享相关博客》 | 持续更新中~ | 分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具 |
吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤
非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!