1 作用域
什么是作用域:作用域分為全局作用域和函數(shù)作用域烈炭。全局作用域可以理解為是全局對象(window對象榜掌,GO對象)优妙,而函數(shù)作用域可以理解為是一個函數(shù)內(nèi)部的對象(AO對象)。
全局作用域:所有在script標簽內(nèi)的語句都處在全局作用域中憎账;
頁面打開時創(chuàng)建GO對象套硼,關(guān)閉時銷毀;
定義在全局作用域中的變量胞皱,變量名是GO對象的屬性名邪意,變量值是屬性值。
函數(shù)作用域:所有在函數(shù)體內(nèi)部的語句的處在函數(shù)作用域中反砌;
函數(shù)執(zhí)行時創(chuàng)建AO對象雾鬼,執(zhí)行完畢銷毀AO對象;
函數(shù)作用域中的變量是AO對象的屬性名宴树,變量的值是AO對象的屬性值策菜;
下一次使用函數(shù)時會創(chuàng)建新的AO對象。
1.1語法分析
Js解釋引擎會先掃描所有的js代碼酒贬,查看代碼有沒有低級的語法錯誤又憨,如果存在語法錯誤,則整個程序就不會執(zhí)行,如果沒有語法錯誤锭吨,則進入預解析(編譯)階段
1.2 預編譯
? 全局對象GO
1竟块,創(chuàng)建AO對象 ==> Activation Object(活動對象,作用域,其實叫執(zhí)行期上下文)
2,找到形參和變量耐齐,把形參和變量作為AO對象的屬性名浪秘,值是undefined
3蒋情,實參把值賦給形參
4,在函數(shù)中找到函數(shù)聲明耸携,把函數(shù)名作為AO對象的屬性名棵癣,值是函數(shù)體
function myTest(a, b) {
/*
第一步,AO{ }
第二步
AO{
a:undefined;
b:undefined;
c:undefined;
}
第三步
AO{
a:10;
b:20;
c:undefined;
}
第四步
AO{
a:function() {
console.log(100)
};
b:20;
c:undefined;
}
*/
console.log('a1', a); //f a(){}
console.log('b1', b); //20
console.log('c1', c); //undefined
var c = 30;
b = 200;
function a() {
};
console.log('a2', a); //f a(){}
console.log('b2', b); //200
console.log('c2', c); //30
};
let result = myTest(10, 20);
console.log(result);
預編譯后就開始一行一行執(zhí)行代碼夺衍,該改值的改值狈谊。要注意的一點是,這里的c是用var聲明的沟沙,用let和const聲明不會提升河劝,會報錯。
2 作用域鏈
1. 變量查詢規(guī)則
當遇見使用一個變量時矛紫,JS引擎會從其所在的作用域依次向外層查找赎瞎,查找會在找到第一個匹配的標識符的時候停止。(有同名的變量會發(fā)生“遮蔽效應”)
let a = 1000;
function myTest() {
let b = 20;
outer()
function outer() {
let c = 30;
b = 200;
console.log(a);//1000
console.log(b);//200
inner()
function inner(){
a = 1;
console.log(a);//1
console.log(c);//30
}
}
}
let result = myTest();
console.log(result);
2. 作用域鏈原理理解:
scope翻譯過來就是范圍的意思颊咬。
[[scope]]中所存儲的就是執(zhí)行期上下文對象的集合,這個幾個呈鏈式連接,我們把這種鏈式鏈接叫做作用域鏈务甥。
比方說上述代碼中
myTest
outer
inner
作用域鏈 = 函數(shù)執(zhí)行時的AO對象 + 函數(shù)創(chuàng)建時的環(huán)境。
變量查找規(guī)則:沿著當前函數(shù)作用域鏈作用域鏈頂端喳篇,自上而下尋找變量敞临。
3 閉包
閉包是什么:閉包是指那些引用另一個函數(shù)作用域中變量的函數(shù),通常是在嵌套函數(shù)中實現(xiàn)的麸澜。把一個函數(shù)從它定義的那個作用域挺尿,挪走,運行炊邦。這個函數(shù)能夠記憶住定義時的那個作用域编矾。不管函數(shù)走到哪里,定義時的作用域就帶到了哪里铣耘。這就是閉包洽沟。
上代碼
function myTest() {
let count = 0;
return function inner() {
console.log(count)
}
}
let count = 10;
let inn = myTest();
let result = inn()//0
我們都知道以故,inner是定義在myTest內(nèi)蜗细,無法通過inner()直接在全局作用域中直接調(diào)用的;
但這里把inner函數(shù) return出來怒详,執(zhí)行myTest等于返回一個inner函數(shù)炉媒。
function sum(x,y) {
return function innerSum(x) {
return x + y
}
}
let test = sum(3,7);
test(1)//8,3被8代替了
每次調(diào)用一個函數(shù),都會產(chǎn)生新的閉包.新的閉包知道是,語句全新,所處環(huán)境也是全新的。
function myTest() {
let count = 0;
return function inner() {
count++;
return count
}
}
let count = 10;
let inn1 = myTest();
let inn2 = myTest();
inn1();//1
inn1();//2
inn1();//3
inn2();//1
閉包的作用
1,函數(shù)累加器
function outer(){
var count = 0;
function inner(){
count++;
console.log(count);
}
return inner;
}
var inn1 = outer();
inn1(); //1
inn1(); //2
2,可做緩存
function test(){
var num = 100;
function aa(){
num ++;
console.log(num);
}
function bb(){
num --;
console.log(num)
}
return [aa,bb]
}
var myArr = test()
myArr[0]()//執(zhí)行num++昆烁,101
myArr[1]() //執(zhí)行num--吊骤,100