资讯专栏INFORMATION COLUMN

JavaScript模块发展变迁史

姘存按 / 344人阅读

摘要:前两天有朋友拿了这样一段代码来问我,我想把一段代码写成模块化的样子,你帮我看看是不是这样的。的一个好处在与依赖前置,所有被使用到的模块都会被提前加载好,从而加快运行速度。

前两天有朋友拿了这样一段代码来问我,“我想把一段代码写成模块化的样子,你帮我看看是不是这样的。”,代码大概是这样的:
(function(global) {
    var myModules = {
        name: "xxx",
        location: "chengdu",
        intro: function() {
                return `his name is ${myModules.name} and come from ${myModules.location}`
            },
        }
        // some other code...
        if(typeof module === "undefined")
            global.myModules = myModules
        else
            module.exports = myModules
})(this)
“可是我这段代码在全局还是能myModules这个属性啊?”
我一脸懵,还有这种操作,为什么你的立即执行函数要把this传进去呢,这样不久将里面的内容挂在到了window上了吗,他似懂非懂,我只好说,你可能需要回头去看看AMDCMD规范了,我大概能够理解这其中的缘由,毕竟这段时间前端发展的速度飞快,加上webpack也不需要自己配,包括vuereactangular在内的框架类库都有一键生成项目的工具,从而只需要使用下import * from "*"export default {},而这种便利会让新人不再需要去学习基本原理就能快速上手,毕竟现在都ES2018了呀。

对象形式

最开始的时候,为了减少全局变量,一般会使用一个对象的方式来对所有的变量方法进行一个包装:

    var obj = {
        a: 1,
        b: 2,
        sum: function(val) {
           return obj.a + obj.b + val
        },
        rest: function(val) {
           return val - obj.a - obj.b
        }
   }
以上代码似乎解决了全局变量的问题,但是其中的ab两个变量还是可能被修改,其中包含的进化有限。

立即执行函数

回过头来看文章开头的代码,姑且不论以上代码的错误之处,稍作修改,这算是最初的一种关于JavaScript模块化的开端,立即执行函数:

var add = (function(){
    var a = 1
    var b = 2
    function sum(count){
        return a + b + count
    }
    function rest(count){
        return count - a - b
    }
    return {
        rest: rest,
        sum: sum
    }
})()
这就是一种最简单的模块化的方式,利用闭包的特性,设置了两个只有在被暴露出来的addreduce方法内部才能访问到的两个变量,从而保证了函数的局部变量不可被修改的特性,这次的进化用到了闭包,从而实现了部分有效的目的。

放大模式

其实开头的代码更符合另外一种叫做放大模式的方法,不过一般来说不会讲window作为放大模式中被传入的对象

    var globalObject = {
      fn1: function() {
        // todo...
      },
      // ...
    }
    globalObject = (function(obj) {
      var name = "xxx"
      var location = "xxx"
      function sum(val) { /* todo... */ }
      function rest(val) { /* todo... */ }
      obj.sum = sum
      obj.rest = rest
      return obj
    })(globalObject)
以这种形式来写,可以让全局变量尽量的减少,同时让一个立即执行函数中的代码尽量做到精简。

但是这种放大模式也存在着问题,比如当globalObject内容足够多的时候,很可能会造成命名重复的情况,并且以上所有的方式都不可以减少script标签的数量,所以,我们还是会被模块的加载顺序,命名空间冲突等问题所困扰,这时候,我们应该跨入新时代了。

CMD规范

CMD规范来自阿里的框架seajs,当初确实有挺多人使用,不过现阶段已经不再维护了,我也不会,就暂时不说了,只列出来。

commonjs

同时,从2009年开始,JavaScript就不再只是一种浏览器端的脚本语言了,nodejs的出现让使用js开发服务端变成了可能,随着node出现的东西还有一个叫做commonjs的规范,在这个规范中,每个文件都是一个模块,有着自己的作用域。

譬如,如下代码

    // 文件a.js
    var a = 1
    // 文件b.js
    console.log(a) // a is not defined.

在这样的特性下,a.jsb.js都有着自己独有的作用域,要在b中对a进行访问,就需要一种加载机制,一般来说,有两种方法能够做到:

方法1
    // 文件a.js
    global.a = 1
    // 文件b.js
    console.log(a) // 1

这种方法挂载在global上,当然是不可取的。

方法2
    // 文件a.js
    exports.a = 1
    // 文件b.js
    var moduleA = require("./a")
    console.log(moduleA.a)

