1. 程式人生 > >JavaScript - 函式 and 迴圈繫結

JavaScript - 函式 and 迴圈繫結

目錄

一、函式

1、建立函式

ES5

ES6

匿名函式

2、函式的呼叫

3、函式的引數

個數不需要統一(多舍,少undefined)

可以任意位置具有預設值

通過 ... 接收多個值

4、返回值

5、函式回撥

- 傳輸資料 普通傳輸 and 回撥傳輸

- 鉤子函式:系統中預設存在回撥

- 鉤子函式獲取網頁點選座標資料等

6、閉包:函式的巢狀

- 提升作用域:全域性定義,區域性賦值(強烈不推薦使用,存在變數不安全的風險)

- 閉包解決區域性變數生命週期:區域性變數繫結閉包函式,閉包函式存在,區域性變數存在

二、迴圈繫結

- 閉包解決變數汙染

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