javascript framework design

来源:互联网 发布:迅雷挖矿 软件 编辑:程序博客网 时间:2024/05/16 08:51

JS中大括号有四种语义作用

语义1,组织复合语句,这是最常见的

 

Js代码  收藏代码
  1. if( condition ) {  
  2.   //...  
  3. }else {  
  4.   //...  
  5. }  
  6. for() {  
  7.   //...  
  8. }  

 

语义2,对象直接量声明

 

Js代码  收藏代码
  1. var obj = {  
  2.     name : 'jack',  
  3.     age : 23  
  4. };  

 

整个是个赋值语句,其中的{name:'jack',age:23}是个表达式。

 

 

语义3,声明函数或函数直接量

 

Js代码  收藏代码
  1. function f1(){  
  2.     //...  
  3. }  
  4.   
  5. var f2 = function(){  
  6.     //...  
  7. }  

 

 

f1与非f2的区别是前者在语法解释期,后者在运行期。区别在于:如果调用该函数的代码在函数定义之后,则没有区别;如果调用该函数的代码在函数定义之前,则f1仍然可以调用,f2则会报错,提示f2未定义。

 

语义4,结构化异常处理的语法符号

 

Js代码  收藏代码
  1. try {  
  2.     //...  
  3. }catch( ex ){  
  4.     //...  
  5. }finally{  
  6.     //...  
  7. }  
 

这里的大括号与符合语句(语义1)是有区别的,大括号中如果只有一条语句,在if/else/for等中大括号是可以省略的,但try/catch/finally则不能省略。

 

 

 

 

  1. function(){}() //匿名函数立即执行, 语法分析期报错  
  2.   
  3. {}.constructor //获取对象直接量的构造器,语法分析期报错  

 

 

令人不解的是为何[].constructor这么写却不报错呢,一个是想获取对象直接量的构造器,一个是获取数组直接量的构造器而已。

 

当然添加个变量接收也不会报错

var c = {}.constructor;

同样的情况如

var fn = function(){}(),也不会报错。

 

 

 

 

 

 

function(){}(),大括号被理解成复合语句,自然前面的function()声明函数的语法不完整导致语法分析期出错。
{}.constructor,大括号被理解成复合语句,大括号后面是点运算符,点运算符前没有合理的对象自然也报错。

修复方式众所周知:加个强制运算符()
(function(){})(),(function(){});//强制其理解为函数(语义3),“函数()”表示执行该函数,即声明后立即执行了。
({}).constructor //({})强制把大括号理解成对象直接量(语义2),“对象.xx”表示获取对象的成员,自然后面的点运算符可以正常执行了。

 

 

 

 

 

 

 

 

 

 

 

 

 

用了很久jQuery,到最近,等到自己想写一个链式库时才对它的内部架构学习了一下。下面就对jQuery框架设计模式的一些感想写一下:

 

首先是jQuery将自己所有的代码封装在一个闭包内;用闭包防止命名空间的变量污染,继续在本身闭包中初始化了一个名为jQuery的自动运行闭包函数;

形如:

 

Js代码  收藏代码
  1. (function(window, undefined){  
  2.     var jQuery = (function(){})();  
  3. })();  

想如此的匿名函数赋值语句,一般就会返回一个内嵌函数如下:

 

Js代码  收藏代码
  1. var jQuery = (function(){  
  2.   
  3.     var jQuery = function(){  
  4.         //TODO  
  5.     }  
  6.     return (window.$ = window.jQuery = jQuery);       
  7. });  
  

而这个内嵌函数在return时做了外部对象(window)的引用以及外部的调用的,所以根js的词法作用域规则,在上层函数运行后它仍将保存其作用域(相当于包含其函数内部调用的外层函数私有变量不会被垃圾回收机制回收掉),这里的jQuery函数在作为window这个全局对象中的引用后便保存了它在上层jQuery函数中调用的所用变量作用域。而这些变量也是jQ自身的属性。

 

然后会在这个内嵌函数中写入如此的语句:

 

