JavaScript---js的模块化

  

  js的模块模式被定义为给类提供私有和公共封装的一种方法,也就是我们常说的“模块化”。

    怎么实现“模块化”?

      通过闭包的原理来实现“模块化”  ,具体实现:1.必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例);2.封闭函数必须返回至少一个内部函数(返回多个函数时,以对象字面量的形式返回)

      先看一个实例:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Document</title>
 6 </head>
 7 <body>
 8     <script>
 9         function Module (){
         //内部变量
10 var something = 'cool'; 11 var another = [1,2,3]; 12        //内部函数 13 function doSomething(){ 14 console.log(something); 15 } 16 17 function doAnother(){ 18 console.log(another); 19 } 20        //返回对象字面量形式,里面包含内部函数的引用, 这样就保持了内部变量是隐藏且私有的状态。 21 return { 22 doSomething: doSomething, 23 doAnother: doAnother 24 }; 25 } 26      //调用外部函数Module创建一个模块实例foo 27 var foo = Module(); 28 foo.doSomething();//cool 29 foo.doAnother();//[1,2,3] 30 </script> 31 </body> 32 </html>

  简单分析一下代码:

      首先,Module只是一个函数,必须通过他才能创建一个模块实例,如果不执行他,内部作用域和闭包都无法被创建。 其次,Module函数返回一个字面量对象,这个返回的对象含有对内部函数而不是内部变量的引用。这样就保持了内部变量是隐藏且私有的状态。可以将这个对象类型的返回值看作模块的公共API 这个API最终会被赋值给外部的变量foo,通过他就可以访问API中的属性方法,比如:foo.doSomething()。

 

  上面的实例中,Module函数可以调用任意多次,每次调用都会创建一个新的模块实例。当只需要一个实例时,可以对这个模块进行简单的改进来实现单例模式:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script>
        var foo = (function Module (){
            var something = 'cool';
            var another =  [1,2,3];

            function doSomething(){
                console.log(something);
            }

            function doAnother(){
                console.log(another);
            }

            return {
                doSomething: doSomething,
                doAnother: doAnother
            };
        })();

        foo.doSomething();//cool
        foo.doAnother();//[1,2,3]
    </script>
</body>
</html>

  通过将模块函数转换为IIFE(立即执行函数),立即调用这个函数并将返回值直接赋值给电力的模块实例foo

 

  模块也是普通的函数,因此可以接收参数:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script>
        // var foo = (function Module (){
        //     var something = 'cool';
        //     var another =  [1,2,3];

        //     function doSomething(){
        //         console.log(something);
        //     }

        //     function doAnother(){
        //         console.log(another);
        //     }

        //     return {
        //         doSomething: doSomething,
        //         doAnother: doAnother
        //     };
        // })();

        // foo.doSomething();//cool
        // foo.doAnother();//[1,2,3]

        var foo = (function Module(id){
            function change(){
                publicAPI.id = id2;
             }

             function id1(){
                 console.log(id);
             }

             function id2(){
                 console.log(id.toUpperCase());
             }

             var publicAPI = {
                 change: change,
                 id: id1
             };

             return publicAPI;
        })('hello');

        foo.id();//hello
        foo.change();
        foo.id();//HELLO
    </script>
</body>
</html>

 

  可以看出:通过在模块实例的内部保留公共API对象的内部引用(API指的是引用return回来的字面量对象 {...}),可以从内部模块实例进行修改,包括添加、删除方法和属性,以及修改它们的值。

 

  现代的模块机制

    大多数模块依赖加载器/管理器,本质上都是将模块定义为封装进一个API。 现在,宏观了解一下模块机制:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script>
        var myModule = (function Module(){
            var modules = [];
       //定义一个define函数用于定义一个模块
            function define(name, deps, impl){
                for (var i = 0;i < deps.length;i++){
                    deps[i] = modules[deps[i]];
                }
                modules[name] = impl.apply(impl, deps);
            }

            function get(name){
                return modules[name];
            }

            return {
                define: define,
                get: get
            };
        })();
    </script>
</body>
</html>

 

  这段代码的核心就是:modules[name] = impl.apply(impl, deps)。 为了模块的定义(define函数)引入包装函数(可以传入任何依赖),并且将返回值,也就是模块的API,存储在一个根据名字来管理的模块列表中。(不是很理解啊??)

  下面展示了如何使用它来定义模块:

  

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script>
        var myModule = (function Module(){
            var modules = [];

            function define(name, deps, impl){
                for (var i = 0;i < deps.length;i++){
                    deps[i] = modules[deps[i]];
                }
                modules[name] = impl.apply(impl, deps);
            }

            function get(name){
                return modules[name];
            }

            return {
                define: define,
                get: get
            };
        })();

        myModule.define('bar', [], function(){
            function hello(who){
                return "Hello," + who; 
            };

            return{
                hello:hello
            };
        });

        myModule.define('foo', ['bar'], function(bar){
            var hungry = 'hippo';

            function awesome(){
                console.log(bar.hello(hungry).toUpperCase());
            };

            return {
                awesome: awesome
            };
        });

        var bar = myModule.get('bar');
        var foo = myModule.get('foo');

        console.log(bar.hello('hippo'));//hello, hippo
        foo.awesome();//HELLO, HIPPO 
    </script>
</body>
</html>

  ‘foo’和‘bar’模块都是通过一个返回公共API的函数来定义的。‘foo’甚至接受‘bar’的实例作为依赖参数,并使用它。

    这就是模块的威力!! 

  总结一下:模块并不是什么神秘的东西,他只是一个外部函数返回内部函数的引用(字面量对象的形式返回),从而可以访问内部函数和变量的一种方式。

                              ---摘自《你不知道的JavaScript》(上)   2017-3-22  23:24

 

posted @ 2017-05-08 10:31  游鱼与渔  阅读(294)  评论(0编辑  收藏  举报