淺談let和var的區別javascript
首先來看一看MDN對於var的解釋:java
1: 變量聲明,不管發生在何處,都在執行任何代碼以前進行處理。(變量提高)
2: 聲明變量的做用域限制在其聲明位置的上下文中,而非聲明變量老是全局的
3: 若是你從新聲明一個JavaScript變量,它將不會丟失其值
... ...
複製代碼
我只摘抄了一部分特性,可是這一部分特性就已經可以讓咱們明白var與let的區別了,在談var與let的區別以前,咱們先簡單看一下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);
複製代碼
咱們仍是先看一下MDN對於let的解釋:
1: let的做用域是塊,而var的做用域是函數
2: 一個做用域中用let重複定義一個變量將引發 TypeError
... ...
複製代碼
let的解釋,我也只是摘抄了一部分,若是你想更加詳細地瞭解,能夠搜索MDN~~~
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();
複製代碼
與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 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 究竟有哪些不一樣呢?
若有錯誤,還請指出,不甚感激~