AMD规范

requirejs的出现让script标签的减少变成了可能,在requirejs的时代,我们一般会使用jQueryunderscore这类的类库,如果按照往常的样子我们会将代码写成下面这副模样:





这样的代码乍一看似乎没什么问题,但是当一个项目的代码量上了一个量级,一切就变得不是这么回事儿了,你会被困在加载顺序,加载时间的问题上,这也就是requirejs能够出现的原因了。

requirejs中,你可以如此改写以上代码:

    // `index.js`
   require(["js/lib/jquery.min", "js/lib/underscore.min", "js/app/app"], function($, _, app) {  /*  todo...  */  })
    // `app.js`
   define(["js/lib/jquery.min", "js/lib/underscore.min"], function($, _) {  /*  todo...  */  })

这里当然显得更加优雅了,在requirejs的推广过程中,AMD规范也就应运而生了,那么,requirejs或者说AMD规范到底解决了什么样的问题呢,主要有几点:

AMD是“异步模块定义”的缩写,也就是说,其中内容是异步加载的,从而让页面不被js的加载阻塞,最大程度上的避免了页面假死等情况的产生。

AMD的一个好处在与依赖前置,所有被使用到的模块都会被提前加载好,从而加快运行速度。

那么,commonjs规范和AMD规范有什么区别呢

运行环境不同,commonjs规范只能运行在node端,而AMD规范则被用到浏览器端

由于运行环境的不同,二者的加载机制也不同,commonjs中的require是同步执行的,而AMD中则是异步的。

ES2015模块化

ES2015中,可以使用export, export default, import import * as 等操作符来作模块化的功能,但是这个规范现在尚未被任何浏览器加入规范中,我目前的Chrome版本为63.0.3239.132,也无法原生支持,不过现阶段我们几乎都用上了这个规范,这一切都只能归功于babel,webpackrollup等新工具的出现,既然如此,那就拥抱未来吧,不过有一点,需要在了解原理的前提下,不然,倘若有一天,真的需要我们来封装一个小小的模块的时候,没有了那些工具,我们该从何下手呢。

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

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

相关文章

  • 阿里巴巴运维体系变迁

    摘要:反正在阿里巴巴,很多的运维人员都说了,我们每年的工作中有一项不用写的工作就是搬迁。未来我们确实相信阿里巴巴,可能在未来搬迁会相对更少一点,我们认为不能让搬迁成为阿里巴巴运维团队的核心竞争力。以上,正是阿里巴巴的运维团队所覆盖的五个领域。 随着大数据、机器学习和 AI 技术的飞速发展,智能化运维成为运维的热点领域。Gartner 的报告宣称,到 2020 年,将近 50% 的企业将会在他们的业...

    MobService 评论0 收藏0
  • ELSE 技术周刊(2017.12.18期)

    摘要:程序人生从黑客到创业,他说技术创业该这么做知道创宇,安全焦点民间白帽黑客组织核心成员,分享他创业感悟和踩过的那些坑。技术周刊由小组出品,汇聚一周好文章,周刊原文。 业界动态 他们写的代码能上天!NASA的10条安全编码准则大公开 NASA的10条代码编写规范准则 本期推荐 Node.js 中遇到含空格 URL 的神奇Bug——小范围深入 HTTP 协议 本文阐述了博主遇到含空格 URL...

    douzifly 评论0 收藏0
  • 前端每周清单第 51 期: React Context API 与模式变迁, Webpack 与 W

    摘要:前端每周清单第期与模式变迁与优化界面生成作者王下邀月熊编辑徐川前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点分为新闻热点开发教程工程实践深度阅读开源项目巅峰人生等栏目。 showImg(https://segmentfault.com/img/remote/1460000013279448); 前端每周清单第 51 期: React Context A...

    Jiavan 评论0 收藏0
  • 精读《前端未来展望》

    摘要:精读前端可以从多个角度理解,比如规范框架语言社区场景以及整条研发链路。同是前端未来展望,不同的文章侧重的格局不同,两个标题相同的文章内容可能大相径庭。作为使用者,现在和未来的主流可能都是微软系,毕竟微软在操作系统方面人才储备和经验积累很多。 1. 引言 前端展望的文章越来越不好写了,随着前端发展的深入,需要拥有非常宽广的视野与格局才能看清前端的未来。 笔者根据自身经验,结合下面几篇文章...

    MadPecker 评论0 收藏0

发表评论

0条评论

姘存按

|高级讲师

TA的文章

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