技术控

    今日:80| 主题:49471
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] 模块化你的JS代码

[复制链接]
魂牵梦萦 发表于 2016-9-30 12:00:32
89 2

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
为什么要使用模块模式?

  因为在全局作用域中声明的变量和函数都自动成为全局对象Window的属性,这经常会导致命名冲突,还会导致一些非常重要的可维护性难题,全局变量越多,引入错误BUG的概率就越大!所以我们应当尽可能少地使用全局变量,模块化的目的之一就是为了解决该问题的!
  零全局变量模式

  该模式应用场景较少,通过一个IIFE(立即执行的匿名函数),将所有代码包装起来,这样一来所有的变量、函数都被隐藏在该函数内部,不会污染全局。
  使用情景:
  
       
  •       当该代码不会被其它代码所依赖时;
       
  •       当不需要在运行时不断的扩展或修改该代码时;
       
  •       当代码较短,且无需和其它代码产生交互时;
      
  单全局变量模式

  基本定义

  单全局变量模式即只创建一个全局变量(或尽可能少地创建全局变量),且该全局变量的名称必须是独一无二的,不会和现在、将来的内置API产生冲突,将所有的功能代码都挂载到这个全局变量上。
  它已经被广泛应用于各种流行的类库中,如:
  
       
  •       YUI定义了唯一的YUI全局对象
       
  •       JQuery定义了两个全局对象,$和JQuery
       
  •       Dojo定义了一个dojo全局对象
       
  •       Closure定义了一个goog全局对象
      
  例子:
  1. var Mymodule= {};
  2. Mymodule.Book = function(){...};
  3. Mymodule.Book.prototype.getName = function(){....};
  4. Mymodule.Car = function(){...};
  5. Mymodule.Car.prototype.getWheels = function(){....};
复制代码
一个模块的定义

  模块是一种通用的功能片段,它并没有创建新的全局变量或命名空间,相反,所有的代码都存放于一个单函数中,可以用一个名称来表示这个模块,同样这个模块可以依赖其他模块。
  1. function CoolModule(){
  2.         var something = 'cool';
  3.         var another = [1,2,3];
  4.         function doSomething(){
  5.             console.log( something);
  6.         }
  7.         function doAnother(){
  8.             console.log(another.join('!'));
  9.         }
  10.         return {
  11.             doSomething: doSomething,
  12.             doAnother: doAnother
  13.         };
  14. }
  15. var foo = CoolModule();
  16. foo.doSomething(); //cool
  17. foo.doAnother(); //1!2!3
复制代码
这里的CoolModule 就是一个模块,不过它只是一个函数,这里调用CoolModule函数来创建一个模块的实例foo,此时就形成了闭包(因为CoolModule返回一个对象,其中的一个属性引用了内部函数),模块CoolModule返回的对象就是该模块的公共API(也可以直接返回一个内部函数)
  所以,模块模式需要具备两个必要条件:
  
       
  •       必须有外部的封闭函数,且该函数必须至少被调用一次(每次调用都会创建一个新的模块实例),如CoolModule
       
  •       封闭函数必须至少有一个内部函数被返回,这样内部函数才能在私有作用域中形成闭包,并且可以访问或修改私有的状态
      
  单例模块模式的实现:
  1. var foo = ( function CoolModule(){
  2.         ...//代码同上例
  3. })();
  4. foo.doSomething();
  5. foo.doAnother();
复制代码
还可以通过在模块内部保留对公共API对象的内部引用,这样就可以在内部对模块实例进行修改,包括添加、删除方法和属性
  1. function CoolModule(){
  2.     var something = 'cool';
  3.     var another = [1,2,3];
  4.     function change() {
  5.         pubicAPI.doSomething = doAnother;
  6.     }
  7.     function doSomething(){
  8.         console.log( something);
  9.     }
  10.     function doAnother(){
  11.         console.log(another.join('!'));
  12.     }
  13.     var pubicAPI = {
  14.         change: change,
  15.         doSomething: doSomething
  16.     };
  17.     return pubicAPI;
  18. }
  19. var foo = CoolModule();
  20. foo.doSomething(); //cool
  21. foo.change();
  22. foo.doSomething(); //1!2!3
  23. var foo1 = CoolModule();
  24. foo1.doSomething(); //cool
复制代码
现代的模块机制

    命名空间是简单的通过在全局变量中添加属性来表示的功能性分组。
    将不同功能按照命名空间进行分组,可以让你的单全局变量变得井然有序,同时可以让团队成员能够知晓新功能应该在哪个部分中定义,或者去哪个部分查找已有功能。
    例如:定义一个全局变量Y,Y.DOM下的所有方法都是和操作DOM相关的,Y.Event下的所有方法都是和事件相关的。
   
       
  •       常见的用法是为每一个单独的JS文件创建一个新的全局变量来声明自己的命名空间;
       
  •       每个文件都需要给一个命名空间挂载功能;这时就需要首先保证该命名空间是已经存在的,可以在单全局变量中定义一个方法来处理该任务:该方法在创建新的命名空间时不会对已有的命名空间造成破坏,使用命名空间时也不需要再去判断它是否存在。
      
  1. var MyGolbal = {
  2.     namespace: function (ns) {
  3.         var parts = ns.split('.'),
  4.             obj = this,
  5.             i, len = parts.length;
  6.         for(i=0;i<len;i++){
  7.             if(!obj[parts[i]]){
  8.                 obj[parts[i]] = {}
  9.             }
  10.             obj = obj[parts[i]];
  11.         }
  12.         return obj;
  13.     }
  14. };
  15. MyGolbal.namespace('Book'); //创建Book
  16. MyGolbal.Book; //读取
  17. MyGolbal.namespace('Car').prototype.getWheel = function(){...}
