您的位置:首页 > Web前端 > JavaScript

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

2016-12-16 17:24 615 查看

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;
}();


这个模块模式使用了一个返回对象的匿名函数。在这个匿名函数内部,首先定义了私有变量和函数。然后,将一个对象字面量作为函数的值返回。返回的对象字面量中只包含可以公开的属性和方法。由于这个对象是在匿名函数内部定义的,因此它的公有方法有权访问私有变量和函数。从本质上来讲,这个对象字面量定义的是单例的公共接口。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  javascript web前端