• 深入理解模块化编程


    1.模块化开发规范

    JavaScript中所有对象的属性都是公共的,并没有什么明确的方法来表明属性能否从对象的外部被访问,而有时候我们并不希望对象的属性被外界访问。一种方式方式通过命名约定的形式,比如在变量的前面加下划线(_)。还有一些其他的方式是属性完全私有化。

    2.为什么要模块化

    在模块化没有出现之前,我们JavaScript脚本大概是这样的:

    <script src="module1.js"></script>
        <script src="module2.js"></script>
        <script src="module3.js"></script>
        <script src="module4.js"></script>
        ....

    但我们引入多个js文件时,会存在一些问题:

    • 把所有的代码放到全局环境会引起冲突
    • 各个脚本必须按照严格的依赖顺序放置
    • 在大型的项目中,可能会引入很多的js脚本,script就会变得很多,并且难以维护。

    为了解决这些问题,涌现了各种模块化的方案。

    3.模块化的方式

    这种方式是创建对象的一种方法,用于创建具有私有属性的对象。基本思路是使用一个立即执行的函数表达式,返回一个对象。该函数表达式可以包含任意数量的局部变量,这些变量从函数的外部是无法访问到的。因为返回的对象是在自执行函数内部声明的,所以对象中定义的方法可以访问自执行函数内的局部变量,这些方法被具有特权的方法。

     var p2 = (function(){
    
                var money = 30000;
    
                return {
                    name: 'lisi',
                    sayMoney: function(){
                        return money;
                    }
                };
    
            }());

    4. CommonJS

    我们在前端的js代码在没有模块化之前也能正常执行,但是在服务器端的js脚本必须要被模块化才能正常工作。所以虽然JavaScript在前端发展了这么多年,第一个流行的模块化规范却是由服务器端的js应用发展起来的。CommonJS规范是由NodeJS发扬光大的。

    1. 定义模块

    在CommonJS规范中,一个单独的JS文件就是一个模块。每个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为global对象的属性。

    1. 模块输出

    模块只有一个出口,module.exports对象,我们需要把需要输出的内容放入该模块

    1. 加载模块

    加载模块使用require()方法,该方法读取一个文件并执行,返回文件内部的module.exprots对象

    例如,我们写了这样一个文件myModule1.js

    var name = '无忌';
    function sayName(){
        console.log(name);
    };
    
    function sayFullName(firstName){
        console.log(firstName + name);
    };
    module.exports = {
        printName:sayName,
        printFullName:sayFullName
    };

    我们的模块定义好了,那我们怎样使用这个模块呢?例如,我们创建了myModule2.js文件:

    var module1 = require('./myModule1.js');
    
    module1.printName();
    module1.printFullName('张');

    在node环境下,require方法在引入其他模块的时候是同步的,可以轻松的控制模块的引入顺序,保证了模块之间的依赖顺序。但是在浏览器中却不是这样的,因为我们的<script>标签天生异步,在加载js文件的时候是异步的,也就意味着不能保证模块之间的正确依赖。

    5. AMD规范

    AMD即Asynchronous Module Definition,异步模块定义。它是在浏览器端实现模块化开发的规范。由于该规范不是JavaScript原始支持的,使用AMD规范进行开发的时候需要引入第三方的库函数,也就是鼎鼎大名的RequireJS

    RequireJS主要解决两个问题:

    1. 多个js文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器。
    2. js加载的时候浏览器会停止页面渲染,加载的文件越多,页面失去响应的时间越长。

    3. 定义模块

    RequireJS定义了一个define函数,用来定模块。

    define(id, [dependencies], factory);

    • id:可选参数,用来定义模块的标识,如果没有提供参数的话,默认为文件的名字。
    • dependencies:当前模块依赖的其他模块,数组。
    • factory:工厂方法,初始化模块需要执行的函数或对象。如果为函数,它只被执行一次。如果是对象,此对象会作为模块的输出值。
    // amdModule1.js
    
    define('amdModule1',[],function(){
        
        console.log('模块一');
        
        var name= '张三';
        
        var money = 1000;
        
        var sayName = function(){
            return name;
        }
        var sayMoney = function(){
            return money;
        }
        return {
            sayName:sayName,
            sayMoney:sayMoney
        }
    })
    
    // module2.js
    
    
    define('amdModule2',['amdModule1'],function(amdModule1){
        
        console.log('模块二');
        
        var name = '李四';
        
        var weight = 80;
        
        var sayName = function(){
            return amdModule1.sayName();
        }
        var sayWeight = function(){
            return weight;
        }
        return {
            sayWeight:sayWeight,
            name:name,
            sayName:sayName
        };
    })
    1. 在页面中加载模块

    加载模块需要使用require()函数。

    require([dependencies], function(){});

    • dependencies:该参数是一个数组,表示所依赖的模块。
    • function:这是一个回调函数,当所依赖的模块都加载成功之后,将调用该回调方法。依赖的模块会以参数的形式传入该函数,从而在回调函数内部就可以使用这些模块。
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title></title>
        </head>
        <body>
            <script src="require.js"></script>
            <script type="text/javascript" src="amdModule1.js"></script>
            <script type="text/javascript" src="amdModule2.js"></script>
            <script type="text/javascript">
                require(['amdModule1','amdModule2'],function(amdModule1,amdModule2){
                    console.log(amdModule1.sayName());  //张三
                    console.log(amdModule1.name); //undefined
                    console.log(amdModule2.sayName()); //张三
                    console.log(amdModule2.sayWeight()); //80
                });
            </script>
        </body>
    </html>

    require()函数在加载依赖的模块时,是异步加载的,这样浏览器就不会失去响应,回调函数只有在所依赖的全部模块都被被成功加载之后才执行,因此,解决了依赖性的问题。

    6. CMD规范

    CMD即Common Module Definition通用模块定义。它解决的问题和AMD规范是一样的,只不过在模块定义方式和模块加载时机上不同。CMD也需要额外的引入第三方的库文件,SeaJS。

    SeaJS推崇一个模块一个文件,遵循统一的写法:

    define(id, dependencies, factory);

    因为CMD推崇一个文件一个模块,所以经常使用文件名作为模块的ID;CMD推崇就近原则,所以一般不再define的参数中写依赖,在factory函数中写。

    • require:我们定义的模块可能会依赖其他模块,这个时候就可使用require()引入依赖。
    • exports:等价于module.exports,只是为了方便我们使用
    • module.exports:用于存放模块需要暴露的属性
    1. 定义模块
    // cmdModule1.js
    
    define(function(require,exports,module){
        var name = '张三';
        function sayName(){
            return name;
        }
        module.exports={
            sayName:sayName
        }
    })
    
    // cmdModule2.js
    
    
    define(function(require,exports,module){
        
        var cmdModule1 = require('cmdModule1.js');
        console.log('模块');
        function sayName(){
            return cmdModule1.sayName();
        }
        module.exports={
            sayName:sayName
        }
    })
    1. 使用模块
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title></title>
        </head>
        <body>
            <script src="sea.js"></script>
            <script src="cmdModule2.js"></script>
            <script src="cmdModule1.js"></script>
            <script type="text/javascript">
                seajs.use(['cmdModule2.js','cmdModule1.js'],function(cmdModule2,cmdModule1){
                    console.log(cmdModule2.sayName());
                    console.log(cmdModule1.sayName());
                })
            </script>
        </body>
    </html>

    7. AMD和CMD规范的区别

    AMD在加载模块完成后会立即执行该模块,所有的模块都加载完成后执行require方法中的回调函数,执行主逻辑,这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,看网速,谁先下载下来,谁先执行,但是我们的主逻辑一定是在所有的依赖模块都被加载完成后才执行。

    CMD加载完某个模块的时候并不执行,只是把它们下载下来而已,在所有的模块下载完成后,当使用require请求某个模块的时候,才执行对应的模块。

  • 相关阅读:
    54、操控变形—调整
    leetcode 106. Construct Binary Tree from Inorder and Postorder Traversal 从中序与后序遍历序列构造二叉树(中等)
    leetcode 219. Contains Duplicate II 存在重复元素 II(简单)
    leetcode 145. Binary Tree Postorder Traversal 二叉树的后序遍历 (中等)
    leetcode 530. Minimum Absolute Difference in BST二叉搜索树的最小绝对差 (简单)
    leetcode 889. Construct Binary Tree from Preorder and Postorder Traversal 根据前序和后
    leetcode 146. LRU Cache LRU 缓存 (简单)
    leetcode 785. Is Graph Bipartite判断二分图 (中等)
    leetcode 94. Binary Tree Inorder Traversal 二叉树的中序遍历(中等)
    leetcode 220. Contains Duplicate III 存在重复元素 III(困难)
  • 原文地址:https://www.cnblogs.com/zhoulifeng/p/7545387.html
Copyright © 2020-2023  润新知