JavaScript 模块化之路

我们都知道 js 在诞生时,都只是为了操作一些 dom,所以也就是被随意的写在一个 js 文件里,放上几个 function,绑定上 document.ready,或者是某个 button 的 click 事件。

但恰恰历史选择了 js,这门语言被无限制的放大使用场景。以前的种种简单都变成了束缚。如何在大型应用中使用 js 成了一个重大话题。

模块化

现代软件的构成基石就是模块化,

- 模块定义

模块可以有很多种形式,一个完成可用功能的函数,类语言中的一个类文件,

- 引用机制

模块还需要一种引用机制。让模块可被其它模块所使用。除了定义。

物理模块,引用模块。

如果从这个角度来看,就是你要引用的模块,是不是在同一个文件中。如果是,那就是一个引用型模块。如果不是,还需要找到对应的模块。

JS 的模块化之路

Object

这种方式,把所有的变量,函数封装在一个 Object 里,来实现模块化,缺点是,里面的变量都能被直接读取,所有的函数都被暴露。

1
2
3
4
5
6
7
const study = {
let _count = 0;
method() {
//...
};
}

它是 new Object 形式的缩写:

1
2
3
4
5
6
7
var module1 = new Object({
_name : 'foo',
 
method : function (){
  //...
 }
});

funcation

使用立即执行函数,可以将内部的变量的访问被锁住。

1
2
3
4
5
6
7
8
9
10
11
var module = (function() {
var _name = 'foo';
var method = function() {
// ...
};
return {
method: method
};
})();

这样,只能访问到 return 函数中提供的方法 method,里面的变量是不可见的。即使有其它方法,如果不在 return 中绑定,也是不可见的。

Class

ES 2015 中提出了 Class,这是个好东西,因为它符合了当下主流主言的语法,贴近人类思想,而且对继承等进行了修正。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Foo {
construct() {
this.name = 'foo';
this.objectMethod = function { return 'objectMethod'}
}
prototypeMethod() {
return 'classMethod';
}
static classMethod() {
return 'classMethod';
}
}

Class 解决了变量作用域的问题。

这样,之前两种的模块方式,都可以用 Class 代替掉。
之前,object 的 prototype 可以用 static method 来代替。

class 是个语法糖

把上面的类定义,转成之前的 ES5 语法,是可以对应上的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Foo() {
this.name = 'foo';
this.objectMethod = function() {
return 'objectMethod';
}
}
Foo.classVariable = 'classVariable';
Foo.classMethod = function() { return 'classMethod'};
Foo.prototype.color = "red";
Foo.prototype.prototypeMethod = function() {
return "prototypeMethod" + this.name + this.color;
}

使用:

1
2
3
var foo = new Foo();
var temp = foo.name;
foo.method();

原型链方法函数是读写不对称,默认它是在prototype 的,但如果往这个上面写值的话,就会被复制一份到对象上去。

像一些变量,不可能在每个实例间共享的,所以它一定是实例级的,实例方法变量,会加在每一个实例上,而原型方法变量,则是共享的,但一量为原型方法变量进行赋值,其实是在实例上放上一个同名的方法,依据 JS 原型链查找顺序,只会用最近的方法变量。

除了模块的定义之外,模块的引用也有多条路在走。

  • CommonJS(require)
  • AMD
  • ES Module(import)

CommonJS

module.export()

1
module.exports = function (x){ console.log(x);};

require

1
var foo = require('foo');

ES Module

export & import

因为ES6模块是静态加载,而CommonJS模块只有运行时才知道输出哪些接口。

因为它是 static ,所以可以编译优化。

https://github.com/joshgav/CTC/blob/b50b790a89ea8c7271cee2356f6ee6abf8224906/meetings/2017-01-11.md#rewrite-002---esm-node-eps39

ES Module 的现状

  • Edge 第一个开启
  • WebKit 已开启
  • Firefox 已开启
  • Chrome 61 即将开启
  • Node.js …

可以看到在 2017 年,除了 Node,其它浏览器均已支持 ES Module。代码编译的时代即将属于历史。