《无所不能的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 的大前端时代即将来临,不管你学不学,它就在那里!

 
posted @ 2017-02-10 15:52  山人行  阅读(1117)  评论(5编辑  收藏  举报