javascript基礎var、 let、const

1. 變量聲明、初始化

Javascript中使用一個變量以前須要先聲明,咱們可使用var、let、const來聲明一個變量。若是在給聲明的變量指定初始值,就是初始化。如:javascript

var a = 1; 
let b =1; 
const c = '123'

初始化後,這個變量就在內存中分配了空間,後面的程序就能夠調用了。var、let、const均可以聲明變量,可是各自又不相同。在ES6中,const 定義的變量是必需要初始化賦值,並且之後不能變動, 是一個固定值。而像var,let是能夠只聲明,可是不進行初始化。java

var a; let b;
console.log(a) // => undefined
console.log(b) // => undefined

而在咱們熟知java代碼中,這樣只聲明,可是未賦值是會報錯的面試

int a;
System.out.print(a); // The local variable a may not have been initialized

這是由於javascript的執行引擎(例如V8)會在運行這段代碼以前檢查會這段代碼, 也被稱爲預編譯。發現有var和let聲明的變量,會給這個變量提供一個默認的初始化值undefined。也正是由於要檢查代碼,因此像const所聲明的變量必須初始化、let,const所聲明的變量不能重複聲明。都是在檢查代碼時候,拋出錯誤。瀏覽器

const 必須初始化函數

console.log('hello world')
const a  // a 必須初始化,若是不初始化將會報 Missing initializer in const declaration

clipboard.png
這段代碼在執行的時候,沒有先打印"hello world", 而是直接報錯。我這裏理解是代碼檢查的時候,就已經把錯誤拋出來,程序沒有運行.lua

let聲明後的變量不能重複聲明spa

let b = 1; 
console.log('hello world'); 
let b =2;

clipboard.png
這段代碼在執行的時候,沒有先打印"hello world", 而是直接報錯。我這裏理解是代碼檢查的時候,就已經把錯誤拋出來,程序沒有運行.code

2. 做用域

一個變量的做用域是源程序代碼中定義這個變量的區域。blog

全局做用域:在最頂層聲明的變量成爲全局變量,全局變量擁有全局做用域,它們在程序的任何地方都是可以被訪問到。ip

函數做用域:在函數中聲明的變量只能在函數中被訪問到,函數外面是訪問不到的。

塊級做用域:任何一對花括號({和})中的語句集都屬於一個塊,在這之中定義的全部變量在代碼塊外都是不可見的,咱們稱之爲塊級做用域。

做用域的主要做用是可以控制變量是使用範圍。

在Javascript中,var聲明的變量使用的是函數做用域,而let聲明的變量使用的塊級做用域。ES5中已經存在var來聲明變量,那爲啥到ES6還要使用let來聲明變量?我我的以爲是可以更好的規範咱們程序代碼,避免出現出現一些咱們所沒法預料的錯誤。
經典例子以下:

var arr = [];
for(var i = 0; i < 10; i++) {
    var j = i;
    arr[i] = function() {
        console.log(j)
    }
}
arr[0](); // =>9 而咱們預想的是0

而使用let就能避免出現這樣的問題

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

這是由於let聲明的變量屬於塊級做用域,上面let所聲明的變量j是隻能在for循環每個{}中能被訪問。大體流程以下
當 i = 0,大體生成這樣一個塊

{
   let j = 0;  
   arr[i] = function() {
       console.log(j)
   }
}

當i = 1

{
  let j = 1;
  arr[i] = function() {
     console.log(j)
  }
}

. . . . . .
當i = 9

{
  let j = 10;
  arr[i] = function() {
     console.log(j)
  }
}

當後面去調用arr[0]()這個方法, 這是第一個塊定義的方法, 由於let聲明的變量j是塊{}做用域, 這個方法就只能訪問第一個塊{}中j的值,也就是0;
而當咱們使用var的時候
當 i = 0

{
   var j = 0;  
   arr[i] = function() {
      console.log(j)
   }
}

當 i = 1

{
  var j = 1;
  arr[i] = function() {
     console.log(j)
  }
}

當咱們調用arr[0]()這個方法, 是第一個塊定義的方法,由於var聲明的變量是函數做用域,
因此上面的程序,能夠理解爲

var j; // var 聲明的變量具備聲明提早的做用
var arr = [];
{
    j = 0;
    arr[i] = function() {
        console.log(j)
    }
}
{
    j = 1;
    arr[i] = function() {
        console.log(j)
    }
}
**. . . . . .**
{
    j = 9;
    arr[i] = function() {
        console.log(j)
    }
}
arr[0](); // =>9

arr[0]()運行的時候,因爲沒有做用域的限制,j已經被賦值爲9。因此輸出結果就是9

有人會疑問,爲啥不能直接使用i,而是使用中間變量j去替換?

