この記事では、JavaScript モジュラー プログラミング (コード例) について詳しく説明します。これには一定の参考価値があります。必要な友人は参照できます。お役に立てば幸いです。
モジュール性とは何ですか?
モジュールとは、特定の機能を実現するためのメソッドのセットです。モジュール化とは、モジュール コードに独自のスコープを作成し、パブリック メソッドと変数のみを外部に公開することです。これらのメソッドは高度な機能を備えています。互いに取り外し可能なカップリング。
JS を記述するときにモジュール型プログラミングが必要なのはなぜですか?
フロントエンドを作成するとき、一部のフォーム送信と Web ページ上のクリック インタラクションのみを処理しました。JS モジュール性の概念は強化されていませんでした。フロントエンド ロジックは複雑になり、インタラクションはさらに多くなり、データ量はますます大きくなり、フロントエンドからは JS モジュール型プログラミングへの要求が強くなってきています。
多くのシナリオでは、モジュール性を考慮する必要があります。
したがって、上記のシナリオに基づいて、現在の JS モジュール化は主に次の目的を持っています:
ES6 より前では、JS はそうではなかったことがわかっています。ブロック スコープでは、プライベート変数とメソッドの分離は主に関数スコープに依存し、パブリック変数とメソッドの分離は主にオブジェクト属性参照に依存します。
カプセル化関数JS にモジュール仕様がなかったとき、モジュール性を実現するために、一部の共通関数と基礎関数が抽象化され、関数に分割されました。 utils.jsツール関数ファイルを書く// utils.js
function add(x, y) {
if(typeof x !== "number" || typeof y !== "number") return;
return x + y;
}
function square(x) {
if(typeof x !== "number") return;
return x * x;
}
<script></script>
<script>
add(2, 3);
square(4);
</script>
グローバル変数の下にマウント:
var mathUtils1 = { add: function(x, y) { return x + y; }, } var mathUtils2 = { add: function(x, y, z) { return x + y + z; }, } mathUtils.add(); mathUtils.square();
モジュールにプライベート変数があることを考慮して、IIFE (即時実行式) を使用してプライベート変数をカプセル化するクロージャを作成します。プライベート変数 外部からアクセスできません モジュールに他の依存関係を導入する必要がある場合はどうすればよいでしょうか?
var module = (function(){ var count = 0; return { inc: function(){ count += 1; }, dec: function(){ count += -1; } } })() module.inc(); module.dec();
var utils = (function ($) { var $body = $("body"); var _private = 0; var foo = function() { ... } var bar = function () { ... } return { foo: foo, bar: bar } })(jQuery);
Nodejs の登場により、サーバーサイド環境で JavaScript を実行できるようになりましたが、現時点では、統一されたモジュール システムを実装する標準の確立が急務となっています。これは後に CommonJS になりました。
<script></script> <script></script> <script></script> <script></script> <script></script>
CommonJS は、各モジュール内で、 module が現在のモジュールを表すことを規定しています。このモジュールは、id、ファイル名、ロード済み、親、子、エクスポートなどの属性を持つオブジェクトです。 module.exports 属性は、現在のモジュールの外部出力インターフェイス、他のファイルはモジュールをロードし、実際には module.exports 変数を読み取ります。
// math.js exports.add = function(x, y) { return x + y; } // base.js var math = require("./math.js"); math.add(2, 3); // 5 // 引用核心模块 var http = require('http'); http.createServer(...).listen(3000);
便宜上、Node はモジュールごとに module.exports を指すエクスポート自由変数を提供します。これは、各モジュールの先頭に次のような行があることと同じです。
// utils.js // 直接赋值给 module.exports 变量 module.exports = function () { console.log("I'm utils.js module"); } // base.js var util = require("./utils.js") util(); // I'm utils.js module 或者挂载到 module.exports 对象下 module.exports.say = function () { console.log("I'm utils.js module"); } // base.js var util = require("./utils.js") util.say();
var exports = module.exports;
CommonJS 仕様のロード モジュールは同期されており、サーバー側で使用されます。CommonJS は起動時に組み込みモジュールをメモリにロードするため、モジュールをロードします。渡されたモジュールはメモリに配置されます。したがって、Node 環境で同期ロードを使用しても大きな問題はありません。 さらに、CommonJS モジュールは出力値のコピーを読み込みます。つまり、外部モジュールの出力値が変化しても、現在のモジュールのインポート値は変化しません。
CommonJS 规范的出现,使得 JS 模块化在 NodeJS 环境中得到了施展机会。但 CommonJS 如果应用在浏览器端,同步加载的机制会使得 JS 阻塞 UI 线程,造成页面卡顿。
利用模块加载后执行回调的机制,有了后面的 RequireJS 模块加载器, 由于加载机制不同,我们称这种模块规范为 AMD(Asynchromous Module Definition 异步模块定义)规范, 异步模块定义诞生于使用 XHR + eval 的开发经验,是 RequireJS 模块加载器对模块定义的规范化产出。
AMD 的模块写法:
// 模块名 utils // 依赖 jQuery, underscore // 模块导出 foo, bar 属性 <script></script> // main.js require.config({ baseUrl: "script", paths: { "jquery": "jquery.min", "underscore": "underscore.min", } }); // 定义 utils 模块,使用 jQuery 模块 define("utils", ["jQuery", "underscore"], function($, _) { var body = $("body"); var deepClone = _.deepClone({...}); return { foo: "hello", bar: "world" } })
AMD 的特点在于:
AMD 支持兼容 CommonJS 写法:
define(function (require, exports, module){ var someModule = require("someModule"); var anotherModule = require("anotherModule"); someModule.sayHi(); anotherModule.sayBye(); exports.asplode = function (){ someModule.eat(); anotherModule.play(); }; });
SeaJS 是国内 JS 大神玉伯开发的模块加载器,基于 SeaJS 的模块机制,所有 JavaScript 模块都遵循 CMD(Common Module Definition) 模块定义规范.
CMD 模块的写法:
<script></script> <script> // seajs 的简单配置 seajs.config({ base: "./script/", alias: { "jquery": "script/jquery/3.3.1/jquery.js" } }) // 加载入口模块 seajs.use("./main") </script> // 定义模块 // utils.js define(function(require, exports, module) { exports.each = function (arr) { // 实现代码 }; exports.log = function (str) { // 实现代码 }; }); // 输出模块 define(function(require, exports, module) { var util = require('./util.js'); var a = require('./a'); //在需要时申明,依赖就近 a.doSomething(); exports.init = function() { // 实现代码 util.log(); }; });
CMD 和 AMD 规范的区别:
AMD推崇依赖前置,CMD推崇依赖就近:
AMD 的依赖需要提前定义,加载完后就会执行。
CMD 依赖可以就近书写,只有在用到某个模块的时候再去执行相应模块。
举个例子:
// main.js define(function(require, exports, module) { console.log("I'm main"); var mod1 = require("./mod1"); mod1.say(); var mod2 = require("./mod2"); mod2.say(); return { hello: function() { console.log("hello main"); } }; }); // mod1.js define(function() { console.log("I'm mod1"); return { say: function() { console.log("say: I'm mod1"); } }; }); // mod2.js define(function() { console.log("I'm mod2"); return { say: function() { console.log("say: I'm mod2"); } }; });
以上代码分别用 Require.js 和 Sea.js 执行,打印结果如下:
Require.js:
先执行所有依赖中的代码
I'm mod1 I'm mod2 I'm main say: I'm mod1 say: I'm mod2
Sea.js:
用到依赖时,再执行依赖中的代码
I'm main I'm mod1 say: I'm mod1 I'm mod2 say: I'm mod2
umd(Universal Module Definition) 是 AMD 和 CommonJS 的兼容性处理,提出了跨平台的解决方案。
(function (root, factory) { if (typeof exports === 'object') { // commonJS module.exports = factory(); } else if (typeof define === 'function' && define.amd) { // AMD define(factory); } else { // 挂载到全局 root.eventUtil = factory(); } })(this, function () { function myFunc(){}; return { foo: myFunc }; });
应用 UMD 规范的 JS 文件其实就是一个立即执行函数,通过检验 JS 环境是否支持 CommonJS 或 AMD 再进行模块化定义。
CommonJS 和 AMD 规范都只能在运行时确定依赖。而 ES6 在语言层面提出了模块化方案, ES6 module 模块编译时就能确定模块的依赖关系,以及输入和输出的变量。ES6 模块化这种加载称为“编译时加载”或者静态加载。
写法:
// math.js // 命名导出 export function add(a, b){ return a + b; } export function sub(a, b){ return a - b; } // 命名导入 import { add, sub } from "./math.js"; add(2, 3); sub(7, 2); // 默认导出 export default function foo() { console.log('foo'); } // 默认导入 import someModule from "./utils.js";
ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
另,在 webpack 对 ES Module 打包, ES Module 会编译成 require/exports 来执行的。
JS 的模块化规范经过了模块模式、CommonJS、AMD/CMD、ES6 的演进,利用现在常用的 gulp、webpack 打包工具,非常方便我们编写模块化代码。掌握这几种模块化规范的区别和联系有助于提高代码的模块化质量,比如,CommonJS 输出的是值拷贝,ES6 Module 在静态代码解析时输出只读接口,AMD 是异步加载,推崇依赖前置,CMD 是依赖就近,延迟执行,在使用到模块时才去加载相应的依赖。
以上がJavaScript モジュラー プログラミングの詳細な紹介 (コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。