Skip to content

Latest commit

 

History

History

chapter-02

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

ES6 讀書會

let 與 const

tags: let const

關於我


let 與 const

ES6 入門 第二章 作者:阮一峰 http://es6.ruanyifeng.com/#docs/let


let 指令

基本用法

使用 `let` 陳述式來宣告變數,其範圍限於宣告所在的`區塊`
在 `javascript` 中 我們使用 {} 來代表`區塊`

var 與 let 的差別

{
  let a = 10;
  var b = 1;
}

console.log(a) // ReferenceError: a is not defined.
console.log(b) // 1
區塊中,分別用 var 與 let 宣告了兩個不同的變數
在區塊外部,很明確地可以看出差異,使用`let`的變數,只能用在區塊內部。

使用時機

let 適合用在 for 迴圈

與 var 比較
var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[1](); //10
a[6](); //10
使用 var 的時候,i 會不斷地被覆蓋,結果永遠等於 10

使用時機

在迴圈中使用 let

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[1](); // 1
a[6](); // 6
使用 let 的時候,i 不會被覆蓋,每次循環都會是一個新的宣告

Hoisting 提升

宣告提升到其所在區域內頂端的行為

與 var 不同,不會有變數提升的現象。
console.log(foo); //顯示 undefined;
console.log(bar); //報錯 ReferenceError
var foo = 2;
let bar = 2;

暫時性死區

