變數宣告提升
ES6之前我們一般使用var來宣告變數,提升簡單來說就是把我們所寫的類似於var a = 123;這樣的程式碼,宣告提升到它所在作用域的頂端去執行,到我們程式碼所在的位置來賦值。
function test () {
console.log(a); //undefined
var a = 123;
};
test();
//上述程式碼a的結果是undefined,它的實際執行順序如下:
function test () {
var a;
console.log(a);
a = 123;
}
test();
複製程式碼
a = 1;
var a;
console.log(a); //1
複製程式碼
第一眼看到的時候是不是會認為undefined, 記住宣告會提升到作用域頂端。
console.log(v1);
var v1 = 100;
function foo() {
console.log(v1);
var v1 = 200;
console.log(v1);
}
foo();
console.log(v1);
複製程式碼
列印出來的結果
//undefined
//undefined
//200
//100
複製程式碼
其實上面程式碼等價於
var v1
console.log(v1);
v1 = 100;
(function(){
var v1 ;
console.log(v1);
v1 = 200;
console.log(v1);
})();
console.log(v1);
複製程式碼
這樣就很容易看出來了。
函式提升
javascript中不僅僅是變數宣告有提升的現象,函式的宣告也是一樣;具名函式的宣告有兩種方式:
函式宣告式 和 函式字面量式
//函式宣告式
function bar () {}
//函式字面量式
var foo = function () {}
複製程式碼
函式字面量式的宣告和變數提升的結果是一樣的,函式只是一個具體的值;
但是函式宣告式的提升現象和變數提升略有不同
console.log(bar);
function bar () {
console.log(1);
}
//列印結果:ƒ bar () {
// console.log(1);
//}
複製程式碼
執行順序相當於:
function bar () {
console.log(1);
}
console.log(bar);
複製程式碼
函式提升是整個程式碼塊提升到它所在的作用域的最開始執行
foo(); //1
var foo;
function foo () {
console.log(1);
}
foo = function () {
console.log(2);
}
複製程式碼
這就是函式優先規則。
let的含義及let與var的區別:
1 let 宣告的變數只在它所在的程式碼塊有效;
for (let i = 0; i < 10; i++) {
console.log(i);
}
console.log('aaa');
console.log(i); // i is not defined
複製程式碼
上面程式碼中,計數器i只在for迴圈體內有效,在迴圈體外引用就會報錯。如下var程式碼:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
}
}
a[6](); // 10
複製程式碼
變數i是var宣告的,在全域性範圍內都有效,所以每一次迴圈,新的i值都會覆蓋舊值,導致最後輸出的是最後一輪i的值。 但是如果使用let,宣告的變數僅在塊級作用域內有效,最後輸出的是6. 如下:
var b = [];
for (let j = 0; j < 10; j++) {
b[j] = function() {
console.log(j);
}
}
b[6](); // 6
複製程式碼
2 不存在變數提升
let 不像var 那樣會發生 ‘變數提升’ 現象,因此,變數需要先宣告然後再使用,否則報錯;
// var 的情況
console.log(foo); // undefined
var foo = 2;
// let的情況;
console.log(bar); // 報錯
let bar = 2;
複製程式碼
3 暫時性死區
快級作用域記憶體在let命令,它所宣告的變數就繫結在這個區域,不再受外部影響;如下程式碼:
var tmp = 123;
if (true) {
tmp = 'abc';
let tmp;
console.log(tmp); // tmp is not defined
}
複製程式碼
上面程式碼定於全域性變數tmp,但是在快級作用域內let又宣告瞭一個區域性變數tmp,導致繫結了這個快級作用域;因此列印出tmp會報錯。
不允許重複宣告
let 不允許在相同作用域內,重複宣告同一個變數。如下程式碼報錯
function a() {
let a = 10;
var a = 1;
console.log(a);
}
a();
function a() {
let a1 = 10;
let a1 = 1;
console.log(a1);
}
a();
複製程式碼
也不能在函式內部重新宣告引數。
function func1(arg) {
let arg; // 報錯
}
function func2(arg) {
{
let arg; // 不報錯
}
}
複製程式碼
ES6的塊級作用域
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
f1()
複製程式碼
上面的程式碼有2個程式碼塊,都宣告瞭變數n,執行後輸出5,說明了外層程式碼塊不受內層程式碼塊的影響,如果使用了變數var,那麼輸出的就是10;
const命令
const 宣告一個只讀的常量,一旦宣告,常量的值就不允許改變。
const a = 1;
a = 2;
console.log(a); //報錯
複製程式碼
也就是說 const 一旦宣告瞭變數,就必須初始化,不能留到以後賦值。如果使用const宣告一個變數,但是不賦值,也會報錯;如下程式碼:
const aa; // 報錯
複製程式碼
const的作用域與let命令相同;只在宣告所在的塊級作用域內有效。
if (true) {
const aa = 1;
}
console.log(aa); // 報錯
複製程式碼
不可重複宣告 (和let一樣)
var message = "Hello!";
let age = 25;
// 以下兩行都會報錯
const message = "Goodbye!";
const age = 30;
複製程式碼
但是對於複合型別的變數,比如陣列,儲存的是一個地址,不可改變的是這個地址,即不能把一個地址指向另一個地址,但是物件本身是可變的,比如可以給它新增新的屬性;如下程式碼:
const a33 = [];
a33.push('Hello'); // 可執行
a33.length = 0; // 可執行
a33 = ['55'] // 報錯
複製程式碼