Js代码  收藏代码
  1. var jQuery = (function(){  
  2.   
  3.     var jQuery = function(){  
  4.         return new jQuery.fn.init(selector, context);  
  5.     }  
  6.     return (window.$ = window.jQuery = jQuery);       
  7. });  

 

这里的jQ函数返回一个新new出来的对象,相当于类的构造函数实际是一个对象工厂函数,而这个对象则是自己prototype里的一个方法的对象,这边的fn属性既是jQuery的原型而已,而下面的init则是其初始化函数

我们平时用的fn的插件实际是jQ的原型(注意:这边返回的必须是构造函数而不是new好的对象

 

Js代码  收藏代码
  1. var jQuery = (function(){  
  2.   
  3.     var jQuery = function(){  
  4.         return new jQuery.fn.init(selector, context);  
  5.     }  
  6.   
  7.     jQuery.fn = jQuery.prototype = {  
  8.         init: function(){}  
  9.     };  
  10.   
  11.     return (window.$ = window.jQuery = jQuery);   
  12. });  
 

 

恩,上面的便是返回了一个init为构造函数的一个对象,相当于说是我们平时用的jQuery对象实质上是jQ对象自己原型中的一个构造函数对象,这个对象是init对象,并不是jQuery对象本身,所以它的原型是空的,并不包含jQ的原型,所以下面这句话异常重要。

 

将init的原型指向fn,而fn是指向jQ的原型,相当于init的原型是指向jQ的原型的

jQuery.fn.init.prototype = jQuery.fn;

 

之后代码变成了这样:

 

Js代码  收藏代码
  1. var jQuery = (function(){  
  2.   
  3.     var jQuery = function(){  
  4.         return new jQuery.fn.init(selector, context);  
  5.     }  
  6.   
  7.     jQuery.fn = jQuery.prototype = {  
  8.         init: function(){}  
  9.     };  
  10.   
  11.     jQuery.fn.init.prototype = jQuery.fn;  
  12.   
  13.     return (window.$ = window.jQuery = jQuery);       
  14. });  
 

再然后便可以往jQ类中加入静态对象,放在内核内的这里的对象或方法往往是要调用对象内私有属性的,下面是对jQ命名空间的工厂函数:

jQuery.extend = jQuery.fn.extend = function(){}

 

实际上也是jQ原型中定义的一个扩展函数

 

至此jQ的内核已经结束。接下来时外层闭包中的jQuery对象,这个对象首先持有了内层返回的jQ对象,而这个对象同样其实是一个函数,或者更确切的说是一个类构造函数,然后就可以在外闭包中放一些无需依赖jQ类私有变量的库函数。再对这个类附上库函数。

Js代码  收藏代码
  1. (function(window, undefined){  
  2. <span style="white-space: pre;">    </span>//内核  
  3.     var jQuery = (function(){})();  
  4.   
  5.         //非依赖私有变量的类方法  
  6.         (function(){  
  7.   
  8.               //可以直接调用jQuery外层应用类对其进行扩展  
  9.               jQuery.support = {};   
  10.               jQuery.extend({/*TODO*/}  
  11.   
  12.         })();  
  13.   
  14. })();  
  

而我们平时用的$(//TODO)或是jQuery(//TODO)则是在用jQ中的init方法new 出一个jQ的对象(更确切说是init对象)

 

下面是类似于jQ的全局对象构造机制的自己实现的一个函数

 

Js代码  收藏代码
  1. (function(){  
  2.     function _$(ele){  
  3.         this.eles = [];  
  4.         for(var i = 0, i < eles.length ;++i){  
  5.             var ele = ele[i];  
  6.             if(typeof ele == 'string'){  
  7.                 ele = document.getElementById(element);  
  8.             }  
  9.             this.eles.push(ele);  
  10.         }  
  11.     }  
  12.   
  13.     window.$ = function(){  
  14.         return new _$(arguments);     
  15.     }  
  16. })();  

 

 

总结:对于jQ的这种closure的封装机制的确对项目的空间隔离有很大的借鉴意义,双层闭包和内调用首先可以防止命名空间污染,其次逻辑结构也非常清晰,对于内层私有变量的保护和外层库的扩展这种设计的模式在以后的开发中也有很大的借鉴意义。