函数表达式
函数表达式是定义函数的一种方式,如下:
1 2 3 4 5 6 7 8
| var add = function(v1, v2){ return v1 + v2; }
function add(v1, v2) { return v1 + v2; }
|
函数表达式可以创建一个匿名函数,它与JS的很多特性与模式有关,包括闭包、私有变量创建等等。
闭包
闭包指一个函数,该函数有权访问其他函数作用域中的变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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; } } }
var compare = createComparisonFunction("name"); console.log(compare({name: "syz"}, {name: "xj"}));
compare = null;
|
放回的匿名函数引用了外包函数createComparisonFunction()的变量propertyName。
需要注意的是,在createComparisonFunction()执行完毕产生比较函数compare()时,其作用域链被销毁,但是其活动对象(此处指propertyName)仍被compare()函数引用,并没有销毁。
闭包实现的具体原理是:
首先,每一个执行环境都有一个变量对象表示。全局执行环境的变量对象始终存在,而函数内部的变量对象在执行过程中创建,执行完毕销毁。createComparisonFunction()函数执行过程,首先创建一个包含全局变量对象的作用域链,并被内部属性[[Scope]]引用,其中的参数、变量与方法都作为活动对象(该函数的变量对象)被推入作用域链的顶端,构成该函数的作用域链。作用域链提供一个变量对象的引用链,通过它可以访问链上的变量。
然后,内部函数的创建,首先创建一个包含全局变量对象的作用域链。如果内部函数引用了外部函数的变量,即闭包,那么内部函数的作用域链会添加外部函数的活动对象,最后再将本函数的活动对象推入作用域链顶端。
最后,外部函数createComparisonFunction()执行完毕,本来所有作用域链与活动对象全部销毁,但由于内部函数的引用,导致只销毁作用域链,而活动对象继续被内部函数引用,直到内部函数被销毁。
匿名函数中的this对象
要熟知函数的this对象指向其对应的执行环境。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| var object = { name: "this indicator", getName: function(){ return function(){ return this.name; } } }
console.log(object.getName()());
var object = { name: "this indicator", getName: function(){ var that = this; return function(){ return that.name; } } }
console.log(object.getName()());
|
模拟块级作用域
由于JS不存在块级作用域,因此if、for语句中声明的变量存在于全局变量中,会导致内存的溢出及命名的冲突。
可以通过匿名函数的方式模拟模拟块级作用域,将生命的变量随匿名函数的生命周期绑定。
1 2 3 4 5
| (function() { for(var i = 0; i <10; i++){ console.log(i); } })();
|
自定义类型的私有变量
JS的所有属性没有私有概念。但是函数内的变量是局部的,外部不可访问的。
可在构造函数或私有作用域中声明私有变量与方法,在匿名函数中引用外部函数的私有变量,并提供可访问这些方法的特权函数,实现自定义类型的私有变量。
构造函数中创建私有变量
1 2 3 4 5 6 7 8 9 10 11 12
| function Person(){ var name = "syz"; function getGender(){ return "man"; } this.getInfo = function(){ name = "xj"; return getGender(); } }
|
还可以通过匿名函数实现私有变量的不可修改与直接读取的设定。
1 2 3 4 5 6 7 8 9 10
| function Person(name){ this.getName = function(){ return name; } this.setName = function(value){ name = value; } }
|
静态私有变量
采用构造函数声明私有变量对每个实例都是唯一的,会造成代码的重复。
对属于类的公共的属性与方法,可以通过静态私有变量的方式私有化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
(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("syz"); console.log(person1.getName());
var person2 = new Person("xj"); console.log(person1.getName()); console.log(person2.getName());
|
单例的私有变量
JS中的单例很简单。
1 2 3
| var singleton = { description: "I am a singleton"; }
|
模块模式
单例可以通过模块模式的方式实现变量的私有化。基本思想是通过将私有变量与方法保存在外部匿名函数的活动对象中,然后返回一个具有特权访问函数的匿名对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var application = function() { var components = new Array(); components.push(new BaseComponent());
return { getComponentCount : function(){ return components.length; }, registerComponent : function(component){ if (typeof component == "object"){ components.push(component); } } } }();
|
增强模块模式
如果想创建指定类型的单例,并为其添加私有变量及增加属性与方法,可采用增强模块模式。基本思想与模块模式差不多,只不多不返回匿名对象,而是先创建指定类型的单例对象,然后为该对象添加特权访问函数、增强属性与方法,然后返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var application = function(){ var components = new Array(); components.push(new BaseComponent()); var app = new BaseComponent(); app.getComponentCount = function(){ return components.length; }; app.registerComponent = function(component){ if (typeof component == "object"){ components.push(component); } }; return app; }();
|