淺談let和var的區別

淺談let和var的區別javascript

關於var

首先來看一看MDN對於var的解釋:java

1: 變量聲明,不管發生在何處,都在執行任何代碼以前進行處理。(變量提高)
2: 聲明變量的做用域限制在其聲明位置的上下文中,而非聲明變量老是全局的
3: 若是你從新聲明一個JavaScript變量,它將不會丟失其值
... ...
複製代碼

我只摘抄了一部分特性,可是這一部分特性就已經可以讓咱們明白var與let的區別了,在談var與let的區別以前,咱們先簡單看一下var的這些特性。面試

var的變量提高及做用域

首先來看一道面試題:請問下面的代碼會打印出什麼樣的結果?瀏覽器

var a = 99;
f();
console.log(a);
function f(){
    console.log(a);
    var a = 10;
    console.log(a);
}
複製代碼

這道題的考點是做用域及變量提高,首先咱們先將全部的變量進行提高:bash

var a;
function f(){
    var a;
    console.log(a); 
    a = 10;
    console.log(a); 
}
a = 99;
f();
console.log(a);
複製代碼

結果爲:函數

undefined
10
99
複製代碼

咱們首先將全局變量a與函數f以及函數內部的局部變量a進行了變量提高,執行函數f時,因爲函數f內部的第一個console.log打印的a在函數f這個做用域已經聲明過了,但還並未賦值因此首先會打印出來undefined;在函數f內部的第二個console.log打印的結果則是賦值之後的a,且這個a仍然是函數這個做用域中的a,因此打印出來的結果爲 10;在 代碼最後的console.log中的a則是當前做用域即全局變量的a,這個a聲明過且賦值爲99,因此最後一個結果是99。若是你徹底理解了這道題目,那麼相信你天然會了解var的變量提高以及做用域。學習

一不當心就聲明瞭全局變量

咱們首先來看一個MDN的例子:ui

function x() {
  y = 1;   // 在嚴格模式(strict mode)下會拋出 ReferenceError 異常
  var z = 2;
}
x();
console.log(y); // 打印出結果1
console.log(z); // ReferenceError: z 未在 x 外部聲明
複製代碼

在函數x的內部,咱們未對y變量進行任何聲明而是直接對其進行了賦值,這樣作,咱們一不當心就聲明瞭一個全局變量,咱們的本意是在函數x的內部聲明一個局部變量,可是在函數x的外部使用console.log也是能打印出來y的值,其實上面的代碼至關於:spa

var y = 1;
function x(){
    var z;
    y = 1;
    z = 2;
}
x();
console.log(y);
console.log(z);
複製代碼

關於第三點解釋

3: 若是你從新聲明一個JavaScript變量,它將不會丟失其值
複製代碼

關於第三點解釋,咱們用本身的話來翻譯一下翻譯

var 能夠重複對一個變量進行聲明,可是不管怎麼聲明它都是那個當前做用域的那個變量
複製代碼

聽起來有些難懂,咱們不妨看一下代碼:

HTML中有:
<div id=parent></div>
=====================>我是分割線<=====================
在script標籤中:
var parent = document.getElementById('parent');
試問:
console.log(parent);
console.log(window.parent);
這兩個結果在瀏覽器的控制檯中分別打印出什麼?
複製代碼

答案爲:

打印出的結果均爲:id爲parent的div元素
複製代碼

實際上,咱們聲明瞭一個已經聲明過的全局屬性,window.parent全局屬性爲:若是有父窗口,即返回父窗口;若是沒有則返回當前窗口。在本例中,咱們對parent再次聲明,而且咱們對聲明的變量進行了賦值,這也至關於對原有的全局屬性parent進行了覆蓋。也就是說:

var a = 1;
var a = 2;
var a = 3;
var a = 4;
console.log(a);
複製代碼

這段代碼沒有語法錯誤,不過它至關於:

var a;
a = 1;
a = 2;
a = 3;
a = 4;
console.log(a);
複製代碼

關於let

咱們仍是先看一下MDN對於let的解釋:

1: let的做用域是塊,而var的做用域是函數
2: 一個做用域中用let重複定義一個變量將引發 TypeError
... ... 
複製代碼

let的解釋,我也只是摘抄了一部分,若是你想更加詳細地瞭解,能夠搜索MDN~~~

