JS函数类型(一)

一、函数类型

1.1  在JS中,每个函数都是Function类型的实例。而且都与其他类型一样,具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针。不会与某个函数绑定,函数通常是使用函数声明语法定义的(函数声明)。

function sum(num1,num2){
   return num1 + num2  
}

这与下面函数表达式声明的方式相差无几(函数表达式声明)

var sum = function(sum1,sum2){
    return num1 + num2
}

在使用函数表达式声明函数时,没有必要使用函数名,如上所示,通过变量sum即可引用函数。

最后一种定义函数的方法是Function构造函数,Function构造函数可以接收任意数量的参数,但最后一个参数始终被看成是函数体,而前面的参数则枚举出了新函数的参数。如下

var sum = new Function("num1","num2","return num1 + num2");//不推荐

从技术上来讲,这的确是一个函数表达式,但是不推荐这样做,因为这种语法会导致解析两次代码。

函数是对象,函数名是指针。由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的变量没有什么不同。换句话说,一个函数可能会有多个名字。

function sum(num1,num2){
   return num1 + num2 ;
}
alert(sum(10,10))//20
var anthorSum = sum;
alert(anthorSum(10,10))//20
sum = null;
alert(anthorSum(10,10))//20

以上代码首先定义了一个名为sum()的函数,然后,又声明变量anthorSum,并将其设置为与sum相等,(注意:使用不带圆括号的函数名是访问函数指针,而非调用函数。)此时anthorSum与sum都指向了同一个函数,所以即使将sum设置为null,仍然可以正常调用anthorSum。

1.2  没有重载

将函数名想象成指针,也有助于理解为什么js中没有函数重载这个概念。

function addSum(num1){
   return num1 + 100  
}
function addSum(num1){
   return num1 + 200  
}
var result = addSum(100) //300

这个例子声明了两个同名函数,结果是后面的函数覆盖了前面的函数。以上代码实际上与下面代码没什么区别

var addSum =  function (num1){
   return num1 + 100  
}
addSum = function (num1){
   return num1 + 200  
}
var result = addSum(100) //300

由上可以知道,在创建第二个函数的时候,实际上覆盖了引用第一个函数的变量。

1.3  函数声明与函数表达式

实际上,解析器在执行环境中加载数据时,对函数声明和函数表达式并非一视同仁,解析器会率先读取函数声明,并使其在执行任何代码之前可用。至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析执行。

alert(sum(10,10))//20
function sum(num1,num2){
  return num1 + num2;
}

以上代码完全可以正常运行,因为在代码开始执行之前,解析器就通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中。对代码求值时,js引擎在第一遍会声明函数并将其放在源代码树的顶部。所以即使声明函数的代码在调用它的代码之后,js引擎也能把函数声明提升到顶部。如果将函数声明改成等价的函数表达式时,会在执行期间导致报错

alert(sum(10,10))
var sum =  function (num1,num2){
  return num1 + num2;
}

以上代码会报错的原因在于函数位于一个初始化的语句中,而不是一个函数声明。换句话说,在执行到函数的语句之前,变量sum不会保存有对函数的引用;而且,由于第一行代码会报错,实际上也不会执行到下一行。

也可以同时使用函数声明和函数表达式

var sum = function sum(){}

1.4  作为值的函数

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

function callSomeFunction(someFunction,someArgument){
    return someFunction(someArgument);
}

这个函数接收两个参数,第一个参数是一个函数,第二个参数是传递给该函数的一个值,然后就可以像下面的例子一样传递函数了

function add10(num){
    return num + 10;
}
var  result1 = callsomeFunction(add10,10);
alert(result1) //20
function getGreeting(name){
    return 'hello' + name
}
var result2 = callsomeFunction(getGreeting,'Toms');
alert(result2) // hello,Toms

 这里的callsomeFunction函数是同用的,即无论第一个参数值传递进来的是什么函数,它都会返回执行第一个参数后的结果,要访问函数的指针而不执行函数的话,就不要带后面的两个圆括号。因此上面函数传递的是add10和getGreeting,而不是他们的函数返回结果。

当然,可以从一个函数中返回另一个函数。例如,假设有一个对象数组,我们想要根据某个对象属性对数组进行排序,而传递给数组sort()方法的比较函数要接收两个参数,即要比较的值。可是,我们需要一种方式来指明按照哪个属性来排序。要解决这个问题,可以定义一个函数。它接收一个属性名,然后根据这个属性名来创建一个比较函数。下面就是这个函数的定义

function cereteComparisonFunction(propertyName){
    return function(object1,object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        if(value1 < value2){
            return -1;
        } else if (value1 > value2){
            return 1;
        } else{
            return 0;
        }
    };
}    

这个函数看起来有些复杂,但实际上无非就是一个函数中嵌套了另一个函数,而且内部函数前面加了一个return操作符,在内部函数接收到propertyName参数后,它会使用方括号表示法来取得给定属性的值,取得了想要的属性值之后,定义比较函数就非常简单了。上面这个函数可以向下面例子这样使用

var data = [{name: "Zachary",age:28},{name:"Nicholas",age:29}];
data.sort(createComparisonFunction("name"));
alert(data(0).name); //Nicholas
data.sort(createComparisonFunction("age"));
alert(data(0).name); //Zachary

这里,我们创建了一个包含两个对象的数组,每个对象都包含了一个name属性和age属性,在默认情况下,sort方法会调用每个对象的toString()方法以确定他们的顺序,但得到的结果往往并不符合人类的思维习惯。因此,我们调用createComarsionFunction("age")方法返回的比较函数,这个是按照对象的age属性进行排序。

1.5  函数内部属性

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

function factorial(num){
    if(num <= 1) {
      return 1;
    } else {
      return num * factorial(n - 1)
    }
}

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

function factorial(num){
    if(num <= 1) {
      return 1;
    } else {
      return num * arguments.callee(n - 1)
    }
}

在这个重写后的函数中,没有引用到函数名,这样,无论函数使用什么名字,都能正常的完成递归调用。

var trueFactorial = factorial;
factorial = function(){
    return 0 ;
}
alert(trueFactorial(5)) //120
factorial(5) //0

在此,变量trueFactoraial获得了factorial的值,实际上是在另一个位置保存了函数的指针,然后,我们又将一个简单的返回0的函数赋值给factorial变量,如果像原来的factorial()那样,不使用arguments.callee(),调用trueFactorial()就会返回0,可是,在解除了函数体内的代码与函数名的耦合状态后,trueFactorial()仍然能够正常的返回阶乘,至于factorial(),他现在只是一个返回0 的函数。

函数内部的另一个特殊对象是this,this引用的是函数据以执行的环境对象,或者也可以说是this值(当在网页的全局作用域调用函数时,this对象引用的就是window)。

window.color = "red";
var o = {color:"blue"};
function sayColor(){
    alert(this.color);
}
sayColor(); //red
o.sayColor = sayColor();
o.sayColor(); //blue 

猜你喜欢

转载自www.cnblogs.com/yu-lin/p/9217143.html