js模块化编程思想、实现与规范

更新日期: 2019-05-01阅读: 2k标签: 规范

随着BS架构的发展,网站逐渐变成了互联网应用程序,嵌入网络的JavaScript代码越来越庞大,越来越复杂(业务逻辑处理或用户交互很多写在前端)。网页越来越像桌面程序,需要一个团队分工协作、进度管理、单元测试等。。开发者不得不使用软件工程的方法,管理网页的业务逻辑。因此JavaScript模块化编程已经成了一个迫切的需求,理想的情况下是开发者只需要实现核心的业务逻辑,其他业务处理都可以加载别人已经写好的模块,做到明确分工而不会相互影响。

但是,JavaScript却不是一种模块化编程语言,它不支持类(class),更别说模块(module)了。虽然ECMAScipt正在谋划支持和推广类和模块的概念,但要实际投入生产还是遥遥无期,只能自己另外想办法。为此JavaSript社区做了很多努力,努力在现有的运行环境中,利用现有的资源,实现模块化的效果。


模块化的原始写法

模块的定义就是实现特定功能的一组方法,只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块了。

function f1() {
    // doSomething
}

function f2() {
    // doSomething
}

上面的函数f1()和f2()共同组成了一个模块,使用的时候直接通过函数名调用就行了。这种做法的缺点很明显,既污染了全局变量(f1和f2处在全局的上下文栈中,属于window的属性),也无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接的关系。


模块化的对象写法

为了解决上面的缺点,可以把模块写成一个对象,所有的模块成员都放到这个对象里面。

var module1 = {
    status: 233,
    
    f1: function() {
        // doSomething
    },
    
    f2: function() {
        // doSomething
    }
}

上面的函数f1()和函数f2()都封装在了module1对象里,使用的时候就是通过访问module1对象的属性。

module1.f1();

但是,这样的写法会暴露所有的模块成员,且内部的状态可以被外部改写。

module1.status = 666;


模块化的立即执行函数写法

使用立即执行函数(Immediately-Invoked Function Expression,IIFE),可以达到不暴露私有成员的目的。

var module1 = (function() {
    var status = 233;
    
    var f1 = function() {
        // doSomething
    };

    var f2 = function() {
        // doSomething
    };
})();

使用这样的写法,外部的代码就无法读取到内部的变量。

console.log(module1.status); // undefined

这种写法,就是JavaScript模块化的基本写法,后面的实现基本上都是依照这个思路。


模块化的放大模式

如果一个模块很大,就会要拆分成几个小的模块,或者一个模块需要继承另一个模块,这个时候就要采用放大模式(Augmentation)。

var module1 = (function(mod) {
    mod.f3 = function() {
        // doSomething
    };

    return mod;
})(module1);

这里为module1模块添加了一个新函数f3(),然后返回新的module1模块。也就是把旧的对象传进来,给这个对象添加属性,然后返回添加了属性后的对象,相当于扩展。


模块化的宽放大模式

浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。如果采用上一节的写法,第一个执行的部分有可能加载一个不存在的空对象,这时就要采用宽放大模式(Loose Augmentation)。

var module1 = (function(mod) {
    // doSomething

    return mod;
})(window.module1 || {});

与放大模式相比,宽放大模式就是立即执行函数的参数可以是空对象。


模块化的全局变量输入

独立性是模块化的重要特点,模块内部最好不与程序的其他部分直接交互。

为了在模块内部调用全局变量,必须显式地将其他变量输入模块。

var module1 = (function($, YAHOO) {
    // doSomething
})(jquery, YAHOO);

这里的module1模块中需要使用jQuery库和YUI库,于是就把这两个库(其实是两个模块)当作参数输入module1。这样做除了保证模块的独立性,也使得模块之间的依赖关系变得明显。


模块化的几种规范

模块化的前提是要遵循同一套规范,否则模块之间的调用会十分困难。

JavaScript官方没有模块化的规范,目前通用的民间规范主要有CommonJS(服务端js模块化的规范,NodeJS是这种规范的实现)、AMD(Asynchronous Module Definition异步模块定义,RequireJS遵循此规范)和CMD(Common Module Definition,通用模块定义,SeaJS遵循此规范)。

模块化在服务端的规范:CommonJS

2009年,美国程序员Ryan Dahl创造了node.js项目,将JavaScipt语言用于服务器端编程(后端)。这标志着JavaScipt模块化编程正式诞生。因为老实说,在浏览器环境下,没有模块也不是特别大的问题,毕竟网页程序的复杂性有限;但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。

node.js的模块系统,就是参照CommonJS规范实现的。在CommonJS中,有一个全局性的方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载并调用模块中提供的方法:

var math = require(‘math‘);
math.add(2, 3); // 5

更多的用法这里就不说了,只需要知道CommonJS是使用require()函数加载模块就行了。

模块化规范从服务端到客户端的发展

有了服务端的模块化之后,大家就想要客户端的模块化了。而且最好两者能兼容,一个模块不用修改,在服务器和浏览器都可以运行。但是由于一个重大局限,使得CommonJS规范不适用于浏览器环境。这是因为,在上面的代码中,调用math的方法必须要在math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。

