let和const的一些知識點

let和const

  1. 不能夠重複聲明
  2. 不會發生變量提高,所以必須在聲明以後使用,不然報錯!
  3. 只在聲明所在的塊級做用域內有效

let

同一個做用域內不能重複聲明同一個變量:數組

function func() {
  let a = 10;
  var a = 1;
}
func()    // 報錯

function func() {
  let a = 10;
  let a = 1;
}
func()    // 報錯
function func(arg) {
  let arg;
}
func()    // 不能在函數內部從新聲明參數!!!!!由於參數等同於在函數內部var聲明的一個局部變量

function func(arg) {
  {
    let arg;
  }
}
func()    // 該例子不報錯,是由於在func裏面增長{}等同於又新建了一個做用域,所以{}裏的arg不是參數arg

let能夠只聲明不賦值,沒有值的時候會輸出undefined閉包

console.log(let1)  // let1 is not defined
let let1;
console.log(let1)  //  undefined
let1 = 1;
console.log(let1)  // 1

let在for循環中的應用:【下面將給出我對這兩個例子的理解】

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

該demo的執行過程以下:函數

var a = [];   // 全局變量spa

 

var i = 0;    // 全局變量,第1次循環指針

a[0] = function(){ console.log(i) }   // 此時的i不是0,是由於這裏只是聲明瞭這個函數,並無執行它,即沒有建立這個函數的上下文,所以i不會沿着做用域鏈向外去找i的值code

 

var i = 1;  // 第2次循環,由於是全局變量,所以i = 1會替換上一個i = 0對象

a[1] = function(){ console.log(i) }  // 同a[0],全部該函數的執行的做用域是全局做用域blog

 

var i = 2  // 第3次循環,同理,i = 2會替換上一個i = 1內存

...作用域

var i = 9;  // 第10次循環,i = 9會替換上一個i = 8

a[9] = function(){ console.log(i) }

直到var i = 10,10<10不知足循環條件,則跳出循環,繼續向下執行全局做用域下面的語句:a[6](); 

調用a[6]函數,並建立a[6]函數的函數上下文,執行該函數內部的console.log(i),這個函數中沒有i,所以順着做用域鏈向外去找i,而此時全局變量var i = 10,所以輸出10

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

第二個demo的執行過程以下:

 

let a = [];   // 全局變量

 

 

{  // 第1次循環

  let i = 0;  // let使得for循環變成一個塊級做用域,則let i = 0是這個塊級做用域下的局部變量

 

  a[0] = function(){ console.log(i) }   // 注意!!!因爲let的聲明建立了一個塊級做用域,此時的a[0]這個函數就是一個閉包

 

}

 

{  // 第2次循環 

  let i = 1;   // 注意!!!此時的let i = 1和let i = 0是屬於兩個不一樣的塊級做用域,所以二者的值並不會相互影響

  a[1] = function(){ console.log(i) }   // 同a[0]

}

 

...

{  // 第10次循環

 

  let i = 9;

  a[9] = function(){ console.log(i) }  // 一樣該函數也是一個閉包!

}

{

 

  直到let i = 10,不知足循環條件,跳出循環,注意!!該代碼塊中不存在閉包所以,let i = 10在此次循環以後代碼塊隨即被銷燬

}

繼續向下執行全局做用域下面的語句:a[6](); 

 

調用a[6]函數,進入該塊級做用域的代碼環境,在該閉包內部找i值,若是沒有則順着做用域鏈向外去找i,而此時塊級做用域內有let i = 6,所以輸出6

此時閉包被調用,因此整個代碼塊中的變量i和a[6]函數被銷燬。

 

 

暫時性死區

在代碼塊內,凡是用let聲明變量以前,該變量都是不可以使用的,這在語法上叫作「暫時性死區」(Temporal Dead Zone,簡稱TDZ)。因此,凡是在聲明以前使用該變量,就會報錯!

var tmp = 'ning';

if (true) {
  // TDZ開始
  tmp = 'abc'; // ReferenceError  
  console.log(tmp); // ReferenceError  去掉這兩句才能繼續向下執行代碼並輸出響應的結果

  let tmp; // TDZ結束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}

從上面的代碼能夠看出雖然全局有一個tmp變量,可是在if這個塊級做用域下let聲明瞭一個局部變量tmp,致使該局部變量tmp綁定(binding)了這個區域,因此在let聲明以前使用它,都屬於該tmp的死區,會報錯!

在沒有let以前,使用typeof是百分之百不會報錯的,由於若是typeof一個未聲明的變量,會輸出'undefined'

可是,暫時性死區的出現使得typeof的使用須要當心!由於若是在let聲明前typeof該變量則會報錯!

typeof x; // ReferenceError
let x;

 

 

const

const聲明一個只讀的常量。一旦聲明其值就不能改變,而且一旦聲明變量就必須當即初始化,只聲明不賦值就會報錯

console.log(const1)  // Missing initializer in const declaration
const const1;
console.log(const1)  // Missing initializer in const declaration

cosnt也是在塊級做用域內有效,例子以下:

        if (true) {
            const MAX = 5;
        }
        console.log(MAX);   // Uncaught ReferenceError: MAX is not defined

const命令聲明的常量不能提高,只能在聲明的位置以後使用const,例子以下:

        if (true) {
            console.log(MAX); // Uncaught ReferenceError: MAX is not defined
            const MAX = 5;
        }

const一樣不能重複聲明!例子以下:

function func() {
  const a = 10;
  var a = 1;
}
func()    // 報錯

function func() {
  const a = 10;
  let a = 1;
}
func()    // 報錯

 

const的本質:

其實是保證的是變量指向的內存地址保存的數據不能改動!

對於基本數據類型而言,內存地址裏保存的就是值,因此等同於常量

而對於引用類型而言(主要是對象和數組),變量指向的內存地址中保存的是 指向實際數據的指針,所以只要該指針是固定不變的,該指針指向的堆內存的數據是否變化const是不在乎的!所以,將一個對象聲明爲常量必須很是當心!!!!例子以下:

        const obj = {};

        obj.attr = 'ning';
        console.log(obj.attr)  // 'ning'

        obj = { attr: 'li' }
        console.log(obj.attr)  // Uncaught TypeError: Assignment to constant variable.

常量obj存的是一個地址,指向一個對象,不可變的是這個地址,而對象自己裏的內容是可變的!

相關文章
相關標籤/搜索