August 08, 2017

# 本章内容

• 函数表达式的特征
• 使用函数实现递归
• 使用闭包定义私有变量

``````function functionName(arg0, arg1, arg2){
//函数体
}
//只在Firefox、safari、chrome和opera中有效
console.log(functionNmae.name);//"functionNmae"``````

``````sayHi();
function sayHi(){
console.log("Hi!");
}``````

``````var functionName = function(arg0, arg1, arg2){
//函数体
};//这里有分号``````

``````sayHi();
var sayHi = function(){
console.log("Hi!");
}``````

# 递归

``````function factorial(num){
if(num <= 1){
return 1;
}else{
return num * factorial(num - 1);
}
}``````

``````var anotherFactorial = factorial;
factorial = null;
console.log(anotherFactorial(4));//error``````

arguments.callee是一个指向正在执行的函数的指针，因此可以用它来实现对函数的递归调用，例如：

``````function factorial(num) {
if(num <= 1){
return 1;
} else {
return num * arguments.callee(num - 1);
}
}``````

``````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;
}
};
}``````

``````function compare(value1, value2){
if(value1 < value2){
return -1;
}else if(value1 > value2){
return 1;
}else{
return 0;
}
}``````

``````var compare = createComparisonFunction("name");
var result = compare({name: "Nicholas"}, {name: "Greg"});``````

``````//创建函数
var compareNames = createComparisonFunction("name");

//调用函数
var result = compareNames({ name: "Nicholas"}, {name: "Greg"});

//解除对匿名函数的引用
compareNames = null;``````

## 闭包与变量

``````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;
};
}
}

``````var name = "The Window";

var object = {
name : "My Object",

getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
}

``````var name = "The Window";

var object = {
name : "My Object",

getName : function() {
return this.name;
}
}

console.log(object.getName());//My Object
console.log((object.getName)());//My Object
console.log((object.getName = object.getName)());//The Window``````

## 内存泄露

IE9之前的版本对JScript对象和COM对象使用不同的垃圾收集例程。

``````function assignHandle(){
var element = document.getElementById("someElement");
element.onclick = function(){
};
}``````

``````function assignHandle(){
var element = document.getElementById("someElement");
var id = element.id;
element.onclick = function(){
};
element = null;
}``````

# 模仿块级作用域

``````function outputNumbers(count){
for(var i = 0; i < count ; i++){
console.log(i);
}
console.log(i);
}
outputNumbers(2);//0,1,2``````

``````(function(){
//这里是块级作用域
})();``````

``````function(){
//这里是块级作用域
}();//error``````

``````function outputNumbers(count){
(function(){
for(var i = 0; i < count ; i++){
console.log(i);
}
})();
console.log(i);//error
}
outputNumbers(2);//0,1,error``````

# 私有变量

``````function MyObject(){
//私有变量和私有函数
var privateVariable = 10;

function privateFunction(){
return false;
}

//特权方法
this.publicMethod = function(){
privateVariable++;
return privateFunction();
};
}``````

``````function Person(name){
this.getName : function(){
return name;
};
this.setName : function(){
name = value;
};
}

var person = new Person("Nicholas");
console.log(person.getName());//Nicholas
person.setName("Greg");
console.log(person.getName());//Greg``````

## 静态私有变量

``````(function(){
//私有变量和私有函数
var privateVariable = 10;

function privateFunction(){
return false;
}

//构造函数
MyObject = function(){

};
//公有/特权方法
MyObject.prototype.publicMethod = function(){
privateVariable++;
return privateFunction();
};
})();``````

``````(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");
console.log(person1.getName());//"Nicholas"
person1.setName("Greg");
console.log(person1.getName());//"Greg"

var person2 = new Person("Michael");
console.log(person1.getName());//"Michael"
console.log(person2.getName());//"Michael"
``````

## 模块模式

``````var singleton = {
name : value,
method : function(){
//这里是方法的代码
}
};``````

``````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.pulicMethod = function(){
privateVariable++;
return privateFunction();
};

//返回这个对象
return object;
}();``````

``````var application = function(){
//私有变量和函数
var components = new Array();

//初始化
components.push(new BaseComponent());

//创建application的一个局部副本
var app = new BaseComponent();

//公共接口
app.getComponentCount = function(){
retrun components.length;
};

app.rigisterComponent = function(component){
if(typeof component == "object"){
components.push(component);
}
};
//返回这个副本
return app;
}();``````

# 小结

• 函数表达式不同于函数声明。函数声明要求有名字，但函数表达式不需要。没有名字的函数表达式也叫做匿名函数。
• 无法确定如何引用函数的情况下，递归函数就会变得比较复杂
• 递归函数应该始终使用arguments.callee来递归地调用自身，不要使用函数名

• 后台执行环境中，闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域。
• 通常，函数的作用域以及所有变量都会在函数执行结束后被销毁。
• 但是，当函数返回了一个闭包时，这个函数的作用域将会一直在内存中保存到闭包不存在为止。

• 创建并立即调用一个函数
• 以上的结果会因为函数被调用过后，内部的所有变量会被立即销毁——除非某些变量赋值给了包含作用域中的变量

• 可以使用闭包来实现公有方法，而通过公有方法可以访问在包含作用域中定义的变量。
• 有权访问私有变量的公有方法叫做特权方法
• 可以使用构造函数模式、原型模式来实现自定义类型的特权方法，也可以使用模块模式、增强的模块模式来实现单例的特权方法。

Written by 何坤舆 EFE