<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## 1.5 作用域閉包 當函數可以**記住并訪問**所在的詞法作用域時,就產生了閉包,即使函數是在當前詞法作用域之外執行。 ~~~ function foo() { var a = 2; function bar() { console.log( a ); } return bar; } var baz = foo(); baz(); // 2 ~~~ 無論使用何種方式對函數類型的值進行傳遞,當函數在別處被調用時都可以觀察到閉包。 ~~~ function foo() { var a = 2; function baz() { console.log( a ); // 2 } bar( baz ); } function bar(fn) { fn(); // 這就是閉包! } ~~~ 把內部函數baz 傳遞給bar,當調用這個內部函數時(現在叫作fn),它涵蓋的foo() 內部作用域的閉包就可以觀察到了,因為它能夠訪問a。 傳遞函數當然也可以是間接的。 ~~~ var fn; function foo() { var a = 2; function baz() { console.log( a ); } fn = baz; // 將baz 分配給全局變量 } function bar() { fn(); //這就是閉包! } foo(); bar(); // 2 ~~~ 無論通過何種手段將內部函數傳遞到所在的詞法作用域以外,它都會持有對原始定義作用域的引用,無論在何處執行這個函數都會使用閉包。 ### 1.5.1 深入理解 ~~~ function wait(message) { setTimeout( function timer() { console.log( message ); }, 1000 ); } wait( "Hello, closure!" ); ~~~ 將一個內部函數(名為timer)傳遞給setTimeout(..)。timer 具有涵蓋wait(..) 作用域的閉包,因此還保有對變量message 的引用。 wait(..) 執行1000 毫秒后,它的內部作用域并不會消失,timer 函數依然保有wait(..)作用域的閉包。 **本質上無論何時何地,如果將函數(訪問它們各自的詞法作用域)當作第一級的值類型并到處傳遞,你就會看到閉包在這些函數中的應用。在定時器、事件監聽器、Ajax 請求、跨窗口通信、Web Workers 或者任何其他的異步(或者同步)任務中,只要使用了回調函數,實際上就是在使用閉包!** 通常認為IIFE 是典型的閉包例子,但根據先前對閉包的定義,這不是嚴格意義上的閉包。 ~~~ var a = 2; (function IIFE() { console.log( a ); })(); ~~~ 雖然這段代碼可以正常工作,但嚴格來講它并不是閉包。為什么?因為函數(示例代碼中的IIFE)并不是在它本身的詞法作用域以外執行的。它在定義時所在的作用域中執行(而外部作用域,也就是全局作用域也持有a)。a 是通過普通的詞法作用域查找而非閉包被發現的。 ### 1.5.2 循環與閉包 要說明閉包,for 循環是最常見的例子。 ~~~ for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); } ~~~ 正常情況下,我們對這段代碼行為的預期是分別輸出數字1~5,每秒一次,每次一個。但實際上,這段代碼在運行時會以每秒一次的頻率輸出五次6。 改進: ~~~ for (var i=1; i<=5; i++) { (function(j) { setTimeout( function timer() { console.log( j ); }, j*1000 ); })( i ); } ~~~ IIFE 也不過就是函數,因此我們可以將i 傳遞進去,如果愿意的話可以將變量名定為j,當然也可以還叫作i。無論如何這段代碼現在可以工作了。 在迭代內使用IIFE 會為每個迭代都生成一個新的作用域,使得延遲函數的回調可以將新的作用域封閉在每個迭代內部,每個迭代中都會含有一個具有正確值的變量供我們訪問。 #### 重返塊作用域 1.3節中說過,let 聲明,可以用來劫持塊作用域,并且在這個塊作用域中聲明一個變量。 本質上這是將一個塊轉換成一個可以被關閉的作用域。 ~~~ for (var i=1; i<=5; i++) { let j = i; // 是的,閉包的塊作用域! setTimeout( function timer() { console.log( j ); }, j*1000 ); } ~~~ 上面代碼還不完美,進一步改進 ~~~ for (let i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); } ~~~ for 循環頭部的let 聲明還會有一個特殊的行為。這個行為指出變量在循環過程中不止被聲明一次,每次迭代都會聲明。隨 后的每個迭代都會使用上一個迭代結束時的值來初始化這個變量。 ### 1.5.3 模塊 考慮以下代碼: ~~~ function CoolModule() { var something = "cool"; var another = [1, 2, 3]; function doSomething() { console.log( something ); } function doAnother() { console.log( another.join( " ! " ) ); } return { doSomething: doSomething, doAnother: doAnother }; } var foo = CoolModule(); foo.doSomething(); // cool foo.doAnother(); // 1 ! 2 ! 3 ~~~ 這個模式在JavaScript 中被稱為模塊。最常見的實現模塊模式的方法通常被稱為模塊暴露,這里展示的是其變體。 **模塊模式需要具備兩個必要條件:** * 必須有外部的封閉函數,該函數必須至少被調用一次(每次調用都會創建一個新的模塊實例)。 * 封閉函數必須返回至少一個內部函數,這樣內部函數才能在私有作用域中形成閉包,并且可以訪問或者修改私有的狀態。 一個具有函數屬性的對象本身并不是真正的模塊。一個從函數調用所返回的,只有數據屬性而沒有閉包函數的對象也并不是真正的模塊。 模塊也是普通的函數,因此可以接受參數: ~~~ function CoolModule(id) { function identify() { console.log( id ); } return { identify: identify }; } var foo1 = CoolModule( "foo 1" ); var foo2 = CoolModule( "foo 2" ); foo1.identify(); // "foo 1" foo2.identify(); // "foo 2" ~~~ 模塊模式另一個簡單但強大的變化用法是,命名將要作為公共API 返回的對象: ~~~ var foo = (function CoolModule(id) { function change() { // 修改公共API publicAPI.identify = identify2; } function identify1() { console.log(id); } function identify2() { console.log(id.toUpperCase()); } var publicAPI = { change: change, identify: identify1 }; return publicAPI; })("foo module"); foo.identify(); // foo module foo.change(); foo.identify(); // FOO MODULE ~~~ 通過在模塊實例的內部保留對公共API 對象的內部引用,可以從內部對模塊實例進行修改,包括添加或刪除方法和屬性,以及修改它們的值。 **1. 現代的模塊機制** 大多數模塊依賴加載器/ 管理器本質上都是將這種模塊定義封裝進一個友好的API。 ~~~ var MyModules = (function Manager() { var modules = {}; function define(name, deps, impl) { for (var i = 0; i < deps.length; i++) { deps[i] = modules[deps[i]]; } modules[name] = impl.apply(impl, deps); } function get(name) { return modules[name]; } return { define: define, get: get }; })(); ~~~ 這段代碼的核心是modules[name] = impl.apply(impl, deps)。為了模塊的定義引入了包裝函數(可以傳入任何依賴),并且將返回值,也就是模塊的API,儲存在一個根據名字來管理的模塊列表中。 下面展示了如何使用它來定義模塊: ~~~ MyModules.define("bar", [], function () { function hello(who) { return "Let me introduce: " + who; } return { hello: hello }; }); MyModules.define("foo", ["bar"], function (bar) { var hungry = "hippo"; function awesome() { console.log(bar.hello(hungry).toUpperCase()); } return { awesome: awesome }; }); var bar = MyModules.get("bar"); var foo = MyModules.get("foo"); console.log( bar.hello("hippo") ); // Let me introduce: hippo foo.awesome(); // LET ME INTRODUCE: HIPPO ~~~ "foo" 和"bar" 模塊都是通過一個返回公共API 的函數來定義的。"foo" 甚至接受"bar" 的示例作為依賴參數,并能相應地使用它。 **2. 未來的模塊機制** ES6 中為模塊增加了一級語法支持。但通過模塊系統進行加載時,ES6 會將文件當作獨立的模塊來處理。每個模塊都可以導入其他模塊或特定的API 成員,同樣也可以導出自己的API 成員。 基于函數的模塊并不是一個能被穩定識別的模式(編譯器無法識別),它們的API 語義只有在運行時才會被考慮進來。因此可以在運行時修改一個模塊的API(參考前面關于公共API 的討論)。 相比之下,ES6 模塊API 更加穩定(API 不會在運行時改變)。由于編輯器知道這一點,因此可以在(的確也這樣做了)編譯期檢查對導入模塊的API 成員的引用是否真實存在。如果API 引用并不存在,編譯器會在運行時拋出一個或多個“早期”錯誤,而不會像往常一樣在運行期采用動態的解決方案。 ES6 的模塊沒有“行內”格式,必須被定義在獨立的文件中(一個文件一個模塊)。瀏覽器或引擎有一個默認的“模塊加載器”(可以被重載)可以在導入模塊時異步地加載模塊文件。 考慮以下代碼: ~~~ bar.js ---------------------------- function hello(who) { return "Let me introduce: " + who; } export hello; foo.js ---------------------------- // 僅從"bar" 模塊導入hello() import hello from "bar"; var hungry = "hippo"; function awesome() { console.log( hello(hungry).toUpperCase() ); } export awesome; baz.js ---------------------------- // 導入完整的"foo" 和"bar" 模塊 module foo from "foo"; module bar from "bar"; console.log( bar.hello("rhino") ); // Let me introduce: rhino foo.awesome(); // LET ME INTRODUCE: HIPPO ~~~ 需要用前面兩個代碼片段中的內容分別創建文件foo.js 和bar.js。然后如第三個代碼片段中展示的那樣,bar.js 中的程序會加載或導入這兩個模塊并使用它們。 * `import `可以將一個模塊中的一個或多個API 導入到當前作用域中,并分別綁定在一個變量上(例子里是hello)。 * `module `會將整個模塊的API 導入并綁定到一個變量上(在我們的例子里是foo 和bar)。 * `export `會將當前模塊的一個標識符(變量、函數)導出為公共API。這些操作可以在模塊定義中根據需要使用任意多次。 模塊文件中的內容會被當作好像包含在作用域閉包中一樣來處理,就和前面的函數閉包模塊一樣。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看