第十一章 Function类型

时间:2023-12-25 22:35:19

  在ECMAScript中,Function(函数)类型实际上是对象。每个函数也是Function类型的实例,而且都与其它引用类型一样具有属性和方法。由于是函数对象,因此函数名实际上也是一个指向函数对象的指针。

一、函数的声明方式

  函数通常是使用函数声明语法定义:

  1、普通的函数声明

  function sum(sum1,sum2){

  return sum1+sum2;

  }

  2、使用变量初始化函数

  var sum=function(){

  return sum1+sum2;

  }

  3、使用function构造函数

  var sum=new Function("sum1","sum2","return sum1+sum2");  //不推荐 因为这种会导致解析两次代码,一次解析常规ECMAScript,一次解析传入构造函数中的字符串

二、作为值类型

  因为ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另外一个函数,而且可以将一个函数作为另一个函数的结果返回。

  function callSomeFunction(someFunction,someArgument){

    return someFunction(someArgument);

  }

  这个函数接受两个参数。第一个参数应该是一个函数,第二个参数应该是要传递给该函数的一个值。

  function someFunction(someArgument){

    return  someArgument+100;

  }

  var result=callSomeFunction(someFunction,100);     //200

三、函数内部属性

在函数内部,有两个特殊的对象:agruments和this。其中,arguments是一个类型组对象,包含着传入函数中的所有参数。虽然arguments的主要用途是保存函数参数,但这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。

  function factorial(num){

    if(num<=1){

      return  1;

    } else {

      return num * factorial(num-1);

    }

  }

  定义阶乘函数一般用到递归算法;如果上面的代码所示,在函数有名字,而且名字以后也不会变的情况下,这样定义是没有问题的。但问题是这个函数的执行与函数名factorial紧紧耦合在一起。为了消除这种紧密耦合的现象,可以像下面这样使用arguments.callee。

  function factorial(num){

    if(num <= 1){

      return 1;

    } else {

      return num * arguments.callee(num - 1);

    }

  }

  在这个重写后的factorial()函数体内,没有再引用函数名factorial。这样,无论引用函数时使用的是什么名字,都可以保存正常完成递归调用。

  var trueFactorial=factorial;

  factorial=function(){

    return 0;

  }

  alert(trueFactorial(5));  //120

  alert(factorial(5));        //0

  函数内部另外一个对象是this,其行为与java和C#中的this大致相同。this引用的是函数据以执行操作对象,或则说函数调用语句所处的那个作用域。

  window.color = "red";

  var o = { color : "blue";};

  function sayColor(){

    alert(this.color);

  }

  sayColor();           //red

  o.sayColor = sayColor;

  o.sayColor();      //blue

  上面这个函数sayColor()是在全局作用域中定义的,它引用了this对象。由于在调用函数之前,this的值不确定,因此this可能会在代码执行过程中引用不同的对象。当在全局作用域中调用sayColor()时,this引用的是全局对象window;换句话说,对this.color求值会转换成对window.color求值,于是结果就返回了“red”。而当把这个函数赋值给对象o并调用o.sayColor()时,this引用的对象o,因此对this.color求值会转换成对o.color求值,结果就返回了“blue”;

  备注:函数名字仅仅是一个包含指针的变量而已。因此,即使是在不同的环境中执行,全局的sayColor()函数与o.sayColor()指向的任然是同一个函数。

四、函数属性和方法

  每个函数包含两个属性:length和prototype。其中,length属性表示函数希望接受的命名参数的个数:

  function sayName(name){

    alert(name);

  }

  function sum(num1,num2){

    reutrn num1 + num2 ;

  }

  function sayHi(){

    alert("hi");

  }

  alert(sayName.length);  //1

  alert(sum.length);        //2

  alert(sayHi.length);      //0

  以上三个函数,每个函数接受的命名参数个数不同。

  每个函数都包含两个非继承而来的方法:apply()和call()。这个两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另外一个是参数数组。其中,第二个参数可以是Array的实例,也可以使arguments对象。

  function sum(sum1,sun2){

    return sum1 + sum2;

  }

  function callSum1(num1,num2){

    return sum.apply(this,arguments);   //传入arguments对象

  }

  function callSum2(num1,num2){

    return sum.apply(this,[num1,num2]);   //传入数组

  }

  alert(callSum1(10,10)); //20

  alert(callSum2(10,10));//20

  call()方法和 apply()方法的作用相同,他们的区别仅在于接受参数的方法不同。对于call()方法而言,第一个参数是this值没有变法,变法的是其余参数都直接传递给函数。换句话说,在使用call方法时,传递给函数的参数必须一个个的列举出来。

  function sum(sum1,sum2){

    return num1 + num2;

  }

  function callSum(num1,num2){

    return sum.call(this,num1,num2);

  }

  alert(callSum(10,10));  //20

  事实上,传递参数并非apply()和call()真正的用武之地;他们真正强大的地方是能够扩充函数赖以运行的作用域。

  window.color = "red";

  var o = { color : "blue" };

  function sayColor(){

    alert(this.color);

  }

  sayColor();                //red

  sayColor.call(this);    //red

  sayColor.call(window);  //red

  sayColor.call(o);     //blue