定义函数的方式有两种:
- 函数声明
- 函数表达式
函数声明的语法
function funtionName(arg0, arg1, arg2){
// 函数体
}
关于函数声明,他有一个重要特征就是函数声明提升,就是在执行代码之前会读取函数声明。
sayHi();
function sayHi(){
alert("hi!");
}
函数表达式
var functionName = function(arg0, arg1, arg2){
// 函数体
};
这种形式的看起来像常规的变量赋值语句,即创建一个函数并将它赋值给变量,这时候也叫匿名函数。
函数表达式,在使用前必须先赋值。
sayHi(); // 错误,函数不存在
var sayHi = function(){
alert("sayHi");
}
递归
递归函数是在函数通过名字调用自身情况下构成的:
function factorial(num){
if( num <=1 ){
return 1;
} else{
return num * factorial(num - 1);
}
}
这是一个经典的递归阶乘函数。虽然这个函数表面没什么问题,但下面的代码可能导致出错。
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); // 出错!
我们知道,arguments.callee
是一个指向正在执行的函数指针,可以用它来实现对函数的递归调用。
function factorial(num){
if( num < =1 ){
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
通过使用 arguments.callee
代替函数名,无论如何也不会出问题。
闭包
闭包 是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
function createComparisonFunction(propertyName){
return function(object1, object2){
var value1 = object[propertyName];
var value2 = object[propertyName];
if(value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else{
return 0;
}
}
}
闭包与变量
根据作用域链的匹配机制,闭包只能取得包含函数中任何变量的最后一个值。闭包保存的是整个变量对象,而不是特殊的变量。
function createFunction(){
var result = new Array();
for(var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
// for语句中创建的变量i即使在for循环结束,也依旧存在于循环外部的执行环境
return result;
}
这个函数会返回一个数组,表面上看,似乎每个函数都应该返自己的索引值,即位置0返回0,位置1返回1。实际上,每个函数都返回10。
因为每个函数的作用域链中都保存着 createFunctions()函数的活动对象,所以他们都引用同一个变量i。当createFunctions()函数返回后,变量i的值是10,因此每个函数都饮用者保存变量i的同一个变量对象,所以在每个函数内部i都是10。也可以创建另一个匿名函数强制让闭包行为符合预期:
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等于 window,当函数作为每个对象调用时,this等于那个对象。
匿名函数的执行环境具有全局性,因此其 this对象通常指向 window。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
}
alert(object.getNameFunc()()); // "The Window"
可以通过外部作用域中的this对象保存在一个闭包能够访问的变量里,这样就可以让闭包访问该对象了。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); // "My Object"
或者修改代码,也可以访问到 “My Object”:
var name = "The Window";
var object = {
name : "My Object",
getName : function(){
return this.name;
}
};
object.getName(); // "My Object"
模仿块级作用域
用作块级作用域(通常称为私有作用域)的匿名函数语法如下:
(function(){
// 块级作用域
})();
以上定义并立即调用了一个匿名函数。将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。紧跟着一个括号会立即调用这个函数。通过创建私有作用域,开发人员既可以使用自己的变量,又不必担心搞乱全局变量。
在匿名函数中定义的任意变量,都会在执行结束时被销毁。
私有变量
任何在函数中定义的变量,都可以认为是私有变量,因为不能再函数外部访问这些变量。
私有变量包括函数的参数 、局部变量 和在函数内部定义的其他函数。
我们把有权访问私有变量和私有函数的公有方法称为特权方法。
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){
return name = value;
}
};
var person = new Person("Lyf");
alert(person.getName()); // "Lyf"
person.setName("JackLiu");
alert(person.getName()); // "JackLiu"
静态私有变量
通过私有作用域中定义私有变量或函数,同样也可以创建特权方法
(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("Lyf");
alert(person1.getName()); // "Lyf"
person1.setName("JackLiu");
alert(person1.getName()); // "JackLiu"
var person2 = new Person("Mike");
alert(person1.getName()); // "Mike"
alert(person2.getName()); // "Mike"
在这种模式下,变量name就变成一个静态的、由所有实例共享的属性。也就是说,调用setName()会影响所有实例。
模块模式
前面的模式是用于为自定义类型创建私有变量和特权方法的。
而模块模式是为单例创建私有变量和特权方法。
所谓单例,指的是只有一个实例的对象。按照惯例,JavaScript是以对象字面量的方式来创建单例对象。
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 application = function(){
// 私有变量和函数
var components = new Array();
// 初始化
components.push(new BaseComponent());
// 公共
return {
getComponentsCount : function(){
return components.length;
},
registerComponent : function(){
if(typeof component == "object"){
component.push(component);
}
}
};
}();
增强的模块模式
这种增强的模块模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性和方法对其加以增强的情况。
var singleton = function(){
// 私有变量和私有函数
var privateValue = 10;
function privateFunction(){
return false;
};
// 创建对象
var object = new CustomType();
// 添加特权/公有属性和方法
object.publicProperty = true;
object.publicMethod = function(){
privateVariable ++;
return privateFunction();
};
// 返回这个对象
return object;
}