變數和函式宣告提升,let和var const區別

南果梨發表於2018-10-31

變數宣告提升

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']  // 報錯
複製程式碼

相關文章