Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产二区在线播放,激情五月色婷婷丁香伊人,成人精品视频一区二区在线

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          「THE LAST TIME」深入淺出 JavaSc

          「THE LAST TIME」深入淺出 JavaScript 模塊化

          The last time, I have learned

          【THE LAST TIME】一直是我想寫的一個系列,旨在厚積薄發,重溫前端。

          也是對自己的查缺補漏和技術分享。

          歡迎大家多多評論指點吐槽。

          系列文章均首發于公眾號【全棧前端精選】,筆者文章集合詳見GitHub 地址:Nealyang/personalBlog。目錄和發文順序皆為暫定

          隨著互聯網的發展,前端開發也變的越來越復雜,從一開始的表單驗證到現在動不動上千上萬行代碼的項目開發,團隊協作就是我們不可避免的工作方式,為了更好地管理功能邏輯,模塊化的概念也就漸漸產生了。

          好的書籍會分章節,好的代碼得分模塊。

          JavaScript 在早期的設計中就沒有模塊、包甚至類的概念,雖然 ES6 中有了 class 關鍵字,那也只是個語法糖。隨意隨著項目復雜度的增加,開發者必然需要模擬類的功能,來隔離、封裝、組織復雜的 JavaScript 代碼,而這種封裝和隔離,也被被我們稱之為模塊化。

          模塊就是一個實現特定功能的文件 or 代碼塊。隨著前端工程體系建設的愈發成熟,或許模塊化的概念已經在前端圈子里已經耳熟能詳了。

          但是對于很多開發者而言,ES6 中的 export、import,nodejs 中的 require、exports.xx、module.exports到底有什么區別?為什么又有 CommonJS,又有 AMD,CMD,UMD?區別是什么?甚至我們在編寫 ts 文件的時候,還需要在配置文件里面說明什么模塊方式,在項目中使用的時候,我們又是否真正知道,你用的到底是基于哪一種規范的模塊化?

          本文對你寫代碼沒有一點幫助,但是如果你還對上述的問題存有疑惑或者想了解JavaScript 模塊化的前世古今,那么我們開始吧~

          公眾號回復【xmind2】獲取源文件

          模塊化的價值

          所謂的模塊化,粗俗的講,就是把一大坨代碼,一鏟一鏟分成一個個小小坨。當然,這種分割也必須是合理的,以便于你增減或者修改功能,并且不會影響整體系統的穩定性。

          個人認為模塊化具有以下幾個好處:

          • 可維護性,每一個模塊都是獨立的。良好的設計能夠極大的降低項目的耦合度。以便于其能獨立于別的功能被整改。至少維護一個獨立的功能模塊,比維護一坨凌亂的代碼要容易很多。
          • 減少全局變量污染,前端開發的初期,我們都在為全局變量而頭疼,因為經常會觸發一些難以排查且非技術性的 bug。當一些無關的代碼一不小心重名了全局變量,我們就會遇到煩人的“命名空間污染”的問題。在模塊化規范沒有確定之前,其實我們都在極力的避免于此。(后文會介紹)
          • 可復用性,前端模塊功能的封裝,極大的提高了代碼的可復用性。這點應該就不用詳細說明了。想想從 npm 上找 package 的時候,是在干啥?
          • 方便管理依賴關系,在模塊化規范沒有完全確定的時候,模塊之間相互依賴的關系非常的模糊,完全取決于 js 文件引入的順序。粗俗!絲毫沒有技術含量,不僅依賴模糊且難以維護。

          原始模塊化

          對于某一工程作業或者行為進行定性的信息規定。主要是因為無法精準定量而形成的標準,所以,被稱為規范。在模塊化還沒有規范確定的時候,我們都稱之為原始模塊化。

          函數封裝

          回到我們剛剛說的模塊的定義,模塊就是一個實現特定功能的文件 or 代碼塊(這是我自己給定義的)。專業定義是,在程序設計中,為完成某一功能所需的一段程序或子程序;或指能由編譯程序、裝配程序等處理的獨立程序單位;或指大型軟件系統的一部分。而函數的一個功能就是實現特定邏輯的一組語句打包。并且 JavaScript 的作用域就是基于函數的。所以最原始之處,函數必然是作為模塊化的第一步。

          基本語法

          //函數1
          function fn1(){
            //statement
          }
          //函數2
          function fn2(){
            //statement
          }
          

          優點

          • 有一定的功能隔離和封裝...

          缺點

          • 污染了全局變量
          • 模塊之間的關系模糊

          對象封裝

          其實就是把變量名塞的深一點。。。

          基本語法

          let module1={
            let tag : 1,
            let name:'module1',
            
            fun1(){
              console.log('this is fun1')
            },
            
            fun2(){
              console.log('this is fun2')
            }
          }
          

          我們在使用的時候呢,就直接

          module1.fun2();
          

          優點

          • 一定程度上優化了命名沖突,降低了全局變量污染的風險
          • 有一定的模塊封裝和隔離,并且還可以進一步語義化一些

          缺點

          • 并沒有實質上改變命名沖突的問題
          • 外部可以隨意修改內部成員變量,還是容易產生意外風險

          IIFE

          IIFE 就是立即執行函數,我們可以通過匿名閉包的形式來實現模塊化

          基本語法

          let global='Hello, I am a global variable :)';
          
          (function () {
            // 在函數的作用域中下面的變量是私有的
          
            const myGrades=[93, 95, 88, 0, 55, 91];
          
            let average=function() {
              let total=myGrades.reduce(function(accumulator, item) {
                return accumulator + item}, 0);
          
              return 'Your average grade is ' + total / myGrades.length + '.';
            }
          
            let failing=function(){
              let failingGrades=myGrades.filter(function(item) {
                return item < 70;});
          
              return 'You failed ' + failingGrades.length + ' times.';
            }
          
            console.log(failing());
            console.log(global);
          }());
          
          // 控制臺顯示:'You failed 2 times.'
          // 控制臺顯示:'Hello, I am a global variable :)'
          

          這種方法的好處在于,你可以在函數內部使用局部變量,而不會意外覆蓋同名全局變量,但仍然能夠訪問到全局變量

          類似如上的 IIFE ,還有非常多的演進寫法

          比如引入依賴:

          // module.js文件
          (function(window, $) {
            let data='www.baidu.com'
            //操作數據的函數
            function foo() {
              //用于暴露有函數
              console.log(`foo() ${data}`)
              $('body').css('background', 'red')
            }
            function bar() {
              //用于暴露有函數
              console.log(`bar() ${data}`)
              otherFun() //內部調用
            }
            function otherFun() {
              //內部私有的函數
              console.log('otherFun()')
            }
            //暴露行為
            window.myModule={ foo, bar }
          })(window, jQuery)
          
           // index.html文件
            <!-- 引入的js必須有一定順序 -->
            <script type="text/javascript" src="jquery-1.10.1.js"></script>
            <script type="text/javascript" src="module.js"></script>
            <script type="text/javascript">
              myModule.foo()
            </script>
          

          還有一種所謂的揭示模塊模式 Revealing module pattern

          var myGradesCalculate=(function () {
          
             // 在函數的作用域中下面的變量是私有的
            var myGrades=[93, 95, 88, 0, 55, 91];
          
            var average=function() {
              var total=myGrades.reduce(function(accumulator, item) {
                return accumulator + item;
                }, 0);
          
              return'Your average grade is ' + total / myGrades.length + '.';
            };
          
            var failing=function() {
              var failingGrades=myGrades.filter(function(item) {
                  return item < 70;
                });
          
              return 'You failed ' + failingGrades.length + ' times.';
            };
          
            // 將公有指針指向私有方法
          
            return {
              average: average,
              failing: failing
            }
          })();
          
          myGradesCalculate.failing(); // 'You failed 2 times.' 
          myGradesCalculate.average(); // 'Your average grade is 70.33333333333333.'
          

          這和我們之前的實現方法非常相近,除了它會確保,在所有的變量和方法暴露之前都會保持私有.

          優點

          • 實現了基本的封裝
          • 只暴露對外的方法操作,有了 public 和 private 的概念

          缺點

          • 模塊依賴關系模糊

          CommonJS

          上述的所有解決方案都有一個共同點:使用單個全局變量來把所有的代碼包含在一個函數內,由此來創建私有的命名空間和閉包作用域。

          雖然每種方法都比較有效,但也都有各自的短板。

          隨著大前端時代的到來,常見的 JavaScript 模塊規范也就有了:CommonJS、AMD、CMD、UMD、ES6 原生。

          基本介紹

          CommonJS 是 JavaScript 的一個模塊化規范,主要用于服務端Nodejs 中,當然,通過轉換打包,也可以運行在瀏覽器端。畢竟服務端加載的模塊都是存放于本地磁盤中,所以加載起來比較快,不需要考慮異步方式。

          根據規范,每一個文件既是一個模塊,其內部定義的變量是屬于這個模塊的,不會污染全局變量。

          CommonJS 的核心思想是通過 require 方法來同步加載所依賴的模塊,然后通過 exports 或者 module.exprots 來導出對外暴露的接口。

          模塊定義

          CommonJS 的規范說明,一個單獨的文件就是一個模塊,也就是一個單獨的作用域。并且模塊只有一個出口,module.exports/exports.xxx

          // lib/math.js
          const NAME='Nealayng';
          module.exports.author=NAME;
          module.exports.add=(a,b)=> a+b;
          

          加載模塊

          加載模塊使用 require 方法,該方法讀取文件并且執行,返回文件中 module.exports 對象

          // main.js
          const mathLib=require('./lib/math');
          
          console.log(mathLib.author);//Nealyang
          console.log(mathLib.add(1,2));// 3
          

          在瀏覽器中使用 CommonJS

          由于瀏覽器不支持 CommonJS 規范,因為其根本沒有 module、exports、require 等變量,如果要使用,則必須轉換格式。Browserify是目前最常用的CommonJS格式轉換的工具,我們可以通過安裝browserify來對其進行轉換.但是我們仍然需要注意,由于 CommonJS 的規范是阻塞式加載,并且模塊文件存放在服務器端,可能會出現假死的等待狀態。

          npm i browserify -g
          

          然后使用如下命令

          browserify main.js -o js/bundle/main.js
          

          然后在 HTML 中引入使用即可。

          有一說一,在瀏覽器中使用 CommonJS 的規范去加載模塊,真的不是很方便。如果一定要使用,我們可以使用browserify編譯打包,也可以使用require1k,直接在瀏覽器上運行即可。

          特點

          • 以文件為一個單元模塊,代碼運行在模塊作用域內,不會污染全局變量
          • 同步加載模塊,在服務端直接讀取本地磁盤沒問題,不太適用于瀏覽器
          • 模塊可以加載多次,但是只會在第一次加載時運行,然后在加載,就是讀取的緩存文件。需清理緩存后才可再次讀取文件內容
          • 模塊加載的順序,按照其在代碼中出現的順序
          • 導出的是值的拷貝,這一點和 ES6 有著很大的不同(后面會介紹到)

          補充知識點

          其實在 nodejs 中模塊的實現并非完全按照 CommonJS 的規范來的,而是進行了取舍。

          Node 中,一個文件是一個模塊->module

          源碼定義如下:

          function Module(id='', parent) {
            this.id=id;
            this.path=path.dirname(id);
            this.exports={};
            this.parent=parent;
            updateChildren(parent, this, false);
            this.filename=null;
            this.loaded=false;
            this.children=[];
          }
          
          //實例化一個模塊
          var module=new Module(filename, parent);
          

          CommonJS 的一個模塊,就是一個腳本文件。require命令第一次加載該腳本,就會執行整個腳本,然后在內存生成一個對象。

          {
            id: '...',
            exports: { ... },
            loaded: true,
            ...
          }
          

          上面代碼就是 Node 內部加載模塊后生成的一個對象。該對象的id屬性是模塊名,exports屬性是模塊輸出的各個接口,loaded屬性是一個布爾值,表示該模塊的腳本是否執行完畢。其他還有很多屬性,這里都省略不介紹了。

          以后需要用到這個模塊的時候,就會到exports屬性上面取值。即使再次執行require命令,也不會再次執行該模塊,而是到緩存之中取值。也就是說,CommonJS 模塊無論加載多少次,都只會在第一次加載時運行一次,以后再加載,就返回第一次運行的結果,除非手動清除系統緩存。

          再去深究具體的實現細節。。那就。。。下一篇分享吧~

          AMD

          Asynchronous Module Definition:異步模塊定義。

          也就是解決我們上面說的 CommonJS 在瀏覽器端致命的問題:假死。

          介紹

          CommonJS規范加載模塊是同步的,也就是說,只有加載完成,才能執行后面的操作。AMD規范則是異步加載模塊,允許指定回調函數。

          由于其并非原生 js 所支持的那種寫法。所以使用 AMD 規范開發的時候就需要大名鼎鼎的函數庫 require.js 的支持了。

          require.js

          https://github.com/requirejs/requirejs

          關于 require.js 的更詳細使用說明可以參考官網 api:https://requirejs.org/docs/api.html

          require.js 主要解決兩個問題:

          • 異步加載模塊
          • 模塊之間依賴模糊

          定義模塊

          define(id,[dependence],callback)
          
          • id,一個可選參數,說白了就是給模塊取個名字,但是卻是模塊的唯一標識。如果沒有提供則取腳本的文件名
          • dependence,以來的模塊數組
          • callback,工廠方法,模塊初始化的一些操作。如果是函數,應該只被執行一次。如果是對象,則為模塊的輸出值

          使用模塊

          require([moduleName],callback);
          
          • moduleName,以來的模塊數組
          • callback,即為依賴模塊加載成功之后執行的回調函數(前端異步的通用解決方案),

          data-main

          <script src="scripts/require.js" data-main="scripts/app.js"></script>
          

          data-main 指定入口文件,比如這里指定 scripts 下的 app.js 文件,那么只有直接或者間接與app.js有依賴關系的模塊才會被插入到html中。

          require.config

          通過這個函數可以對requirejs進行靈活的配置,其參數為一個配置對象,配置項及含義如下:

          • baseUrl——用于加載模塊的根路徑。
          • paths——用于映射不存在根路徑下面的模塊路徑。
          • shims——配置在腳本/模塊外面并沒有使用RequireJS的函數依賴并且初始化函數。假設underscore并沒有使用 RequireJS定義,但是你還是想通過RequireJS來使用它,那么你就需要在配置中把它定義為一個shim
          • deps——加載依賴關系數組
          require.config({
          //默認情況下從這個文件開始拉去取資源
              baseUrl:'scripts/app',
          //如果你的依賴模塊以pb頭,會從scripts/pb加載模塊。
              paths:{
                  pb:'../pb'
              },
          // load backbone as a shim,所謂就是將沒有采用requirejs方式定義
          //模塊的東西轉變為requirejs模塊
              shim:{
                  'backbone':{
                      deps:['underscore'],
                      exports:'Backbone'
                  }
              }
          });
          

          Demo 演示

          • 創建項目
          |-js
            |-libs
              |-require.js
            |-modules
              |-article.js
              |-user.js
            |-main.js
          |-index.html
          
          • 定義模塊
          // user.js文件
          // 定義沒有依賴的模塊
          define(function() {
            let author='Nealyang'
            function getAuthor() {
              return author.toUpperCase()
            }
            return { getAuthor } // 暴露模塊
          })
          
          
          //article.js文件
          // 定義有依賴的模塊
          define(['user'], function(user) {
            let name='THE LAST TIME'
            function consoleMsg() {
              console.log(`${name} by ${user.getAuthor()}`);
            }
            // 暴露模塊
            return { consoleMsg }
          })
          
          // main.js
          (function() {
            require.config({
              baseUrl: 'js/', //基本路徑 出發點在根目錄下
              paths: {
                //映射: 模塊標識名: 路徑
                article: './modules/article', //此處不能寫成article.js,會報錯
                user: './modules/user'
              }
            })
            require(['article'], function(alerter) {
              article.consoleMsg()
            })
          })()
          
          // index.html文件
          <!DOCTYPE html>
          <html>
            <head>
              <title>Modular Demo</title>
            </head>
            <body>
              <!-- 引入require.js并指定js主文件的入口 -->
              <script data-main="js/main" src="js/libs/require.js"></script>
            </body>
          </html>
          

          如果我們需要引入第三方庫,則需要在 main.js 文件中引入

          (function() {
            require.config({
              baseUrl: 'js/',
              paths: {
                article: './modules/article',
                user: './modules/user',
                // 第三方庫模塊
                jquery: './libs/jquery-1.10.1' //注意:寫成jQuery會報錯
              }
            })
            require(['article'], function(alerter) {
              article.consoleMsg()
            })
          })()
          

          特點

          • 異步加載模塊,不會造成因網絡問題而出現的假死裝填
          • 顯式地列出其依賴關系,并以函數(定義此模塊的那個函數)參數的形式將這些依賴進行注入
          • 在模塊開始時,加載所有所需依賴

          關于 require.js 的使用,仔細看文檔,其實還是有很多知識點的。但是鑒于我們著實現在使用不多(我也不熟),所以這里也就參考網上優秀文章和自己實踐,拋磚引玉。

          CMD

          基本介紹

          CMD是阿里的玉伯提出來的(大神的成長故事可在公眾號回復【大佬】),js 的函數為 sea.js,它和 AMD 其實非常的相似,文件即為模塊,但是其最主要的區別是實現了按需加載。推崇依賴就近的原則,模塊延遲執行,而 AMD 所依賴模塊式提前執行(requireJS 2.0 后也改為了延遲執行)

          //AMD
          define(['./a','./b'], function (a, b) {
          
            //依賴一開始就寫好
            a.test();
            b.test();
          });
            
          //CMD
          define(function (requie, exports, module) {
            
            //依賴可以就近書寫
            var a=require('./a');
            a.test();
            
            ...
            //按需加載
            if (status) {
              var b=requie('./b');
              b.test();
            }
          });
          

          SeaJs

          https://github.com/seajs/seajs

          https://seajs.github.io/seajs/docs/

          準確的說 CMD 是 SeaJS 在推廣過程中對模塊定義的規范化產物。

          也可以說SeaJS 是一個遵循 CMD 規范的 JavaScript 模塊加載框架,可以實現 JavaScript 的 CMD 模塊化開發方式。

          SeaJS 只是實現 JavaScript的模塊化和按需加載,并未擴展 JavaScript 語言本身。SeaJS 的主要目的是讓開發人員更加專注于代碼本身,從繁重的 JavaScript 文件以及對象依賴處理中解放出來。

          毫不夸張的說,我們現在詳情頁就是 SeaJS+Kissy。。。(即將升級)

          Seajs 追求簡單、自然的代碼書寫和組織方式,具有如下核心特性:

          • 簡單友好的模塊定義規范:Sea.js 遵循 CMD 規范,可以像 Node.js 一般書寫模塊代碼。
          • 自然直觀的代碼組織方式:依賴的自動加載、配置的簡潔清晰,可以讓我們更多地享受編碼的樂趣。

          Sea.js 還提供常用插件,非常有助于開發調試和性能優化,并具有豐富的可擴展接口。

          Demo 演示

          examples/
            |-- sea-modules      存放 seajs、jquery 等文件,這也是模塊的部署目錄
            |-- static           存放各個項目的 js、css 文件
            |     |-- hello
            |     |-- lucky
            |     `-- todo
            `-- app              存放 html 等文件
                  |-- hello.html
                  |-- lucky.html
                  `-- todo.html
          

          我們從 hello.html 入手,來瞧瞧使用 Sea.js 如何組織代碼。

          在 hello.html 頁尾,通過 script 引入 sea.js 后,有一段配置代碼

          
          // seajs 的簡單配置
          seajs.config({
            base: "../sea-modules/",
            alias: {
              "jquery": "jquery/jquery/1.10.1/jquery.js"
            }
          })
          
          // 加載入口模塊
          seajs.use("../static/hello/src/main")
          
          

          sea.js 在下載完成后,會自動加載入口模塊。頁面中的代碼就這么簡單。

          這個小游戲有兩個模塊 spinning.js 和 main.js,遵循統一的寫法:

          // 所有模塊都通過 define 來定義
          define(function(require, exports, module) {
          
            // 通過 require 引入依賴
            var $=require('jquery');
            var Spinning=require('./spinning');
          
            // 通過 exports 對外提供接口
            exports.doSomething=...
          
            // 或者通過 module.exports 提供整個接口
            module.exports=...
          
          });
          

          上面就是 Sea.js 推薦的 CMD 模塊書寫格式。如果你有使用過 Node.js,一切都很自然。

          以上實例,來源于官網 Example。更多 Demo 查看:https://github.com/seajs/examples

          特點

          • 相對自然的依賴聲明風格,且社區不錯
          • 文件即模塊
          • 模塊按需加載。
          • 推崇依賴就近的原則,模塊延遲執行

          UMD

          UMD 其實我個人還是覺得非常。。。。不喜歡的。ifElse 就 universal 了。。。。

          基本介紹

          UMD 是 AMD 和 CommonJS 的綜合產物。如上所說,AMD 的用武之地是瀏覽器,非阻塞式加載。CommonJS 主要用于服務端 Nodejs 中使用。所以人們就想到了一個通用的模式 UMD(universal module definition)。來解決跨平臺的問題。

          沒錯!就是 ifElse 的寫法。

          核心思想就是:先判斷是否支持Node.js的模塊(exports)是否存在,存在則使用Node.js模塊模式。

          在判斷是否支持AMD(define是否存在),存在則使用AMD方式加載模塊。

          常規用法

          (function (window, factory) {
              if (typeof exports==='object') {
               
                  module.exports=factory();
              } else if (typeof define==='function' && define.amd) {
               
                  define(factory);
              } else {
               
                  window.eventUtil=factory();
              }
          })(this, function () {
              //module ...
          });
          

          關于 UMD 更多的example 可移步github:https://github.com/umdjs/umd

          ES6

          如果你一直讀到現在,那么恭喜你,我們開始介紹我們最新的模塊化了!

          通過上面的介紹我們知道,要么模塊化依賴環境,要么需要引入額外的類庫。說到底就是社區找到的一種妥協方案然后得到了大家的認可。但是歸根結底不是官方呀。終于,ECMAScript 官宣了模塊化的支持,真正的規范

          基本介紹

          在ES6中,我們可以使用 import 關鍵字引入模塊,通過 export 關鍵字導出模塊,功能較之于前幾個方案更為強大,也是我們所推崇的,但是由于ES6目前無法在所有瀏覽器中執行,所以,我們還需通過babel將不被支持的import編譯為當前受到廣泛支持的 require。

          ES6 的模塊化汲取了 CommonJS 和AMD 的優點,擁有簡潔的語法和異步的支持。并且寫法也和 CommonJS 非常的相似。

          關于 ES6 模塊的基本用法相比大家都比較熟悉了。這里我們主要和 CommonJS 對比學習。

          與 CommonJS 的差異

          兩大差異:

          • CommonJS 模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用
          • CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口

          值拷貝&值引用

          // lib/counter.js
          
          var counter=1;
          
          function increment() {
            counter++;
          }
          
          function decrement() {
            counter--;
          }
          
          module.exports={
            counter: counter,
            increment: increment,
            decrement: decrement
          };
          
          
          // src/main.js
          
          var counter=require('../../lib/counter');
          
          counter.increment();
          console.log(counter.counter); // 1
          

          在 main.js 當中的實例是和原本模塊完全不相干的。這也就解釋了為什么調用了 counter.increment() 之后仍然返回1。因為我們引入的 counter 變量和模塊里的是兩個不同的實例。

          所以調用 counter.increment() 方法只會改變模塊中的 counter .想要修改引入的 counter 只有手動一下啦:

          counter.counter++;
          console.log(counter.counter); // 2
          

          而通過 import 語句,可以引入實時只讀的模塊:

          // lib/counter.js
          export let counter=1;
          
          export function increment() {
            counter++;
          }
          
          export function decrement() {
            counter--;
          }
          
          
          // src/main.js
          import * as counter from '../../counter';
          
          console.log(counter.counter); // 1
          counter.increment();
          console.log(counter.counter); // 2
          

          加載 & 編譯

          因為 CommonJS 加載的是一個對象(module.exports),對象只有在有腳本運行的時候才能生成。而 ES6 模塊不是一個對象,只是一個靜態的定義。在代碼解析階段就會生成。

          ES6 模塊是編譯時輸出接口,因此有如下2個特點:

          • import 命令會被 JS 引擎靜態分析,優先于模塊內的其他內容執行
          • export 命令會有變量聲明提升的效果,所以import 和 export 命令在模塊中的位置并不影響程序的輸出。
          // a.js
          console.log('a.js')
          import { foo } from './b';
          
          // b.js
          export let foo=1;
          console.log('b.js 先執行');
          
          // 執行結果:
          // b.js 先執行
          // a.js
          
          // a.js
          import { foo } from './b';
          console.log('a.js');
          export const bar=1;
          export const bar2=()=> {
            console.log('bar2');
          }
          export function bar3() {
            console.log('bar3');
          }
          
          // b.js
          export let foo=1;
          import * as a from './a';
          console.log(a);
          
          // 執行結果:
          // { bar: undefined, bar2: undefined, bar3: [Function: bar3] }
          // a.js
          

          循環加載的差異

          “循環加載”(circular dependency)指的是,a腳本的執行依賴b腳本,而b腳本的執行又依賴a腳本。

          // a.js
          var b=require('b');
          
          // b.js
          var a=require('a');
          

          循環加載如果處理不好,還可能導致遞歸加載,使得程序無法執行,因此應該避免出現。

          在 CommonJS 中,腳本代碼在 require 的時候,就會全部執行。一旦出現某個模塊被"循環加載",就只輸出已經執行的部分,還未執行的部分不會輸出

          // a.js
          exports.done=false;
          var b=require('./b.js');
          console.log('在 a.js 之中,b.done=%j', b.done);
          exports.done=true;
          console.log('a.js 執行完畢');
          
          // b.js
          exports.done=false;
          var a=require('./a.js');
          console.log('在 b.js 之中,a.done=%j', a.done);
          exports.done=true;
          console.log('b.js 執行完畢');
          
          // main.js
          var a=require('./a.js');
          var b=require('./b.js');
          console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done);
          

          輸出結果為:

          在 b.js 之中,a.done=false
          b.js 執行完畢
          在 a.js 之中,b.done=true
          a.js 執行完畢
          在 main.js 之中, a.done=true, b.done=true
          

          從上面我們可以看出:

          • 在b.js之中,a.js沒有執行完畢,只執行了第一行。
          • main.js執行到第二行時,不會再次執行b.js,而是輸出緩存的b.js的執行結果,即它的第四行

          ES6 處理“循環加載”與 CommonJS 有本質的不同**。ES6 模塊是動態引用**,如果使用import從一個模塊加載變量(即import foo from 'foo'),那些變量不會被緩存,而是成為一個指向被加載模塊的引用,需要開發者自己保證,真正取值的時候能夠取到值。

          // a.mjs
          import {bar} from './b';
          console.log('a.mjs');
          console.log(bar);
          export let foo='foo';
          
          // b.mjs
          import {foo} from './a';
          console.log('b.mjs');
          console.log(foo);
          export let bar='bar';
          

          運行結果如下:

          b.mjs
          ReferenceError: foo is not defined
          

          上面代碼中,執行a.mjs以后會報錯,foo變量未定義.

          具體的執行結果如下:

          • 執行a.mjs以后,引擎發現它加載了b.mjs,因此會優先執行b.mjs,然后再執行a.mjs
          • 執行b.mjs的時候,已知它從a.mjs輸入了foo接口,這時不會去執行a.mjs,而是認為這個接口已經存在了,繼續往下執行。
          • 執行到第三行console.log(foo)的時候,才發現這個接口根本沒定義,因此報錯。

          解決這個問題的方法,就是讓b.mjs運行的時候,foo已經有定義了。這可以通過將foo寫成函數來解決。

          // a.mjs
          import {bar} from './b';
          console.log('a.mjs');
          console.log(bar());
          function foo() { return 'foo' }
          export {foo};
          
          // b.mjs
          import {foo} from './a';
          console.log('b.mjs');
          console.log(foo());
          function bar() { return 'bar' }
          export {bar};
          

          最后執行結果為:

          b.mjs
          foo
          a.mjs
          bar
          

          特點

          • 每一個模塊加載多次, JS只執行一次, 如果下次再去加載同目錄下同文件,直接從內存中讀取。 一個模塊就是一個單例,或者說就是一個對象
          • 代碼是在模塊作用域之中運行,而不是在全局作用域運行。模塊內部的頂層變量,外部不可見。不會污染全局作用域;
          • 模塊腳本自動采用嚴格模式,不管有沒有聲明use strict
          • 模塊之中,可以使用import命令加載其他模塊(.js后綴不可省略,需要提供絕對 URL 或相對 URL),也可以使用export命令輸出對外接口
          • 模塊之中,頂層的this關鍵字返回undefined,而不是指向window。也就是說,在模塊頂層使用this關鍵字,是無意義的

          關于 ES6 詳細的模塊的介紹,強烈推薦阮一峰的 ES6 入門和深入理解 ES6 一書

          參考文獻

          • 30分鐘學會前端模塊化開發
          • 阮一峰 ES6
          • 前端模塊化詳解(完整版)
          • 理解CommonJS、AMD、CMD三種規范
          • 前端模塊化開發那點歷史
          • JavaScript 模塊化入門Ⅰ:理解模塊
          • 前端模塊化開發的價值

          學習交流

          • 關注公眾號【全棧前端精選】,每日獲取好文推薦
          • 添加微信號:is_Nealyang(備注來源) ,入群交流

          公眾號【全棧前端精選】 個人微信【is_Nealyang】

          頁切圖過程中div+css命名規則


          標簽屬性命名規范 (建議)

          下劃線連接符命名法“hello_world”

          中杠 連接符命名法“hello-world”

          駱駝式命名法“helloWorld”


            內容:content/container 導航:nav 側欄:sidebar    
            欄目:column 標志:logo 頁面主體:main   
            廣告:banner 熱點:hot 新聞:news
            下載:download 子導航:subnav 菜單:menu
            搜索:search 頁腳:footer 滾動:scroll
            版權:copyright 友情鏈接:friendlink 子菜單:submenu
            內容:content 標簽頁:tab 文章列表:list
            注冊:regsiter 提示信息:msg 小技巧:tips
            加入:joinus 欄目標題:title 指南:guild
            服務:service 狀態:status 投票:vote
             尾:footer 合作伙伴:partner 登錄條:loginbar
            頁面外圍控制整體布局寬度:wrapper 左右中:left right center   
            
            
          (二)注釋的寫法:
            /* Footer */
            內容區
            /* End Footer */
            
            
          (三)id(具有唯一性)的命名:
            
            
          (1)頁面結構
            容器: container 頁頭:header 內容:content/container
            頁面主體:main 頁尾:footer 導航:nav
            側欄:sidebar 欄目:column 左右中:left right center
            頁面外圍控制整體布局寬度:wrapper
            
          (2)導航
            導航:nav
            主導航:mainbav
            子導航:subnav
            頂導航:topnav
            邊導航:sidebar
            左導航:leftsidebar
            右導航:rightsidebar
            菜單:menu 子菜單:submenu 標題: title 摘要: summary
            
            
          (3)功能
            標志:logo
            廣告:banner
            登陸:login
            登錄條:loginbar
            注冊:regsiter
            搜索:search
            功能區:shop
            標題:title
            加入:joinus
            狀態:status
            按鈕:btn
            滾動:scroll
            標簽頁:tab
            文章列表:list
            提示信息:msg
            當前的: current
            小技巧:tips
            圖標: icon
            注釋:note
            指南:guild
            服務:service
            熱點:hot
            新聞:news
            下載:download
            投票:vote
            合作伙伴:partner
            友情鏈接:link
            版權:copyright
            
            
          (四)class的命名:
            (1)顏色:使用顏色的名稱或者16進制代碼,如
            .red { color: red; }
            .f60 { color: #f60; }
            .ff8600 { color: #ff8600; }
            (2)字體大小,直接使用"font+字體大小"作為名稱,如
            .font12px { font-size: 12px; }
            .font9pt {font-size: 9pt; }
            (3)對齊樣式,使用對齊目標的英文名稱,如
            .left { float:left; }
            .bottom { float:bottom; }
            (4)標題欄樣式,使用"類別+功能"的方式命名,如
            .barnews { }
            .barproduct { }
            
            
          注意事項:
            1.一律小寫;
            2.盡量用英文;
            3.不加中杠和下劃線;
          (我倒是經常加)
            4.盡量不縮寫,除非一看就明白的單詞.
          (偷懶經常縮寫)
            主要的 master.css 模塊 module.css 基本共用 base.css
            主題 themes.css 專欄 columns.css 打印 print.css
            文字 font.css 表單 forms.css 補丁 mend.css
            布局,版面 layout.css

          者:Jiawei Pan

          轉發鏈接:https://mp.weixin.qq.com/s/hDdF8rni6MX4U55l3DKLQw


          主站蜘蛛池模板: 无码视频一区二区三区在线观看| 一区二区三区在线视频播放| 波多野结衣免费一区视频| 日韩成人无码一区二区三区| 国产一区二区视频免费| 国产成人无码精品一区在线观看| 区三区激情福利综合中文字幕在线一区| 亚洲性日韩精品国产一区二区| 日韩制服国产精品一区| 亚洲AV无码一区二区乱孑伦AS | 国产成人高清亚洲一区91| 青娱乐国产官网极品一区| 亚洲综合国产一区二区三区| 久久久久人妻一区二区三区vr| 精品人妻无码一区二区色欲产成人| 国产精品视频一区麻豆| 日本一区二区三区不卡视频中文字幕| 久久无码精品一区二区三区| 日韩精品一区二区亚洲AV观看| 成人区人妻精品一区二区三区| 丝袜人妻一区二区三区网站| 91国偷自产一区二区三区| 国产主播福利精品一区二区| 日韩制服国产精品一区| 日本免费一区尤物| 91精品一区二区三区久久久久 | 伊人久久精品一区二区三区| 日韩少妇无码一区二区三区| 91一区二区视频| 亚洲一区无码精品色| 一本一道波多野结衣一区| 国产精华液一区二区区别大吗| 在线观看一区二区三区视频| 久久久久99人妻一区二区三区| 日本香蕉一区二区三区| 国产福利一区二区三区在线观看| 上原亚衣一区二区在线观看| 综合一区自拍亚洲综合图区| 精品福利一区二区三区免费视频| 国产午夜精品一区二区三区小说| 相泽南亚洲一区二区在线播放|