目录
创建一个函数并将它赋值给变量 functionName。这种情况下创建的函数叫做匿名函数(anonymous function),因为 function 关键字后面没有标识符。匿名函数有时候也叫拉姆达函数,name 属性是空字符串。
//不要这样做!一般后面的会覆盖前面的
if(condition){
function sayHi(){
alert("Hi!");
}
} else {
function sayHi(){
alert("Yo!");
}
}
//可以这样做
var sayHi;
if(condition){
sayHi = function(){
alert("Hi!");
};
} else {
sayHi = function(){
alert("Yo!");
};
}
递归
arguments.callee 是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用。
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}
//严格模式下,不能通过脚本访问 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];//访问了外部函数中的变量 propertyName
var value2 = object2[propertyName];
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
};
}
function compare(value1, value2){
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
}
var result = compare(5, 10);
无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。但是,闭包的情况又有所不同。
//创建函数
var compareNames = createComparisonFunction("name");
//调用函数
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
//解除对匿名函数的引用(以便释放内存)
compareNames = null;
闭包与变量
作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。
/*表面上看,似乎每个函数都应该返自己的索引值,即位置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(){
return i;
};
}
return result;
}
//通过创建另一个匿名函数强制让闭包的行为符合预期
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
关于this对象
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"(在非严格模式下)
每个函数在被调用时都会自动取得两个特殊变量: 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"
内存泄漏
由于 IE9 之前的版本对 JScript 对象和 COM 对象使用不同的垃圾收集例程因此闭包在 IE 的这些版本中会导致一些特殊的问题。具体来说,如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁。
function assignHandler(){
var element = document.getElementById("someElement");
element.onclick = function(){
alert(element.id);
};
}
模仿块级作用域
JavaScript 没有块级作用域的概念。这意味着在块语句中定义的变量,实际上是在包含函数中而非语句中创建的。
function outputNumbers(count){
for (var i=0; i < count; i++){
alert(i);
}
alert(i); //计数
}
在 Java、 C++等语言中,变量 i只会在 for 循环的语句块中有定义,循环一旦结束,变量 i 就会被销毁。可是在 JavaScrip 中,变量 i是定义在 ouputNumbers()的活动对象中的,因此从它有定义开始,就可以在函数内部随处访问它。
JavaScript 从来不会告诉你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(不过,它会执行后续声明中的变量初始化)。匿名函数可以用来模仿块级作用域并避免这个问题。
/*
* (function(){
* //这里是块级作用域
* })();
*以上代码定义并立即调用了一个匿名函数。将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。
*而紧随其后的另一对圆括号会立即调用这个函数。
*/
function outputNumbers(count){
(function () {
for (var i=0; i < count; i++){
alert(i);
}
})();
alert(i); //导致一个错误!
}
(function(){
var now = new Date();
if (now.getMonth() == 0 && now.getDate() == 1){
alert("Happy new year!");
}
})();
私有变量
严格来讲,JavaScript 中没有私有成员的概念;所有对象属性都是公有的。不过,倒是有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。
function add(num1, num2){
var sum = num1 + num2;
return sum;
}
/*
* 在这个函数内部,有 3 个私有变量: num1、 num2 和 sum。在函数内部可以访问这几个变量,但在
*函数外部则不能访问它们。如果在这个函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这
*些变量。而利用这一点,就可以创建用于访问私有变量的公有方法。
*/
特权方法(privileged method)
//在构造函数中定义特权方法
function MyObject(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//特权方法
this.publicMethod = function (){
privateVariable++;
return privateFunction();
};
}
静态私有变量
//在私有作用域中定义私有变量或函数,同样也可以创建特权方法
(function(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//构造函数
MyObject = function(){
};
//公有/特权方法
MyObject.prototype.publicMethod = function(){
privateVariable++;
return privateFunction();
};
})();
模块模式
//通过为单例添加私有变量和特权方法能够使其得到增强
var singleton = function(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//特权/公有方法和属性
return {
publicProperty: true,
publicMethod : function(){
privateVariable++;
return privateFunction();
}
};
}();
增强的模块模式
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;
}();