定义函数的方式有两种:一种是函数声明,另一种是函数表达式。
//函数声明
function functionName(arg0, arg1, arg2){
//函数体
}
//函数表达式(匿名函数)
var functionName = function(arg0, arg1, arg2){
//函数体
}
注意:函数声明有一个函数声明提升的特性,而函数表达式没有。一、递归
我们知道对于一个递归阶乘函数,将函数名赋值给另一个变量时,此变量指向原始函数,如果我们将原始函数名值设为null,再访问此函数,就会导致错误,前面第3章说过,可以使用arguments.callee来解决,但在严格模式下,不能通过脚本访问它,此时我们可以使用命名函数表达式可解决。
var factorial = (function f(num){
if (num <=1){
return 1;
} else{
return num*f(num-1);
}
});
二、闭包
闭包是指有权访问另一个函数作用域中的变量的函数。
//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;
}
};
}
例如以上函数中的匿名函数就是一个闭包,它可以访问包含它的函数createComparisonFunction作用域中的变量propertyName。我们知道,通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,当函数返回一个闭包时,这个函数的作用域将会一直在内存中保存到闭包不存在为止。
例如:
//创建函数
var compareNames = createComparisonFunction("name");
//调用函数
var result = compareNames({ name: "Nicholas"}, { name: "Greg"});
//解除对匿名函数的引用(以便释放内存)
compareNames = null;
下图展示了调用compareNames()的过程中产生的作用域链之间的关系。
因此,闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域。
闭包的问题:
- 闭包只能取得包含函数中任何变量的最后一个值,它所保存的是整个变量对象而不是某个特殊的变量。
- 在闭包中使用this对象时,由于每个函数在被调用时都有this和arguments两个特殊变量,内部函数在搜索这两个变量时只会搜索到其活动对象为止,因此闭包永远不可能直接访问外部函数中的这两个变量。
- 在IE9之前的版本中,如果闭包的作用域链中保存着一个HTML元素,那么该元素无法被销毁。
使用闭包可以在JavaScript中模仿块级作用域(JavaScript本身没有块级作用域的概念)。这种方式可以减少闭包占用的内存问题。
(function (){
//这里是块级作用域
})();
注意:函数声明function后面不能跟圆括号,而函数表达式的后面可以。四、私有变量
私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。
通过闭包可以在对象中创建私有变量。这里,我们把有权访问私有变量和私有函数的公有方法称为特权方法。
function MyObject(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//特权方法
this.publicMethod = function (){
privateVariable++;
return privateFunction();
};
}
但是,在构造函数中定义特权方法有一个缺点,就是针对每个实例都会创建同样一组新方法,而使用静态私有变量实现特权方法可以避免。
1.静态私有变量
(function(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//构造函数
MyObject = function(){
};
//公有/特权方法
MyObject.prototype.publicMethod = function (){
privateVariable++;
return privateFunction();
};
})();
这个模式与在构造函数中定义特权方法的区别在于,私有变量和函数是由实例共享的。
2.模块模式
模块模式指的是为单例创建私有变量和特权方法,而单例指的是只有一个实例的对象。
var singleton = function(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//特权/公有方法和属性
return {
publicProperty: true,
publicMethod: function(){
privateVariable++;
return privateFunction();
}
};
}();
3.增强的模块模式
增强的模块模式适合那些必须是某种类型的实例,同时还必须添加某种属性和(或)方法对其加以增强的情况。
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;
}();