JavaScript - 函式 and 迴圈繫結
阿新 • • 發佈:2018-11-12
目錄
- 提升作用域:全域性定義,區域性賦值(強烈不推薦使用,存在變數不安全的風險)
- 閉包解決區域性變數生命週期:區域性變數繫結閉包函式,閉包函式存在,區域性變數存在
- ES6語法簡單處理變數汙染:塊級作用於不存在全域性變數的重新命名。
- 物件屬性解決變數汙染:物件思想,利用屬性的區域性性和臨時性儲存資料。
一、函式
1、建立函式
ES5
function 函式名 (引數列表) { 函式體; } var 函式名 = function (引數列表) { 函式體; }
ES6
let 函式名 = (引數列表) => { 函式體; }
匿名函式
(function (引數列表) { 函式體; }) // 匿名函式需要自呼叫 (function (引數列表) { 函式體; })(引數列表);
2、函式的呼叫
函式名(引數列表)
function myFunction() { var x=5; return x; } myFunction()
3、函式的引數
個數不需要統一(多舍,少undefined)
function fn (a, b, c) { console.log(a, b, c); // 100 undefined undefined } fn(100); function fn (a) { console.log(a) // 100 } fn(100, 200, 300) // 200,300被丟棄
可以任意位置具有預設值
function fn (a, b=20, c, d=40) { console.log(a, b, c, d); // 100 200 300 40 } fn(100, 200, 300);
通過 ... 接收多個值
function fn (a, ...b) { console.log(a, b); // 100 [200 300] } fn(100, 200, 300); // ...變數必須出現在引數列表最後
4、返回值
function fn () { return 返回值; } // 1.可以空return操作,用來結束函式 // 2.返回值可以為任意js型別資料 // 3.函式最多隻能擁有一個返回值 若:return data,a;則只返回後者a
5、函式回撥
在一個函式(fn)中,滿足條件情況下,呼叫另外一個函式(func)作為引數
注:func函式 是 fn函式 的引數(函式作為函式引數傳入)// 回撥的函式 function callback(data) {} // 邏輯函式 function func(callback) { // 函式回撥 if (callback) callback(data); } // 函式回撥的本質:在一個函式中(呼叫函式),當滿足一定條件,呼叫引數函式(回撥函式) // 回撥函式作為呼叫函式的引數傳入 // 回撥函式通過引數將呼叫還是內部資料傳出 <script type="text/javascript"> // 解決問題: // 請求資料 => 資料(return | 函式回撥) => 外界 function a_fn(data) { console.log('a_fn'); // 如果要使用資料,那麼定義形參接收引數 console.log(data); } function b_fn(a_fn) { var data = "b_fn 的 資料"; console.log('b_fn'); // 條件: a_fn有值(有函式實現體) if (a_fn) { // 呼叫引數函式 a_fn(data); } } b_fn(a_fn); // 1.一個函式(函式名)作為另外一個函式的引數 // 2.滿足一定的條件,呼叫引數函式 // 3.形成了函式回撥,回撥的函式可以獲取呼叫函式的區域性變數(將資料攜帶出去) </script>
- 傳輸資料 普通傳輸 and 回撥傳輸
<!-- 普通函式實現資料的傳輸 --> <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>資料請求</title> </head> <body> </body> <!-- 外部要接收資料 --> <!-- 函式callback接受資料傳輸 --> <script type="text/javascript"> var callback = function (data,a) { // 使用資料 console.log(data); console.log(a); } </script> <!-- 模擬請求資料 --> <script> var a_fn = function(){ console.log("開始請求資料..."); // ... var data = [1, 2, 3, 4, 5]; a = 123 console.log("資料請求完畢!!!"); return data,a; } // 模擬接受資料資料 callback(a_fn()) </script> </html>
<!-- 回撥函式模擬資料傳輸 --> <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>資料請求</title> </head> <body> </body> <!-- 外部要接收資料 --> <!-- 1.外部給內部提供回撥函式(函式名callback) --> <!-- 2.內部將資料反饋給外部回撥函式.外部就可以使用內部資料 --> <script type="text/javascript"> var callback = function (data,a) { // 使用資料 console.log(data); console.log(a); } </script> <!-- 請求資料 --> <script type="text/javascript"> try { // 採用匿名函式自呼叫請求資料,不存在返回值 (function (callback) { console.log("開始請求資料..."); // ... var data = [1, 2, 3, 4, 5]; a = 123 console.log("資料請求完畢!!!"); // 如果回撥函式存在,那麼回撥對應的函式,並將資料攜帶出去 if (callback) { callback(data,a); } })(callback) // 請求資料完畢之後,需要讓外界獲取請求的資料: // 1.函式返回值: 函式的自呼叫,不存在其他呼叫者,也就無法獲取函式的執行結果 // 2.函式回撥 } catch (err) { } </script> <!-- 外部要接收資料 --> </html>
資料傳輸方式總結:
- 普通函式進行傳值,使用return返回,並且只能返回一個值
- 回撥函式傳值,使用匿名函式,可以直接傳輸多個值
- 鉤子函式:系統中預設存在回撥
<script type="text/javascript"> // 通常情況下,我們的頁面標籤存不存在點選事件? => 存在 // 點選事件觸發後,可不可以對外傳送資料 => 可以,eg:點在頁面的什麼位置 // 系統已經書寫好了這種函式回撥,但是沒有回撥體,回撥體(回撥函式)由普通開發者提供 // 鉤子:滿足條件情況下被系統回撥的函式(方法),稱之為鉤子函式(方法) <=> 回撥函式 document.onclick = function (a, b , c) { console.log("點選事件"); console.log(a, b , c); } </script>
- 鉤子函式獲取網頁點選座標資料等
document.onclick = function (event) { console.log("點選事件"); console.log(event); console.log(event.clientX); console.log(event.clientY); }
6、閉包:函式的巢狀
閉包: 函式的巢狀定義,內層函式就是閉包
閉包產生的原因:
- 需求:外部使用,函式區域性作用域內的引數, -- 解決方法:返回值 | 函式回撥 | 閉包 | 提升作用域
- 需求:外部,另一個函式中使用該函式體內的區域性變數 -- 解決方法: 函式回撥 | 閉包
- 需求:不能使用函式回撥(函式已有固定引數,或不能擁有引數),只能將函式定義到擁有區域性變數函式的內部 => 閉包
- 閉包總結:內部函式要使用,外部函式的區域性變數
閉包函式的優點:
- 外部函式不需要強制擁有引數以及返回值
- 外部函式的區域性變數也無需提升作用域,可以保證引數的安全性
- 內部函式不需要強制擁有引數與返回值,便可以直接使用外部函式的區域性變數
- 區域性變數的持久化(提升區域性變數的生命週期)
- 變數汙染(頁面標籤迴圈繫結)
function outer() { var data = {} function inner() { return data; } return inner; } // 閉包目的:不允許提升變數作用域時,該函式的區域性變數需要被其他函式使用 // 閉包本質:函式的巢狀,內層函式稱之為閉包 // 閉包的解決案例:①影響區域性變數的生命週期,持久化區域性變數;②解決變數汙染 function a_fn() { var data = [1, 2, 3, 4, 5]; // 閉包 function b_fn() {console.log('>>>', data);} b_fn();} a_fn();
- 提升作用域:全域性定義,區域性賦值(強烈不推薦使用,存在變數不安全的風險)
// 提升作用域 var a; // 作用域被提升 function fn() { a = 10; // 區域性變數 => 在外部定義 } fn(); // 外部使用區域性變數: 返回值 | 函式回撥 | 閉包 | 提升作用域 console.log(a);
- 閉包解決區域性變數生命週期:區域性變數繫結閉包函式,閉包函式存在,區域性變數存在
生命週期:就是一個變數在程式執行過程中的“有效期”,比如說全域性變數,那它在整個程式執行過程中都有效,及它的生命週期是整個程式執行過程,而對於一些在函式裡定義的區域性變數,它只是在呼叫函式是有效,函式呼叫結束,它的生命週期也完了。
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>區域性變數生命週期</title> </head> <body> 區域性變數生命週期 </body> <script type="text/javascript"> function outer() { // 方法執行完畢後,資料data就會被銷燬 var data = [1, 2, 3, 4, 5]; console.log(data); // 若通過閉包函式繫結區域性變數,則可保證區域性變數不被銷燬 function inner() { return data; } // 資料被inner操作返回,inner屬於outer,屬於需要outer將inner返回出去(跟外界建立起聯絡) return inner; } // 將區域性變數生命週期提升於inner函式相同,inner存在(即inner未被呼叫),區域性變數data就一直存在 var inner = outer(); //執行inner 生命週期結束 console.log(inner()); </script> </html>
二、迴圈繫結
迴圈繫結產生的變數汙染問題的解決方法:
- ES5下閉包解決(優先推薦)
- ES6的塊級作用域:let定義迴圈內變數
- 物件屬性解決
- 閉包解決變數汙染
變數汙染:全域性變數的重新命名覆蓋問題,例:a=1; a=2則後者覆蓋前者造成變數汙染問題
迴圈繫結造成的變數汙染問題(本質是var定義的全域性變數迴圈,賦值):
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>閉包解決變數汙染</title> <style type="text/css"> ul { margin: 0; padding: 0; list-style: none; } li { width: 80px; height: 35px; background-color: pink; border-radius: 5px; float: left; margin-left: 3px; } </style> </head> <body> <ul> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> </body> <script type="text/javascript"> // 需求:點選li,列印li在ul中的索引 => 0, 1, 2, 3, 4 var lis = document.querySelectorAll('ul li'); for (var i = 0; i < lis.length; i++) { lis[i].onclick = function () { alert(i); } } </script> </html>
解決方法(閉包):本質是將var定義的全域性變數i,變成閉包內的區域性變數進行儲存、
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>閉包解決變數汙染</title> <style type="text/css"> ul { margin: 0; padding: 0; list-style: none; } li { width: 80px; height: 35px; background-color: pink; border-radius: 5px; float: left; margin-left: 3px; } </style> </head> <body> <ul> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> </body> <script type="text/javascript"> // 需求:點選li,列印li在ul中的索引 => 0, 1, 2, 3, 4 // 1.lis var lis = document.querySelectorAll('ul li'); // 2.迴圈繫結 for (var i = 0; i < lis.length; i++) { // 解決的原理:一共產生了5個外層函式,儲存的形參i的值分別是0, 1, 2, 3, 4 // 內層函式也產生了5個,且和外層函式一一對應,列印的i就是外層函式的形參i // 外層函式 (function (i) { // 內層函式:閉包 lis[i].onclick = function () { alert(i) } })(i) } console.log(i); </script> </html>
- ES6語法簡單處理變數汙染:塊級作用於不存在全域性變數的重新命名。
!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>塊級作用域解決迴圈繫結</title> <style type="text/css"> ul { margin: 0; padding: 0; list-style: none; } li { width: 80px; height: 35px; background-color: pink; border-radius: 5px; float: left; margin-left: 3px; } </style> </head> <body> <ul> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> </body> <script type="text/javascript"> // ES6語法中支援塊級作用域 let lis = document.querySelectorAll('li'); for (let i = 0; i < lis.length; i++) { lis[i].onclick = function () { alert(i) } } </script> </html>
- 物件屬性解決變數汙染:物件思想,利用屬性的區域性性和臨時性儲存資料。
<script type="text/javascript"> var lis = document.querySelectorAll('li'); for (var i = 0; i < lis.length; i++) { lis[i].index = i; lis[i].onclick = function () { // var temp = lis[i].index; // lis[i]中的i一樣存在變數汙染 alert(this.index) // 當前被點選的li } } </script>