《无所不能的JavaScript编程系列:arguments 参数对象》
前言:无所不能的JavaScript
JavaScript起源于Netscape公司的LiveScript语言,这是一种基于对象和事件驱动的客户端脚本语言,最初的设计是为了检验HTML表单输入的正确性,只是用于网页开发的一个弱类型脚本语言。随着Html5在PC和移动端越来越流行,JavaScript变得更加重要了,各种层出不穷的框架使得JavaScript的开发更加简捷,V8的性能带来了Node.js,将JavaScript 推向了服务器端,同时还被用在桌面应用、游戏、AR等各种场景,JavaScript已经变成了全能型选手。时至今日,JavaScript可算是世界上最流行的编程语言之一,这个被大量的开发者与设计师随手拈来增强他们的Web前端的脚本语言,越来越被重视。,
JavaScript成功在于它的无所不为。从一个小脚本到前、后端通吃,这足以证明了它的强大之处。
JavaScript一度被认为是一种玩具编程语言,它有很多缺陷,所以不被大多数后端开发人员所重视。很多人认为,相较Java、Ruby、Python、.Net等语言来说,写JavaScript代码很简单,并且JavaScript只是为了在网页上添加一点交互和动画效果,认为只需要会使用jQuery 做一些页面以及前后端交互就可以在简历写上熟悉JavaScript。
但这是完全错误的理解。JavaScript确实很容易上手,学习性价比高,稍微掌握一点JS基础知识,外加HTML和CSS,就可以做一个简单网页或者拿来应聘一份可以糊口的工作。但其精髓却不为大多数开发人员所熟知,如果想精通JS,使用JS进行随心所欲地编程,编写高质量的JavaScript代码更是难上加难。一个合格的开发人员应该精通JavaScript和其他编程语言。如果你已经掌握了其他编程语言,或者你还什么都不会,请立刻开始学习JavaScript,不要被Web时代所淘汰。
函数的实参和形参
在Java 编程中,函数的形参(parameter)和实参(argument)概念屡见不鲜,但是对JavaScript而言,这两个概念通常被弱化了。但其实在JS编程中,还是需要明确理解一下这两个概念。
弱类型脚本语言JS,它的函数定义并未指定函数形参的类型,函数调用也未对传入的实参值做任何类型检查,对传入参数个数也不检查。
一句话来说,JavaScript 的实参和形参可以不一致。
JavaScript 的arguments 参数对象
严格来说,我们应该称arguments 为“实参对象”,因为arguments 对象的长度是由实参个数而不是形参个数决定的。形参是函数内部重新开辟内存空间存储的变量,但是其与arguments对象内存空间并不重叠。对于arguments和值都存在的情况下,两者值是同步的,但是针对其中一个无值的情况下,对于此无值的情形值不会得以同步。
如下代码可以得以验证:
1 function test(arg1, arg2, arg3){ 2 console.log(arguments.length); // result: "2" 3 arg1 = 100; 4 console.log(arguments[0]); // result: "100" 5 arguments[0] = "200"; 6 console.log(arg1); // result: "200" 7 8 console.log(arg3); // result: "undefined" 9 arg3 = true; 10 console.log(arguments[2]); // result: "undefined" 11 console.log(arg3) // result: true 12 } 13 test(1, 2);
根据代码我们可以得到JS实参对象的一些规则:
规则1:当传入的实参比形参个数少的时候,剩下的形参都将设置为undefined;
规则2:利用arguments.length可以获取调用函数的参数个数,它的值取决于调用处(实参),不一定等于形参个数;
规则3:需要使用可选实参的时候,需要将可选实参放在实参列表的最后,但应对这些可选形参加以控制,代码如下:
1 function getName(param1, /* optional */ param2) { 2 if(param2 === undefined) param2 = ""; // 严格相等,防止null;不需要判断空的话,param2 = param2 || "" 3 dosomething();4 }
arguments.callee 与函数形参
说了半天的实参,那么形参在哪里?先看arguments 对象的callee 属性,callee 属性是 arguments 对象的一个成员,它表示对函数对象本身的引用,这有利于匿名,该属性仅当相关函数正在执行时才可用。
arguments.callee 得到函数本身,arguments.callee() 是调用自己,arguments.callee.length 是获取形参个数(PS:当然,函数名.length 亦可)。
好,有了这些知识,我们能做什么?
形参的作用无非就是在JS组件编程中,配合实参做一些校验处理和程序分支处理。arguments.callee 的作用就大了,利用它可以在函数内部进行递归逻辑。
代码如下:
1 function test(n){ 2 if(n>0){ 3 arguments.callee(n - 1); 4 alert(n); 5 } 6 } 7 var n = 5; 8 test(n);
如果知道结果是多少,那就OK了。
注:callee在严格模式下被禁用。
arguments 与类数组对象
arguments 对象是比较特别的一个对象,实际上是当前函数的一个内置属性。arguments 非常类似Array,但实际上又不是一个Array实例。
我们称呼这类对象为类数组对象。《JS权威指南》中提到过这一概念。
JS数组的一些特性是其他对象所没有的:
1.当有新的元素加进来的时候,自动更新length属性;
2.设置length为一个较小值将截断数组;
3.从Array.prototype中继承一些有用的方法;
4.其类属性为”Array“;
这些属性让数组和常规对象有所区别,称为”特殊的对象“。当然,这不是我们这边要关心的,上面这些属性并不是数组的本质特性。
一种完全合理的看法是:"把拥有一个数值length属性和对应非负整数属性的对象看做一种类型的数组,即类数组对象"。
这些类数组对象(参数对象arguments),并不能调用数组的方法或者期望length属性有什么特殊的行为,但是仍然可以类似数组那样遍历它们,并且可以使用call来操作数组的方法。
上一段代码,验证一下:
1 function ArgTest(a, b) 2 var numargs = arguments.length; 3 alert(numargs); //获取实际传递参数长度 4 5 var expargs = ArgTest.length; 6 alert(expargs); // 获取应该传递参数长度,使用 arguments.callee.length 可以获得同样的效果 7 8 alert(arguments.callee); //可以打印函数本身 9 10 Array.prototype.selfvalue = 1; 11 alert(new Array().selfvalue); 12 } 13 function testAguments(){ 14 alert(arguments.selfvalue); 15 }
运行代码你会发现第一个alert显示1,这表示数组对象拥有selfvalue属性,值为1,而当你调用函数testAguments时,你会发现显示的是“undefined”,说明了不是arguments的属性,即arguments并不是一个数组对象。
因此,直接调用arguments.slice() 将返回一个"Object doesn't support this property or method"错误,因为arguments不是一个真正的数组。
使用Array.prototype.slice.apply(arguments)能将函数的参数对象转化为一个真正的数组。
JavaScript 的函数重载
对于arguments 参数说了这么多,最后再扯一下函数重载吧。本人是Java 程序猿出身,对于面向对象的三大特性(封装、继承、多态)是根深蒂固,JavaScript的封装和继承在后续文章中会介绍到,那么多态和重载,JavaScript是如何处理的呢?
很遗憾,由于JavaScript中函数的声明和调用特性,可以看出JavaScript中函数是不能重载的。
根据其他语言中重载的依据:"函数返回值不同或形参个数不同",我们可以得出上述结论:
第一:Javascript函数的声明是没有返回值类型这一说法的;
第二:JavaScript中形参的个数严格意义上来讲只是为了方便在函数中的变量操作,实际上实参已经存储在arguments对象中了。
另外,从JavaScript函数本身深入理解为什么JavaScript中函数是不能重载的:在JavaScript中,函数其实也是对象,函数名是关于函数的引用,或者说函数名本身就是变量。对于如下所示的函数声明与函数表达式,其实含以上是一样的(在不考虑函数声明与函数表达式区别的前提下),非常有利于我们理解JavaScript中函数是不能重载的这一特性。
编后语
那么问题来了,为什么我们要学JavaScript?尤其是当你已经掌握了某些其他编程语言如Java、C++的情况下。
简单粗暴的回答就是:因为你没有选择。在Web世界里,只有JavaScript能跨平台、跨浏览器驱动网页,与用户交互。
本人工作从事后端开发,属于一个“半吊子”的前端攻城狮,不会切图,不会设计,CSS和HTML会一些,但这些丝毫不能影响我对JavaScript 热爱。
JavaScript 和nodejs 的大前端时代即将来临,不管你学不学,它就在那里!