01. JavaScript的運行機制 (1)所有同步任務都在主線程上執行,形成一個執行棧。(2)主線程之外,還有一個“任務隊列”,只要非同步任務有了運行結果,就在“任務隊列”之中放置一個事件。(3)一旦“執行棧”中的所有同步任務執行完畢了,系統就會讀取“任務隊列”,看看裡面有哪些事件。那些對應的非同步 ...
01. JavaScript的運行機制
(1)所有同步任務都在主線程上執行,形成一個執行棧。
(2)主線程之外,還有一個“任務隊列”,只要非同步任務有了運行結果,就在“任務隊列”之中放置一個事件。
(3)一旦“執行棧”中的所有同步任務執行完畢了,系統就會讀取“任務隊列”,看看裡面有哪些事件。那些對應的非同步任務,於是結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重覆上面的三步。(事件迴圈)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>00_引入</title> </head> <body> <div id="btn01"> <button>測試11</button> <button>測試12</button> <button>測試13</button> </div> <hr/> <div id="btn02"> <button>測試21</button> <button>測試22</button> <button>測試23</button> </div> <hr/> <div id="btn03"> <button>測試31</button> <button>測試32</button> <button>測試33</button> </div> <!-- 需求: 點擊某個按鈕, 提示"點擊的是第n個按鈕" --> <script type="text/javascript"> var btns01 = document.getElementById('btn01').getElementsByTagName('button'); var btns02 = document.getElementById('btn02').getElementsByTagName('button'); var btns03 = document.getElementById('btn03').getElementsByTagName('button'); console.log(btns01) // 有問題 JavaScript的運行機制 主線程上執行,形成一個執行棧,一旦“執行棧”中的所有同步任務執行完畢了,系統就會讀取“任務隊列” for(var i=0,length=btns01.length;i<length;i++) { var btn = btns01[i] console.log(btn) btn.onclick = function () { alert('第'+(i)+'個 ' + btn.innerHTML) } } // 解決一: 保存下標 for(var i=0,length=btns02.length;i<length;i++) { var btn02 = btns02[i] btn02.index = i btn02.onclick = function () { alert('第'+(this.index+1)+'個 ' + btns02[this.index].innerHTML ) } } // 解決辦法二: 利用閉包 for(var i=0,length=btns03.length;i<length;i++) { (function (i) { var btn03 = btns03[i] btn03.onclick = function () { alert('第'+(i+1)+'個 ' + btn03.innerHTML ) } })(i) } </script> </body> </html>
02. 理解閉包
1. 如何產生閉包?
* 當一個嵌套的內部(子)函數引用了嵌套的外部(父)函數的變數(函數)時, 就產生了閉包
2. 閉包到底是什麼?
* 使用chrome調試查看
* 理解一: 閉包是嵌套的內部函數(絕大部分人)
* 理解二: 包含被引用變數(函數)的對象(極少數人)
* 註意: 閉包存在於嵌套的內部函數中
3. 產生閉包的條件?
* 函數嵌套
* 內部函數引用了外部函數的數據(變數/函數)
function fn1 () { var a = 3 function fn2 () { console.log(a) } fn2() } fn1()
03. 常見的閉包
1. 將函數作為另一個函數的返回值
2. 將函數作為實參傳遞給另一個函數調用
// 1. 將函數作為另一個函數的返回值 function fn1() { var a = 2 function fn2() { a++ console.log(a) } return fn2 } var f = fn1() f() // 3 f() // 4 // 2. 將函數作為實參傳遞給另一個函數調用 function showMsgDelay(msg, time) { setTimeout(function () { console.log(msg) }, time) } showMsgDelay('hello', 1000)
04. 閉包的作用
1. 使用函數內部的變數在函數執行完後, 仍然存活在記憶體中(延長了局部變數的生命周期)
2. 讓函數外部可以操作(讀寫)到函數內部的數據(變數/函數)
問題:
1. 函數執行完後, 函數內部聲明的局部變數是否還存在?
2. 在函數外部能直接訪問函數內部的局部變數嗎?
function fun1() { var a = 3; function fun2() { a++; //引用外部函數的變數--->產生閉包 console.log(a); } return fun2; } var f = fun1(); //由於f引用著內部的函數-->內部函數以及閉包都沒有成為垃圾對象 f(); // 4 間接操作了函數內部的局部變數 f(); // 5
05. 閉包的生命周期
1. 產生: 在嵌套內部函數定義執行完時就產生了(不是在調用)
2. 死亡: 在嵌套的內部函數成為垃圾對象時
function fun1() { //此處閉包已經產生 var a = 3; function fun2() { a++; console.log(a); } return fun2; } var f = fun1(); f(); f(); f = null //此時閉包對象死亡
06. 閉包的應用
自定義JS模塊
* 具有特定功能的js文件
* 將所有的數據和功能都封裝在一個函數內部(私有的)
* 只向外暴露一個包信n個方法的對象或函數
* 模塊的使用者, 只需要通過模塊暴露的對象調用方法來實現對應的功能
// 自定義模塊1 function coolModule() { //私有的數據 var msg = 'message' var names = ['I', 'Love', 'you'] //私有的操作數據的函數 function doSomething() { console.log(msg.toUpperCase()) } function doOtherthing() { console.log(names.join(' ')) } //向外暴露包含多個方法的對象 return { doSomething: doSomething, doOtherthing: doOtherthing } }
var module = coolModule() module.doSomething() module.doOtherthing()
自定義模塊2
// 自定義模塊2 (function (window) { //私有的數據 var msg = 'atguigu' var names = ['I', 'Love', 'you'] //操作數據的函數 function a() { console.log(msg.toUpperCase()) } function b() { console.log(names.join(' ')) } window.coolModule2 = { doSomething: a, doOtherthing: b } })(window)
coolModule2.doSomething()
coolModule2.doOtherthing()
07. 閉包的缺點及解決
1. 缺點
* 函數執行完後, 函數內的局部變數沒有釋放, 占用記憶體時間會變長
* 容易造成記憶體泄露
2. 解決
* 能不用閉包就不用
* 及時釋放
function fn1() { var a = 2; function fn2() { a++; console.log(a); } return fn2; } var f = fn1(); f(); // 3 f(); // 4 f = null // 釋放
08. 面試題
// 說說它們的輸出情況 //代碼片段一 var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { return function () { return this.name; }; } }; console.log(object.getNameFunc()()); //The Window //代碼片段二 var name2 = "The Window"; var object2 = { name2: "My Object", getNameFunc: function () { var that = this; return function () { return that.name2; }; } }; console.log(object2.getNameFunc()()); // My Object
// 說說它們的輸出情況 function fun(n, o) { console.log(o) return { fun: function (m) { return fun(m, n) } } } var a = fun(0) a.fun(1) a.fun(2) a.fun(3) //undefined,0,0,0 var b = fun(0).fun(1).fun(2).fun(3) //undefined,0,1,2 var c = fun(0).fun(1) c.fun(2) c.fun(3) //undefined,0,1,1