ES6 明確規定,區塊中如果存在 `let` 或 `const` 宣告、這個區塊對這些宣告
會產生封閉的作用區。
簡單說:在宣告 let 跟 const 變數之前,該變數都是不可用的。  
這在語法上,稱為「暫時性死區」(temporal dead zone,簡稱TDZ)。
if (true) {
  // TDZ開始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ結束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}
這樣的設計是為了讓大家養成良好的編程習慣,變量一定要在宣告之後使用

實際測試的時候,測試環境結果有所不同
chrome 50 版本會根據本書上的敘述,表示錯誤。
若用 `Babel` 轉譯則不會顯示錯誤
支援程度可以參考:http://kangax.github.io/compat-table/es6/

暫時性死區

這也表示使用`typeof`來檢查使用`let`宣告的變數,可能會出現錯誤  
範例:
typeof x;// 出現錯誤
let x;
因為在 let 宣告 x 之前,無法使用 x 這個變數

暫時性死區

較為不容易察覺的死區

function bar (x=y,y=2){
	return [x,y];
}
// 參數 x 預設為 y
// 參數 y 預設為 2
因為此時 y 尚未被宣告,所以會報錯

修正的方式

function bar (x=2,y=x){
	return [x,y];
}
// 先宣告 x 再宣告 y

let 不允許重複

`let` 不允許在同一個區塊內宣告同一個變數
function(){
	let a = 10;
	let a = 9 // 發生錯誤
}
所以不能再函式內部重新宣告參數
function func(arg){
	let art; // 發生錯誤
}
function func(arg){
	{
		let arg; // 不發生錯誤
	}
}

為什麼需要區塊範圍變數 ?

在`ES5`中只有全域變數與函式變數,沒有區塊範圍變數。
因此造成了很多不合理的狀況。

狀況一

var tmp = new Data();

function f(){
	console.log(tmp); // undefined
	if(false){
		var tmp = "hello world";
	}
}
f();
在 `es5` 執行之後,tmp 在函數內輸出的結果會是 `undefined`,
由於 `tmp` 變數提升,導致函式內部的變數,覆蓋了外部的 `tmp`

狀況二

var s = 'hello';
for(var i=0 ; i< s.length; i++){
	console.log(s[i]);
}
console.log(i) //5
變數 i 只用來提供迴圈執行,執行結束後,並沒有消失,變成了全域變數。

ES6 區塊作用域

let 提供了新的變數作用域

function f1() {
  let n = 5; //5
  if (true) {
    let n = 10; //10
  }
  console.log(n); // 5
}
若使用 var 最後輸出結果會變成 10

let 限制變數作用區域

{{{
    {
      let a = 1;
        {let b = 1};
        console.log(b); //報錯
    }
  	console.log(a); //報錯
}}}

過去我們希望隔離不同的變數區域時,
會需要用到立即執行匿名函式
像這樣:

(function(){
  var a = 0;
})();
//確保 a 不會污染全域

console.log(a); // undefined

//在ES6中可以改成用區塊撰寫
{
 let b = 0;
}
console.log(b);

區塊中的函式

ES6 中規定 函數本身的作用域,在其所在的塊級作用域之內。
function f(){ console.log('我在外面');}
(function(){
	if(true){
		function f(){ console.log('我在裡面');}
	}
	f();
})();
若由 es5 的執行環境執行時,結果會是'我在裡面'  
若由 es6 執行環境執行,結果會是'我在外面'
在ES6中內部宣告的函數皆不會影響到作用域的外部

嚴格模式 use strict

在ES5嚴格模式規定,函式只能在頂層作用域或是函式內宣告,  
其他狀況(例如`if`指令、循環指令區)的宣告都會報錯。
//ES5
`use strict`
if(true){
	function f(){ } //報錯
}

ES6 嚴格模式

ES6由於引入了塊級作用域,這種情況可以理解成函數在塊級作用域內聲明
// 不報錯
'use strict';
if (true) {
  function f() {}
}

// 報錯 不能缺少 { }
'use strict';
if (true)
  function f() {}

f();  // 無法執行因為在區塊外部

Const 命令

基本用法


宣告常數

const 是一個唯讀的常數。
一旦被宣告,常數的值就不能變。

嚴格模式
'use strict';
const PI = 3.1415; // 3.1415
PI = 3;  // TypeError: "PI" is read-only
非嚴格模式,不報錯,且PI的值不變。
const PI = 3.1415; // 3.1415
PI = 3;  // 3.1415

宣告常數

嚴格模式'use strict';
'use strict';
const foo;
// SyntaxError: missing = in const declaration
非嚴格模式
const foo;
foo = 1; // 重新賦值無效
foo // undefined

const 作用域

作用域與 let 相同,同樣存在暫時性死區,只能在宣告後面的位置使用。
if(true){
	console.log(MAX); //錯誤參照
	const MAX = 5;
}
MAX // 未定義

宣告不可重複

與 let 相同,同一個變數名稱在同一區塊不得重複宣告。
var message = "Hello!";
let age = 25;

// 以下兩行都會報錯
const message = "Goodbye!";
const age = 30;

const 宣告物件及陣列

對於複合資料類型之數據,像是 array 或 obj,  
宣告時指向的是數據所在的參考,無法保證變數內的數據不變。
所以當宣告一個物件為常數的時候要很小心。
const foo = {}; //foo 指向物件位置
foo.prop = 123; //物件內容可以被修改

foo.prop; //123

避免常數物件或陣列異動

為了避免宣告的物件或陣列被修改,
可以使用 Object.freeze();
方法凍結該物件或陣列。

const obj = Object.freeze({}); //宣告一個不能異動的常數物件
const arr = Object.freeze([]); //宣告一個不能異動的陣列物件
// 下面的指令沒有作用
// 嚴格模式下會顯示錯誤
obj.prop = 123;
arr[0] = 123;
console.log(obj.prop);  //無法執行
console.log(arr);		//無法執行
在 http://www.es6fiddle.net/ 中執行會出錯,必須拿掉修改才會正常執行

凍結屬性

除了物件本身應該要凍結之外,物件的屬性若指向物件時應該也要凍結,凍結的方法
var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, value) => {
    if ( typeof obj[key] === 'object' ) {
      constantize( obj[key] );
    }
  });
};

變數宣告

ES5只有兩種聲明變量的方法:
varfunction

除了原本的 var 跟 function 之外

ES6有 letconst
還有 importclass

共六種變數宣告方式

全域物件

全域物件,是指最上層的物件,
在瀏覽器中指的是 `window` 物件
在`Node.js`指的是`global` 物件
//在 ES5 中 全域物件的屬性,就等於全域變數
window.a = 1;
console.log(a); // 1

a = 2;
console.log(window.a); //2;

b = 3; //沒有宣告
console.log(window.b); //3;
在 ES5 中沒有宣告的全域變數,自動變成為全域物件window的屬性,
不容易除錯,很容易就產生問題。

全域物件

從ES6開始,全域變數將逐步與全域物件的屬性脫鉤。

ES6為了改變這一點,但仍然保持兼容性,  
var 和 function 宣告的全域變數,依舊是全域物件的屬性;
另一方面規定,let命令、const命令、class命令聲明的全域變數,
不屬於全域物件的屬性。
//如果是在最外層宣告
var a = 1;
window.a; //1

let b = 1;
window.b // undefined

結論

let 與 const 是用來宣告區塊中的變數
這些變數在區塊外部是無效的
並且要注意宣告與使用的順序

謝謝指教
期待下次的分享