let的做用域

let聲明的變量只在其聲明的塊或子塊中可用,這一點,與var類似。兩者之間最主要的區別在於var聲明的變量的做用域是整個封閉函數。 示例以下:

{
    var dobby = 666;
}
console.log(dobby); // 打印出666
{
    let kim = 666;
}
console.log(kim); // 報錯ReferenceError,kim is not defined
複製代碼

MDN上的例子更加生動形象,我就借來用一下了 : -)

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // 一樣的變量!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // 不一樣的變量
    console.log(x);  // 2
  }
  console.log(x);  // 1
}
複製代碼

從上面的例子能夠看出來,let的做用域,咱們回過頭再來看一看這個問題:

HTML中有:
<div id=parent></div>
=====================>我是分割線<=====================
在script標籤中:
var parent = document.getElementById('parent');
複製代碼

若是咱們非要使用parent這樣一個變量(固然不推薦,由於全局屬性可恥!),讓它表示id爲parent的div元素,而且咱們但願全局屬性window.parent不受影響,咱們就可使用let~

{
    let parent = document.getElementById('parent');
    console.log(parent);
}
console.log(window.parent);
// 除此以外,也可使用當即執行函數,由於再次強調var的做用域是函數
(function(){
    var parent = document.getElementById('parent');
    console.log(parent);
}).call();
複製代碼

let沒有變量提高

與var不一樣,let沒有變量提高,沒有變量提高,沒有變量提高。仍是借用一下MDN的好例子 :-)

function do_something() {
  console.log(bar); // undefined
  console.log(foo); // ReferenceError: foo is not defined
  var bar = 1;
  let foo = 2;
}
複製代碼

咱們來看一下阮一峯的解釋:

ES6 明確規定,若是區塊中存在let和const命令,這個區塊對這些命令聲明的變量,從一開始就造成了封閉做用域。凡是在聲明以前就使用這些變量,就會報錯。
總之,在代碼塊內,使用let命令聲明變量以前,該變量都是不可用的。這在語法上,稱爲「暫時性死區」(temporal dead zone,簡稱 TDZ)
複製代碼

其實簡單一句話歸納就是:let不存在變量的提高,這樣是和var的區別之一

重複定義let變量會引發錯誤

直接上示例:

{
    let dobby = 1;
    let dobby = 2;// TypeError thrown.
}
複製代碼

沒什麼好解釋的 :-)

一道面試題

for (var i = 0; i <10; i++) {  
  setTimeout(function() {  
    console.log(i);        
  }, 1000);
}
複製代碼

for (let i = 0; i <10; i++) {  
  setTimeout(function() {  
    console.log(i);        
  }, 1000);
}
複製代碼

兩個代碼打印的結果是什麼?


這是一道已經被玩爛的面試題,由於我尚未學習到JS的事件循環機制,若是真的讓我說出個之因此然來,個人確沒法進行詳細的說明,可是在後續,我會繼續將這道題單獨寫一篇博客,好好深刻研究其中的機制與奧祕~

var 循環的結果爲:10個10
let 循環的結果爲:0,1,2,3,4,5,6,7,8,9
複製代碼

其實拋開上面的諸多知識點,咱們也能夠簡單先進行一波分析,對於var循環來說,咱們能夠先這樣寫:

var i;
for (i = 0; i <10; i++) {  
  setTimeout(function() {  
    console.log(i);        
  }, 1000);
}
複製代碼

因爲var的變量提高機制,咱們實際上至關於聲明瞭一個全局變量,再回顧下這個代碼:

var a;
a = 1;
a = 2;
a = 3;
a = 4;
console.log(a);
複製代碼

在var循環中,其實咱們打印的i 就是全局的惟一的那個變量,因此會打印出10個10;而對於let的循環則不一樣,咱們回顧一下let的做用域,當i在for循環這個塊使用時,則不會收到外部的一些影響。

總結

var 和 let 究竟有哪些不一樣呢?

  1. var的做用域是函數,而let的做用域則是塊
  2. var有變量提高,而let則沒有
  3. var能夠重複定義,可是在一個scope內,重複定義也只是那一個變量,而重複定義let變量則會引發TypeError錯誤

若有錯誤,還請指出,不甚感激~

相關文章
相關標籤/搜索