<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>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ![](https://img.kancloud.cn/39/6e/396eb6fba29c47803f3524c342d288b3_469x314.png) ![](https://img.kancloud.cn/eb/4e/eb4e42959777480fdab8a4a3a591293f_683x129.png) ![](https://img.kancloud.cn/08/c2/08c2db58fa6d69facbfb50727b0901d8_616x417.png) ![](https://img.kancloud.cn/f4/83/f4831954354850936d1619e16f73fb5d_721x269.png) ![](https://img.kancloud.cn/3d/c3/3dc39a93cc5bb4df6ac7eb62721e935a_844x292.png) ![](https://img.kancloud.cn/26/25/2625d02a2ae6f426b45e4d7de6aef645_763x226.png) 作用域和閉包是前端面試中,最可能考查的知識點。例如下面的題目: > 題目:現在有個 HTML 片段,要求編寫代碼,點擊編號為幾的鏈接就`alert`彈出其編號 ~~~ <ul> <li>編號1,點擊我請彈出1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> ~~~ 一般不知道這個題目用閉包的話,會寫出下面的代碼: ~~~ var list = document.getElementsByTagName('li'); for (var i = 0; i < list.length; i++) { list[i].addEventListener('click', function(){ alert(i + 1) }, true) } ~~~ 實際上執行才會發現始終彈出的是`6`,這時候就應該通過閉包來解決: ~~~ var list = document.getElementsByTagName('li'); for (var i = 0; i < list.length; i++) { list[i].addEventListener('click', function(i){ return function(){ alert(i + 1) } }(i), true) } ~~~ 要理解閉包,就需要我們從「執行上下文」開始講起。 ### 執行上下文 先講一個關于**變量提升**的知識點,面試中可能會遇見下面的問題,很多候選人都回答錯誤: > 題目:說出下面執行的結果(這里筆者直接注釋輸出了) ~~~ console.log(a) // undefined var a = 100 fn('zhangsan') // 'zhangsan' 20 function fn(name) { age = 20 console.log(name, age) var age } console.log(b); // 這里報錯 // Uncaught ReferenceError: b is not defined b = 100; ~~~ 在一段 JS 腳本(即一個`<script>`標簽中)執行之前,要先解析代碼(所以說 JS 是解釋執行的腳本語言),解析的時候會先創建一個**全局執行上下文**環境,先把代碼中即將執行的(內部函數的不算,因為你不知道函數何時執行)變量、函數聲明都拿出來。變量先暫時賦值為`undefined`,函數則先聲明好可使用。這一步做完了,然后再開始正式執行程序。再次強調,這是在代碼執行之前才開始的工作。 我們來看下上面的面試小題目,為什么`a`是`undefined`,而`b`卻報錯了,實際 JS 在代碼執行之前,要「全文解析」,發現`var a`,知道有個`a`的變量,存入了執行上下文,而`b`沒有找到`var`關鍵字,這時候沒有在執行上下文提前「占位」,所以代碼執行的時候,提前報到的`a`是有記錄的,只不過值暫時還沒有賦值,即為`undefined`,而`b`在執行上下文沒有找到,自然會報錯(沒有找到`b`的引用)。 另外,一個函數在執行之前,也會創建一個**函數執行上下文**環境,跟**全局上下文**差不多,不過**函數執行上下文**中會多出`this``arguments`和函數的參數。參數和`arguments`好理解,這里的`this`咱們需要專門講解。 總結一下: * 范圍:一段`<script>`、js 文件或者一個函數 * 全局上下文:變量定義,函數聲明 * 函數上下文:變量定義,函數聲明,`this`,`arguments` ### `this` 先搞明白一個很重要的概念 ——**`this`的值是在執行的時候才能確認,定義的時候不能確認!**為什么呢 —— 因為`this`是執行上下文環境的一部分,而執行上下文需要在代碼執行之前確定,而不是定義的時候。看如下例子 ~~~ var a = { name: 'A', fn: function () { console.log(this.name) } } a.fn() // this === a a.fn.call({name: 'B'}) // this === {name: 'B'} var fn1 = a.fn fn1() // this === window ~~~ `this`執行會有不同,主要集中在這幾個場景中 * 作為構造函數執行,構造函數中 * 作為對象屬性執行,上述代碼中`a.fn()` * 作為普通函數執行,上述代碼中`fn1()` * 用于`call``apply``bind`,上述代碼中`a.fn.call({name: 'B'})` 下面再來講解下什么是作用域和作用域鏈,作用域鏈和作用域也是常考的題目。 > 題目:如何理解 JS 的作用域和作用域鏈 ### 作用域 ES6 之前 JS 沒有塊級作用域。例如 ~~~ if (true) { var name = 'zhangsan' } console.log(name) ~~~ 從上面的例子可以體會到作用域的概念,作用域就是一個獨立的地盤,讓變量不會外泄、暴露出去。上面的`name`就被暴露出去了,因此,**JS 沒有塊級作用域,只有全局作用域和函數作用域**。 ~~~ var a = 100 function fn() { var a = 200 console.log('fn', a) } console.log('global', a) fn() ~~~ ![](https://img.kancloud.cn/34/a4/34a4ef0ad0a122d9b6d642f6ce12e7b6_251x147.png) 全局作用域就是最外層的作用域,如果我們寫了很多行 JS 代碼,變量定義都沒有用函數包括,那么它們就全部都在全局作用域中。這樣的壞處就是很容易撞車、沖突。 ~~~ // 張三寫的代碼中 var data = {a: 100} // 李四寫的代碼中 var data = {x: true} ~~~ 這就是為何 jQuery、Zepto 等庫的源碼,所有的代碼都會放在`(function(){....})()`中。因為放在里面的所有變量,都不會被外泄和暴露,不會污染到外面,不會對其他的庫或者 JS 腳本造成影響。這是函數作用域的一個體現。 附:ES6 中開始加入了塊級作用域,使用`let`定義變量即可,如下: ~~~ if (true) { let name = 'zhangsan' } console.log(name) // 報錯,因為let定義的name是在if這個塊級作用域 ~~~ ### 作用域鏈 首先認識一下什么叫做**自由變量**。如下代碼中,`console.log(a)`要得到`a`變量,但是在當前的作用域中沒有定義`a`(可對比一下`b`)。當前作用域沒有定義的變量,這成為**自由變量**。自由變量如何得到 —— 向父級作用域尋找。 ~~~ var a = 100 function fn() { var b = 200 console.log(a) console.log(b) } fn() ~~~ 如果父級也沒呢?再一層一層向上尋找,直到找到全局作用域還是沒找到,就宣布放棄。這種一層一層的關系,就是**作用域鏈**。 ~~~ var a = 100 function F1() { var b = 200 function F2() { var c = 300 console.log(a) // 自由變量,順作用域鏈向父作用域找 console.log(b) // 自由變量,順作用域鏈向父作用域找 console.log(c) // 本作用域的變量 } F2() } F1() ~~~ ### 閉包 講完這些內容,我們再來看一個例子,通過例子來理解閉包。 ~~~ function F1() { var a = 100 return function () { console.log(a) } } var f1 = F1() var a = 200 f1() ~~~ ![](https://img.kancloud.cn/6a/bb/6abbde1359c0f513765e563dda6dd7df_261x150.png) 自由變量將從作用域鏈中去尋找,但是**依據的是函數定義時的作用域鏈,而不是函數執行時**,以上這個例子就是閉包。閉包主要有兩個應用場景: * **函數作為返回值**,上面的例子就是 * **函數作為參數傳遞**,看以下例子 ~~~ function F1() { var a = 100 return function () { console.log(a) } } function F2(f1) { var a = 200 console.log(f1()) } var f1 = F1() F2(f1) ~~~ ![](https://img.kancloud.cn/4b/ea/4bea5fbd9089c2c6183829ad72b70229_289x195.png) 至此,對應著「作用域和閉包」這部分一開始的點擊彈出`alert`的代碼再看閉包,就很好理解了。
                  <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>

                              哎呀哎呀视频在线观看