一直以來咱們都是經過var關鍵字定義JavaScript變量。typescript
var a = 10;
var聲明的變量var聲明能夠在包含它的函數,模塊,命名空間或全局做用域內部任何位置被訪問。
好比在其它函數內部訪問相同的變量:函數
function f() { var a = 10; return function g() { var b = a + 1; return b; } } var g = f(); g(); // returns 11;
這些做用域規則可能會引起一些錯誤。code
function sumMatrix(matrix: number[][]) { var sum = 0; for (var i = 0; i < matrix.length; i++) { var currentRow = matrix[i]; for (var i = 0; i < currentRow.length; i++) { sum += currentRow[i]; } } return sum; } sumMatrix([[1,2],[3,4]])
通常認爲,運行的結果應該爲10。但結果並不是如此,由於全部i都引用相同的函數做用域內的變量,裏層的for循環會覆蓋變量i,因此如今的結果是3而不是10。因此使用var聲明時,它不在意你聲明多少次你只會獲得1個。ip
看下面這段代碼:ci
for (var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 100 * i); }
咱們指望的結果是:每隔100*i毫秒打印出1個數字,順序爲1-10,但實際打印出的所有都是10。這是由於setTimeout在若干毫秒後執行一個函數,而且是在for循環結束後。 for循環結束後,i的值爲10。 因此當函數被調用的時候,它會打印出 10!咱們不得不利用一個當即執行函數解決這個問題:作用域
for(var i = 0; i < 10; i++){ (function(i){setTimeout(()=>console.log(i), i * 100);})(i) }
var聲明的變量帶給咱們這麼多困擾,因此在typescript中加入了let對變量進行聲明。get
let與var的寫法一致:input
let hello = "Hello!";
let聲明的變量和var聲明的變量有不少不一樣之處,let解決了var變量帶來的困擾。it
當用let聲明一個變量,它使用的是塊做用域。 不一樣於使用 var聲明的變量那樣能夠在包含它們的函數外訪問,塊做用域變量在包含它們的塊或for循環以外是不能訪問的。io
function f(input: boolean) { let a = 100; if (input) { let b = a + 1; return b; } return b; }
在上面代碼中,a能夠在if語句中被訪問,由於a在函數語句中聲明,在if語句以外,而b就不能在if語句塊以外被訪問,由於b是在if語句塊中被聲明的。
在catch語句裏聲明的變量也具備一樣的做用域規則。
try { throw "oh no!"; } catch (e) { console.log("Oh well."); } console.log(e); //error
在catch語句塊外,e是不能被訪問的。
在let語句聲明以前訪問let聲明的變量,結果爲undefined。
function foo() { // okay to capture 'a' return a; } console.log(foo()); // let a = 10;
var聲明時,不論你聲明多少次,你只會獲得1個。
var x = 10; console.log(x); //10 var x = 20; console.log(x); //20
let聲明,須要遵循塊做用域規則,在一個塊做用域中重複聲明變量會產生錯誤提示,另外一個用var聲明也不容許。
function g() { let x = 100; var x = 100; // error: can't have both declarations of 'x' }
塊級做用域變量能夠用函數做用域變量來聲明。 可是塊級做用域變量須要在明顯不一樣的塊裏聲明。
function f(condition, x){ if(condition){ let x = 100; return x; } return x; } console.log(f(true, 0)); //100 console.log(f(false, 0)); //0
在一個嵌套做用域裏引入一個新名字的行爲稱作屏蔽。 它是一把雙刃劍,它可能會不當心地引入新問題,同時也可能會解決一些錯誤。 例如,假設咱們如今用 let重寫以前的sumMatrix函數。
function sumMatrix(matrix: number[][]){ let sum = 0; for(let i = 0; i < matrix.length; i++){ let current = matrix[i]; for(let i = 0; i < current.length; i++){ sum += current[i]; } } return sum; } console.log(sumMatrix([[1,2],[3,4]])); //10
此次能夠獲得正確的結果10,由於內層循環中的i屏蔽了外層循環中的i。但這種寫法是不推薦的。
function theCityThatAlwaysSleeps() { let getCity; if (true) { let city = "Seattle"; getCity = function() { return city; } } return getCity(); }
上面這段代碼能夠正常執行。由於咱們已經在city的環境裏獲取到了city,因此就算if語句執行結束後咱們仍然能夠訪問它。
當let聲明出如今循環體裏時擁有徹底不一樣的行爲,針對每次迭代都會建立一個新做用域。因此在 setTimeout例子裏咱們僅使用let聲明就能夠了。
for(let i = 0; i < 10; i ++){ setTimeout(()=>console.log(i), i*100); }
會輸出與預料一致的結果:1 2 3 4 5 6 7 8 9
const 聲明是聲明變量的另外一種方式。它們與let聲明類似,可是就像它的名字所表達的,它們被賦值後不能再改變。
const numLivesForCat = 9;
它們擁有與 let相同的做用域規則,可是不能對它們從新賦值。
const kitty = { name: "Aurora", numLives: numLivesForCat, } // Error kitty = { name: "Danielle", numLives: numLivesForCat };
但const變量內部的狀態仍是能夠改變的。
const kitty = { name: "Aurora", numLives: numLivesForCat, } // all "okay" kitty.name = "Rory"; kitty.name = "Kitty"; kitty.name = "Cat"; kitty.numLives--;
最後,說說let與const的區別,引用官網的建議:
使用最小特權原則,全部變量除了你計劃去修改的都應該使用const。 基本原則就是若是一個變量不須要對它寫入,那麼其它使用這些代碼的人也不可以寫入它們,而且要思考爲何會須要對這些變量從新賦值。 使用 const也可讓咱們更容易的推測數據的流動。