这对于服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是对于浏览器来说,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,可能会导致浏览器处于假死状态。

因此浏览器端的模块化不能使用同步加载(Synchronous),只能使用异步加载(Asynchronous)。这就是AMD规范诞生的背景。

模块化在客户端的规范:AMD

AMD(Asynchronous Module Definition,异步模块定义)采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,直到加载完成之后,这个回调函数才会运行。

AMD也采用require()语句加载模块,不同于CommonJS的是,它要求两个参数:

require([module], callback);

第一个参数[moudle],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是这样:

require([‘math‘], function(math) {
    math.add(2, 3);
});

这样,math.add()与math模块的加载就不是同步的,浏览器也不会发生假死的状况。所以很显然地是AMD比较适合浏览器环境。应用AMD规范的主要有require.js。

模块化在客户端的规范:CMD

CMD是SeaJS在推广过程中对模块定义的规范化产出。

AMD和CMD的区别:

1.对于依赖的模块,AMD是提前执行,CMD是延迟执行。CMD推崇的是as lazy as possible,即尽可能得懒加载(延迟加载),即在需要得时候才加载。

2.CMD推崇依赖就近,AMD推崇依赖前置。

// CMD
define(function(require, exports, module) {
    var a = require(‘./a‘);
    a.doSomething();
    var b = require(‘./b‘);   // 依赖可以就近书写
    b.doSomething();
})

// AMD 默认推荐的是
define([‘./a‘, ‘./b‘], function(a, b) { // 依赖必须一开始就写好
    a.doSomething();
    b.doSomething();
}) 

3.AMD的api默认是一个当多个用,CMD的API则是严格区分,推崇职责单一。比如在AMD里,require分全局和局部,而在CMD里则没有全局require,而是根据模块系统的完备性,提供seajs.use来实现模块系统的加载启动。CMD里,每个API都简单存粹。


模块化的优点总结

1.解决了命名的冲突问题。多人开发的场景下,容易出现命名冲突,模块化通过内部封装与外部隔离能有效防止命名冲突的问题。

2.解决了文件的依赖问题,使文件易于管理。如果有很多js文件相互依赖,依赖关系和加载顺序都是让人头冷的问题。使用模块化就可以很好地实现依赖管理(使用依赖都要提前声明)。

3.提高代码的可读性。各个模块各自完成自己的功能,专司其职,除了问题也会便于维护。

4.提高代码的复用性。可以抽提特定的通用功能作为一个通用的模块。


原文:https://www.cnblogs.com/yanggb/p/10798646.html 

 

链接: https://fly63.com/article/detial/3170

web开发,前后分离接口规范

目前我们现在用的前后端分离模式属于第一阶段,下一阶段可以在前端工程化方面,对技术框架的选择、前端模块化重用方面,可多做考量。也就是要迎来“==前端为主的 MV* 时代==”。

js中箭头函数的编码规范,如何更好的使用箭头函数

当您必须使用匿名函数,请使用箭头函数表示法,它创建了一个在 this 上下文中执行的函数的版本,这通常是你想要的,而且这样的写法更为简洁。如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个声明函数上。

用standard来管理JavaScript 代码规范

standard是一个开源的JS代码规范库,制定了所谓standard(标准)的JS代码规范,配合编辑器插件可以实时检查代码规范以及语法错误,通过执行命令检查代码规范以及语法错误,自动修复(可以直接修复的)不合规范的代码,使其符合规范

Web 前端开发代码规范(基础)

对于一个多人团队来说,制定一个统一的规范是必要的,因为个性化的东西无法产生良好的聚合效果,规范化可以提高编码工作效率,使代码保持统一的风格,以便于代码整合和后期维护。

Node.js的模块加载机制(CommonJS规范)

为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在Node环境中,一个.js文件就称之为一个模块(module)

web前端js中ES6的规范写法

引号的使用,单引号优先(如果不是引号嵌套,不要使用双引号)、空格的使用问题:(关键字后 符号后 排版 函数 赋值符号= )等、不写没有使用过的变量,如果定义了一个变量,后来一直没有参与过运算,那么不应该定义这个变量...

编码规范_html代码规范化编写

嵌套的节点应该缩进;在属性上,使用双引号,不要使用单引号;属性名全小写,用中划线做分隔符;不要在自动闭合标签结尾处使用斜线(HTML5 规范 指出他们是可选的);不要忽略可选的关闭标签;

CommonJS 规范中的 module、module.exports 区别

CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。module.exports属性表示当前模块对外输出的接口

W3C 代码标准规范

W3C通过设立领域(Domains)和标准计划(Activities)来组织W3C的标准活动,围绕每个标准计划,会设立相关的W3C工作组织(包括工作组、社区组、商务组等)。W3C会根据产业界的标准需求调整Domains和Activity的设置及相关的工作组设置。

css3代码书写规范

不要使用 @import 与 <link> 标签相比,@import 指令要慢很多,不光增加了额外的请求次数,还会导致不可预料的问题。CSS有些属性是可以缩写的,比如padding,margin,font等等,这样精简代码同时又能提高用户的阅读体验。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!