ES6學習筆記
學習開課吧 石川老師視頻教程
兼容性
各大瀏覽器的最新版本,對 ES6 的支持可以查看kangax.github.io/compat-table/es6/。隨着時間的推移,支持度已經越來越高了,超過 90%的 ES6 語法特性都實現了。
ES6(ES2015)——IE10+、Chorme、FirFox、移動端、NodeJS
編譯轉換:
- 在線轉換
- 提前編譯(babel==browser.js)
ES6特性
- 變量
- 函數
- 數組
- 字符串
- 面向對象
- Promise(異步操作)
- generator(yield)(異步操作)
- 模塊化
1.變量
var
- 可重複聲明
- 無法限制修改(無常量概念)
- 沒有塊級作用域
let 不能重複聲明,變量:可修改,有塊級作用域,不存在變量提升
const 不能重複聲明,常量:不可修改,有塊級作用域,不存在變量提升,一旦聲明必須初始化
<script>
window.onload = function(){
var aBtn = document.getElementsByTagName('input');
for(var i=0;i<aBtn.length;i++){
aBtn[i].onclick = funtion(){
arlet(i);//3
};
}
}
</script>
<body>
<input type="button" value="按鈕1">
<input type="button" value="按鈕2">
<input type="button" value="按鈕3">
</body>
以前解決:
<script>
window.onload = function(){
var aBtn = document.getElementsByTagName('input');
for(var i=0;i<aBtn.length;i++){
(function (i){
aBtn[i].onclick = funtion(){
arlet(i);
};
})(i);
}
}
</script>
es6 let解決
<script>
window.onload = function(){
var aBtn = document.getElementsByTagName('input');
for(let i=0;i<aBtn.length;i++){
aBtn[i].onclick = funtion(){
arlet(i);//3
};
}
}
</script>
暫時性死區
ES6 明確規定,如果區塊中存在let和const命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。
總之,在代碼塊內,使用let命令聲明變量之前,該變量都是不可用的。這在語法上,稱爲“暫時性死區”(temporal dead zone,簡稱 TDZ)。
變量一定要在聲明之後使用,否則就報錯。(typeof)
暫時性死區的本質就是,只要一進入當前作用域,所要使用的變量就已經存在了,但是不可獲取,只有等到聲明變量的那一行代碼出現,纔可以獲取和使用該變量。
2.箭頭函數
//標準函數:
function 名字(){
}
//箭頭函數
()=>{
}
- 如果只有一個參數,圓括號()可以少
- 如果只有一個return,大括號{}可以少
3.函數參數
參數拓展/展開
擴展運算符(spread)是三個點(…)。它好比 rest 參數的逆運算,將一個數組轉爲用逗號分隔的參數序列。
function show(a,b,...args){
alert(a);
alert(b);
alert(args);
}
show(12,15,8,9,20) //12 15 8,9,20
擴展運算符後面還可以放置表達式。
const arr = [
...(x > 0 ? ['a'] : []),
'b',
];
如果擴展運算符後面是一個空數組,則不產生任何效果。
[...[], 1]
// [1]
注意,只有函數調用時,擴展運算符纔可以放在圓括號中,否則會報錯。
(...[1, 2])
// Uncaught SyntaxError: Unexpected number
console.log((...[1, 2]))
// Uncaught SyntaxError: Unexpected number
console.log(...[1, 2])
// 1 2
作用:
- 收集剩餘的參數 *Rest Parameter必須是最後一個
- 展開數組 *展開後的效果,跟直接把數組內容寫出一樣(替代函數的 apply 方法)
應用:
- 複製數組:直接複製的話,只是複製了指向底層數據結構的指針,而不是克隆一個全新的數組。
- 合併數組
- 與解構賦值結合,注意如果將擴展運算符用於數組賦值,只能放在參數的最後一位,否則會報錯。
- 將字符串轉爲真正的數組。(能夠正確識別四個字節的 Unicode 字符)
let arr = [1,2,3];
//等價於
...arr
默認參數
4.解構賦值
注意:
- 左右兩邊結構必須一樣
- 右邊必須是個東西
- 聲明和賦值不能分開(必須在一句話內完成)
let [a,b,c]=[12,5,8];
let {a,b,c}={a:12,b:5,c:8};
5.數組
ES5新增的數組方法:
- map(映射):一個對一個
let arr = [12,5,8];
let result=arr.map( item=> item*2;);
alert(result);
- reduce (彙總):一堆出來一個(算總數/平均數)
let arr = [12,69,180,8673];
let result =arr.reduce(function(tmp,item,index){
//算總數
return tmp +item;
//算平均數
if(index!=arr.length-1){
return tmp+item;
}else{
return (tmp+item)/arr.length
}
}
alert(result);
- filter (過濾器)
let arr=[12,5,8,99,27,75,11];
let result = arr.filter(item=>item%3==0);
alert(result);//12,99,27,75
let arr=[
{title:'男士襯衫',price:75},
{title:'女士包',price:57842},
{title:'男士包',price:65},
{title:'女士鞋',price:27531},
]
let result = arr.filter(json=>json.price>=10000);
console.log(result);
- foreach(循環/迭代)
let arr=[12,5,8,9];
arr.forEach((item,index)=>{
alert(index +':'+item);
});
ES6新增的數組方法:
- Array.from
將兩類對象轉爲真正的數組:類似數組的對象(array-like object)和可遍歷(iterable)的對象(包括 ES6 新增的數據結構 Set 和 Map)。
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5的寫法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的寫法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
*任何有length屬性的對象,都可以通過Array.from方法轉爲數組,而此時擴展運算符就無法轉換。
Array.from()可以將各種值轉爲真正的數組,並且還提供map功能。這實際上意味着,只要有一個原始的數據結構,你就可以先對它的值進行處理,然後轉成規範的數組結構,進而就可以使用數量衆多的數組方法。
- Array.of
用於將一組值,轉換爲數組。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
Array.of基本上可以用來替代Array()或new Array(),並且不存在由於參數不同而導致的重載。它的行爲非常統一。
Array.of總是返回參數值組成的數組。如果沒有參數,就返回一個空數組。
- 數組實例的 copyWithin()
在當前數組內部,將指定位置的成員複製到其他位置(會覆蓋原有成員),然後返回當前數組。
Array.prototype.copyWithin(target, start = 0, end = this.length)
它接受三個參數。
- target(必需):從該位置開始替換數據。如果爲負值,表示倒數。
- start(可選):從該位置開始讀取數據,默認爲0。如果爲負值,表示從末尾開始計算。
- end(可選):到該位置前停止讀取數據,默認等於數組長度。如果爲負值,表示從末尾開始計算。
這三個參數都應該是數值,如果不是,會自動轉爲數值。
// 將3號位複製到0號位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]
// -2相當於3號位,-1相當於4號位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]
// 將3號位複製到0號位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}
// 將2號位到數組結束,複製到0號位
let i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]
// 對於沒有部署 TypedArray 的 copyWithin 方法的平臺
// 需要採用下面的寫法
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]
- 數組實例的 find() 和 findIndex()
find方法,用於找出第一個符合條件的數組成員。
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
上面代碼中,find方法的回調函數可以接受三個參數,依次爲當前的值、當前的位置和原數組。
數組實例的findIndex方法的用法與find方法非常類似,返回第一個符合條件的數組成員的位置,如果所有成員都不符合條件,則返回-1。
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
這兩個方法都可以接受第二個參數,用來綁定回調函數的this對象。
function f(v){
return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person); // 26
上面的代碼中,find函數接收了第二個參數person對象,回調函數中的this對象指向person對象。
另外,這兩個方法都可以發現NaN,彌補了數組的indexOf方法的不足。
- fill()
fill方法使用給定值,填充一個數組。
fill方法用於空數組的初始化非常方便。數組中已有的元素,會被全部抹去。
fill方法還可以接受第二個和第三個參數,用於指定填充的起始位置和結束位置。
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']
注意,如果填充的類型爲對象,那麼被賦值的是同一個內存地址的對象,而不是深拷貝對象
- entries(),keys() 和 values()
entries(),keys()和values()——用於遍歷數組。
它們都返回一個遍歷器對象,可以用for…of循環進行遍歷,唯一的區別是keys()是對鍵名的遍歷、values()是對鍵值的遍歷,entries()是對鍵值對的遍歷。
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
- includes()
返回一個布爾值,表示某個數組是否包含給定的值,與字符串的includes方法類似。
該方法的第二個參數表示搜索的起始位置,默認爲0。如果第二個參數爲負數,則表示倒數的位置,如果這時它大於數組長度(比如第二個參數爲-4,但數組長度爲3),則會重置爲從0開始。
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
indexOf方法有兩個缺點,一是不夠語義化,它的含義是找到參數值的第一個出現位置,所以要去比較是否不等於-1,表達起來不夠直觀。二是,它內部使用嚴格相等運算符(===)進行判斷,這會導致對NaN的誤判。
includes()可以判斷是否包含NaN。
- flat(),flatMap()
用於將嵌套的數組“拉平”,變成一維的數組。該方法返回一個新數組,對原數據沒有影響。
參數爲想要拉平的層數,默認一層
[1, 2, [3, 4]].flat()
// [1, 2, 3, 4]
[1, 2, [3, [4, 5]]].flat()
// [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2)
// [1, 2, 3, 4, 5]
[1, [2, [3]]].flat(Infinity)
// [1, 2, 3]
[1, 2, , 4, 5].flat() //會跳過空位
// [1, 2, 4, 5]
flatMap()方法對原數組的每個成員執行一個函數(相當於執行Array.prototype.map()),然後對返回值組成的數組執行flat()方法。該方法返回一個新數組,不改變原數組。
只能展開一層數組。
參數是一個遍歷函數,該函數可以接受三個參數,分別是當前數組成員、當前數組成員的位置(從零開始)、原數組。
// 相當於 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]
// 相當於 [[[2]], [[4]], [[6]], [[8]]].flat()
[1, 2, 3, 4].flatMap(x => [[x * 2]])
// [[2], [4], [6], [8]]
- 數組的空位
ES5 對空位的處理,已經很不一致了,大多數情況下會忽略空位。
forEach(), filter(), reduce(), every() 和some()都會跳過空位。
map()會跳過空位,但會保留這個值
join()和toString()會將空位視爲undefined,而undefined和null會被處理成空字符串。
ES6 則是明確將空位轉爲undefined。
Array.from方法會將數組的空位,轉爲undefined。
擴展運算符(…)也會將空位轉爲undefined。
fill()會將空位視爲正常的數組位置。
for…of循環也會遍歷空位。
entries()、keys()、values()、find()和findIndex()會將空位處理成undefined。
6.字符串
startsWith
判斷字符串是否以參數爲開頭,返回一個布爾值。
let str="asdfasw";
alert(str.startsWith('a')) //true
endsWith
判斷字符串是否以參數爲結尾,返回一個布爾值。
let str ="1.txt";
if(str.endsWith('.txt')){
alert('文本文件');
}else if(str.endsWith('.jpg')){
alert('JPG圖片');
}else{
alert('其他')
}
字符串模板
傳統js中,輸出模板:
$('#result').append(
'There are <b>' + basket.count + '</b> ' +
'items in your basket, ' +
'<em>' + basket.onSale +
'</em> are on sale!'
);
ES6 引入了模板字符串:
$('#result').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);
模板字符串(template string)是增強版的字符串,用反引號(`)標識。它可以當作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。
// 普通字符串
`In JavaScript '\n' is a line-feed.`
// 多行字符串
`In JavaScript this is
not legal.`
console.log(`string text line 1
string text line 2`);
// 字符串中嵌入變量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
如果在模板字符串中需要使用反引號,則前面要用反斜槓轉義。
let greeting = `\`Yo\` World!`;
所有的空格和縮進都會被保留
如果你不想要這個換行,可以使用trim方法消除它。
模板字符串中嵌入變量,需要將變量名寫在${}之中。大括號內部可以放入任意的 JavaScript 表達式,可以進行運算,以及引用對象屬性,還能調用函數。
模板字符串甚至還能嵌套。
const tmpl = addrs => `
<table>
${addrs.map(addr => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`).join('')}
</table>
`;
const data = [
{ first: '<Jane>', last: 'Bond' },
{ first: 'Lars', last: '<Croft>' },
];
console.log(tmpl(data));
//結果
// <table>
//
// <tr><td><Jane></td></tr>
// <tr><td>Bond</td></tr>
//
// <tr><td>Lars</td></tr>
// <tr><td><Croft></td></tr>
//
// </table>
字符的 Unicode 表示法
ES6 加強了對 Unicode 的支持,允許採用\uxxxx(\u0000~\uFFFF)形式表示一個字符,其中xxxx表示字符的 Unicode 碼點。
只要將碼點放入大括號,就能正確解讀xxxx超過0xFFFF的數值。
字符串的遍歷器接口
ES6 爲字符串添加了遍歷器接口,使得字符串可以被for…of循環遍歷。最大的優點是可以識別大於0xFFFF的碼點,傳統的for循環無法識別這樣的碼點。
let text = String.fromCodePoint(0x20BB7);
for (let i = 0; i < text.length; i++) {
console.log(text[i]);
}
// " "
// " "
for (let i of text) {
console.log(i);
}
// "𠮷"
直接輸入 U+2028 和 U+2029
JavaScript 字符串允許直接輸入字符,以及輸入字符的轉義形式。
但是,JavaScript 規定有5個字符,不能在字符串裏面直接使用,只能使用轉義形式。
U+005C:反斜槓(reverse solidus)
U+000D:回車(carriage return)
U+2028:行分隔符(line separator)
U+2029:段分隔符(paragraph separator)
U+000A:換行符(line feed)
字符串裏面不能直接包含反斜槓,一定要轉義寫成\或者\u005c。
麻煩在於 JSON 格式允許字符串裏面直接使用 U+2028(行分隔符)和 U+2029(段分隔符)。這樣一來,服務器輸出的 JSON 被JSON.parse解析,就有可能直接報錯。爲了消除這個報錯,ES2019 允許 JavaScript 字符串直接輸入 U+2028(行分隔符)和 U+2029(段分隔符)。
JSON.stringify() 的改造
根據標準,JSON 數據必須是 UTF-8 編碼。但是,現在的JSON.stringify()方法有可能返回不符合 UTF-8 標準的字符串。
爲了確保返回的是合法的 UTF-8 字符,ES2019 改變了JSON.stringify()的行爲。如果遇到0xD800到0xDFFF之間的單個碼點,或者不存在的配對形式,它會返回轉義字符串,留給應用自己決定下一步的處理。