一、作用域
(一)作用域是什么
了解作用域之前先看一下變量和函數(shù),變量和函數(shù)都有一定的訪問權限京革,就是必須滿足條件或者在某個范圍之內(nèi)才能訪問泥栖,這個范圍就是作用域。它具體表現(xiàn)形式就是一段特定的代碼弧满,在該代碼段中的變量和函數(shù)是封閉的婆跑、獨立的,這樣變量才不會泄露庭呜、污染滑进。
var cat = '有魚';
function Person(){
var name = '張三';
console.log(name);//張三,在函數(shù)內(nèi)部募谎,跟變量name在同一個作用域內(nèi)
}
console.log(cat);//有魚
console.log(name);//沒有結果顯示扶关,因為name在函數(shù)內(nèi)部是獨立的,訪問不了
(二)作用域分類
作用域一共有三種数冬,分別為全局作用域节槐、函數(shù)作用域、塊作用域,其中塊作用域是ES6新增的铜异,同時函數(shù)作用域和塊作用域統(tǒng)稱為局部作用域地来。
全局作用域
在代碼的最外層的變量和函數(shù)稱為全局作用域,有以下特點:
①在代碼的任何地方都可以訪問得到
②變量聲明時如果省略var關鍵字熙掺,則為全局變量未斑,擁有全局作用域
③window對象的屬性和方法,擁有全局作用域
④瀏覽器打開時開啟币绩,瀏覽器關閉或者頁面關閉時銷毀
var cat = '有魚';
function Person(){
var name = '張三';
per = '卡卡';// 省略var蜡秽,視為全局變量
console.log(cat);//有魚 cat為全局變量,函數(shù)Person內(nèi)部可以訪問
}
console.log(cat);//有魚 cat為全局變量缆镣,最外層代碼可以訪問
console.log(per);//卡卡 per為全局變量芽突,最外層代碼可以訪問
全局作用域也有弊端,如:變量命名時容易重復董瞻,造成變量污染寞蚌;
函數(shù)作用域
在函數(shù)內(nèi)部的變量權限稱為函數(shù)作用域,有以下特點:
①每個函數(shù)都有自己的作用域钠糊,而且調(diào)用一次就會生成新的作用域
②只能在函數(shù)內(nèi)部才能訪問挟秤,外部是沒有權限訪問的
③進入函數(shù)內(nèi)部時開啟,函數(shù)執(zhí)行完畢后銷毀
var cat = '有魚';
function Person(){
var name = '張三';
console.log(name);//張三 name為函數(shù)作用域抄伍,函數(shù)Person內(nèi)部可以訪問
}
console.log(name);//有魚 name為函數(shù)作用域艘刚,最外層代碼沒有權限訪問
塊作用域(ES6新增)
凡是由{}符號包裹起來的都是塊作用域,ES6新增的知識點截珍,有以下特點:
①只能在{}內(nèi)部起作用
②變量聲明時使用let或const關鍵字
③變量名不能重復攀甚,否則會報錯
④變量不可以在聲明之前使用
使用var進行展示:
if(true){
var cat = '有魚';
console.log(cat);//有魚
}
console.log(cat);//有魚
使用let進行展示:
if(true){
let cat = '有魚';
console.log(cat);// 有魚
// let cat = '年年';// Uncaught SyntaxError: Identifier 'cat' has already been declared
}
console.log(cat);// 訪問不了cat變量 報:Uncaught ReferenceError: cat is not defined
注意:塊作用域一般用在for等循環(huán)語句中,可以避免變量外泄
(三)變量提升與函數(shù)提升
變量提升
①var關鍵字存在變量提升岗喉,let秋度、const不存在;
②變量提升的意思是在程序執(zhí)行前钱床,先去整個代碼中查看是否含有變量聲明荚斯,即是否有var關鍵字,如果有則先執(zhí)行聲明诞丽,然后再去執(zhí)行其他部分鲸拥。
這里我我總結了4句話:
①js程序執(zhí)行的順序,先看var聲明僧免,聲明部分在第一行刑赶,其他(賦值)按照正常順序執(zhí)行
②如果局部變量里面有var聲明,就是局部變量懂衩,不用管外面的全局變量
③如果局部變量里面沒有var聲明撞叨,就去找外面的全局變量
④在局部變量里面金踪,如何使用全局變量,使用window對象調(diào)用
var a = "Hello";
function person(){
var a;
console.log(a);//undifined
a = "World";
console.log(a);//World
}
person();
console.log(a);//Hello
// 先看整個腳本文件的聲明部分牵敷,全局作用域有個變量a胡岔,局部作用域也有個變量a,而且都有var聲明枷餐,說明他們是完全兩個不同的變量靶瘸,此時函數(shù)內(nèi)部不用管外面的全局變量,此時變量a只是聲明而沒有賦值毛肋,所以輸出為 undifined怨咪;
// 接著還是局部作用域里面,給a進行了賦值润匙,此時輸出時為world诗眨,且仍是局部變量;
// 函數(shù)外部的輸出時孕讳,因為局部作用域已經(jīng)結束匠楚,則又恢復到全局變量中,所以此時輸出的是全局變量啊厂财,值為hello芋簿。
var a = "Hello";
function person(){
console.log(a);//Hello
a = "World";
console.log(a);//World
}
person();
console.log(a);//World
// 先看整個腳本文件的聲明部分,全局作用域有個變量a蟀苛,局部作用域里面沒有var聲明益咬,則此時a是一個全部變量,所以輸出時a的值就是全部變量hello帜平;
// 局部作用域里面對全局變量a進行了重新賦值,所以此時輸出時為world
// 函數(shù)外部的輸出時梅鹦,因為跳出局部作用域裆甩,又恢復到全局變量中,之前因為已經(jīng)重新賦值為world齐唆,此時輸出時為world
var a = "Hello";
function person(){
console.log(a);//undifined
var a = "World";
console.log(a);//World
}
person();
console.log(a);// Hello
// 先看整個腳本文件的聲明部分嗤栓,全局作用域有個變量a,局部作用域也有個變量a箍邮,且有var聲明茉帅,說明他們是完全兩個不同的變量,此時函數(shù)內(nèi)部不用管外面的全局變量锭弊,進行變量提升堪澎,記住只是聲明提升,賦值不進行提升味滞,所以輸出為 undifined樱蛤;
// 還是局部作用域里面钮呀,給a進行了賦值,此時輸出時為World昨凡,且仍是局部變量
// 函數(shù)外部的輸出時爽醋,因為局部作用域已經(jīng)結束,則又恢復到全局變量中便脊,所以此時輸出時為Hello
var a = "Hello";
function person(){
console.log(window.a); //Hello 因為是window對象的屬性蚂四,此時a是全局變量
var a = "World"; //局部變量a在這行定義
console.log(a); //World
}
person();
console.log(a);//Hello 全局變量a
// 在局部作用域使用全局變量使用window.全局變量名字
函數(shù)提升
函數(shù)有兩種形式:函數(shù)表達式 var per = function(){} 、函數(shù)聲明 function per(){}哪痰,只有函數(shù)聲明有函數(shù)提升特性
提升的意思是允許先調(diào)用遂赠,后聲明
per();//可以先調(diào)用
function per(){
console('我是函數(shù)聲明形式,允許提升');
}
二妒御、作用域鏈
(一)什么是作用域鏈
每一層作用域其實就是一個執(zhí)行環(huán)境(作用域和執(zhí)行環(huán)境是有區(qū)別的解愤,這里為了容易理解,姑且這樣)乎莉,在該環(huán)境內(nèi)執(zhí)行程序時送讲,會創(chuàng)建一個變量對象,對象包含該有屬于自己的變量和函數(shù)惋啃;
在當前執(zhí)行環(huán)境下找不變量時哼鬓,就會向上一層作用域里面尋找,具體過程還是先找到上一層的變量對象边灭,再去里面找對應的變量异希,如果還是找不到,繼續(xù)向上……
直到最外層的作用域绒瘦,也就是全局作用域称簿,這樣就會形成一個鏈條,稱為作用域鏈惰帽。
(二)作用域特點
作用域鏈最外層是全局作用域憨降,最內(nèi)層是當前作用域
內(nèi)部環(huán)境可以通過作用域鏈訪問所有外部環(huán)境,但外部環(huán)境不能訪問內(nèi)部環(huán)境的任何變量和函數(shù)
由于變量的查找是沿著作用域鏈來實現(xiàn)的该酗,所以也稱作用域鏈為變量查找的機制
var cat = '有魚';
function Person() {
var name = '張三';
function Student() {
var age = 18;
console.log(name);//張三
console.log(cat);//有魚
}
Student();
console.log(age);//Uncaught ReferenceError: age is not defined
}
Person();
①Student函數(shù)內(nèi)部屬于最內(nèi)層作用域授药,找不到name,向上一層作用域Person函數(shù)內(nèi)部找呜魄,找到了輸出‘張三’悔叽;
②在Student內(nèi)部輸出cat時找不到,向上一層作用域Person函數(shù)找爵嗅,還找不到繼續(xù)向上一層找娇澎,即全局作用域,找到了輸出‘有魚’操骡;
③在Person函數(shù)內(nèi)部輸出age時找不到九火,向上一層作用域找赚窃,即全局作用域,還是找不到輸出‘a(chǎn)ge is not defined’岔激;