Dieser Artikel bietet Ihnen eine detaillierte Einführung in die modulare JavaScript-Programmierung (Codebeispiele). Freunde in Not können darauf verweisen.
Was ist Modularität?
Ein Modul ist eine Reihe von Methoden zur Implementierung spezifischer Funktionen. Bei der Modularisierung geht es darum, einen eigenen Bereich für den Modulcode zu erstellen und nur öffentliche Methoden und Variablen nach außen zugänglich zu machen. Diese Methoden sind höchst untrennbar miteinander verbunden voneinander trennen.
Warum erfordert das Schreiben von JS eine modulare Programmierung?
Als das Front-End nur einige Formularübermittlungen und Klickinteraktionen auf der Webseite verarbeitete, wurde das Konzept der JS-Modularität nicht gestärkt. Die Front-End-Logik wurde komplexer, die Interaktionen wurden komplexer und Die Datenmenge wurde immer größer. Zu diesem Zeitpunkt ist die Nachfrage des Front-Ends nach modularer JS-Programmierung stärker geworden.
In vielen Szenarien müssen wir die Modularität berücksichtigen:
Basierend auf den oben genannten Szenarien dient die aktuelle JS-Modularisierung daher hauptsächlich diesen Zwecken:
Erste Schlussfolgerung: JS Module Die Programmierung hat mehrere Phasen durchlaufen:
Geben Sie zunächst die Schlussfolgerung:
Wir wissen, dass JS vor ES6 keinen In-Block-Bereich hatte. Die Isolierung privater Variablen und Methoden hängt hauptsächlich vom Funktionsumfang ab, während die Isolierung öffentlicher Variablen und Methoden hauptsächlich von Objektattributreferenzen abhängt.
Kapselungsfunktion
Als JS keine modularen Spezifikationen hatte, wurden einige allgemeine und zugrunde liegende Funktionen abstrahiert und in Funktionen unterteilt, um Modularität zu erreichen:
Zum Beispiel: Schreiben Sie eine utils.js-Tool-Funktionsdatei
// 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>
Durch das Teilen der js-Funktionsdatei wird die öffentliche Funktion zu diesem Zeitpunkt tatsächlich unter dem globalen Objektfenster bereitgestellt. Wenn andere auch eine definieren möchten Beim Aufrufen der Add-Funktion. oder wenn mehrere js-Dateien zusammengeführt und komprimiert werden, kommt es zu Namenskonflikten.
Unter globalen Variablen gemountet:
Später dachten wir darüber nach, Funktionen unter globalen Objektliteralen zu mounten, indem wir das Konzept von JAVA-Paketen verwendeten, in der Hoffnung, die Benennung der Schwere des Konflikts zu erleichtern.
var mathUtils1 = { add: function(x, y) { return x + y; }, } var mathUtils2 = { add: function(x, y, z) { return x + y + z; }, } mathUtils.add(); mathUtils.square();
Diese Methode erstellt weiterhin globale Variablen, aber wenn der Paketpfad lang ist, verweist die Referenzmethode möglicherweise auf den Code in der Form module1.subModule.subSubModule.add
.
IIFE
Angesichts der Tatsache, dass das Modul über private Variablen verfügt, verwenden wir IIFE (Immediate Execution Expression), um einen Abschluss zum Kapseln der privaten Variablen zu erstellen:
var module = (function(){ var count = 0; return { inc: function(){ count += 1; }, dec: function(){ count += -1; } } })() module.inc(); module.dec();
So Private Variablen sind von außen nicht zugänglich. Was ist, wenn das Modul andere Abhängigkeiten einführen muss?
var utils = (function ($) { var $body = $("body"); var _private = 0; var foo = function() { ... } var bar = function () { ... } return { foo: foo, bar: bar } })(jQuery);
Die obige Art der Kapselung von Modulen heißt: Modulmodus. In der Ära von jQuery war der Modulmodus weit verbreitet:
<script></script> <script></script> <script></script> <script></script> <script></script>
jQuery-Plug-Ins müssen nach JQuery.js stehen Datei, in der Ladereihenfolge der Dateien. Da es streng eingeschränkt ist, gilt: Je mehr Abhängigkeiten vorhanden sind, desto verwirrender sind die Abhängigkeitsbeziehungen und desto einfacher ist es, Fehler zu machen.
Das Aufkommen von Nodejs ermöglicht die Ausführung von JavaScript in einer serverseitigen Umgebung. Derzeit besteht ein dringender Bedarf, einen Standard zur Implementierung eines einheitlichen Modulsystems zu etablieren. welches später CommonJS ist.
// 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);
CommonJS legt fest, dass in jedem Modul das Modul das aktuelle Modul darstellt. Dieses Modul ist ein Objekt mit Attributen wie ID, Dateiname, geladen, übergeordnetes Element, untergeordnete Elemente, Exporte usw. Das Attribut module.exports stellt das dar Externe Ausgabe der aktuellen Modulschnittstelle, andere Dateien laden das Modul, das tatsächlich die Variable module.exports liest.
// 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();
Der Einfachheit halber stellt Node für jedes Modul eine exportfreie Variable bereit, die auf module.exports verweist. Dies entspricht einer Zeile wie dieser am Kopf jedes Moduls.
var exports = module.exports;
Exports und module.exports teilen sich dieselbe Referenzadresse. Das direkte Zuweisen eines Werts zu exports führt dazu, dass die beiden nicht mehr auf dieselbe Speicheradresse verweisen, dies hat jedoch keine Auswirkungen auf module.exports in Ende.
// module.exports 可以直接赋值 module.exports = 'Hello world'; // exports 不能直接赋值 exports = 'Hello world';
CommonJS-Zusammenfassung:
Das Lademodul der CommonJS-Spezifikation ist synchron und wird auf der Serverseite verwendet. Da CommonJS das integrierte Modul beim Start in den Speicher lädt Laden Sie auch das Modul. Die übergebenen Module werden im Speicher abgelegt. Daher wird die Verwendung des synchronen Ladens in der Node-Umgebung kein großes Problem darstellen.
Zusätzlich lädt das CommonJS-Modul eine Kopie des Ausgabewerts. Mit anderen Worten: Wenn sich der Ausgabewert des externen Moduls ändert, ändert sich der Importwert des aktuellen Moduls nicht.
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 是依赖就近,延迟执行,在使用到模块时才去加载相应的依赖。
Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in die modulare JavaScript-Programmierung (Codebeispiele). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!