web前端之JavaScript高级程序设计四:函数表达式

来源:互联网 发布:潍坊行知中学李嘉辉 编辑:程序博客网 时间:2024/05/16 08:45

web前端之JavaScript高级程序设计四:函数表达式

函数表达式是 JavaScript 中的一个既强大又容易令人困惑的特性。定义函数的方式有两种:一种是函数声明,另一种就是函数表达式。

function functionName(arg0,arg1,arg2){    //函数声明};

在执行代码之前会先读取函数声明,这可以把函数声明放在调用它的语句后面:

sayHi();function sayHi(){    alert("Hi");};
var functionName=function(arg0,arg1,arg2){    //函数表达式};

推荐使用函数表达式

递归:

递归函数是在一个函数通过名字调用自身的情况下构成的。

function factorial(num){    if(num <= 1){        return 1;    }else{        return num*factorial(num-1)    }}var anotherFactorial=factorial;factorial=null;alert(anotherFactorial(4));

报错。当factorial赋值给anotherFactorial的时候,factorial就不再是函数了,所以报错。

function factorial(num){    if(num <= 1){        return 1;    }else{        return num*arguments.callee(num-1)    }}var anotherFactorial=factorial;factorial=null;alert(anotherFactorial(4));

成功显示。arguments.callee 是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用。通过使用 arguments.callee 代替函数名,可以确保无论怎样调用函数都不会出问题。因此,在编写递归函数时,使用 arguments.callee 总比使用函数名更保险。

但是这个在严格模式下,会出现错误!
我们现在就用函数表达式来书写下面这个例子:

var factorial=(function f(num){    if(num<=1){        return 1;    }else{        return num*f(num-1);    }});var anotherFactorial=factorial;factorial=null;alert(anotherFactorial(4));

以上代码创建了一个名为 f()的命名函数表达式,然后将它赋值给变量 factorial。即便把函数赋值给了另一个变量,函数的名字 f 仍然有效,所以递归调用照样能正确完成。这种方式在严格模式和非严格模式下都行得通。

闭包:

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 value1=object1[propertyName];
var value2=object2[propertyName];
这行代码是内部函数(一个匿名函数)中的代码,这两行代码访问了外部函数中的变量 propertyName。即使这个内部函数被返回了,而且是在其他地方被调用了,但它仍然可以访问变量 propertyName。之所以还能够访问这个变量,是因为内部函数的作用域链包含createComparisonFunction()的作用域。

function createFunctions(){    var result=new Array();    for(var i=0;i<10;i++){        result[i]=function(){            return i;        };    }    return result;}

这个函数会返回一个函数数组。表面上看,似乎每个函数都应该返自己的索引值,即位置 0 的函数返回 0,位置 1 的函数返回 1,以此类推。但实际上,每个函数都返回 10。因为每个函数的作用域链中都 保 存 着 createFunctions() 函 数 的 活 动 对 象 , 所 以 它 们 引 用 的 都 是 同 一 个 变 量 i 。 当createFunctions()函数返回后,变量 i 的值是 10,此时每个函数都引用着保存变量 i 的同一个变量对象,所以在每个函数内部 i 的值都是 10。但是,我们可以通过创建另一个匿名函数强制让闭包的行为符合预期。

function createFunctions(){    var result=new Array();    for(var i=0;i<10;i++){        result[i]=function(num){            return function(){                return num;            }        }(i);    }    return result;}

我们定义了一个匿名函数,并将立即执行该匿名函数的结果赋给数组。这里的匿名函数有一个参数 num,也就是最终的函数要返回的值。在调用每个匿名函数时,我们传入了变量 i。由于函数参数是按值传递的,所以就会将变量 i 的当前值复制给参数 num。而在这个匿名函数内部,又创建并返回了一个访问 num 的闭包。这样一来, result 数组中的每个函数都有自己num 变量的一个副本,因此就可以返回各自不同的数值了。

this:

在闭包中使用 this 对象也可能会导致一些问题。我们知道, this 对象是在运行时基于函数的执行环境绑定的:在全局函数中, this 等于 window,而当函数被作为某个对象的方法调用时, this 等于那个对象。不过,匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>关于this对象</title><script type="text/javascript">var name="The Window";var obejct={    name:"My Object"    ,getNameFunc:function(){        return function(){            return this.name;        };    }};alert(obejct.getNameFunc()());//The Window(在非严格模式下)</script></head><body></body></html>

每个函数在被调用时都会自动取得两个特殊变量: this 和 arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。不过,把外部作用域中的 this 对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了

var name="The Window";var object={    name:"My Object"    ,getNameFunc:function(){        var that=this;        return function(){            return that.name;        };    }};alert(object.getNameFunc()());//My Object

在定义匿名函数之前,我们把 this对象赋值给了一个名叫 that 的变量。而在定义了闭包之后,闭包也可以访问这个变量,因为它是我们在包含函数中特意声名的一个变量。即使在函数返回之后, that 也仍然引用着 object,所以调用object.getNameFunc()()就返回了”My Object”。

模仿块级作用域:

在 JavaScrip 中,变量 i是定义在 ouputNumbers()的活动对象中的,因此从它有定义开始,就可以在函数内部随处访问它。即使像下面这样错误地重新声明同一个变量,也不会改变它的值。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>模仿块级作用域</title><script type="text/javascript">outputNumbers(5);function outputNumbers(count){    for (var i=0; i < count; i++){        alert(i);    }    var i; //重新声明变量    alert(i); //计数}</script></head><body></body></html>
var someFunction = function(){    //这里是块级作用域};someFunction();(function(){    //这里是块级作用域})();

私有变量:

严格来讲, JavaScript 中没有私有成员的概念;所有对象属性都是公有的。不过,倒是有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。

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

sum、num1、num2都是私有变量

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"><title>私有变量</title><script type="text/javascript">function Person(name){    this.getName=function(){        return name;    };    this.setName=function(value){        name=value;    };}var person=new Person("FZW");alert(person.getName());person.setName("CYZ");alert(person.getName());</script></head><body></body></html>

这两个方法都可以在构造函数外部使用,而且都有权访问私有变量 name。但在 Person 构造函数外部,没有任何办法访问
name。由于这两个方法是在构造函数内部定义的,它们作为闭包能够通过作用域链访问 name。私有变量 name在 Person 的每一个实例中都不相同,因为每次调用构造函数都会重新创建这两个方法。

静态私有变量:

通过在私有作用域中定义私有变量或函数,同样也可以创建特权方法,其基本模式如下所示。

(function(){    //私有变量和私有函数    var privateVariable = 10;    function privateFunction(){        return false;    }    //构造函数    MyObject = function(){    };    //公有/特权方法    MyObject.prototype.publicMethod = function(){        privateVariable++;        return privateFunction();    };})();

这个模式创建了一个私有作用域,并在其中封装了一个构造函数及相应的方法。在私有作用域中,首先定义了私有变量和私有函数,然后又定义了构造函数及其公有方法。公有方法是在原型上定义的,这一点体现了典型的原型模式。需要注意的是,这个模式在定义构造函数时并没使用函数声明,而是使用了函数表达式。函数声明只能创建局部函数,但那并不是我们想要的。出于同样的原因,我们也没有在声明 MyObject 时使用 var 关键字。记住:初始化未经声明的变量,总是会创建一个全局变量。因此, MyObject 就成了一个全局变量,能够在私有作用域之外被访问到。

模块模式:

<script type="text/javascript">var singleton = function(){    //私有变量和私有函数    var privateVariable = 10;    function privateFunction(){        return false;    }    //创建对象    var object = new CustomType();    //添加特权/公有属性和方法    object.publicProperty = true;    object.publicMethod = function(){        privateVariable++;        return privateFunction();    };    //返回这个对象    return object;}();

这个模块模式使用了一个返回对象的匿名函数。在这个匿名函数内部,首先定义了私有变量和函数。然后,将一个对象字面量作为函数的值返回。返回的对象字面量中只包含可以公开的属性和方法。由于这个对象是在匿名函数内部定义的,因此它的公有方法有权访问私有变量和函数。从本质上来讲,这个对象字面量定义的是单例的公共接口。

1 0
原创粉丝点击