關於let、var和const

前言

letconst命令是ES6新增的命令,用來聲明變量,這兩個變量跟ES5中的var有許多不一樣,同時letconst也有不同的地方。而且在ES6中也添加了塊級做用域來解決ES5中做用域存在的問題。node

做用域

官方解釋是:「一段程序代碼中所用到的名字並不老是有效/可用的,而限定這個名字的可用性的代碼範圍就是這個名字的做用域。」  es6

舉個例子來形象的解析下上面的定義:數組

function fn () {
   // 聲明變量
   var name = 'marry'
   
   // 定義內部函數
   function innerFn () {
       console.log(name) // 能夠訪問到name變量
   }
}
console.log(name) // undefined
複製代碼

ES5做用域分爲全局做用域和函數做用域。瀏覽器

  1. 全局做用域
var a = 0;

if (true) {
  var b = 1;
}

console.log(b); // 輸出1
複製代碼

上面代碼中,在全局中定義變量a,稱爲全局變量,在任何一個地方均可以訪問到變量a。安全

  1. 局部做用域

局部做用域也能夠稱之爲函數做用域。bash

function fn () {
  var c = 2;
}

console.log(c); // c is not defined
複製代碼
  1. 做用域鏈

Function對象有一個僅供 JavaScript引擎存取的內部屬性。函數

這個屬性就是[[Scope]][[Scope]]包含了一個函數被建立的做用域中對象的集合。這個集合被稱爲函數的做用域鏈,它決定了哪些數據能被函數訪問。 關於做用域鏈,局部做用域能夠訪問到全局做用域中的變量和方法,而全局做用域不能訪問局部做用域的變量和方法。post

var a = 0;

function fn () {
  var b = 1;
  console.log(a); // 輸出 1
}

// 全局做用域並不能訪問 fn 函數中定義的 b 變量
console.log(b); // b is not defined

fn();
複製代碼

塊級做用域

ES6新增塊級做用域,塊做用域由 { }包括,函數內部,if語句和 for語句裏面的{ }也屬於塊做用域。ui

塊級做用域的出現是爲了解決ES5中做用域的問題:this

  1. 內層變量可能覆蓋外層變量
  2. 用來計數的循環變量泄露爲全局變量。
var tmp = new Date();
function f() {
  console.log(tmp); // 想打印外層的時間做用域
  if (false) {
    var tmp = 'hello world'; // 這裏聲明的做用域爲整個函數
  }
}
f(); // undefined

var s = 'hello';
for (var i = 0; i < s.length; i++) {
  console.log(s[i]); // i應該爲這次for循環使用的變量
}
console.log(i); // 5 全局範圍均可以讀到
複製代碼

塊級做用域

經過var聲明的變量存在變量提高的特性

// var 的狀況
console.log(foo); // 輸出undefined
{ var foo = 2; }
console.log(foo) // 2  

// 運行時,真正運行的是下面的代碼
var = foo
console.log(foo); // 輸出undefined
{ foo = 2; }
console.log(foo) // 2  
複製代碼
  • JavaScript 引擎的工做方式是,先解析代碼,獲取全部被聲明的變量,而後再一行一行地運行。這形成的結果,就是全部的變量的聲明語句,都會被提高到代碼的頭部,這就叫作變量提高。
  • 對於var命令來講,JavaScript的單獨的{}區塊不構成單獨的做用域。可是在JavaScript語言中,單獨使用區塊並不常見。

除此以外,在for循環中:

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

上面代碼中,變量i是var命令聲明的,在全局範圍內都有效,因此全局只有一個變量i。

let 和 const

let 和 const 命令

ES6 新增了let命令,用來聲明變量。它的用法相似於var,可是所聲明的變量,只在let命令所在的代碼塊內有效。

  1. let在{}中聲明的變量只在代碼塊中有效(造成塊級做用域)
{
  let a = 10;
  var b = 1;
}

a // ReferenceError: a is not defined.
b // 1
複製代碼
  1. 不存在變量提高(let和const)

let命令所聲明的變量必定要在聲明後使用,不然報錯。

// var 的狀況
console.log(foo); // 輸出undefined
var foo = 2;

// let 的狀況
console.log(bar); // 報錯ReferenceError
let bar = 2;
複製代碼
  1. 重複聲明報錯(let和const)
let value = 2;
let value = 2; //Uncaught SyntaxError: Identifier 'value' has already been declared
複製代碼
  1. 變量不會掛在頂層對象下面(let和const)

瀏覽器環境頂層對象是: window; node環境頂層對象是: global

var a = 1;
// 若是在 Node環境,能夠寫成 global.a
// 或者採用通用方法,寫成 this.a
window.a // 1

let b = 1;
window.b // undefined
複製代碼

const命令須要注意點

  1. 一旦聲明,必須立刻賦值
let p; var p1; // 不報錯
const p3 = 'abc'
const p3; // 報錯 沒有賦值
複製代碼
  1. 一旦聲明值就不能改變
const p = '不能改變';
p = '報錯' //  Assignment to constant variable
複製代碼
  1. const本質是變量指針不能變
const foo = {};

// 爲 foo 添加一個屬性,能夠成功
foo.prop = 123;
foo.prop // 123

// 將 foo 指向另外一個對象,就會報錯
foo = {}; // TypeError: "foo" is read-only
複製代碼

const所說的一旦聲明值就不能改變,實際上指的是:變量指向的那個內存地址所保存的數據不得改動

  • 對於簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個內存地址,所以等同於常量。
  • 對於複合類型的數據(主要是對象和數組),變量指向的內存地址,地址保存的是一個指針,const只能保證指針是固定的(老是指向同一個地址),它內部的值是能夠改變的(不要覺得const就安全了!)

暫時性死區

只要一進入當前做用域,所要使用的變量就已經存在了,可是不可獲取,只有等到聲明變量的那一行代碼出現,才能夠獲取和使用該變量。

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}
複製代碼

總之,在代碼塊內,使用let命令聲明變量以前,該變量都是不可用的。這在語法上,稱爲「暫時性死區」(temporal dead zone,簡稱 TDZ)。

暫時性死區和不能變量提高的意義在於:

爲了減小運行時錯誤,防止在變量聲明前就使用這個變量,從而致使意料以外的行爲。

本章內容總結

var 和 let、const的區別

  1. 塊級做用域
  2. 不存在變量提高
  3. 暫時性死區
  4. 不可重複聲明
  5. let、const聲明的全局變量不會掛在頂層對象下面

const須要注意點

  1. let能夠先聲明稍後再賦值,而const在 聲明以後必須立刻賦值,不然會報錯。
  2. const 簡單類型一旦聲明就不能再更改,複雜類型(數組、對象等)指針指向的地址不能更改,內部數據能夠更改。

參考文章

相關文章
相關標籤/搜索