标签:
整理了JavaScript中函数Function的各种,感觉函数就是一大对象啊,各种知识点都能牵扯进来,不单单是 Function 这个本身原生的引用类型的各种用法,还包含执行环境,作用域,闭包,上下文,私有变量等知识点的深入理解。
function n(){ (function(){ return 5; })(); } n();// undefined //立即执行匿名函数中的return语句其实是返回给它所在的匿名函数的。 function n(){ var num= (function(){ return 5; })(); console.log(num); }
函数实际上是对象,每个函数实际上都是 Function 类型的实例。而且与其他引用类型一样具有属性和方法。函数名实际上是一个指向内存堆中某个函数对象的指针。
定义函数的方式
function sum(num1,num2){ return num1+num2; }
var sum=function(num1,num2){ return num1+num2; };
定义了一个变量 sum 并将其初始化为一个函数,注意到 function 关键字后面并没有函数名,这是因为在使用函数表达式定义函数,没必要使用函数名,通过变量 sum 即可引用函数。还要注意函数末尾有个分号,就像声明其他变量一样。
var sum=new Function(‘num1‘,‘num2‘,‘return num1+num2;‘); sum;// function anonymous(num1,num2 /**/) { return num1+num2; }
当使用不带圆括号的函数名是访问函数指针,而非调用函数。
ECMAScript中所有参数传递的都是值(即使是引用也是传递的地址值,不是引用传递参数(可参考JavaScript传递参数是按值传递还是按引用传递))。ECMAScript函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型。之所以这样,是因为ECMAScript中的参数在内部是用一个数组表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数。在函数体内,可以通过 arguments 对象来访问这个数组。从而获取传递给函数的每个参数。
function func(){ console.log(Object.prototype.toString.call(arguments)); } func();// [object Arguments]
function doAdd(num1,num2){ console.log(arguments.length); console.log(num2) arguments[1]=10; console.log(num2); } doAdd(5,0);//2 0 10 doAdd(5);//1 undefiend undefined
ECMAScript函数不能像传统意义上那样实现重载,而在其他语言中(Java),可以为一个函数编写两个定义,只要这两个定义的签名(接收参数的类型和数量)不同即可。
不能实现重载的原因:
function reload(){ if(arguments.length==0){ console.log(‘没传参‘); }else if(arguments.legth==1){ console.log(‘传了一个参数‘); } }
function add(){ return 100; } function add(num){ return num+200; } //实际上和下面代码没什么区别 function add(){ return 100; } add=function(num){ return num+200; }
实际上解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。
JavaScript运行机制浅探 中了解到对于解释型语言来说,编译步骤为:
解析器会先读取函数声明,并使其在执行任何代码之前可用。至于函数表达式,则必须等到执行阶段才会被真正赋值。什么意思呢?虽然两者都进行了变量提升,待真正执行时构造活动对象从语法树种取声明添加到执行环境中,但一个是函数提升,一个是变量提升。
//函数声明 console.log(func);//function func(){} function func(){ } //函数表达式 console.log(func1);// undefined var func1=function(){}; console.log(func1);// function(){}
因为ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。
function callSomeFunction(someFunction,someArgument){ return someFunction(someArgument); } function concated(str){ return "Hi "+str; } callSomeFunction(concated,‘xx‘);// ‘Hi xx‘
从一个函数中返回另一个函数的应用:假设有一个对象数组,想要根据某个对象属性对数组进行排序,但传给 sort() 方法的比较函数要接收两个参数,即要比较的值。我们需要一种方式来指明按照哪个属性来排序。我们可以定义一个函数它接收一个属性名,然后根据这个属性名来创建一个比较函数。默认情况下, sort 函数会调用每个对象的 toString() 方法以确定它们的次序。
function createCompare(property){ return function(obj1,obj2){ var value1=obj1[property], value2=obj2[property]; if(value1<value2) return -1; else if(value1>value2) return 1; else return 0; } }
var data=[{name:‘aa‘,age:20},{name:‘bb‘,age:12},{name:‘cc‘,age:30}]; data.sort(createCompare("age"));// [{name:‘bb‘,age:12},{name:‘aa‘,age:20},{name:‘bb‘,age:30}]
arguments :类数组对象,包含传入函数中所有参数。是每个函数自身的属性,之所以可以直接访问 arguments ,是因为命名空间??以下变化是为了加强JavaScript语言的安全性,这样第三方代码就不能在相同的环境下窥视其他代码了。
//一般阶乘函数 function factorial(num){ if(num<=1){ return 1;} else { return num*factorial(num-1); } }
定义阶乘函数用到递归算法,这样定义是没问题。
缺点:这个函数的执行与函数名 factorial 紧紧耦合在一起。万一出现改变函数指向的这种情况就不太好了,
factorial=function(){} factorial(3);// undefiend
为了消除这种现象。
function factorial(num){ if(num<=1){ return 1; } else{ return num*arguments.callee(num-1); } }
function a(){ return Object.getOwnPropertyNames(arguments); } a();// ["length", "callee"]
this :行为与Java/C#中的 this 大致类似。 this 引用的是函数据以执行环境对象(当在网页的全局作用域中调用函数时, this 对象引用的就是 window )。
caller :不止是ECMAScript5中新增函数对象上的属性,还是 arguments 上的属性。保存着调用当前函数的函数的引用。如果是在全局作用域中调用当前函数,它的值为 null 。
Object.getOwnPropertyNames(Function);// ["length", "name", "arguments", "caller", "prototype"]
function outer(){ inner(); } function inner(){ console.log(inner.caller); //为了实现更松散的耦合,arguments.callee.caller } outer();// function outer(){ inner()}
严格模式下不能为函数的 caller 属性赋值,否则会导致出错。
function sayName(name){ // } function sum(num1,num2){ // } function sayHi(){ // } sayName.length;// 1 sum.length;// 2 sayHi.length;// 0
function sum(num1,num2){ return num1+num2; } function callSum1(num1,num2){ return sum.apply(this,arguments);//sum.apply(this,[num1,num2]) } callSum1(10,30);// 40
严格模式下,未指定环境对象而调用函数, this 值不会转型为 window 。除非明确把函数添加到某个对象或者调用 apply 或 call ,否则 this 值将是 undefined
function callSum1(num1,num2){ retrun sum.call(this,num1,num2); } callSum1(10,30);// 40
call 和 apply 真正强大的地方是能够扩充函数赖以运行的作用域,改变函数的执行环境。
window.color="red"; var o={color:‘blue‘}; function sayColor(){ console.log(this.color); } var newobj=sayColor.bind(o); newobj;// function sayColor(){ console.log(this.color); } newobj==sayColor;// false newobj();// blue
深入理解:可以将函数绑定到指定环境的函数。接收一个函数和一个环境,返回在给定环境中调用给定函数的函数。
function bind(func,context){ return function(){ func.apply(context,arguments);//这里创建了一个闭包,arguments使用的返回的函数的,而不是bind的 } }
当调用返回的函数时,它会在给定环境中执行被传入的函数并给出所有参数。
function bind(func,context,args){ return function(){ func.call(context,args); }; }
JavaScript接近词法作用域,变量的作用域是在定义时决定而不是在执行时决定,也就是说词法作用域取决于源码。
JavaScript引擎在执行每个函数实例时,都会为其创建一个执行环境,执行环境中包含一个AO变量对象,用来保存内部变量表,内嵌函数表,父级引用列表等语法分析结构(变量提升在语法分析阶段就已经得到了,并保存在语法树中,函数实例执行时会将这些信息复制到AO上)。
ECMA-262定义,JavaScript松散类型的本质决定了它只在特定时间用于保存特定值的一个名字而已,由于不存在定义某个变量必须要保存何种数据类型值得规则,变量的值及其数据类型可在脚本的生命周期内改变。
function setName(obj){ obj.name="xx"; obj=new Object(); obj.name="bb"; } var p=new Object(); setName(p); p.name;// "xx"
如果是按引用传递的,即传递的不是地址值而是堆内存中整个p对象,在 setName 中为其添加了一个新名字叫 obj ,又给其添加 name 属性后,将这个 obj 内容重新填充为新对象,那么之前的那个对象就不存在了更别说有 ”xx” 的名字属性,但是 p.name 仍然访问到了。这说明即使在函数内部修改了参数值,但原始的引用仍然保持未变。实际上,当在函数内部重写 obj 时,这个变量引用的就是一个局部对象了,而这个局部对象会在函数执行完毕后被立即销毁。
var color = "blue"; function changeColor(){ if(color=="blue"){ color="red"; }else{ color="blue"; } } changeColor(); console.log(color);// red
标识符解析是沿着作用域链一级一级地搜索标识符的过程,函数 changeColor 作用域链包含两个对象:它自己的变量对象(其中定义着 arguments 对象)和全局环境的变量对象。可以在函数内部访问到变量 color 就是因为可以在这个作用域链中找到它。内部环境可以通过作用域链访问所有外部环境,但外部环境不能访问内部环境的任何变量和函数。函数参数也被当作变量来对待,因此其访问规则与执行环境中的其他变量相同。
function buildUrl(){ var qs="?debug=true"; with(location){ var url=href+qs; } return url; } buildUrl();// "http://i.cnblogs.com/EditPosts.aspx?postid=5280805?debug=true"
with 语句接收的是一个 location 对象,因此其变量对象中就含有 location 对象的所有属性和方法,且这个变量对象被添加到了作用域链的最前端。当在 with 语句中引用变量 href (实际引用的是 location.href )可以在当前的执行环境中找到,当引用变量 qs 时,引用的则是在下一级执行环境中的变量。由于JavaScript中没有块级作用域,所以在函数内部可以访问 url 才能 return 成功,说明 url 并不是添加到 location 所在的变量对象中。
这两个语句都会在作用域的前端添加一个变量对象。
if(true){ var color="red"; } console.log(color);// red
if(condition){ function sayHi(){ console.log("Hi"); } }else{ function sayHi(){ console.log("Yo"); } }
以上代码会在 condition 为 true 时使用 sayHi() 的定义,否则就使用另一个定义。实际上这在ECMAScript中属于无效语法,JavaScript引擎会尝试修正错误,将其转换为合理的状态。但问题是浏览器尝试修正的做法不一样。大多数浏览器会返回第二个声明。此种方式很危险,不应该出现你的代码中。在chrome中:
if(true){ function sayHi(){ console.log("Hi"); } }else{ function sayHi(){ console.log("Yo"); } }//function sayHi(){ 没有函数声明的变量提升?? console.log("Hi"); }
if(false){ function say(){ console.log("Hi"); } }else{ function say(){ console.log("Yo"); } }//function say(){ console.log("Yo"); }
console.log(sa);//undefined 能输出undefiend说明函数声明并没有提升而是进行的变量提升 if(false){ function sa(){ console.log("Hi"); } }else{ function sa(){ console.log("Yo"); } }//function sa(){ console.log("Yo"); }
修正:使用函数表达式,那就没什么问题了。
var sayHi; if(condition){ sayHi=function(){ console.log("Hi"); } }else{ sayHi=function(){ console.log("Yo"); } }
在严格模式下,不能通过脚本访问 arguments.callee 。不过可以使用命名函数表达式来完成相同结果。
var factorial=(function f(num){ if(num<=1){ return 1; }else{ return num*f(num-1); } });
注意是用命名函数表达式,单单把命名函数赋值给 factorial 也可以,但是并不能通过f的名字访问
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。之所以能够访问外部作用域的变量,是因为内部函数的作用域链中包含外部作用域。当一个函数被调用的时候,
function compare(value1,value2){ if(value1<value2){ return -1; }else if(value1>value2){ return 1; }else{ return 0; } } var result=compare(5,10);
当调用 compare() 时,会创建一个包含 arguments , value1 , value2 的活动对象,全局执行环境的变量对象(包含 result 和 compare )在 compare() 执行环境的作用域链中处于第二位。
后台的每个执行环境都有一个表示变量的对象(变量对象),全局环境的变量对象始终存在,而像 compare() 函数这样的局部环境的变量对象,则只在函数执行过程中存在。在创建 compare() 函数时,会创建一个预先包含全局对象的作用域链,这个作用域链被保存在 compare 内部的 [[Scope]] 属性中。当调用 compare() 函数时,会为函数创建一个执行环境,然后通过复制函数的 [[Scope]] 属性中的对象构建起执行环境的作用域链。此后又有一个活动对象被创建并被推入执行环境作用域链的最前端。对于这个例子中, compare 函数的执行环境而言,其作用链包含两个变量对象:本地活动对象和全局变量对象。显然,作用域链的本质上是一个指向变量对象的指针列表,它只引用但不包含实际的变量对象。
无论什么时候在函数中访问一个变量,就会从作用域链中搜索具有相应名字的变量,一般来讲当函数执行完后,局部活动对象会被销毁,内存中仅保留着全局作用域(全局执行环境的变量对象)。但是闭包的情况又有所不同。在另一个函数内部定义的函数会将包含函数(外部函数)的活动对象添加到它的作用域链里,当外部函数执行完后其活动对象不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。换句话说只是外部函数它自己的作用域链被销毁,但活动对象还存在内存中。直到内部函数被销毁后(例如在外部解除了对闭包即内部函数的引用: func=null; ,解除相当于是闭包仅是执行完后),外部函数的活动对象才会被销毁。
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过多使用闭包可能会导致内存占用过多,建议只在绝对必要再考虑使用。但有的优化后的JavaScript引擎如V8会尝试回收被闭包占用的内存。
闭包缺点:作用域链的这种配置机制引出了一个副作用即闭包只能取得包含函数中任何变量的最后一个值。因为闭包保存的是整个变量对象,而不是某个特殊的变量。
function createFunctions(){ var result=new Array(); for(var i=0;i<3;i++){ result[i]=function(){ return i; }; } return result; } createFunctions()[0]();// 3 createFunctions()[1]();// 3 createFunctions()[2]();// 3
当执行 createFunctions 时,它的活动对象里有 arguments=[] , result=undefiend , i=undefiend ,执行完 createFunctions 后, result=[function(){return i},function(){return i},function(){return i}],i=3 ;当此时执行 result 数组时,访问到的i的值总是为3,因为沿着 function(){return i;} 的作用域链查找变量,在外层函数的活动对象上找到i总是为3。数组中每个函数的作用域链中都保存着 createFunctions 的活动对象,所以这些函数们引用的都是同一个活动对象,同一个变量i。
解决方案:要的就是当时执行时的变量i,那么当时把这个i临时保存一下就可以了,但是保存在哪呢?将i保存在 function(){return i;} 的活动对象中,怎么保存呢?传给 arguments 就好了,只传进来还不行
function createFunctions(){ var result=new Array(); for(var i=0;i<3;i++){ result[i]=function(i){ return i; }; } return result; } createFunctions()[0]();// undefiend
因为访问i的时候先从自己所在函数的执行环境的活动对象搜索起,找到i发现 i=undefiend 有值就停止向上搜索了。问题就出在上一步中将i保存在活动对象中, result[i]=function(i){return i;} 这句的执行并没有给匿名函数传参,这只是表达式的赋值操作,又不是执行匿名函数。所以现在需要的就是通过某种方式去执行函数的操作把i的值当实参传进去,简单!在匿名函数外部加一层立即执行的匿名函数(这也增加了一层作用域了)。
function createFunctions(){ var result=new Array(); for(var i=0;i<3;i++){ result[i]=(function(i){ return function(){ return i; } })(i); } return result; } createFunctins()[0]();// 0
this 对象是在运行时基于函数的执行环境绑定的:
由于闭包编写的方式不同, this 的表现:
var name="the window"; var obj={ name:"the obj", getNameFunc:function(){ //console.log(this==obj); return function(){ console.log(this.name); } } } obj.getNameFunc()();// the window
obj.getNameFunc() 返回了一个新函数,然后在再全局环境中执行该函数。为什么匿名函数没有取得其包含作用域(外部作用域)的 this 对象呢?每个函数在被调用时,都会自动获得两个特殊的变量: this (创建作用域时获得)和 arguments (创建活动对象获得),内部函数在搜索这两个变量时,只会搜索到自己的活动对象为止,因此永远不可能直接访问外部函数的这两个变量。不过把外部函数作用域的 this 保存在一个闭包能够访问到的变量里就可以让闭包访问该对象了。
下面几种情况特殊的 this :
var name="the window"; var obj={ name:"the obj", getName:function(){ return this.name; } }; obj.getName();// "the obj" (obj.getName)();// "the obj" (obj.getName=obj.getName)();// "the window"
第一个是直接调用,第二个是调用后立即执行的表达式,第三个是执行了一条赋值语句,然后再调用返回的结果,赋值语句的返回了一个函数,然后全局环境下调用这个函数,见下图
function outputNumber(count){ for(var i=0;i<count;i++){ console.log(i); } var i;// 只变量提升,到后面执行代码步骤时候略过此 console.log(i); } outputNumber(3);// 0 1 2 3
JavaScript不会告诉你是否多次声明了同一个变量,遇到这种情况,它只会对后续的声明视而不见(不过它会执行后续声明中的变量初始化)。
匿名函数可以用来模仿块级作用域(私有作用域),语法如下:
(function(){ //这里是块级作用域 })();
以上代码定义并立即调用了一个匿名函数,将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。对于这种语法的理解:
var count=5; outputNumbers(count);
这里初始化了变量 count 将其值设为5。但是这里的变量是没有必要的,因为可以把值直接传给函数 outputNumbers(5); 这样做之所以可行,是因为变量不过是值的另一种表现形式,因此用实际的值替换变量没有问题。
var someFunc=function(){ //这里是块级作用域 }; someFunc();
既然可以使用实际的值来取代变量 count ,那这里也用实际的值替换函数名。
function(){ //这里是块级作用域 }();
然而会报错,是因为JavaScript将 function 关键字当作一个函数声明的开始,而函数声明后面不能跟圆括号。但是函数表达式后面可以圆括号,这也就是为什么这样可以执行
var someFunc=function(){ //这里是块级作用域 }();
要将函数声明转化为函数表达式,
(function(){ //这里是块级作用域 })();
function outputNumber(count){ (function(){ for(var i=0;i<count;i++){ console.log(i); } })(); console.log(i);// 报错 } outputNumber(3);// 0 1 2
在 for 循环外边加了一个私有作用域,在匿名函数中定义的任何变量都会在执行结束时被销毁。在私有作用域中访问变量 count ,是因为这个匿名函数是一个闭包,它能访问包含作用域的所有变量。这种技术经常在全局作用域中被用在函数外部从而限制向全局作用域中添加过多的变量和函数。这种做法还可以减少闭包占用内存问题,因为没有指向匿名函数的引用,只要函数执行完毕,就可以立即销毁其作用域链了。
严格来讲,JavaScript中没有私有成员的概念,所有对象属性都是公有的。不过有私有变量的概念,任何在函数中定义的变量,都可认为是私有变量,因为不能在函数外部访问这些变量。私有变量包括函数的参数,局部变量,在函数内定义的其他函数。如果在函数内部创建一个闭包,那么闭包通过自己的作用域也可以访问这些变量。利用这一点创建用于访问私有变量的公有方法。
把有权访问私有变量和私有函数的方法叫特权方法(privileged method)。
function MyObject(){ //私有变量和私有函数 var privateVariable=10; function privateFunction(){ return false; } // 特权方法 this.publicMethod=function(){ privateVariable++; return privateFunction(); }; } new MyObject();
这个模式在构造函数内部定义了所有私有变量和函数,又继续创建了能够访问这些私有成员的特权方法。能在构造函数中定义特权方法是因为特权方法作为闭包有权访问在构造函数中定义的所有变量和函数。对这个例子而言,变量 privateVariable 和方法 privateFunction 只能通过特权方法 publicMethod 访问。在创建 MyObject 实例后除了使用 publicMethod() 这一途径外没任何办法可以直接访问私有变量和函数。
利用私有和特权成员,可以隐藏那些不应该被直接修改的数据
function Person(name){ this.getName=function(){ return name; }; this.setName=function(value){ name=value; }; } var p1=new Person("aa"); p1.getName();// "aa" var p2=new Person("bb"); p2.getName();// "bb" p1.getName();// "aa"
以上方法定义两个特权方法,在 Person 构造函数外部没有任何办法直接访问 name ,由于这两个方法是在构造函数内部定义的,它们作为闭包能够通过作用域链访问 name 。私有变量 name 在每个 Person 实例都不相同,这么说吧,每次调用构造函数都会重新创建这两个方法, p1.getName 和 p2.getName 是不同的函数,虽然调用的是内存中同一个 Person 函数。但 new 构造新实例的步骤是:先创建新实例对象;再在该实例上调用 Person 函数初始化作用域及作用域链 this 等;再添加属性等。不管换成是
var o1={},o2={}; Person.call(o1,‘aa‘); Person.call(o2,‘bb‘); o1.getName();// "aa"
还是换成
function Person(obj,name){ obj.getName=function(){ return name; }; obj.setName=function(value){ name=value; }; } var o1={},o2={}; Person(o1,"aa"); Person(o2,"bb"); o1.getName();// "aa"
都调用了两次 Person ,因为每次调用 Person 就会初始化 Person 的作用域,所以 p1.getName 和 p2.getName 所处的外围作用域是不一样的(之前还认为因为是调用了内存中同一个 Person ,以为 p1.getName 和 p2.getName 有同一个外围作用域,没考虑到每次调用函数实例都会重新初始化作用域)。
缺点:在构造函数中定义特权方法要求你必须使用构造函数模式来达到这个目的。构造函数模式的缺点是针对每个实例都会创建同一组新方法,使用静态私有变量来实现特权方法就可以避免这个问题。
(function(){ //私有变量和私有函数 var privateVariable=10; function privateFunction(){ return false; } //构造函数 MyObject=function(){}; //公有/特权方法 MyObject.prototype.publicMethod=function(){ privateVariable++; return privateFunction(); }; })();
这个模式创建了个私有作用域,并在其中封装了一个构造函数和相应方法。公有方法在原型上定义,这一点体现典型原型模式。注意到这个模式在定义构造函数时并没使用函数声明,而是使用函数表达式,因为函数声明只能创建局部函数,我们也没有在声明 MyObject 时使用 var 关键字,就是想让它成为一个全局变量,能够在私有作用域之外被访问。但严格模式下未经声明的变量赋值会导致出错。可以修改为
‘use strict‘; var MyObject; (function(){ //私有变量和私有函数 var privateVariable=10; function privateFunction(){ return false; } //构造函数 MyObject=function(){}; //公有/特权方法 MyObject.prototype.publicMethod=function(){ privateVariable++; return privateFunction(); }; })();
其实我觉得不用立即执行的匿名函数也可以实现这种在私有作用域中定义私有变量函数的模式,只要把这些放在一个函数中就可以了,然后再执行这个函数。
function staticFunction(){ //私有变量和私有函数 var privateVariable=10; function privateFunction(){ return false; } //构造函数 MyObject=function(){}; //公有/特权方法 MyObject.prototype.publicMethod=function(){ privateVariable++; return privateFunction(); }; } staticFunction();
—–分割线—-
这种模式在与构造函数中定义特权方法的主要区别就在于私有变量和函数是由实例共享的因为只调用了即只初始化了一次父环境(意思就是 p1.getName 和 p2.getName 所在的父环境都是同一个,不像构造函数模式中那样拥有各自父环境)。由于特权方法是在原型上定义的,因此所有实例都使用同一个函数。而这个特权方法作为一个闭包总是保存着对包含作用域的引用。
(function(){ var name=""; Person=function(value){ name=value; }; Person.prototype.getName=function(){ return name; }; Person.prototype.setName=function(value){ name=value; }; })(); var p1=new Person("aa"); p1.getName();// "aa" var p2=new Person("bb"); p2.getName();// "bb" p1.getName();// "bb"
Person 构造函数和 getName 和 setName 都有权访问私有变量 name 。在这种模式下,变量 name 就成了一个静态的,由所有实例共享的属性。在一个实例上调用 setName() 会影响所有实例。
以这种模式创建静态私有变量会因为使用原型而增进代码复用,但每个实例都没有自己的私有变量。
多查找作用域链中的一个层次,就会在一定程度上影响查找速度,这正是使用闭包和私有变量的一个不足之处。
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 { getComponentCount:function(){ return components.length; }, registerComponent:function(component){ if(typeof component=="object"){ components.push(component); } } } }();
在web应用程序中,经常需要使用一个单例来管理应用程序级的信息。如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那就可以使用模块模式。这种模式创建的每个单例都是 Object 的实例。
var singleton=function(){ //私有变量和私有函数 var privateVariable=10; function privateFunction(){ return false; } //创建对象 var obj=new CustomType(); //添加特权/公有属性和方法 obj.publicProperty=true; obj.publicMethod=function(){ privateVariable++; return privateFunction(); } return obj; }();
var application=function(){ //私有变量和函数 var components=new Array(); //初始化 components.push(new BaseComponent()); //创建application的一个局部版本 var app=new BaseComponent(); app.getComponentCount=function(){ return components.length; }; app.registerComponent=function(component){ if(typeof component=="object"){ components.push(component); } }; return app; }();
标签:
原文地址:http://www.cnblogs.com/leetieniu2014/p/5303738.html