一直以来咱们都是经过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也可让咱们更容易的推测数据的流动。