资讯专栏INFORMATION COLUMN

javascript模块化(一)--总览

tanglijun / 611人阅读

摘要:前言很久没写文章总结了,这次主要粗略的总结一下中的模块化模块模块的职责封装实现,暴露接口,声明依赖。

前言:很久没写文章总结了,这次主要粗略的总结一下 js中的模块化

1.模块

模块的职责:封装实现,暴露接口,声明依赖。先来一个对比,下面代码没有应用任何模块系统

(1)无封装

math.js:(1)没有封装性(2)接口不明显

    function add(a,b){
        return a+ b
    }
    function sub(a,b){
        return a - b
    }

caculator.js:(1)依赖math.js但是没有依赖声明 (2)使用全局状态

    var action = "add";
    function compute(a,b){
        switch (action){
            case "add": return add(a,b)
            case "sub": return add(a,b)
        }
    }
(2)字面量

math.js:(1)结构性好(2)访问控制没有

    var math = {
        add:function add(a,b){
            return a+ b
        },
        sub:function sub(a,b){
            return a - b
        }
    }

caculator.js:(1)依赖math.js但是没有依赖声明 (2)无法标明属性是私有,无封装

    var caculator = {
        action:"add";
        compute:function compute(a,b){
            switch (action){
                case "add": return math.add(a,b)
                case "sub": return math.add(a,b)
            }
        }
    }
(3)IIFE:字值型的函数表达式

可以创建一个局部作用域,封装内部成员变量,通过return输出需要输出的接口

版本一:caculator-1.js:(1)实现了访问控制(2)依然没有依赖声明

    var caculator = (function(){
        var action = "add";
        return{
        compute:function compute(a,b){
            switch (action){
                case "add": return math.add(a,b)
                case "sub": return math.add(a,b)
            }
        }
    }
})()

