定义函数的方式有俩种:

  • 函数申明;
  • 函数表达式;
function functionName(arg0,arg1,arg2){
	//函数体
}

var functionName = function(arg0,arg1,arg2){
	//函数体	
}

函数声明提升(function declaration hoisting),意思是在执行代码前会先读取函数声明。意味着可以把函数声明放在调用它的语句后面。

匿名函数(anonymous function,也叫拉姆达函数)function关键字后面没有标识符。匿名函数的name属性是空字符串。
把函数当成值来使用的情况下,都可以使用匿名函数。

1.递归

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

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

var anotherFactorial  = factorial;
factorial = null;
alert(anotherFactorial(4));		//24

2.闭包

闭包是指有权访问另一个函数作用域中的变量的函数。 创建闭包的常见方式,就是在一个函数内部创建另一个函数。

无论什么时候在函数中访问一个变量,就会在作用域链中搜索具有相应名字的变量。一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境中的变量对象)。但是闭包的情况不同。

在闭包中,外部函数执行完毕后,其活动对象并不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。也就是当外部函数返回后,其执行环境的作用域链会被销毁,但其活动对象仍然会留在内存中;直到匿名函数被销毁后,外部函数的活动对象才会被销毁。

由于闭包会携带外部函数的作用域,因此会比其他函数占用更多的内存。

闭包与变量

闭包只能取得包含函数中任何变量的最后一个值。

function createFunction(){
	var result = new Array();

	for(var i = 0; i < 10; i++){
		result[i] = function(){
			return i;
		};
	}

	return result;		//i=10
}

function createFunction(){
	var result = new Array();

	for (var i=0; i < 10; i++){
		result[i] = function(num){
			return function(){
				return num;
			};
		}(i);		//此处的立即执行函数会在定义匿名函数后立即执行,将变量'i'的值复制给参数`num`
	}
	return result;
}

关于this

this对象是在运行时基于函数的运行环境绑定的:在全局环境中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。
然后,匿名函数的执行环境具有全局性,因此其this对象通常指向window

内存泄漏

因为闭包会引用外部函数的整个活动对象,如果该活动对象包含外部函数中的变量,那么即使闭包不直接引用该变量,外部函数的活动对象也仍然会保存一个引用。

3.模仿块级作用域

匿名函数可以用来模仿块级作用域。
用作块级作用域(私有作用域)的匿名函数语法:

(function(){
	//此处为块级作用域
})();
  • 代码定义并立即调用一个匿名函数;
  • 将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。而紧随其后的另一对圆括号会立即调用这个函数;
  • 函数声明后不可跟圆括号;但函数表达式后面可以跟圆括号;
  • 只要临时需要一些变量,就可以使用私有作用域;
  • 这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数;

4.私有变量

任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。
私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。

有权访问私有变量和私有函数的方法称为特权方法:

  • 在构造函数中定义特权方法;
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(value){
		name = value;
	}
}

var person = new Person("Nicholas");
alert(person.getName());		//"Nicholas"
person.setName("Greg");
alert(person.getName());		//"Greg"

静态私有变量

在私有变量作用域中定义私有变量或函数,同样可以创建特权方法。

(function(){
	//私有变量和私有函数
	var privateVariable = 10;
	function  privateFunction(){
		return false;
	}

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

	}

	//公有/特权方法
	MyObject.prototype.publicMethod = function(){
		privateVariable++;
		return privateFunction();
	}
})();
  • 该模式创建了一个私有作用域,然后封装了一个构造函数及相应的方法;
  • 函数声明只能创建局部函数,所以这里采用函数表达式创建了一个全局变量MyObject,能够在私有作用域之外被访问到;

模块模式

为单例创建私有变量和特权方法。
单例:只有一个实例的对象;

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

小结

第二次看这里的内容,又有了一些新的收获,但一样觉得自己的理解不深

  • 闭包大概了解了,但闭包中的变量和this对象依旧迷迷糊糊,尤其是关于"匿名函数的执行环境具有全局性,因此this对象通常指向window"这里理解不够
  • 关于私有作用域和变量这块的理解也有待提高;

有时间再回来补充,归根结底还是自己代码敲少了…