做用域鏈:在js中只有函數有做用域的概念,因爲函數內能訪問函數外部的數據,而函數外部不能訪問函數內部的數據,由上述造成一種做用域訪問的鏈式結構叫作做用域鏈。javascript
閉包:經過某種方式實現的一個封閉的、包裹的對外不公開的結構|空間,有人稱之爲封閉空間,以及匿名函數自調。閉包能夠使函數外部訪問函數內部的數據。html
1、經過return來訪問函數內部的變量java
(function(){var a = 10; return 變量 | 函數 });git
2、經過當即執行函數訪問函數內部的變量github
01、(function(){})(); // 經常使用數組
02、(function(){}()); // 經常使用安全
03、;function(){}();閉包
04、+function(){}();函數
05、-function(){}();spa
1、閉包的做用
01、提供一種間接訪問函數內部變量的方法
02、延長函數內部的局部變量的生命週期(慎用!局部變量過多會佔大量內存)
03、全局變量私有化,減小全局變量污染
04、函數局部變量在函數執行完後釋放內存(函數執行完畢,其內部的變量當即銷燬)
05、更新複雜變量(能夠對變量進行校驗和判斷,保證安全性和穩定性)
2、代碼示例
01、提供一種間接訪問函數內部變量的方法
function A() {
var num = 10;// 局部變量
return num;// 訪問內部數據
}
var B1 = A();
console.log(B1);// 10 說明局部變量沒有銷燬 延長函數內部的局部變量的生命週期
上述代碼能夠在外部訪問函數A內部的局部變量,並延長局部變量num的生命週期,但也有缺陷,每次訪問都是一次性的,沒法修改保存其變量以下代碼所示:
function foo() {
var obj = {
name:"張三",
age:28
};
return obj;
}
var obj1 = foo();
var obj2 = foo();
console.log(obj1 == obj2); //false
上述代碼所示兩次訪問的對象不是同一個`console.log(obj1 == obj2); // false`!
03、全局變量私有化,減小全局變量污染
(function () {
window.onload = function () {// 全局變量私有化
};
})();
(function () {
var d = document;
var btn1 = d.getElementById('btn');
var btn2 = d.getElementById('btn');
var btn3 = d.getElementById('btn');
})();
05、更新複雜變量(能夠對變量進行校驗和判斷,保證安全性和穩定性)
001、設置數據
function test() {
var str1 = '字符串1';
return function (str) {
str1 = str; // 設置數據
return str1;
}
}
var test1 = test();
console.log(test1('hello')); // hello
console.log(test1()); // undefined
上述代碼是設置數據,但並無判斷數據,因爲函數調用時沒有傳參,因此結果是`console.log(test1()); // undefined`,咱們還能夠在其內部添加校驗和判斷,代碼以下:
function test() {
var str1 = '字符串1';
return function (str) {
if(typeof str == 'string'){// 判斷傳參是否是string類型
str1 = str; // 設置數據
}
return str1;
}
}
var test1 = test();
console.log(test1('hello')); // hello
console.log(test1()); // undefined
002、返回多個數據
以數組的形式返回
function person() {
var name = "黃忠";
var age = 45;
return [function () {//以數組的形式返回多個數據
return name;
},
function () {
return age;
}];
}
var p1 = person();
console.log(p1[0]()); // 黃忠
console.log(p1[1]()); // 45
通常不怎麼以數組的形式返回數據!
以對象的形式返回
function person() {
var name = "黃忠";
var age = 45;
return {
getName:function () {//以對象的形式返回多個數據
return name;
},
getAge:function () {
return age;
}
}
}
var p1 = person();
console.log(p1.getName()); // 黃忠
console.log(p1.getAge()); // 45
咱們通常以這種形式返回多個數據!
003、以對象形式返回多個數據並設置多個數據
function person() {
var name = "黃忠";
var age = 45;
return {
getName:function () {//以對象的形式返回多個數據
return name;
},
getAge:function () {
return age;
},
setName:function (value) {
//容錯性處理 + 邏輯校驗
//判斷
if (typeof value != 'string')
{
throw "該函數只接受字符串類型的參數";
}
name = value;
},
setAge:function (num) {
//容錯性處理 + 邏輯校驗
//判斷
//console.log(num == NaN);// false
var str = String(num);// 把獲取的實參轉爲字符串
var str1 = String(NaN);// 把NaN轉爲字符串
if ((str == str1) || typeof num != 'number' )
{//判斷輸入的是否爲數字類型而且不爲NaN
throw "該函數只接受數字類型的參數而且不爲NaN";
}
age = num;
}
}
}
var p1 = person();
console.log(p1.getName()); // 黃忠
console.log(p1.getAge()); // 45
p1.setName('郭嘉');// 修更名字
p1.setAge(28);// 修改年齡
console.log(p1.getName()); // 郭嘉
console.log(p1.getAge()); // 28
上述代碼在返回的對象中多添加了setName方法和setAge方法,並在方法內部作了容錯處理和校驗判斷,這樣使代碼的安全性和穩定性更好!
1、for循環中
For循環中,for循環執行的速率很是快,有時候,咱們但願保存變量i,用於其餘地方。咱們就能夠用到閉包!代碼以下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button>第一個</button>
<button>第二個</button>
<button>第三個</button>
<button>第四個</button>
<button>第五個</button>
<script>
var btns = document.getElementsByTagName('button');
//遍歷按鈕
for(var i= 0;i < btns.length;i++){
(function (a) {// 保存在形參中
btns[a].onclick = function () {// 按鈕點擊事件 每點擊對應的按鈕彈出對應的數字
alert(a+1);
}
})(i) ;// 把i看成實參傳進去
}
console.log(i);// 5 //在js中只有函數有做用域的概念
</script>
</body>
</html>
但上述代碼中變量i是全局變量會影響其餘的for循環,咱們也能夠用閉包把for循環包起來,防止全局變量污染!代碼以下:
var btns = document.getElementsByTagName('button');
//遍歷按鈕
(function () {
for(var i= 0;i < btns.length;i++){
(function (a) {// 保存在形參中
btns[a].onclick = function () {// 按鈕點擊事件 每點擊對應的按鈕彈出對應的數字
alert(a+1);
}
})(i) ;// 把i看成實參傳進去
}
})();
console.log(i);// Uncaught ReferenceError: i is not defined // i 未定義
上述代碼把for循環包起來,就把全局變量私有化了,外面就沒法訪問了,因此報錯未定義。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<button>第一個</button>
<button>第二個</button>
<button>第三個</button>
<button>第四個</button>
<button>第五個</button>
<script>
var btns = document.getElementsByTagName('button');
//遍歷按鈕
for(var i= 0;i < btns.length;i++){
btns[i].onclick = (function (a) {
return function () {
alert(a+1);
}
})(i);
}
</script>
</body>
</html>
for (var i = 0; i < 10; i++) {
setTimeout((function (j) {
return function () {
console.log(j);
}
})(i),1000);
}
閉包的原理:變量的訪問原則(即上一級的做用域沒法訪問下一級的做用域),其實函數自己就是閉包
https://github.com/350469960/OS/blob/master/javascript/closure.md