一、混沌初开时
世上本没有模块,代码写得多了,便成了灾。
那光景,凡写前端者,必在全局撒野。如那未庄的闲汉,赤着膊,将function胡乱抛在window上。你且看这般景象:
// 赵家的函数
function calculate(a, b) {
return a + b + 100 // 赵老爷偏要多加一百
}
// 钱家的函数
function calculate(str) {
return str.slice(0, 5) // 钱太爷定要截断五字
}
console.log(calculate(2,3)) // 究竟用哪个?好比九斤老太的秤,总要缺斤短两!
后有人学那孔乙己,用对象裹着代码当茴香豆——这便是简单对象封装:
// 孔家的算术铺子
var math = {
_pi: 3.14, // 单下划线装体面
add: (a, b) => a + b + this._pi
}
math._pi = 100 // 赵举人抬手就改
console.log(math.add(1,2)) // 103.14变作102,好比辫子盘在头顶,到底遮不住秃
那window对象上,各种命名互相践踏,如辫子般纠缠。有识之士见了,总要摇头叹道:"这代码,怕是要吃人的!"
二、改革者的足迹
后来有人学了乖,把代码裹进麻袋里——这便是IIFE的法子。如闰土在瓜田里扎的稻草人,虽防不得真猹,到底是个模样:
var 周家模块 = (function(){
var 秘银 = "不可示人"
return {
露脸的法子: function(){
console.log(秘银.slice(0,1)) // 只许看个不字
}
}
})()
周家模块.露脸的法子() // 输出"不"
周家模块.秘银 // undefined 好比孔乙己的茴香豆,是摸不着的
这法子虽好,终究是单打独斗。各模块如未庄的乡民,隔着土墙叫骂:
"阿Q!你家的jQuery可传过来了么?我这日历组件离了它,便如辫子离了头,是要出人命的!"
"七斤嫂,你且等等,待我先引了Bootstrap的CDN,再引你的样式文件!"
原来这IIFE好比抓药,须得药引子在前,药材在后。若把当归错放在黄芪前头,便要闹出人命。待到项目大时,几十个script标签排着队,宛如送葬的队伍,稍有不慎便全盘崩溃。
三、新青年的觉醒
CommonJS派的长衫客
CommonJS者,如孔乙己穿长衫,最是四平八稳。此派在Node.js地界盛行,讲究的是同步加载:
// 咸亨酒店的账本(math.js)
let 酒钱 = 9
module.exports = {
赊账: () => 酒钱++,
结账: () => {
if(酒钱 > 0) console.log("多乎哉?不多也!")
return 酒钱
}
}
// 酒客的主顾(main.js)
const 账本 = require('./math')
账本.赊账()
console.log(账本.结账()) // "多乎哉?不多也!"(实则欠10文)
这派有个怪病:输出的皆是死物。好比复制人偶,原身改了,分身却不知:
// 真身.js
let count = 1
setTimeout(() => count = 2, 1000)
module.exports = count
// 分身.js
const num = require('./真身')
console.log(num) // 1
setTimeout(() => console.log(num), 2000) // 还是1
AMD与CMD的辫子军
AMD者,假洋鬼子做派,最喜前置依赖。RequireJS便是其马前卒:
// 假洋鬼子的新派做法
define(['jquery', 'lodash'], function($, _) { // 先备齐弹药
let 银元 = 100
return {
买自由: () => {
$('body').append(`还剩${银元--}块大洋`)
_.debounce(() => alert('革命成功'), 500)
}
}
})
CMD却像改良派,讲究就近取物。Sea.js便是典型:
// 改良派的折衷之道
define(function(require, exports, module) {
// 用时方取
var 大炮 = require('革命炮')
exports.起义 = () => 大炮.开火()
})
这两派都学那西洋钟,异步加载不阻塞。区别在于:AMD如西医开刀,先把零件备齐;CMD似中医煎药,文火慢炖时再取药材。
四、将来的天下
ES6模块的真革命
如今ES6模块一统江湖,如狂人日记撕开铁屋:
// 新青年的呐喊(counter.mjs)
export let 觉醒人数 = 0
export const 敲钟 = () => 觉醒人数++
// 吃人的旧社会终要灭亡(main.mjs)
import { 觉醒人数, 敲钟 } from './counter.mjs'
敲钟()
console.log(觉醒人数) // 1(这是活的数字,不是死物!)
// 若在别处也敲钟...
setTimeout(() => console.log(觉醒人数), 1000) // 看那数字自己会跑!
这般的模块,才是真革命:
-
活的引用:不似CommonJS的纸人纸马,拷贝些死物;ES6的乃是血肉之躯,改一处则处处皆变
-
铁屋中的曙光:编译时就定下规矩,好比先生批注的狂人日记,错处早被圈出
-
静默的革新:Tree Shaking如快刀剪辫,未用的代码皆化作尘土
// CommonJS的纸人纸马
const {活字} = require('./印刷术') // 管你用不用,全套搬来
// ES6的精准打击
import {火药} from './四大发明.mjs' // 只取所需,余者灰飞烟灭
结语
面试官放下茶杯,镜片后透出精光:"依你之见,模块化竟是一场革命?"
我整了整长衫下摆的补丁,答道:
"先生看这代码世界,先前是混沌的奴才,今朝要做自己的主人。模块化哪里是写代码?分明是在铁屋中凿窗——为的是教那变量各得其所,教那依赖不再吃人,教那复用如野草蔓生,便是关了沙箱,也要倔强地长!"
那面试官听罢,手中的笔竟微微颤抖起来。窗外隐约传来《新青年》的卖报声,混着编译器的嗡鸣,恰似新时代的晨钟。