Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

01--javascript模块化进程 #1

Open
@simplexcspp

Description

@simplexcspp

一、早期javascript模块的基本写法

什么模块?
最简单的理解:实现特定功能的一组方法

  • 1、模块的原始写法
function m1(){  // your code}function m2(){  // your code}

m1和m2组成一个模块,使用时直接调用。这种原始方式的模块形式,缺点很明显:

污染了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。

  • 2、模块的对象写法:

针对上面原始写法的缺点,将模块写成一个对象,所有模块成员都放入这个对象里,使用时直接调用此对象的方法。

var myModule = {                _count: 0,                m1: function () {        // your code    },                m2: function () {         // your code    }           };myModule .m1();

那么,对象写法有什么缺点?很明显:

对象写法会暴露所有模块成员,内部私有属性可以被外部改写。

下面代码改写了私有属性_count的值。

myModule._count = 100;
  • 3、立即执行函数写法:

为了解决对象写法暴露所有模块成员的问题,使用立即执行函数写法,则不会暴露私有成员。

var myModule = (function () {            var _count = 0;    function m1 () {        // your code    }              function m2 () {         // your code    }               return {        m1: m1,        m2: m2    };})();

使用立即执行函数的写法,外部代码无法读取内部的_count变量。

console.log(myModule._count); // undefined

如果一个模块很大,必须分成多个部分,又或者一个模块需要继承另一个模块,那么该如何处理?
这时候就要使用放大模式,如下:

  • 4、放大模式
var myModule = (function (m) {        // 给myModule扩展的新方法        m.m3 = function () {        // your code    };    return m;})(myModule);

上面的代码为myModule模块添加了一个新方法m3,然后返回新的myModule模块。

在浏览器环境中,如果我们的模块的各个部分都是从网上获取的,有时无法知道哪个部分会先加载。如果采用放大模式的写法,第一个执行的部分有可能加载一个不存在空对象,该如何处理?
这时候就要使用宽放大模式,如下:

  • 5、宽放大模式
  var myModule = (function (m) {    // 给myModule扩展的新方法    m.m3 = function () {              // your code       };    return m;  })(myModule || {});

与"放大模式"相比,"宽放大模式"就是立即执行函数的参数可以是空对象。这样,当从网上获取的模块异常时,就不会报错。

一个模块,应保证其独立性,模块内部最好不与程序的其他部分直接交互,如果要在模块内部调用全局变量,最好是显式地将其他变量输入模块。

  • 6、模块内部输入全局变量
var myModule = (function ($) {    // your code })(jQuery);

二、早期javascript模块的基本写法

  • 1、CommonJS规范背景简述

早期,一开始大家都认为JS是辣鸡,没什么用,官方定义的API只能构建基于浏览器的应用程序。 随后,CommonJS API定义很多普通应用程序(主要是非浏览器的应用)使用的API,从而填补了这个空白。它的终极目标是提供一个类似Python,Ruby和Java标准库。这样的话,开发者可以使用CommonJS API编写应用程序,然后这些应用可以运行在不同的JavaScript解释器和不同的主机环境中。
   
在兼容CommonJS的系统中,你可以使用JavaScript开发以下程序:

  • 服务器端JavaScript应用程序
  • 命令行工具
  • 图形界面应用程序
  • 混合应用程序(如,Titanium或Adobe AIR)

2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程,这标志Javascript模块化编程正式诞生。

在浏览器环境下,没有模块也不是特别大的问题,毕竟网页程序的复杂性有限;但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。NodeJS是CommonJS规范的实现,webpack 也是以CommonJS的形式来书写。node.js的模块系统,就是参照CommonJS规范实现的

  • 2、CommonJS规范简介

在CommonJS中,有一个全局性方法require(),用于加载模块。假定有一个模块为math.js,就可以像下面这样加载:

var math = require('math');

然后,就可以调用模块提供的方法:

var math = require('math');math.add(2,3);

CommonJS定义的模块分为:

  • 模块引用require
  • 模块定义exports
  • 模块标识module

require用来引入外部模块;exports对象用于导出当前模块的属性和方法,是唯一的出口;module对象代表模块本身。

  • 3、AMD规范背景

基于commonJS规范的nodeJS出来以后,服务端的模块概念已经形成,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境。还是上面的代码,如果在浏览器中运行,会有一个很大的问题。什么问题?

var math = require('math');math.add(2,3);

第二行math.add(2, 3),在第一行require('math')之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等,这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。

浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous),这就是AMD规范诞生的背景。其是RequireJS在推广过程中对模块定义的规范化产出。

CommonJS是主要为了JS在服务端的表现制定的,他是不适合客户端(浏览器)的,AMD(异步模块定义)出现了,它就主要为客户端JS的表现制定规范。

  • 4、AMD规范简介

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

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

require([module], callback);

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

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

math.add()math模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。

  • 5、CMD规范

大佬玉伯写了seajs,CMD规范 SeaJS 在推广过程中对模块定义的规范化产出。

  • 6、AMD、CMD规范异同

AMD(requirejs)和 CMD (seajs)都是模块加载器,倡导模块化开发理念,核心价值是让 JavaScript 的模块化开发变得简单自然。

区别:

  • 对于依赖的模块,AMD提前执行(即时加载),CMD是延迟执行(按需加载)

  • CMD推崇依赖就近,AMD推崇依赖前置,如下:

// CMDdefine(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()        // ...    })
  • AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都简单纯粹。

主要参考资料:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


      [8]ページ先頭

      ©2009-2025 Movatter.jp