复制代码
大多数模块依赖加载器或管理器,本质上都是将这种模块定义封装进一个友好的API
  1. var MyModules = (function Manager() {
  2.     var modules = {};
  3.     function define(name, deps, impl) {
  4.         for(var i=0; i<deps.length; i++){
  5.             deps[i] = modules[deps[i]];
  6.         }
  7.         modules[name] = impl.apply(impl,deps);
  8.     }
  9.     function get(name) {
  10.         return modules[name];
  11.     }
  12.     return {
  13.         define: define,
  14.         get: get
  15.     };
  16. })();
复制代码
   以上代码的核心是      modules[name] = impl.apply(impl,deps);,为了模块的定义引入了包装函数(可以传入任何依赖),并且将模块的API存储在一个根据名字来管理的模块列表modules对象中;   
    使用模块管理器MyModules来管理模块:
   
  1. MyModules.define('bar',[],function () {
  2.     function hello(who) {
  3.         return 'let me introduce: '+who;
  4.     }
  5.     return{
  6.         hello: hello
  7.     };
  8. });
  9. MyModules.define('foo',['bar'],function (bar) {
  10.     var hungry = 'hippo';
  11.     function awesome() {
  12.         console.log(bar.hello(hungry).toUpperCase());
  13.     }
  14.     return {
  15.         awesome: awesome
  16.     };
  17. });
  18. var foo = MyModules.get('foo');
  19. foo.awesome();//LET ME INTRODUCE: HIPPO
复制代码
异步模块定义(AMD):

  1. define('my-books', ['dependency1','dependency2'],
  2.     function (dependency1, dependency2) {
  3.         var Books = {};
  4.         Books.author = {author: 'Mr.zakas'};
  5.         return Books; //返回公共接口API
  6.     }
  7. );
复制代码
   通过调用全局函数define(),并给它传入模块名字、依赖列表、一个工厂方法,依赖列表加载完成后执行这个工厂方法。AMD模块模式中,每一个依赖都会对应到独立的参数传入到工厂方法里,即每个被命名的依赖最后都会创建一个对象被传入到工厂方法内。模块可以是匿名的(即可以省略第一个参数),因为模块加载器可以根据JavaScript文件名来当做模块名字。
    要使用AMD模块,需要通过使用与AMD模块兼容的模块加载器,如RequireJS、Dojo来加载AMD模块
   
  1. requre(['my-books'] , function(books){
  2.             books.author;
  3.             ...
  4.    }
  5. )
复制代码
以上所说的模块都是是基于函数的模块,它并不是一个能被稳定识别的模式(编译器无法识别),它们的API语义只是在运行时才会被考虑进来。因此可以在运行时修改一个模块的API
  未来的模块机制

    ES6为模块增加了一级语法支持,每个模块都可以导入其它模块或模块的特定API成员,同样也可以导出自己的API成员;ES6的模块没有‘行内’格式,必须被定义在独立的文件中(一个文件一个模块)
    ES6的模块API更加稳定,由于编译器可以识别,在编译时就检查对导入的API成员的引用是否真实存在。若不存在,则编译器会在运行时就抛出‘早期’错误,而不会像往常一样在运行期采用动态的解决方案;
    bar.js
  1. function hello(who) {
  2.     return 'let me introduce: '+who;
  3. }
  4. export hello; //导出API: hello
复制代码
foo.js
  1. function CoolModule(){
  2.         var something = 'cool';
  3.         var another = [1,2,3];
  4.         function doSomething(){
  5.             console.log( something);
  6.         }
  7.         function doAnother(){
  8.             console.log(another.join('!'));
  9.         }
  10.         return {
  11.             doSomething: doSomething,
  12.             doAnother: doAnother
  13.         };
  14. }
  15. var foo = CoolModule();
  16. foo.doSomething(); //cool
  17. foo.doAnother(); //1!2!30
复制代码
baz.js
  1. function CoolModule(){
  2.         var something = 'cool';
  3.         var another = [1,2,3];
  4.         function doSomething(){
  5.             console.log( something);
  6.         }
  7.         function doAnother(){
  8.             console.log(another.join('!'));
  9.         }
  10.         return {
  11.             doSomething: doSomething,
  12.             doAnother: doAnother
  13.         };
  14. }
  15. var foo = CoolModule();
  16. foo.doSomething(); //cool
  17. foo.doAnother(); //1!2!31
复制代码
       
  •       import可以将一个模块中的一个或多个API导入到当前作用域中,并分别绑定在一个变量上;
       
  •       module会将整个模块的API导入并绑定到一个变量上;
       
  •       export会将当前模块的一个标识符(变量、函数)导出为公共API;
       
  •       模块文件中的内容会被当做好像包含在作用域闭包中一样来处理,就和函数闭包模块一样;
      
友荐云推荐




上一篇:Hackaday Prize Entry: Explore M3 ARM Cortex M3 Development Board
下一篇:【PHP】高级特性以及设计模式归纳
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

鼬神丨丶 发表于 2016-9-30 17:26:05
路过 帮顶 嘿嘿
回复 支持 反对

使用道具 举报

喵小乐 发表于 2016-10-4 03:01:46
好帖必须得顶起
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表