版本二:caculator-2.js:(1)显示依赖声明(2)仍然污染了全局变量(3)必须手动进行依赖管理
揭露模块模式:return部分与版本一不太一样,把方法定义在函数体里面,return的只是方法。具体可参考这位童鞋的文章:Javascript 设计模式 -- Revealing Module(揭示模块)模式

    var caculator = (function(m){
        var action = "add";
        function compute(a,b(){
            switch (action){
                case "add": return m.add(a,b)
                case "sub": return m.add(a,b)
            }
        }
        return{
            compute:compute
    }
})(math)
(4)命名空间

解决暴露全局变量的问题,只暴露一个类似namespace的全局变量就实现所有模块的声明

math.js:(1)第一个参数是模块声明;(2)第二个参数是声明依赖,目前是没有依赖的;(3)第三个参数是模块的构成

    namespace("math",[],function(){
        function add(a,b){
            return a+ b
        }
        function sub(a,b){
            return a - b
        }
        return{
            add:add,
            sub:sub
        }
    })

caculator.js:(1)有依赖生命(2)依赖math被当做参数传入

    namespace("caculator",["math"],function(m){
        var action = "add"
        function compute(a,b){
            return m[action](a,b)
        }
        return{
            compute:compute
        }
    })

namespace的代码:还是没有解决依赖管理的问题,如果各个模块分散在不同的文件中,就要对脚本加载顺序进行手动的排序

    var namespace = (function(){
        //缓存所有的模块
        var cache = {}
        function createModule(name,deps,definition){  //参数是:模块名,依赖列表,定义
            //先对参数进行判断,如果只有一个参数,就返回
            if(arguments.length === 1){
                return cache[name]
            }
            //必须取得所有依赖的模块,要保证前面的模块已经被定义好了
            deps = deps.map(function(depName){
                return ns(depName)
            })
            //初始化模块并返回
            cache[name] = definition.apply(null,deps)
            return cache[name];
        }
        return createModule
    })()
2.模块系统

职责:(1)依赖管理:加载/分析/注入/初始化 (2)决定模块的写法
下面总结三种典型的模块系统的写法

(1)commonjs

优点:

依赖管理成熟可靠

社区活跃,规范接受度高

运行时支持,模块定义非常简单

文件级别的模块作用域隔离

可以处理循环依赖

缺点:

不是标准组织的规范

同步的require,没有考虑浏览器异步加载的过程

但是还是有办法使用的,目前有很多工具可以把多个模块的文件打包成一个文件:browserify,webpack,component

看下面用commonjs写就的代码:
math.js:

    function add(a,b){
        return a+ b
    }
    function sub(a,b){
        return a - b
    }
    exports.add = add
    exports.sub = sub

caculator.js:

       var math = require("./math");  //依赖声明
        function Caculator(container){
            this.left = container.querySelector(".j-left" );
            this.right = container.querySelector(".j-right" );
            this.add = container.querySelector(".j-add" );
            this.result = container.querySelector(".j-result");
            
            this.add.addEventListener("click",this.compute.bind(this));
        }
        Caculator.prototype.compute = function(){
            this.result.textContent = math.add(+this.left.value, +this.right.value)
        }
        exports.Caculator = Caculator;  //暴露接口

用前端打包工具进行打包math.jscaculator.js
首先安装browserify,在命令行输入命令:browserify caculator.js > caculator-bundle.js
打包成形如命名空间的文件形式

(2)AMD

天然的作用于异步环境
AMD代码写法:
math.js:第一个参数是依赖列表

    define([],function(){
        function add(a,b){
            return a+ b
        }
        function sub(a,b){
            return a - b
        }
        return{  //接口暴露
            add:add,
            sub:sub
        }
    })

caculator.js:参数一是依赖声明,参数二是依赖注入

    define(["./math"],function(math){
    
        function Caculator(container){
                this.left = container.querySelector(".j-left" );
                this.right = container.querySelector(".j-right" );
                this.add = container.querySelector(".j-add" );
                this.result = container.querySelector(".j-result");
                
                this.add.addEventListener("click",this.compute.bind(this));
            }
            Caculator.prototype.compute = function(){}
            return{
                Caculator:Caculator
            }
        })

AMD还支持一个叫Simplified CommonJS wrapping

    define(function(require,exports){
        var math = require("./math");
        function Caculator(container){
                this.left = container.querySelector(".j-left" );
                this.right = container.querySelector(".j-right" );
                this.add = container.querySelector(".j-add" );
                this.result = container.querySelector(".j-result");
                
                this.add.addEventListener("click",this.compute.bind(this));
            }
            Caculator.prototype.compute = function(){}
            exports.Caculator = Caculator;
        })

上述如何获取依赖列表呢?
函数通过toString可以打印出它的函数体,然后用正则表达式提取出来
factory.toString()
/require([""]([^""]*)[""])/.exec(factory.toString())[1]

优点:

依赖管理成熟可靠

社区活跃,规范接受度高

专为异步IO打造,适合浏览器环境

支持类似Commonjs的书写方式

通过插件api可支持加载非js资源

成熟的打包构建工具,并可结合插件

缺点:

模块定义繁琐,需要额外嵌套

只是库级别的支持,需要引入额外的库

无法处理循环依赖

无法实现条件加载

(3)ES6/module

语言级别的支持,未来的模块化

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79894.html

相关文章

  • 浅析webpack源码之NodeEnvironmentPlugin模块总览(六)

    摘要:进入传入地址出来一个复杂对象把挂载到对象上太复杂我们先看可以缓存输入的文件系统输入文件系统输出文件系统,挂载到对象传入输入文件,监视文件系统,挂载到对象添加事件流打开插件读取目录下文件对文件名进行格式化异步读取目录下文件同步方法就 进入webpack.js //传入地址,new Compiler出来一个复杂对象 compiler = new Compiler(options.conte...

    ChristmasBoy 评论0 收藏0
  • react源码总览(翻译)

    摘要:每次都信誓旦旦的给自己立下要好好学习源码的,结果都是因为某个地方卡住了,或是其他原因没看多少就放弃了。这次又给自己立个坚持看完源码。我看的源码版本是。本篇文章是官方文档里边的一篇文章的翻译,原文地址。 每次都信誓旦旦的给自己立下要好好学习react源码的flag,结果都是因为某个地方卡住了,或是其他原因没看多少就放弃了。这次又给自己立个flag-坚持看完react源码。为了敦促自己,特...

    Tikitoo 评论0 收藏0
  • CSS布局总览(1)

    摘要:布局定位浮动外边距操纵的好处之一是,它能够控制页面布局而不需要使用表现性标记。但是布局被误认为是难以理解的,在初学者当中,这种想法相当普遍。是一个标准的行内元素,行内元素可以在段落中包裹文字而不打乱段落的布局。 css 布局 定位 浮动 外边距操纵 display flex CSS的好处之一是,它能够控制页面布局而不需要使用表现性标记。但是CSS布局被误认为是难以理解的,在初学者当...

    YuboonaZhang 评论0 收藏0
  • Python行结构与缩进、 标识符和保留字

    摘要:缩进缩进用于表示不同的代码块,如函数条件语句循环和类的主主体。标识符和保留字标识符是用来识别变量函数类模块和其他对象的名称。标识符可以包含字母数字和下划线,但必须以非数字字符开始。由于标识符是区分大小写的,所以和是两个不同的标识符。 上一篇文章:Python词法约定和语法专题:总览下一篇文章:Python词法约定和语法专题:总览Python词法约定和语法专题:总览 行结构 Pytho...

    xiaotianyi 评论0 收藏0
  • Flow, 个新的Javascript静态类型检查器

    摘要:原文链接翻译于今天我们兴奋的发布了的尝鲜版,一个新的静态类型检查器。为添加了静态类型检查,以提高开发效率和代码质量。这最终形成一个高度并行增量式的检查架构,类似。知道缩小类型范围时做动态检查的影响。 原文链接:https://code.facebook.com/posts/1505962329687926/flow-a-new-static-type-checker-for-java...

    liangzai_cool 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<