這裏是爲了讓你們更好的理解,下面直接使用i

當使用var聲明變量

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

換成let聲明變量

var arr = [];
for(let i = 0 ; i < 10; i++) {
   arr[i] = function() {
       console.log(i);
  }
}
arr[0](); // => 0 這是正常的

這樣也是正常的符合預期,可是當我把代碼作了修改後

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

這裏也是let聲明的變量i,可是輸出的結果是10,這裏我看完後很不理解。因而去閱讀了ES的規範 http://www.ecma-international...
clipboard.png

這裏是被認爲第一種狀況。let聲明的i和var聲明i在上面的代碼執行的邏輯是同樣的,就致使上面的情形。這裏小夥伴們須要特別注意。

3. let暫存死區,var聲明提早

先從一個簡單的示例開始吧
執行代碼:

console.log(a);

瀏覽器運行結果以下:
clipboard.png

執行代碼:

if (false) {var b = 1}; 
console.log(b);

瀏覽器運行結果以下:
clipboard.png

執行代碼:

if (false) {let c = 1}; console.log(c);

瀏覽器運行結果以下:
clipboard.png

第一種情形下: 當咱們直接在瀏覽器輸入未聲明的變量a, 有報 a is not defined的錯誤。
第二種情形下: b => undefined
第三種情形下: c is not defined
第二種情形下,變量在聲明以前就已經可以被使用,這種特性被稱非官方的稱爲聲明提早。變量可以提早到哪裏呢?全部使用var聲明的變量被提早至函數頂部。
因而面試中經常使用這樣的問題出現

var global = 'global'
function test () {
    console.log(global);
    var global = 'local';
    console.log(global);
}
test() // undefined, local

這是由於在test函數中,var聲明的global被提早到test函數頂部,注意聲明提早是在預編譯時候執行的,而且給global初始化賦值undeifined。因此實際執行的邏輯,應該是這樣

var global = 'global';
function test() {
    var global = undefined;
   console.log(global);
  global = 'local';
  console.log(global);
}
test(); //undefined, local

變量聲明提早好很差,很明顯,這樣很很差。一個變量尚未聲明就可以被使用,太奇怪了,也太危險了。尤爲是那些習慣了java、c的人看起來,這就是一個異類的特性。
因此,在ES6中,就推薦使用了let來聲明變量。

做用域內 let 聲明的變量不能在聲明以前被使用,若是發如今let聲明的變量在聲明以前被使用,將會拋出 is not defined的錯誤。程序將會被中斷,這個特性被稱爲 暫存死區

關於暫存死區,我沒有看過V8的源碼,不過有個大膽的猜測

let 變量聲明的做用域內,javaScript引擎會把let聲明的變量名收集起來。當程序開始執行的時候,代碼一行行的往下執行,引擎獲取變量以前,首先會判斷此變量的變量名是不是在let聲明的變量集合中。不存在,程序就繼續往下執行。若是存在,就判斷變量名的上一個字符串是否是let。若是是let,就把此變量名從let聲明的集合中去掉,若是不是就拋出錯誤,程序被中斷。
(純屬於我的猜測,方便理解,你們不做爲參考)

下面兩個小例子用於理解暫存死區

console.log('hello world'); c = 1; let c;

clipboard.png

這裏是先打印"hello world",而後再進行報錯,說明是程序在執行到 c = 1的時候報錯。
是運行的錯誤。而你們能夠對比前面的let聲明同一個變量屢次,是程序尚未執行就報錯。因此這裏是運行中的錯誤。能夠推斷出變量暫存死區是發生程序運行中產生的,let聲明的變量不能在聲明以前使用,發現有聲明以前使用的,就會拋出錯誤,中斷程序。

另一個程序

let x = x; 
// x is not defined
x
// x is not defined
let x
// Uncaught SyntaxError: Identifier 'x' has already been declared

clipboard.png
let x = x, 賦值從右往左執行,因此先獲取變量 x, 因而x陷入暫存死區。拋出x is not defined的錯誤,程序中斷。後面再獲取x,仍是拋出x is not defined,x仍是陷入暫存死區。後面的let x,拋出Uncaught SyntaxError: Identifier 'x' has already been declared 錯誤是由於檢查這段代碼的時候,發現let x 已經被聲明,因而拋出錯誤。不能被重複聲明瞭。

總結

let特性大體就幾條

1. let 聲明的變量的做用域是塊級的;

2. let 不能重複聲明已存在的變量;

3. let 有暫時死區,不會被提高;

const特性

1. let的特性它基本都有

2. const必須初始化賦值

var特性

1. var 聲明的變量的做用域是函數做用域;

2. var 可以重複聲明變量;

3. var 聲明的變量可以聲明提早

相關文章
相關標籤/搜索