JavaScript之Function类型

2017-12-08  本文已影响0人  Julian1009

定义Function两种方式:

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

以上代码定义了变量sum并将其初始化为一个函数。后一个例子中,function关键字后面没有函数名。这是因为在使用函数表达式定义函数时,没有必要使用函数名——通过变量sum即可以引用函数。另外,还要注意函数末尾有一个分号,就像声明其他变量时一样。

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

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

从技术角度讲,这是一个函数表达式。但是,我们不推荐读者使用这种方法定义函数,因为这种语法会导致解析两次代码(第一次是解析常规ECMAScript代码,第二次是解析传入构造函数中的字符串),从而影响性能。不过,这种语法对于理解“函数是对象,函数名是指针”的概念倒是非常直观的。

没有重载(深入理解)

function addSomeNumber(num){
    return num + 100;
}

function addSomeNumber(num) {
    return num + 200;
}

var result = addSomeNumber(100); //300

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

var addSomeNumber = function (num){
    return num + 100;
};

addSomeNumber = function (num) {
    return num + 200;
};

var result = addSomeNumber(100); //300

这个例子中在创建第二个函数时,实际上覆盖了引用第一个函数的变量addSomeNumber

函数声明与函数表达式

函数声明与函数表达式的区别是,解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。

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

正常

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

以上代码之所以会在运行期间产生错误,原因在于函数位于一个初始化语句中,而不是一个函数声明。换句话说,在执行到函数所在的语句之前,变量sum中不会保存有对函数的引用;而且,由于第一行代码就会导致“unexpected identifier”(意外标识符)错误,实际上也不会执行到下一行。
注:
也可以同时使用函数声明和函数表达式,例如var sum = function sum(){}。不过,这种语法在Safari中会导致错误。

作为值的函数

Array.prototype.sort()

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

function createComparisonFunction(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;
        }
    };
}
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

这里,我们创建了一个包含两个对象的数组data。其中,每个对象都包含一个name属性和一个age属性。在默认情况下,sort()方法会调用每个对象的toString()方法以确定它们的次序;但得到的结果往往并不符合人类的思维习惯。因此,我们调用createComparisonFunction("name")方法创建了一个比较函数,以便按照每个对象的name属性值进行排序。而结果排在前面的第一项是name"Nicholas"age是29的对象。然后,我们又使用了createComparisonFunction("age")返回的比较函数,这次是按照对象的age属性排序。得到的结果是name值为"Zachary"age值是28的对象排在了第一位。

函数内部属性

在函数内部,有两个特殊的对象:argumentsthis。其中,arguments是一个类数组对象,包含着传入函数中的所有参数。虽然arguments的主要用途是保存函数参数,但这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。请看下面这个非常经典的阶乘函数。

function factorial(num){
    if (num <=1) {
        return 1;
    } else {
        return num * factorial(num-1)
//      return num * arguments.callee(num-1);  //与上一行等价,但是消除了与函数名的紧密耦合的现象
    }
}

函数内部的另一个特殊对象是this,其行为与Java和C#中的this大致类似。换句话说,this引用的是函数执行的环境对象——或者也可以说是this值(当在网页的全局作用域中调用函数时,this对象引用的就是window)。

window.color = "red";
var o = { color: "blue" };

function sayColor(){
    alert(this.color);
}

sayColor();     //"red"  这次函数调用时,this指向的是函数sayColor执行的环境对象window

o.sayColor = sayColor;  //给对象o动态添加属性,属性值为方法sayColor
o.sayColor();   //"blue"  这次调用时,sayColor执行的环境对象是o

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

ECMAScript 5也规范化了另一个函数对象的属性:caller。除了Opera的早期版本不支持,其他浏览器都支持这个ECMAScript 3并没有定义的属性。这个属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null

function outer(){
    alert(outer.caller)  //  null
    inner(); 
}

function inner(){
    alert(inner.caller);
//  alert(arguments.callee.caller)  //  为了实现更松散的耦合,也可以通过arguments.callee.caller来访问相同的信息
}

outer();

注:
当函数在严格模式下运行时,访问arguments.callee会导致错误。ECMAScript 5还定义了arguments.caller属性,但在严格模式下访问它也会导致错误,而在非严格模式下这个属性始终是undefined。定义这个属性是为了分清arguments.caller和函数的caller属性。以上变化都是为了加强这门语言的安全性,这样第三方代码就不能在相同的环境里窥视其他代码了。
严格模式还有一个限制:不能为函数的caller属性赋值,否则会导致错误。

严格模式中,递归函数使用arguments.caller(或者callee)会引起this混乱出现

var global = this;

var sillyFunction = function(recursed) {
    if (!recursed) { return arguments.callee(true); }
    if (this !== global) {
        alert('This is: ' + this);  // 若!recursed为false,则this!==global
    } else {
        alert('This is the global');  // 若!recursed为true,则this===global
    }
}

sillyFunction();

函数属性和方法

ECMAScript中的函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:lengthprototype

length属性表示函数希望接收的命名参数的个数

function sayName(name){
    alert(name);
}      

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

function sayHi(){
    alert("hi");
}

alert(sayName.length);      //1
alert(sum.length);          //2
alert(sayHi.length);        //0
上一篇 下一篇

猜你喜欢

热点阅读