JavaScript モジュラー プログラミングの詳細な紹介 (コード例)

不言
リリース: 2019-03-11 17:07:08
転載
2859 人が閲覧しました

この記事では、JavaScript モジュラー プログラミング (コード例) について詳しく説明します。これには一定の参考価値があります。必要な友人は参照できます。お役に立てば幸いです。

モジュール性とは何ですか?

モジュールとは、特定の機能を実現するためのメソッドのセットです。モジュール化とは、モジュール コードに独自のスコープを作成し、パブリック メソッドと変数のみを外部に公開することです。これらのメソッドは高度な機能を備えています。互いに取り外し可能なカップリング。

JS を記述するときにモジュール型プログラミングが必要なのはなぜですか?
フロントエンドを作成するとき、一部のフォーム送信と Web ページ上のクリック インタラクションのみを処理しました。JS モジュール性の概念は強化されていませんでした。フロントエンド ロジックは複雑になり、インタラクションはさらに多くなり、データ量はますます大きくなり、フロントエンドからは JS モジュール型プログラミングへの要求が強くなってきています。

多くのシナリオでは、モジュール性を考慮する必要があります。

  1. チーム内の多くの人が共同作業を行っており、他の人のコードを引用する必要があります
  2. プロジェクトの引き継ぎ中に、次のことが必要です。他の人のコードを読んで再構築する
  3. コードをレビューするときは、コードが標準化されているか、問題がないかを確認してください
  4. コードを書いた後、自分が書いたコードが美しいかどうかをレビューしてください。 )
  5. Different 環境と環境変数が異なります

したがって、上記のシナリオに基づいて、現在の JS モジュール化は主に次の目的を持っています:

  1. コードの再利用性
  2. 関数コードの疎結合
  3. ##名前の競合を解決
  4. ##コードの保守性
  5. ##コードの可読性
  6. ##最初に結論を述べます。 : JS モジュール プログラミングはいくつかの段階を経ます:
名前空間形式でのコードのカプセル化

即時実行機能 (IIFE) によって作成された名前空間
  1. サーバー側ランタイム Nodejs の CommonJS 仕様
  2. ブラウザ側でモジュール式に実行される AMD/CMD 仕様
  3. CMD および AMD の UMD 仕様との互換性
  4. 言語標準を通じてサポートされる ES モジュール
  5. 最初に結論を言います:


1. 名前空間

JavaScript モジュラー プログラミングの詳細な紹介 (コード例)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>
ログイン後にコピー
js関数ファイルを分割することで、このときのパブリック関数は実際にはグローバルオブジェクトウィンドウの下にマウントされますが、他の人も定義したいときはadd関数を呼び出します。または、複数の js ファイルをマージして圧縮すると、名前の競合が発生します。


グローバル変数の下にマウント:

その後、名前の競合の深刻さを軽減するために、JAVA パッケージの概念を使用して、グローバル オブジェクト リテラルの下に関数をマウントすることを考えました。

var mathUtils1 = {
    add: function(x, y) {
        return x + y;
    },
}

var mathUtils2 = {
    add: function(x, y, z) {
        return x + y + z;
    },
}

mathUtils.add();

mathUtils.square();
ログイン後にコピー
このメソッドでもグローバル変数が作成されますが、パッケージのパスが非常に長い場合、参照メソッドは最終的に module1.subModule.subSubModule.add## の方法でコードを参照する可能性があります。 # 。

IIFE

モジュールにプライベート変数があることを考慮して、IIFE (即時実行式) を使用してプライベート変数をカプセル化するクロージャを作成します。プライベート変数 外部からアクセスできません モジュールに他の依存関係を導入する必要がある場合はどうすればよいでしょうか?

var module = (function(){
    var count = 0;
    return {
        inc: function(){
            count += 1;
        },
        dec: function(){
            count += -1;
        }
    }
})()

module.inc();
module.dec();
ログイン後にコピー
モジュールをカプセル化する上記の方法は、モジュール モードと呼ばれます。jQuery の時代には、モジュール モードが広く使用されていました:

var utils = (function ($) {
    var $body = $("body"); 
    var _private = 0;
    var foo = function() {
        ...
    }
    var bar = function () {
        ...
    }
    
    return {
        foo: foo,
        bar: bar
    }
})(jQuery);
ログイン後にコピー
jQuery プラグインは、JQuery.js の後にある必要があります。厳密に制限されると、依存関係が増えるほど依存関係が混乱し、間違いが発生しやすくなります。
2. CommonJS

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();
ログイン後にコピー

exports と module.exports は同じ参照アドレスを共有します。exports に値を直接割り当てると、2 つは同じメモリ アドレスを指すことはなくなりますが、その値は次の module.exports には影響しません。終わり。

var exports = module.exports;
ログイン後にコピー

CommonJS の概要:

CommonJS 仕様のロード モジュールは同期されており、サーバー側で使用されます。CommonJS は起動時に組み込みモジュールをメモリにロードするため、モジュールをロードします。渡されたモジュールはメモリに配置されます。したがって、Node 環境で同期ロードを使用しても大きな問題はありません。

さらに、CommonJS モジュールは出力値のコピーを読み込みます。つまり、外部モジュールの出力値が変化しても、現在のモジュールのインポート値は変化しません。

三、AMD

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 的特点在于:

  1. 延迟加载
  2. 依赖前置

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();
  };
});
ログイン後にコピー

四、CMD

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

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 再进行模块化定义。

六、ES6 Module

CommonJS 和 AMD 规范都只能在运行时确定依赖。而 ES6 在语言层面提出了模块化方案, ES6 module 模块编译时就能确定模块的依赖关系,以及输入和输出的变量。ES6 模块化这种加载称为“编译时加载”或者静态加载。

JavaScript モジュラー プログラミングの詳細な紹介 (コード例)

写法:

// 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 サイトの他の関連記事を参照してください。

関連ラベル:
ソース:segmentfault.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート