一、存在變量提高,實際上var不管在哪裏聲明,都會被當作當前的做用域頂部聲明變量。 二、能夠重複聲明,後聲明的變量會覆蓋前聲明的變量。
一、不存在變量提高。 二、禁止重複聲明。 三、塊級做用域,只在當前做用域塊有用。 四、臨時死區,並且不能在聲明以前訪問它。
一、const 聲明的是常量,其值一旦肯定後不能夠修改 二、const 聲明常量時候必需要進行賦值 三、const 不存在變量提高,一旦執行快外就會當即銷燬。 四、const 只能在當前代碼塊級有效, 五、const 不能重複聲明相同常量。 六、const聲明不容許修改綁定,但容許修改值,也就是說用const建立對象後,能夠修改該對象的屬性值。
每種編程語言都有變量,聲明變量的方法各不一樣,在JavaScript裏面,最經典的var聲明一個變量,當ECMAScript6出現後,新增了2個聲明變量的方法:let和const,那什麼時候建立變量,用什麼聲明變量方法會更好呢?編程
var聲明一個變量時候,只須要 var name; 或者聲明賦值var name = "Bob";閉包
實際上var不管在哪裏聲明,都會被當作當前的做用域頂部聲明變量。編程語言
// var 的變量提高機制 function getValue(condition) { if (condition) { var values = 'Bob'; return values; } else { console.log(values); // 這裏訪問到values 是undefined,緣由下面解釋: return null; } } // 緣由解釋:爲何上面的代碼else還能訪問values的值,雖然是undefined // 不管變量values都會被建立,在編譯過程當中,JavaScript引擎會將上面的getValue函數修改爲這樣: function getValue(condition) { // 重點看這裏,變量values的聲明被提高到函數頂部 var values; if (condition) { values = 'Bob'; return values; } else { console.log(values); // 因此這裏訪問到是聲明過的但未賦值的values,因此是undefined。 return null; } }
塊級聲明用於聲明在指定的塊的做用域以外沒法訪問的變量函數
let聲明變量和var聲明變量,但let有本身的四個特徵:code
咱們能夠把剛纔聊到的getValue函數修改一下:對象
// let 塊級做用域 && 不存在變量提高 function getValue(condition) { if (condition) { // 使用let聲明變量 let values = 'Bob'; return values; } else { console.log(values); // 這裏報錯: ReferenceError: values is not defined.. // 緣由就是用let聲明的變量,是不存在變量提高的, // 並且values變量只能在if{ 這個做用塊裏面有效 } 外面是訪問不到的 // 同時,在外面訪問不只會訪問不到,並且會報錯 return null; } } // let 禁止重複聲明相同變量 function getValue() { var values = "Bob"; let values = {name: 'Bob'}; // 使用let聲明變量禁止重複聲明已經有的變量名 // 不然報錯:SyntaxError: Identifier 'values' has already been declared }
function getValue() { // 聲明一個常量 const USER_NAME = "梁鳳波"; // 禁止重複聲明相同常量,不然報錯:TypeError: Assignment to constant variable. // const USER_NAME = "Bob"; // 記住:const聲明不容許修改綁定,但容許修改值, // 也就是說用const建立對象後,能夠修改該對象的屬性值 const STUDYENT = { name: '梁鳳波' }; console.log(`STUDYENT.name = ${STUDYENT.name}`); // STUDYENT.name = 梁鳳波 STUDYENT.name = 'Bob'; console.log(`STUDYENT.name = ${STUDYENT.name}`); // STUDYENT.name = Bob }
// 在for循環內用var 聲明,在外面訪問到的是for循環後的結果 for (var i = 0; i < 10; i++) { } console.log(`i = ${i}`); // i = 10 // 在for循環內用let 聲明,在外面 訪問不到,塊級做用域問題 for (let i = 0; i < 10; i++) { } console.log(`i = ${i}`); // ReferenceError: i is not defined
// 通過for循環後,在外面訪問i,是直接訪問到告終果i = 10 let funcs = []; for (var i = 0; i < 10; i++) { funcs.push(function () { console.log(i); }) } funcs.forEach(func => { func() // 分別輸出10次10 });
緣由:循環裏每次迭代同時共享着變量i,循環內部建立的函數全保留相同變量的引用,循環結束時候i的值變爲10,因此每次調用console.log(i)時候回輸出數字10ip
爲了解決這個問題,能夠在循環中使用當即調用函數表達式(IIFE),以強制生成計數器變量的副本:作用域
// 若是要理想效果,在外面分別輸出 0 ~ 9, // 可使用閉包暴露出去 let funcs = []; for (var i = 0; i < 10; i++) { funcs.push((function (val) { return function () { console.log(val); } }(i))) } funcs.forEach(func => { func() });
let funcs = []; for (let i = 0; i < 10; i++) { funcs.push(function () { console.log(i); }) } funcs.forEach(func => { func() // 分別輸出 0 ~ 9 });
let 聲明模仿上述示例IIFE所作的一切簡化循環過程,每次迭代循環都會建立一個新變量,並以以前迭代中同名變量的值將其初始化。get
let funcs = []; let obj = { a: true, b: true, c: true } for (const key in obj) { funcs.push(function () { console.log(key); }) } funcs.forEach(func => { func() // 分別輸出 a, b, c Authorization });
let和const聲明循環,const循環是不能改變key的值,const 循環應該使用for-in,for-of,其餘和let示例同樣,由於每次迭代不會像var循環例子同樣修改已有的綁定,而是會建立一個新綁定。it
var RegExp = "Bob"; // 即便是全局對象RegExp定義在window,也不能倖免被var聲明覆蓋 console.log(RegExp); // Bob console.log(window.RegExp); // Bob let RegExp = "Bob"; // 用let或const聲明不能覆蓋全局變量,而只能屏蔽它 console.log(RegExp); // Bob console.log(window.RegExp); // undefined console.log(window.RegExp === RegExp); // false const ncz = 'Hi!' console.log('ncz' in window); // false
默認使用const,只在確實需求改變變量的值使用let,這樣就能夠在某種程度上實現代碼的不可變,從而防止默寫錯誤產生。