JavaScript高级程序设计的第五章: 函数表达式
递归
在ECMAScript中,通过名字进行的递归,会由于函数被赋值到其他变量上,而导致错误:
1 2 3 4 5 6 7 8 9 10 11
| function factorial(num) { if (num <= 1) { return 1; } else { return num * factorial(num - 1); } }
var anotherFactorial = factorial; factorial = null; alert(anotherFactorial); // error
|
于是,应该这样解决:
1 2 3 4 5 6 7
| function factorial(num) { if(num <= 1) { return 1; } else { return num * arguments.callee(num - 1); } }
|
函数的arguments的callee对象指向了正在执行的函数的指针。
但是在严格模式下,是不能使用callee对象的。于是,可以使用明明函数表达式来达成相同的结果:
1 2 3 4 5 6 7
| var factorial = (function f(num) { if(num <= 1) { return 1; } else { return f(num - 1); } })
|
上述代码,创建了一个名为f()的明明函数表达式。然后将它赋值给了factorial。这样一来,就算把函数赋值给了另一个变量,函数的名字f依然有效。
闭包
闭包是指,有权访问另一个函数作用域中的变量的函数。
创建闭包的常用方式,就是在一个函数内部创建另一个函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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; } } }
|
上述的:
1 2
| var value1 = object1[propertyName]; var value2 = object2[propertyName];
|
这两行代码,访问来外部函数的propertyName变量。并且,即时这个内部函数被返回了,并且被其他地方调用了,它依旧可以访问propertyName变量。
这是因为,内部函数的作用域链中包含了createComparisonFunction()的作用域。
闭包与变量
闭包,只能取得包含函数中任何变量的最后一个值。意思就是说,闭包中获取的外部函数的变量的值,应该就它的最新的值。
1 2 3 4 5 6 7 8 9 10 11
| function createFunction() { var result = new Array();
for(var i = 0; i < 10; i++) { result[i] = function() { return i; } }
return result; }
|
例如,以上代码中,每个函数都会返回10。因为,上述createFunction的活动对象引用的i是同一个变量。
我们可以这样做,来强制让闭包的行为符合预期:
1 2 3 4 5 6 7 8 9 10 11
| 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对象
this对象是在运行时基于函数的执行环境绑定的。
匿名函数的执行环境具有全局性。其this对象通常指向window。但有时候由于编写闭包的方式不同,可能会不明显:
1 2 3 4 5 6 7 8 9 10 11 12 13
| var name = "The Window";
var object = { name: "My Object",
getNamePunc: function() { return function() { return this.name; }; } };
alert(object.getNameFunc());
|
以我的理解来看,object.getNameFunc()只是返回了内部的匿名函数,而这个匿名函数真正的调用,应该是在全局环境中,而非object下的。
不过,可以这样做,让闭包访问这个对象:将外部作用域中的this对象保存在一个闭包能够访问的变量里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var name = "The Window";
var object = { name: "My Object",
getNamePunc: function() { var that = this return function() { return that.name; }; } };
alert(object.getNameFunc());
|
模仿块级作用域
js是没有块级作用域的。嗯。在块语句中定义的变量,实际上是包含在函数中而非语句中创建的。
可以用匿名函数
模仿块级作用域:
以上代码定义并调用了一个匿名函数。
可以理解为:
1 2 3 4
| var someFunction = function() {
}; someFunction();
|
这种方式,经常被用在函数外部,用于限制向全局作用域中添加过多的变量和函数。
私有变量
在函数中定义的变量,都可以认为是私有变量。
私有变量包括函数的参数、局部变量和函数内部定义的其他函数。
eg:
1 2 3 4
| function add(num1, num2) { var sum = num1 + num2; return sum; }
|
上述代码包含了三个私有变量:num1、num2、sum;
可以在这个函数的内部创建一个闭包,这个闭包通过自己的作用域链也可以访问到这些变量。利用这点,就可以创建用于访问私有变量的公共方法了。
1 2 3 4 5 6 7 8 9 10 11 12
| function MyObject() { var privateVariable = 10;
function privateFunction() { return false; }
this.publicMethod = function() { privateVariable ++; return privateFunction(); } }
|
这种有权访问私有变量和私有函数的公共方法被称为特权方法。
利用私有和特权成员,可以隐藏安歇不应该被直接修改的数据:
1 2 3 4 5 6 7 8 9
| function Person(name) { this.getName = function() { return name; };
this.setName = function(value) { name = value; }; }
|
静态私有变量
在私有作用域中定义私有变量或函数,同样可以创建特权方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| (function(){ var privateVariable = 10;
function privateFunction() { return false; }
MyObject = function() {
};
MyObject.prototype.publicMethod = function() { privateVariable++; return privateFunction(); } })();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| (function() { var name = "";
Person = function(value) { name = value; };
Person.prototype.getName = function() { return name; }
Person.prototype.setName = function(value){ name = value; } })();
var person1 = new Person("Nicholas"); alert(person1.getName());
var person2 = new Person("Michael"); alert(person1.getName()); alert(person2.getName());
|
模块模式
模块模式,是为单例创建私有变量和特权方法的模式。
单例:指的是只有一个实例的对象。
eg: js通过对象字面量创建单例
1 2 3 4 5 6
| var singleton = { name: value, method: function(){
} };
|
模块模式为单例添加私有变量和特权方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| var singleton = function() { var privateVariable = 10;
function privateFunction() { return false; }
return { publicProperty: true,
publicMethod: function() { privateVariable++; return privateFunction(); } } }();
|
增强的模块模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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; }
|