{"componentChunkName":"component---src-templates-blog-post-js","path":"/javascript-modularization-summary/","result":{"data":{"markdownRemark":{"html":"<h2>什么是模块化？</h2>\n<p>模块化是以提高代码复用率、减少代码管理的成本为核心思想的开发方式，一个模块有自己的私有作用域，只向外部暴露一些接口(方法，变量)，借此和其他模块进行通信，目前比较热门的js模块化规范：CommonJS、AMD、CMD以及ES6 Module。</p>\n<h2>CommonJS</h2>\n<p>Node.js应用就是采用CommonJS规范组成的，它有四个环境变量为模块化的实现提供支持：global、require、module、exports。在模块中定义的变量、方法都是私有的，外部模块需要使用，需在模块内部用exports暴露。</p>\n<h3>CommonJS运行时</h3>\n<ul>\n<li>模块加载的顺序，按照该模块在代码中出现的顺序(运行时加载)</li>\n<li>所有代码都运行在模块私有作用域，不会污染全局作用域。</li>\n<li>加载模块时会缓存运行结果，后续的加载会直接读取缓存，想要模块再次运行，需要清楚缓存</li>\n</ul>\n<h3>CommonJS使用</h3>\n<ol>\n<li>module.exports属性</li>\n</ol>\n<p>module.exports属性表示当前模块对外输出的接口，其他文件加载该模块，实际上就是读取module.exports变量。</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">function</span> <span class=\"token function\">example</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> a <span class=\"token operator\">+</span> b<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  add<span class=\"token operator\">:</span>example\n<span class=\"token punctuation\">}</span></code></pre></div>\n<ol start=\"2\">\n<li>exports变量</li>\n</ol>\n<p>exports变量指向module.exports，这相当于在每个模块中都加入var exports = module.exports，也就是对外输出时可以在这个变量上添加方法，</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">exports<span class=\"token punctuation\">.</span>example <span class=\"token operator\">=</span> example</code></pre></div>\n<p><strong>注意</strong>,不能把exports直接指向一个值，这样就相当于切断了exports和module.exports的关系</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">// 例：</span>\nexports <span class=\"token operator\">=</span> <span class=\"token string\">'dd'</span> </code></pre></div>\n<ol start=\"3\">\n<li>require命令</li>\n</ol>\n<p>require命令用于加载模块文件，相当于读入并执行一个js文件，然后返回该模块的exports对象，没有发现指定模块，则就会报错。\n基本使用：</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">// 引用自定义的模块时，参数包含路径，可省略.js</span>\n<span class=\"token keyword\">let</span> example <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'./example'</span><span class=\"token punctuation\">)</span>\nexample<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">,</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// 引用核心模块时，不需要带路径</span>\n<span class=\"token keyword\">var</span> http <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'http'</span><span class=\"token punctuation\">)</span>\nhttp<span class=\"token punctuation\">.</span><span class=\"token function\">createService</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">listen</span><span class=\"token punctuation\">(</span><span class=\"token number\">3000</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>加载规则：<br>\n引用阮老师的博客</p>\n<blockquote>\n<ul>\n<li>如果参数字符串以“/”开头，则表示加载的是一个位于绝对路径的模块文件。比如，require('/home/marco/foo.js')将加载/home/marco/foo.js。</li>\n<li>如果参数字符串以“./”开头，则表示加载的是一个位于相对路径（跟当前执行脚本的位置相比）的模块文件。比如，require('./circle')将加载当前脚本同一目录的circle.js。</li>\n<li>如果参数字符串不以“./“或”/“开头，则表示加载的是一个默认提供的核心模块（位于Node的系统安装目录中），或者一个位于各级node_modules目录的已安装模块（全局安装或局部安装）</li>\n<li>如果参数字符串不以“./“或”/“开头，而且是一个路径，比如require('example-module/path/to/file')，则将先找到example-module的位置，然后再以它为参数，找到后续路径。</li>\n<li>如果指定的模块文件没有发现，Node会尝试为文件名添加.js、.json、.node后，再去搜索。.js件会以文本格式的JavaScript脚本文件解析，.json文件会以JSON格式的文本文件解析，.node文件会以编译后的二进制文件解析。</li>\n<li>如果想得到require命令加载的确切文件名，使用require.resolve()方法。</li>\n</ul>\n</blockquote>\n<ol start=\"4\">\n<li>注意\n<ul>\n<li>(加载方式)：在服务端CommonJS用同步的方式加载模块。</li>\n<li>(加载方式)：在浏览器端，限于网络原因，更合理的方案是使用异步加载。</li>\n<li>(加载机制)：输入的是被输出的值的拷贝。也就是说，一旦输出一个值，模块内部的变化就影响不到这个值，想要影响到外面的值，需要把值暴露写成一个函数形式。</li>\n</ul>\n</li>\n</ol>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">let</span> count <span class=\"token operator\">=</span> <span class=\"token number\">0</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token operator\">++</span>count\n<span class=\"token punctuation\">}</span>\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">get</span> <span class=\"token function\">count</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> count\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  add<span class=\"token operator\">:</span>add\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2>ES6 Module</h2>\n<p>ES6 在语言标准的层面上，实现了模块功能，旨在成为浏览器和服务器通用的模块解决方案。主要由两个命令实现：export命令用于规定模块的对外接口，import命令用于输入其他模块提供的功能。</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">//定义模块example</span>\n<span class=\"token keyword\">let</span> count <span class=\"token operator\">=</span> <span class=\"token number\">10</span>\n<span class=\"token keyword\">let</span> <span class=\"token function-variable function\">add</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> a <span class=\"token operator\">+</span> b\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">export</span> <span class=\"token punctuation\">{</span> count<span class=\"token punctuation\">,</span>add <span class=\"token punctuation\">}</span>\n<span class=\"token comment\">//引用模块</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> count<span class=\"token punctuation\">,</span>add <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'./example'</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">math</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token number\">99</span><span class=\"token punctuation\">,</span>count<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>使用import命令的时候，用户需要知道所要加载的变量名或函数名。ES6还提供了export default命令，为模块指定默认输出，对应的import语句不需要使用大括号。这也更趋近于ADM的引用写法。</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">//定义输出</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token punctuation\">(</span> count<span class=\"token punctuation\">,</span>add <span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//引入</span>\n<span class=\"token keyword\">import</span> example <span class=\"token keyword\">from</span> <span class=\"token string\">'./example'</span>\n<span class=\"token comment\">//调用</span>\nexample<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token number\">99</span><span class=\"token punctuation\">,</span>count<span class=\"token punctuation\">)</span></code></pre></div>\n<p><strong>注意</strong>：ES6的模块不是对象，import命令会被 JavaScript 引擎静态分析，在编译时就引入模块代码，而不是在代码运行时加载，所以无法实现条件加载。也正因为这个，使得静态分析成为可能，webpack也可以进行Tree-Sharking</p>\n<h2>AMD</h2>\n<p>AMD规范采用异步方式加载模块，模块的加载不影响它后面语句的运行。允许指定回调函数，所有依赖这个模块的语句，都定义在这个回调函数中，等到加载完成之后，回调函数才会运行。<br>\n基本使用：</p>\n<ul>\n<li>require.config()指定引用路径等</li>\n<li>define()定义模块</li>\n<li>require()加载模块</li>\n</ul>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">//定义没有依赖的模块</span>\n<span class=\"token function\">define</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">return</span> 模块\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n定义有依赖的模块\n<span class=\"token function\">define</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'module1'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'module2'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">m1<span class=\"token punctuation\">,</span> m2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">return</span> 模块\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// require.config()指定各模块路径和引用名</span>\nrequire<span class=\"token punctuation\">.</span><span class=\"token function\">config</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  baseUrl<span class=\"token operator\">:</span> <span class=\"token string\">\"js/lib\"</span>，\n  paths<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token string\">\"jquery\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"jquery.min\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">\"underscore\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"underscore.min\"</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// 执行基本操作</span>\n<span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'jquery'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'underscore'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">m1<span class=\"token punctuation\">,</span> m2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n   使用模块\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div>\n<h2>CMD</h2>\n<p>CMD规范专门用于浏览器端，模块的加载是异步的，模块使用时才会加载执行。CMD规范整合了CommonJS和AMD规范的特点。在 Sea.js 中，所有 JavaScript 模块都遵循 CMD模块定义规范。\n基本使用：</p>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">//定义模块</span>\n<span class=\"token function\">define</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">require<span class=\"token punctuation\">,</span> exports<span class=\"token punctuation\">,</span> module</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">{</span>\n  exports<span class=\"token punctuation\">.</span>add <span class=\"token operator\">=</span> <span class=\"token operator\">...</span>\n  module<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> <span class=\"token operator\">...</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//引入模块</span>\n<span class=\"token function\">define</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">require</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> m1 <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'./m1'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">var</span> m2 <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'./m2'</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div>\n<h2>总结</h2>\n<p>主要总结下CommonJS和es module的区别：</p>\n<ol>\n<li>CommonJS模块输出的是一个值的拷贝，ES6模块输出的是值的引用。</li>\n</ol>\n<ul>\n<li>CommonJS 模块输出的是值的拷贝，一旦输出一个值，模块内部的变化就影响不到这个值，会将结果进行缓存。</li>\n<li>ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候，遇到模块加载命令import，就会生成一个只读引用。等到脚本真正执行时，再根据这个只读引用，到被加载的那个模块里面去取值。因此ES6模块是动态引用，并且不会缓存值，对此webpack可以做tree-sharking处理。</li>\n</ul>\n<ol start=\"2\">\n<li>CommonJS模块是运行时加载，ES6模块是编译时输出接口</li>\n</ol>\n<ul>\n<li>CommonJS是运行时进行加载：模块就是对象，即在输入时是先加载整个模块，生成一个对象，然后再从这个对象上面读取方法，这种加载称为“运行时加载”。</li>\n<li>ES6是编译时加载：模块不是对象，而是通过export命令显式指定输出的代码，import时采用静态命令的形式。即在import时可以指定加载某个输出值，而不是加载整个模块，这种加载称为“编译时加载”。</li>\n</ul>","timeToRead":5,"frontmatter":{"title":"JavaScript模块化总结","date":"October 07, 2020","spoiler":null},"fields":{"slug":"/javascript-modularization-summary/"}}},"pageContext":{"slug":"/javascript-modularization-summary/","previous":{"fields":{"slug":"/js-implicit-conversion/"},"frontmatter":{"title":"js隐式转换"}},"next":{"fields":{"slug":"/garbage-collection-mechanism-of-v8-engine/"},"frontmatter":{"title":"v8引擎垃圾内存回收机制"}}}},"staticQueryHashes":["3649515864","63159454"]}