Js整理备忘(05)——函数基础(一)

概念:函数(function)可以简单地定义为“具有可执行代码的对象”。

  • 具体地说,函数是定义一次却可以调用或执行任意多次的一段Javascript(以下简称Js)代码。
  • 函数可以指定参数(arguments),常用来参与运算得到函数的返回值,返回值即为函数调用表达式的值。
  • 在对象内部定义的函数叫做方法(method),对象在调用方法时,该对象也会作为方法的一个隐式参数。

类型:对一个函数进行typeof()运算,返回类型为 function

  • 但是函数定义中又说,“函数是……的对象”,怎么回事??——唉,这只能说是Javascript语言的宽松特性了。而且,函数与对象的关系比数组与对象间的关系更复杂更微妙,需要用心慢慢感受,呵呵(^_^)。 后面计划专门用一篇来说明函数与对象的微妙关系

特点:

  • 函数最重要的特点就是能够被调用。
  • 尽管定义函数时可以指定固定数目的参数,但调用时,允许传递任意数目的实际参数。

 

1、函数的创建(定义)

(1)函数直接量

——函数直接量是一个表达式,它通常定义为一个匿名函数,直接赋给一个变量,或者直接用作某个函数的参数。例如:

var f=function(x){ return x*x;}; //整体是个语句,“=”右边的表达式是一个函数直接量

当然,也可以给函数直接量指定一个函数名,如下:

var f=function fn(x){ return x;};//这样定义完全没有问题

 

(2)function语句

——只是单纯定义一个函数

例如:

function print(msg) {
    document.write(msg+"<br/>");
}

该函数名为“print”,有一个参数msg,函数体中没有return语句,即没有返回值,或者说返回“undefined”。

调用时,可以直接写:print("hello");

再看个例子:

function distance(x1, y1, x2, y2) {
    var dx = x2 - x1;
    var dy = y2 - y1;
    return Math.sqrt(dx * dx + dy * dy);
}

很明显,该函数计算两点之间的距离,并返回计算后的数值。

可以如此调用:var d=distance(1,1,4,5);//d=5

 

(3) 使用Function对象创建函数

——Function是一个构造函数,它用来创建函数。它是Js中比较重要的一个概念,尽管真正用它创建函数的情况并不多见,但是概念上很值得好好了解。

用法:var f=new Function("x","y","return x+y;");//该方法比较笨拙,真正创建函数时一般不使用

相当于function f(x,y){return x+y;}

 

2、函数的参数(arguments)

(1)可选参数

当实际传递的参数数目少于定义时声明的参数数目,那么其他参数就为undefined值。

注意:由于这个特性,设计函数时应该把可选的参数放到参数列表的末尾。否则就有可能要显式传递undefined或者null值作为其中一个参数。

 

(2)Arguments对象

本文开头特点中也提到,向函数传递任意数目的参数都是合法的,不管函数定义时声明了几个参数。这一特点要归功于函数中的arguments属性,它引用一个Arguments对象,这是一个“类似数组的对象”。Arguments对象允许存取所有的实际参数,使用下标来访问这些实际参数,其length属性表示实际参数的个数。

arguments对象的存在很有意义,例如,可以编写一个可变参数函数。例如求一系列数字中的最大值(数字的数目可以是不确定的)。

要始终明确:

  • arguments 并非数组,它是一个Arguments对象。将它看作具有一些带编码的属性的对象更加合适。
  • Arguments还有一个特性,arguments通过下标可以访问这些参数的值,也可以改变参数的值。
function f(x) {
    print(x);
    arguments[0] = null;    // x的值也会被改变
    print(x);               // 输出null
}
f("hi",123);//调用函数f

也就是说该函数中的arguments[0]就相当于参数变量x,一个值改变则另一个也跟着改变。

这一特性同时也说明了arguments不是普通意义上的数组。普通的数组若其中某个元素最初跟某个变量的值相等,那么之后改变该元素的值应该不会导致变量的值改变。

  • Arguments还有一个属性callee。用来引用当前正在执行的函数。可以用来允许为命名的函数递归调用自身。

 

(3)参数的类型

由于Js的宽松特性,函数在声明时不需要指明参数的类型,并且调用时对实际传递的参数值也不执行类型检查。

有时候定义函数的时候会希望参数是某种类型,这时可以在参数中用注释指明类型,增强代码的可读性,例如:

function fn(/*number*/n, /*string*/s /* ,optional...*/) {
    /* code here */
}

函数fn第一个参数应该为number类型,第二个参数应该为string类型,剩下的参数可以是有选择的(optional)。

 

3、函数的应用

(1)作为数据

Js中函数不只是一种语法,还可以用作数据,此时通常是指具有返回值的函数,作为数据使用主要体现在:

  • 把函数赋给一个变量,例如:
function square(x) { return x * x; } //先定义一个函数
var a = square(4);                   //调用函数,将返回值赋给变量
  • 存储在对象的属性中,例如:
