目錄2:let 和 const 命令
- var 存在變量提升。爲了糾正這個現象,let,const 聲明的變量會產生“暫時性死區”
暫時性死區的本質:只要進入當前作用域,所使用的變量就已經存在,但是不可獲取。只有等到聲明變量的那一行代碼出現,纔可以獲取和使用該變量。 - let const 不允許在相同作用域內重複聲明一個變量。var 可以!
- ES5規定,函數只能在頂層作用域和函數作用域內聲明,不能在塊級作用域聲明。 但爲了兼容以前的舊代碼,瀏覽器並沒有遵循這個規定,還是支持在塊級作用域內聲明函數,函數會被提升到頂部;
- ES6引入了塊級作用域,明確可以在塊級作用域中聲明函數。ES6規定,塊級作用域之中,函數聲明語句的行爲類似於let,在塊級作用域之外不可引用。但是,實際上,爲了減輕因此產生的不兼容問題,瀏覽器可以不遵守上面的規定,有自己的行爲方式(只對ES6瀏覽器有效):
- 允許在塊級作用域內聲明函數。
- 函數聲明類似於var,即會提升到全局作用域或函數作用域的頭部。
- 同時,函數聲明還會提升到所在的塊級作用域的頭部。
實際運行如下:// 瀏覽器的 ES6 環境 function f() { console.log('I am outside!'); } (function () { if (false) { // 重複聲明一次函數f function f() { console.log('I am inside!'); } } f(); }()); // Uncaught TypeError: f is not a function
注意:// 瀏覽器的 ES6 環境 function f() { console.log('I am outside!'); } (function () { var f = undefined; if (false) { function f() { console.log('I am inside!'); } } f(); }()); // Uncaught TypeError: f is not a function
-
考慮到環境導致的行爲差異太大,應該避免在塊級作用域內聲明函數。如果確實需要,應寫成函數表達式。
-
ES6的塊級作用域必須有大括號
// 第一種寫法,報錯 沒有大括號,所以不存在塊級作用域,而let只能出現在當前作用域的頂層 if (true) let x = 1; // 第二種寫法,不報錯 if (true) { let x = 1; }
-
函數聲明也是如此,嚴格模式下,函數只能聲明在當前作用域的頂層。
- const 實際上保證的,並不是變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動。如果想將對象凍結,應該使用
Object.freeze()
方法。
除了將對象本身凍結,對象的屬性也應該凍結:const foo = Object.freeze({}); // 常規模式時,下面一行不起作用; // 嚴格模式時,該行會報錯 foo.prop = 123;
var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach( (key, i) => { if ( typeof obj[key] === 'object' ) { constantize( obj[key] ); } }); };
- ES5有兩種聲明變量的方法:
var
和function
。ES6除了let
和const
,還有import
和class
。 - 頂層對象:在瀏覽器中指
window
對象,在Node中指global
對象。ES5中,頂層對象的屬性和全局變量是等價的。但是在ES6中,var
function
聲明的全局變量依舊是頂層對象的屬性,而let
const
class
聲明的全局變量不屬於頂層對象的屬性。
var a = 1;
// 如果在 Node 的 REPL 環境,可以寫成 global.a
// 或者採用通用方法,寫成 this.a
window.a // 1
let b = 1;
window.b // undefined
- 頂層對象在各種實現裏面是不統一的:
- 瀏覽器,頂層對象獲取:
window
,self
,frames
, 全局環境中的this
, 函數不作爲對象的方法運行,而是單純作爲函數運行,this
會指向頂層對象,但是嚴格模式下,返回undefined
- Web Worker,頂層對象獲取:
self
- Node,頂層對象獲取:
global
- ES2020引入了
globalThis
作爲頂層對象,任何環境下都可以通過它拿到頂層對象;
- 瀏覽器,頂層對象獲取:
目錄3:變量的解構賦值
- ES6內部使用嚴格相等運算符
===
,來判斷一個位置是否有值。只有嚴格等於undefined
,默認值纔會生效。 - 對象的解構與數組的解構有一個重要的不同。數組的元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量必須與屬性名同名,才能取到正確的值;
- 解構失敗等於
undefined
- 對象解構,如果變量名與屬性名不一致,必須寫成下面這樣:
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
- 對象的解構賦值可以取到繼承的屬性
- 注意點:
- 將一個已經聲明的變量用於解構賦值,需要非常小心:
// 錯誤的寫法 let x; {x} = {x: 1}; // SyntaxError: syntax error // 正確的寫法 let x; ({x} = {x: 1});
- 解構賦值允許等號左邊的模式之中,不放置任何變量名:
({} = [true, false]); ({} = 'abc'); ({} = []);
- 數組本質是特殊的對象,因此可以對數組進行對象屬性的解構:
let arr = [1, 2, 3]; let {0 : first, [arr.length - 1] : last} = arr; first // 1 last // 3
- 字符串也可以解構賦值,如下:字符串會被轉換爲一個類似數組的對象:
類似數組的對象都有一個length
屬性,因此還可以對這個屬性解構賦值const [a, b, c, d, e] = 'hello'; a // "h" b // "e" c // "l" d // "l" e // "o" let {length : len} = 'hello'; len // 5
- 解構賦值的規則是:只要等號右邊的值不是對象或數組,就先將其轉爲對象。由於,
undefined
和null
無法轉爲對象,所以對它們進行解構賦值,都會報錯。 - 解構賦值中,不能使用圓括號:變量聲明語句,函數參數,賦值語句的模式; 可以使用圓括號:賦值語句的非模式部分;
10.變量解構賦值的用途:- 交換變量的值
- 從函數返回多個值
- 函數參數的定義
- 提取JSON數據
- 函數參數的默認值
- 遍歷Map結構
- 輸入模塊的指定方法
目錄四:字符串的擴展
- 字符的Unicode表示法;ES6加強了對
Unicode
的支持,允許採用\uxxxx
形式表示一個字符,其中xxxx
表示字符的Unicode碼點;'\z' === 'z' // true '\172' === 'z' // true '\x7A' === 'z' // true '\u007A' === 'z' // true '\u{7A}' === 'z' // true
- ES6爲字符串添加了遍歷器接口
iterator
,使得字符串可以被for ... of
循環遍歷
傳統的for
循環無法識別這樣的碼點let text = String.fromCodePoint(0x20BB7); //for循環會認爲它包含兩個字符(都不可打印) for (let i = 0; i < text.length; i++) { console.log(text[i]); } // " " // " " for (let i of text) { console.log(i); } // "𠮷"
- 根據標準,JSON數據必須是UTF-8編碼。但是
JSON.stringify()
方法有可能返回不符合UTF-8標準的字符串。UTF-8標準規定,0xD800
到0xDFFF
之間的碼點,不能單獨使用,必須配對使用;比如,\uD834\uDF06
是兩個碼點,但是必須放在一起配對使用,代表字符𝌆
。這是爲了表示碼點大於0xFFFF
的字符的一種變通方法。
JSON.stringify()
的問題在於,它可能返回0xD800
到0xDFFF
之間的單個碼點。爲了確保返回的是合法的UTF-8字符,ES2019改變了JSON.stringify()
的行爲。如果遇到0xD800
到0xDFFF
之間的單個碼點,或者不存在的配對形式,它會返回轉義字符串,留給應用自己決定下一步的處理。·
JSON.stringify('\u{D834}') // ""\\uD834""
JSON.stringify('\uDF06\uD834') // ""\\udf06\\ud834""
目錄5:字符串的新增方法
- ES5提供
String.fromCharCode()
方法,用於從Unicode
碼點返回對應字符,但是這個方法不能識別碼點大於0xFFFF
的字符; - ES6提供了
String.fromCodePoint()
方法,可識別大於0xFFFF
的字符; - 傳統上,JS只有
indexOf
方法,用來確定一個字符串是否包含在另一個字符串中。ES6又提供了三種新的方法:- includes():返回布爾值,表示是否找到了參數字符串。
- startsWith():返回布爾值,表示參數字符串是否在原字符串的頭部。
- endsWith():返回布爾值,表示參數字符串是否在原字符串的尾部。
- 實例方法:
normalize()
includes()
startsWith()
endsWith()
repeat()
:返回一個新的字符串,將原字符串重複n次- ES2017引入了字符串補全長度的功能;如果某個字符串不夠指定長度,會在頭部或尾部補全;
padStart()
用於頭部補全,padEnd()
用於尾部補全;
第一個參數是字符串補全生效的最大長度,第二個參數是用來補全的字符串。'x'.padStart(5, 'ab') // 'ababx' 'x'.padStart(4, 'ab') // 'abax' 'x'.padEnd(5, 'ab') // 'xabab' 'x'.padEnd(4, 'ab') // 'xaba'
- ES2019對字符串實例新增了
trimStart()
和trimEnd()
方法;它們的行爲與trim()
一致,trimStart()
消除字符串頭部的空格,trimEnd()
消除尾部的空格。它們返回的都是新字符串,不會修改原始字符串。
瀏覽器還部署了額外的兩個方法,trimLeft()
是trimStart()
的別名,trimRight()
是trimEnd()
的別名。 matchAll()
方法返回一個正則表達式在當前字符串的所有匹配;