一、函数表达式
1.定义函数2种方式
- 函数声明
function functionName(arg0, arg1, arg2) {
//函数体
}
Firefox、Safari、Chrome 和 Opera 给函数定义了一个非标准的 name 属性,可以访问函数名。
重要特征:函数声明提升(function declaration hoisting)
- 函数表达式
var functionName = function(arg0, arg1, arg2){
//函数体
};
这种情况下创建的函数叫做匿名函数(anonymous function)(也叫拉姆达函数)。
匿名函数的 name 属性是空字符串。
函数表达式,使用前必须先赋值。
匿名函数的另一使用:函数作为返回值时。
2.递归
递归函数:在一个函数通过名字调用自身的情况下构成的。
function factorial(num){
if (num <= 1){
return 1;
} else {
// return num * factorial(num-1);
return num * arguments.callee(num-1);
}
}
arguments.callee 是一个指向正在执行的函数的指针,可以用来实现对函数的递归调用。
编写递归函数时,使用 arguments.callee 比使用函数名更保险。但严格模式下会报错。
解决方法:使用命名函数表达式。
var factorial = (function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num-1);
}
});
3.闭包
闭包:指有权访问另一个函数作用域中的变量的函数。
创建闭包的常见方式:在一个函数内部创建另一个函数。
函数被调用时发生的事:
当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链。然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象(activation object)。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,……直至作为作用域链终点的全局执行环境。
作用域链本质:是一个指向变量对象的指针列表。
它只引用但不实际包含变量对象。
在内部匿名函数从外部函数中被返回后,它的作用域链被初始化为包含外部函数的活动对象和全局变量对象。
–> 匿名函数就可以访问在外部函数中定义的所有变量。
在后台执行环境中,闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域。
外部函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。
换句话说:当外部函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然会留在内存中;直到匿名函数被销毁后,外部函数的活动对象才会被销毁。
//创建函数
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(num){
return function(){
return num;
};
}(i);
}
return result;
}
关于 this 对象
在闭包中使用 this 对象可能会导致一些问题。
匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。
每个函数在被调用时都会自动取得两个特殊变量: this 和 arguments 。
内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。
要想访问到,可以使用that保存this值,arguments同理。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); //"My Object"
注意:几种特殊情况下, this 的值可能会意外地改变。
var name = "The Window";
var object = {
name : "My Object",
getName: function(){
return this.name;
}
}
object.getName(); //"My Object"
(object.getName)(); //"My Object"
(object.getName = object.getName)(); //"The Window",在非严格模式下
最后一行的赋值表达式的值是函数本身,所以 this 的值不能得到维持,结果就返回了 “The Window” 。
内存泄漏
闭包在 IE 9之前版本中会导致一些特殊的问题:如果闭包的作用域链中保存着一个HTML 元素,该元素将无法被销毁。
4.模仿块级作用域
用作块级作用域(通常称为私有作用域)的匿名函数:
(function(){
//这里是块级作用域
})();
JavaScript 将 function 关键字当作一个函数声明的开始,而函数声明后面不能跟圆括号。而函数表达式的后面可以跟圆括号。
要将函数声明转换成函数表达式,只要给函数声明加上一对圆括号。
私有作用域 - 限制向全局作用域中添加过多的变量和函数
5.私有变量
私有变量:在函数中定义的变量。因为不能在函数的外部访问这些变量。
私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。
特权方法(privileged method):有权访问私有变量和私有函数的公有方法。
在对象上创建特权方法的2种方式:
- 在构造函数中定义特权方法
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();
};
})();
和前一种方式的主要区别:私有变量和函数是由实例共享。
优缺点:因为使用原型而增进代码复用,但每个实例都没有自己的私有变量。
结论:是使用实例变量,还是静态私有变量,根据具体需求而定。
模块模式
前面2种模式 – 为自定义类型创建私有变量和特权方法
模块模式 – 为单例创建私有变量和特权方法
单例(singleton):只有一个实例的对象。通常使用对象字面量创建单例对象。
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;
}();
适应场景:适合那些单例必须是某种类型的实例,同时还必须添加某些属性和方法对其加以增强的情况。=