一、详述
arguments 是表达式std第一类,常被称作“类字符串”(array-like)。形如:
arguments ={ 0: xx,1: xx,…, n -1: xx, length: n // n 依赖于std的数目}
arguments 的许多优点:
function foo(){ //1. arguments 并非字符串,大自然也不具备字符串 forEach 等方式 Object.prototype.toString.call(arguments)//”[object Arguments]” arguments instanceof Array // false //2. arguments 对象具备位数检索优点 arguments[0]//”a” arguments[1]//”b”//3. arguments 第一类有 length 优点,充分反映std特点值。//但与表达式的 length 优点相同,foo.length 充分反映实参特点值。可在 ES6后虽然模块缺省、REST 模块等新优点,使 foo.length 显得不可信 arguments.length //2 foo.length //0}foo(a,b)
类字符串第一类的特点:所含 length 优点、检索原素优点,但不包涵字符串任何人方式。
常用的类字符串,除 arguments 以外,除了 HTMLCollection(透过 getElementsByName()等回到的 DOM 条目)、NodeList(透过 querySelectorAll()回到的结点条目)。
二、arguments 采用
arguments 一般来说用作无法确认std个数的应用领域情景,比如表达式科尔曼化等。
它还时常被切换为字符串采用。
// ES5function foo(){ //方式一:会制止这类 JS 发动机的强化,如 V8//请看:https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments var args = Array.prototype.slice.call(arguments)// var args =[].slice.call(arguments)//方式二(所推荐,虽然丑了点) var args = arguments.length ===1 ?[arguments[0]]: Array.apply(null, arguments)}// ES6function foo(){ //利用 arguments 的 Iterator 接口,可以快速转化为字符串 const args =[…arguments]// const args = Array.from(arguments)}function foo(…args){ //直接采用 REST 模块,args 天生就是字符串,可以直接采用 Array 的方式}
像 Function.prototype.apply()、Array.prototype.slice()等方式也可接受类字符串,无需切换为字符串再进行操作。举个例子:
//求最大数function getMax(){ return Math.max.apply(null, arguments)}getMax(1,2,3)//3
都2021年了,更被所推荐按 ES6的写法。
三、注意点
关于 arguments 只能在所有(非箭头)表达式内部可用的局部变量。
存在于所有(非箭头)表达式内部。表达式上下文的 AO 第一类就包括 argumearguments 允许重新赋值。而且实参的更新,也会伴随着 arguments 第一类相应优点值的更新。严格模式下,则不允许对 arguments 第一类赋值,且不会最终模块的变化。也不允许采用 arguments.callee 方式(关于严格模式对 arguments 第一类的限制,可看这篇文章)。
在表达式外采用会抛出 ReferenceError。此时它就是一个标识符而已。
//1.相当于一个变量 arguments,因此会抛出引用错误console.log(arguments)// ReferenceError: arguments is not defined//2.非严格模式下,可将其作为变量let arguments =1 // or arguments =anyconsole.log(arguments)//1//3.严格模式下,还是将其声明变量或对其进行赋值操作use strictarguments =1 // Wrong, SyntaxError:arguments cant be defined or assigned to in strict mode code
在箭头表达式内,没有 arguments 变量。比如以下这样采用同样会报错。
const foo =()=>{ console.log(arguments)// ReferenceError: arguments is not defined}foo()
但这样用不会报错,这就是前面提到的“视觉认知”错误。
function foo(){ const bar =()=>{ console.log(arguments)//虽然箭头表达式 bar 没有 arguments,//这里引用的 arguments 第一类其实是表达式 foo 的std第一类。} bar(b)}foo(a)//{ 0:a, length:1 }
非严格模式与严格模式,对 arguments 第一类的操作。
function foo(x){ x =10 console.log(x)//10 console.log(arguments[0])//10}function bar(x){ use strict x =10 console.log(x)//10 console.log(arguments[0])//1 //以下这样将会直接抛出语法错误:SyntaxError: Unexpected eval or arguments in strict mode // arguments ={}}foo(1)bar(1)
四、求和表达式(科尔曼化)
假设我们有一个求和表达式 sum(),要实现下面的需求:
sum(1,2,3)//6sum(1,2)(3)(4)//10sum(1)(2,3)(4,5,6)//21//…
其实上面的需求是有问题的,它没有出口,导致不知道什么时候求和。我们可以稍微改下需求:
sum(1,2,3).value()//6sum(1,2)(3)(4).value()//10sum(1)(2,3)(4,5,6).value()//21//…
就是说 sum()表达式的结束时机(出口)是调用 value()方式的时候。
function sum(…args){ const arr =[…args] function repeat(…nextArgs){ ;[].push.apply(arr, nextArgs) return repeat } repeat.value =()=>{ if (!arr.length) return 0 return arr.reduce((a, b)=> a + b)} return repeat}
也可以改成调用 sum(1,2)(3)(4)()时进行求和,我们修改一下:
function sum(…args){ if (!args.length) return 0 const arr =[…args] function repeat(…nextArgs){ if (!nextArgs.length){ return arr.reduce((a, b)=> a + b)} ;[].push.apply(arr, nextArgs) return repeat } return repeat}sum()//0sum(1,2,3)()//6sum(1,2)(3)(4)()//10sum(1)(2,3)(4,5,6)()//21