var o = {}; //定义一个对象                                
o.square = function(x) { return x * x; };//给对象添加属性square,赋给它一个函数,此时square就作为对象的方法
y = o.square(4);//调用对象的方法
  • 存储在数组的元素中
var a = [];
a[0] = function(x) { return x * x; }//将函数赋给数组的第一个元素
a[2] = a[0](12);//如此调用,a[2]值为144
  • 作为参数传递给某个函数

可参考数组方法sort()时所举的例子,不再赘述。

 

(2)作为方法

即相当于(1)中所讲的将函数存储在对象的属性中的做法。函数此时被称为该对象的方法。

注意:在方法体中可以用this关键字来引用当前(调用这个方法的)对象。(有点拗口,不过很重要)

var cal = {
    x: 100,y: 50,
    add: function() {
        var result = this.x + this.y; //这里的this相当于当前对象cal的引用,可以调用该对象的属性x和y
        return this.result;
    }
};

当一个函数作为函数而不是作为对象的方法调用时,这个this则引用全局对象。

 

(3)作为构造函数(constructor function)创建对象

构造函数是用来创建对象的一个函数。例如:

    function point(x, y) { this.x = x; this.y = y; } //定义一个构造函数,利用this给该构造函数创建的对象添加属性。
    var p = new point(2, 3);  //调用构造函数point,创建点对象p,此时point中的this就是p对象的引用,即p.x=2,p.y=3。
//相当于p={x:2,y:3}
    var q = new point(-1, -3);//同样的构造函数创建一个新对象q,这时point中的this就是q对象的引用,即q.x=-1,q.y=-3。
//相当于q={x:-1,y:-3}

对象创建过程可以这么理解,用 new 运算符创建一个新的对象,然后调用构造函数,把新的对象作为this关键字的值来传递。

注意:这里一定要使用new运算符,否则就不是创建对象,例如:

var p=point;只是相当于将函数的引用赋给另一个变量p,即此时p跟point代表一个函数的引用。

var p=point(1,1);只是相当于执行函数,并将返回值赋给变量p,该语句执行后p为undefined,因为调用的函数point没有设置返回值。

——了解面向对象的话,可以看出,构造函数的概念很像面向对象语言中的“类”的概念。

此处不再细说,后面讲函数与对象的微妙关系时会再谈到构造函数的具体使用。

 

4、函数的属性和方法

当typeof()运算符用于一个函数的时候,返回字符串“function”,但是函数确实是一种特殊的对象(具有可执行代码的对象)。

既然是对象的一种,那么就可以具有属性和方法。

就像Date对象一样,Date是构造函数(本身的类型是function),跟new运算符一起使用可以创建新的Date对象,创建的对象实例的类型是object,新对象一旦创建便拥有了Date对象的所有属性和方法。

(1)属性length

函数的length属性是指该函数声明的形式参数的个数。而前面提到的函数体中的arguments.length是指调用函数时实际参数的个数。

例如,以下函数check()可以用来检测当前被调用的函数,形参与实参数目是否相等。

<script language="javascript" type="text/javascript">
    function check(args) {
        var actual = args.length;           //实参个数
        var expected = args.callee.length;  //当前执行的函数的形参个数
        //callee是Arguments的属性,引用当前执行的函数,本文介绍Arguments对象时提到
        if (actual != expected) {
            alert("传入的参数个数不匹配,请检查!");
        }
    }
    //在函数中调用check()
    function f(x, y, z) {
        check(arguments); //arguments是函数内部属性
        document.write(x + y + z);
    }
    //调用函数f() 
    f(1, 2);    //<1>弹出提示框,页面输出非数值NaN,因为z参数此时为undefined
    f(1, 2, 3); //<2>正常执行,页面输出6
</script>

 

(2)属性prototype

每个函数都有一个prototype属性,他引用的是预定义的原型对象。它在定义新的对象类型时起着非常重要的作用,后面还需要详细说明。

 

(3)方法apply()和call()

——这也是一个比较有趣的方法。ECMAScript规范给所有的函数都定义了这两个方法。

  • 这两个方法可以像调用其他对象的方法一样调用函数。例如,要把两个参数传给函数f(),并将它作为对象o的方法调用,如下:
f.call(o, 123, "asd");
这句代码相当于:
o.m = f;        //将函数赋给对象o的属性m,则m成为o的一个方法
o.m(123, "asd");//调用o的方法m
delete o.m;     //清除方法m

 

  • 两个方法作用相同,只不过传参方式不同

例如,上面的例子若使用apply()方法的话,调用方式为

f.apply(o, [123, "asd"]);其中的参数是存放在数组中的。

当一个函数需要接受很多个参数时,可以将这些参数放到一个数组中,然后调用该函数的apply方法,传递一个数组作为参数。

 

——先写到这里,下一节计划学习函数基础(二)——函数的作用域和闭包。

posted on 2010-01-14 09:34  lihua好心情  阅读(232)  评论(0编辑  收藏  举报