一.作用域
1. 函數(shù)能封閉住定義域
一個變量如果定義在了一個function里面烈掠,那么這個變量就是一個局部變量煮仇,只在這個function里面有定義,出了這個function,就如同沒有定義過一樣
function fn(){
var a = 3;//定義在函數(shù)里的變量,那么這個變量 就是局部變量碟婆,只有在函數(shù)里面有定義
console.log("我是函數(shù)里面的語句电抚,所以我知道a值是'' + a ");
}
fn();
console.log("我是函數(shù)外面的語句惕稻,我認為a的值是'' + a"); //undefined
a被var在了function里面,所以現(xiàn)在這個a變量只在函數(shù)內定義
JavaScript變量作用域非常的簡單蝙叛,沒有塊級作用域俺祠,管理住作用域的只有一個東西,函數(shù)
如果一個變量借帘,沒有定義在任何的function中蜘渣,那么它將在全部程序范圍內都有定義:就是你在JS的任何位置都能夠使用它。
var a = 10;//定義在全局范圍內的一個變量肺然,全局變量蔫缸,在程序任何一個區(qū)域都有定義
function fn(){
console.log("我是函數(shù)里面的語句,我認識全局變量a 际起,它的值為'' + a");
}
fn();
console.log("函數(shù)外面的語句拾碌,我也認識a,它的值為'' + a");
總結:
- 定義在function里面的變量街望,叫做局部變量校翔,只在function里面有定義,出了function沒有定義的
- 定義在全局范圍的 灾前,沒寫在任何function里面的防症,叫做全局變量,全局都認識
2.作用域鏈
當遇見一個變量時,JS引擎會從其所在的作用域依此向外層 查找蔫敲,查找會在找到第一個匹配的標識符的時候停止
function outer(){
var a = 3; //a的作用域就是outer
function inner(){
var b = 5; //b的作用域就是inner
console.log(a); //能夠正常輸出3饲嗽,a在本層沒有定義,就是找上層
console.log(b); //能夠正常輸出5
}
inner();
}
outer();
console.log(a); //報錯奈嘿,因為a的作用域outer
多層嵌套喝噪,如果有同名的變量,那么就會發(fā)生”遮蔽效應“
var a =2;//全局變量
function fn(){
var a = 3;//就把外層的a給遮蔽了指么,這個函數(shù)內部看不見外層的a了酝惧,
console.log(a);//輸出3,變量在當前作用域尋找伯诬,找到a的定義為3
}
fn();
console.log(a);//輸出為2晚唇, 變量在當前作用域尋找,找到a的定義值為2
作用域鏈:一個變量在使用的時候盗似,就會在當前層尋找它是否被定義哩陕,如果找不到,就去找上一層function赫舒,知道找到全局變量悍及,如果全局頁面沒有,就報錯接癌。
var a = 1;//全局變量
var b = 2; //全局變量
function outer(){
var a = 3; //遮蔽了外層a心赶,a局部變量
function inner(){
var b = 4;//遮蔽了外層的b,b局部變量
console.log(a);//① 輸出3缺猛,a現(xiàn)在在當前層找不到定義缨叫,所以就上一層尋找
console.log(b); //② 輸出4
}
inner(); //調用函數(shù)
console.log(a); //③ 輸出3
console.log(b); //④ 輸出2 b現(xiàn)在在當前層找不到定義就上一層尋找
}
outer(); //執(zhí)行函數(shù),控制權交給了outer
console.log(a); //⑤ 輸出1
console.log(b); //⑥ 輸出2
3. 不寫var就自動成全局變量
// var a, 相當于在全局var了一個a
function fn(){
a = 3; //這個a第一次賦值的時候荔燎,并沒有var過
// 所以就自動的在全局的范圍幫你var了一次
}
fn();
console.log(a);
這是JS的一個機理耻姥,如果遇見了一個標識符,從來沒有var過有咨,并且還賦值了:
num = 12;
那么就會自動幫你在全局范圍內定義 var num琐簇;
4. 函數(shù)的參數(shù),會默認定義為這個函數(shù)的局部變量
function fn(a,b,c,d){
// var a, b, c, d; 相當于在函數(shù)內部var四個變量座享,是局部變量
}
a, b, c, d就是一個fn的局部變量婉商,出了fn就沒有定義
5. 全局變量的作用
5.1 通信,共同操作一個變量
兩個函數(shù)同時操作同一個變量征讲,一個增加据某,一個減少,函數(shù)和函數(shù)通信
var num = 0;
function add(){
num++;
}
function remove(){
num--诗箍;
}
5.2 累加癣籽,重復調用函數(shù)的時候挽唉,不會重置
var num = 0;
function baoshu(){
num++;
console.log(num);
}
baoshu(); //1
baoshu();//2
baoshu();//3
如果num定義在baoshu里面,每次執(zhí)行就會把num重置為0筷狼,
function baoshu(){
var num = 0;
num++;
console.log(num);
}
baoshu(); //1
baoshu();//1
baoshu();//1
6.函數(shù)的定義也有作用域
//這個函數(shù)返回a的平方加b的平方
function pingfanghe(a,b){
return pingfang(a) + pingfang(b);
//返回m的平方
function pingfang(m){
return Math.pow(m,2)
}
}
// 現(xiàn)在相求4的平方瓶籽,想輸出16
pingfang(4); //報錯,因為全局作用域下埂材,沒有一個函數(shù)叫做pingfang
機理:
function big{
function small{
}
small();//可以運行
}
small(); //不能運行塑顺,因為小small函數(shù)定義在了big函數(shù)里面,離開big函數(shù)沒有作用域
二.閉包
1. 閉包
function outer(){
var a =33;
function inner(){
console.log(a);
}
return inner;
}
var inn = outer();
inn(); //彈出33
推導過程:
inner()這個函數(shù)不能在outer外面調用俏险,因為outer外面沒有inne的定義
function outer(){
var a = 88;
function inner(){
console.log(a);
}
}
//在全局調用inner 但是全局沒有inner定義严拒,所以會報錯
inner();
但是我們現(xiàn)在就想在全局作用域下,運行outer內部的inner,此時我們必須想一些奇奇怪怪的方法竖独。
有一個簡單可行的辦法裤唠,就是讓outer自己return掉inner:
function outer(){
var a = 3;
function inner(){
console.log(a);
}
return inner;//outer返回了inner的引用
}
var inn = outer(): //inn就是inner函數(shù)了
inn(); //執(zhí)行inn,全局作用下沒有a的定義
//但是函數(shù)閉包莹痢,能夠把定義函數(shù)的時候的作用域的時候一起記憶住
//能夠輸出33
這就說明了种蘸,inner函數(shù)能夠持久保存自己定義是的所處環(huán)境,并且及時自己在其他的環(huán)境被調用的時候竞膳,依然可以訪問自己定義時所處環(huán)境的值
一個函數(shù)可以把它自己內部的語句航瞭,和自己聲明時所處的作用域一起封裝乘了一個密閉的環(huán)境,我們稱之為“閉包”(Closures)
每一個函數(shù)都是閉包坦辟,每個函數(shù)天生就能記憶自己定義時所處的作用域環(huán)境刊侯。但是,我們必須將這個函數(shù)挪到別的作用域长窄,才能更好的觀察閉包滔吠。這樣才能實驗它有沒有把作用域給“記住”
2. 閉包的性質
每次重新引用函數(shù)的時候,閉包時全新的挠日。
function outer(){
var count = 0;
function inner(){
count++;
console.log(count);
}
return inner;
}
var inn1 = outer();
var inn2 = outer();
inn1();//1
inn1();//1
inn1();//1
inn1();//1
inn2();//1
inn2();//2
inn1();//1
無論它在何處被調用,它總是能訪問它定義時所處作用域中的全部變量