作用域
作用域:程式語言最基本的功能就是儲存和訪問變數的值,如何對變數儲存/訪問的某種良好的規則,就是作用域。作用域負責收集並維護由所有宣告的標示符(變數)組成的一系列查詢,並實施一套非常嚴格的規則,確定當前執行的程式碼對這些識別符號的訪問許可權。
作用域是根據名稱查詢變數的一套規則。當一個塊或者函式巢狀在另外一個塊或者函式中時,就發生了作用域巢狀。
詞法作用域:定義在詞法階段的作用域。詞法作用域是你在寫程式碼時將變數和塊作用域寫在哪裡來決定的,因此詞法分析器處理程式碼時保持作用域不變。
函式作用域:每宣告一個函式都會為自身建立一個作用域,屬於這個函式的全部變數都可以在這個函式範圍內使用/複用。一段程式碼我們可以在它的外部新增一個包裝函式,這樣就可以將內部的變數和函式定義隱藏(這種方法可以有效解決變數或者函式汙染外部作用域,閉包和函式自呼叫也是這種方法的延伸)。外部作用域無法訪問包裝函式內部的任何內容。
宣告提升
es5及之前只有函式作用域和全域性作用域同時也只有兩種變數宣告,var 和function。 使用函式及變數的宣告都將被提升到當前作用域的最頂部。
1.變數提升(var的起作用,只有宣告提升,初始化不會):
var a =3;
function b( ) {
//實際上var了的變數提升到了函式內部的頂部
console.log( a );
var a=4;
}
b( ); // undefind
實際執行
var a =3;
function b( ) {
var a ; //var a 宣告!提升到此,並值為undefined
console.log( a );
a=4; // 初始化
}
b();
2.函式提升(函式宣告起作用):
函式提升是把整個函式都提到前面去。
function f( ) {
console.log('I am outside!');
}
( function ( ) {
if ( false ) {
// 重複宣告一次函式f
function f( ) {
console.log( 'I am inside!' );
}
}
f( );
}( ) );//輸出'I am inside!'
實際執行:
function f( ) { console.log( 'I am outside!' ); }
( function ( ) {
function f( ) { console.log( 'I am inside!' ); }
if ( false ) {
// 重複宣告一次函式f
}
f( );
}( ) );
3.全域性作用域的會被後面提升上來的覆蓋( var的變數為undefined ), 區域性作用域的會提升到函式內部的頂部;變數只有var的會提升,函式只有函式宣告會提升;
4:函式宣告和函式表示式
function a( ){ } //函式宣告 會提升
所以 :
a( );
function a ( ){ } //正確
var b = function ( ) { } //函式表示式 不會提升
所以:
b( );
var b = function ( ) { } //錯誤
複製程式碼
塊級宣告
es6引入了塊級作用域,並新增了四種宣告let/const/import/class。
1.// 只在當前作用域內可以訪問
{
let a = 10;
var b = 1;
}
b // 1
a // ReferenceError: a is not defined.
2.// 同一作用域裡不允許重複宣告
let a = 'aaa';
let a = 'bbb'; //報錯 :語法錯誤
let a = 'aaa';
{let a = 'bbb';} // 不會報錯
3.// 不會變數提升
console.log(a); // 報錯
let a;
4.//暫時性死區 TDZ
var a =1;
{
let a = 3;
}
// 報錯 :引用錯誤
5.//全域性宣告不會賦值到window物件
window.d = 123
let d = 234
console.log(d); // 234
2.let/const/import/class也遵守上述規則。
3.const是常量,不可再次被賦值(只限於棧記憶體裡資料,棧記憶體放的基本資料型別和引用型別的地址不可變)
複製程式碼
迴圈中使用
1.let
for(let i =0; i<10; i++){
let i = 123;
setTimeout(
()=>{console.log(i);},
1000);
}
// 30786
// 123
console.log(i) // ReferenceError: i is not defined
for(var i =0; i<10; i++){
setTimeout(
()=> {console.log(i);},
1000);
}
// 51
// 10
for(let i =0; i<10; i++){
setTimeout(
()=> {console.log(i);},
1000);
}
// 0 1 2 3 4 5 6 7 8 9
2.const
const 在for迴圈中迴圈一次後會報錯,因為i++會修改值;
在for in 和for of中則可以正常使用,因為每次迴圈會建立一個常量。
// 51
// 10
複製程式碼