定义函数的方式有俩种:
- 函数申明;
- 函数表达式;
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
"这里理解不够 - 关于私有作用域和变量这块的理解也有待提高;
有时间再回来补充,归根结底还是自己代码敲少了…