文章目錄
一.ES6是什麼?
ES6, 全稱 ECMAScript 6.0 ,是 JavaScript 的下一個版本標準,2015.06 發版。
ES6 主要是爲了解決 ES5 的先天不足,比如 JavaScript 裏並沒有類的概念,(在dw中js文件中直接編寫ES6也會提示報錯,報錯信息’is avaliable in ES6(use'esversion:6')or Mozilla JS extension(use moz)
)
但是目前瀏覽器的 JavaScript 是 ES5 版本,大多數高版本的瀏覽器也支持 ES6,不過只實現了 ES6 的部分特性和功能。
目標
適應更復雜的應用;實現代碼庫之間的共享;不斷迭代維護新版本。
二.新特性
1.const 與 let 變量
使用var帶來的麻煩:
function getClothing(isCold) {
if (isCold) {
var freezing = 'Grab a jacket!';
} else {
var hot = 'It's a shorts kind of day.';
console.log(freezing);
}
}
運行getClothing(false)
後輸出的是,因爲在腳本開始運行的時候,var聲明的變量freezing已經存在,但是還沒有賦值所以會輸出undefined,這也就是執行function函數之前,所有變量都會被提升, 提升到函數作用域頂部.
let與const聲明的變量解決了這種問題,因爲他們是塊級作用域, 在代碼塊(用{}表示)中使用let或const聲明變量, 該變量會陷入暫時性死區直到該變量的聲明被處理.
function getClothing(isCold) {
if (isCold) {
const freezing = 'Grab a jacket!';
} else {
const hot = 'It's a shorts kind of day.';
console.log(freezing);
}
}
運行**getClothing(false)**後輸出的是ReferenceError: freezing is not defined
,因爲 freezing 沒有在 else 語句、函數作用域或全局作用域內聲明,所以拋出 ReferenceError。
let與const規則和var的區別
- let 聲明的變量只在 let 命令所在的代碼塊內有效。var 是在全局範圍內有效:
- let 只能聲明一次, var 可以聲明多次:
例如: for 循環計數器很適合用 let
for (var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i);
})
}
// 輸出十個 10
for (let j = 0; j < 10; j++) {
setTimeout(function(){
console.log(j);
})
}
// 輸出 0123456789
變量 i 是用 var 聲明的,在全局範圍內有效,所以全局中只有一個變量 i, 每次循環時,setTimeout 定時器裏面的 i 指的是全局變量 i ,而循環裏的十個 setTimeout 是在循環結束後才執行,所以此時的 i 都是 10。
變量 j 是用 let 聲明的,當前的 j 只在本輪循環中有效,每次循環的 j 其實都是一個新的變量,所以 setTimeout 定時器裏面的 j 其實是不同的變量,即最後輸出 12345。(若每次循環的變量 j 都是重新聲明的,如何知道前一個循環的值?這是因爲 JavaScript 引擎內部會記住前一個循環的值)。
- const 聲明一個只讀的常量,一旦聲明,常量的值就不能改變。這意味着一旦聲明必須初始化,否則會報錯。
- let 不存在變量提升,var 會變量提升:所以當腳本開始運行的時候,var定義的變量 已經存在了,但是還沒有賦值,所以會輸出 undefine
ES6 明確規定,代碼塊內如果存在 let 或者 const,代碼塊會對這些命令聲明的變量從塊的開始就形成一個封閉作用域。
const 如何做到變量在聲明初始化之後不允許改變的?
const 保證變量指向的內存地址所保存的數據不允許改動。
而簡單類型和複合類型保存值的方式是不同的。
簡單類型(數值 number、字符串 string 、布爾值 boolean),值就保存在變量指向的那個內存地址,因此 const 聲明的簡單類型變量等同於常量。
複雜類型(對象 object,數組 array,函數 function),變量指向的內存地址其實是保存了一個指向實際數據的指針,所以 const 只能保證指針是固定的,至於指針指向的數據結構變不變就無法控制了,所以使用 const 聲明覆雜類型對象時要慎重。
2.模版字變量
**在ES6之前,將字符串連接到一起的方法是+或者concat()方法,**如
const student = {
name: 'Richard Kalehoff',
guardian: 'Mr. Kalehoff'
};
const teacher = {
name: 'Mrs. Wilson',
room: 'N231'
}
let message = student.name + ' please see ' + teacher.name + ' in ' + teacher.room + ' to pick up your report card.';
模板字面量本質上是包含嵌入式表達式的字符串字面量.
模板字面量用倒引號 ( `` )倒引號在1的左邊
可以包含用 ${expression} 表示的佔位符
let message = `${student.name} please see ${teacher.name} in ${teacher.room} to pick up your report card.`;
3.解構賦值
定義:
解構賦值是對賦值運算符的擴展。
他是一種針對數組或者對象進行模式匹配,然後對其中的變量進行賦值。
在代碼書寫上簡潔且易讀,語義更加清晰明瞭;也方便了複雜對象中數據字段獲取。
解構模型
- 解構的源,解構賦值表達式的右邊部分。
- 解構的目標,解構賦值表達式的左邊部分。
解構數組的值:
const point = [10, 25, -34];
const [x, y, z] = point;
console.log(x, y, z);
Prints: 10 25 -34
[]表示被解構的數組, x,y,z表示要將數組中的值存儲在其中的變量, 在解構數組是, 還可以忽略值, 例如const[x,,z]=point,忽略y座標.
解構對象中的值:
const gemstone = {
type: 'quartz',
color: 'rose',
karat: 21.29
};
const {type, color, karat} = gemstone;
console.log(type, color, karat);
花括號 { } 表示被解構的對象,type、color 和 karat 表示要將對象中的屬性存儲到其中的變量
數組模型的解構(Array)
基本
let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3
可嵌套
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1
// b = 2
// c = 3
可忽略
let [a, , b] = [1, 2, 3];
// a = 1
// b = 3
不完全解構
let [a = 1, b] = []; // a = 1, b = undefined
剩餘運算符
let [a, ...b] = [1, 2, 3];
//a = 1
//b = [2, 3]
字符串等
在數組的解構中,解構的目標若爲可遍歷對象,皆可進行解構賦值。可遍歷對象即實現 Iterator 接口的數據。
let [a, b, c, d, e] = 'hello';
// a = 'h'
// b = 'e'
// c = 'l'
// d = 'l'
// e = 'o'
解構默認值
let [a = 2] = [undefined]; // a = 2
當解構模式有匹配結果,且匹配結果是 undefined 時,會觸發默認值作爲返回結果。
let [a = 3, b = a] = []; // a = 3, b = 3
let [a = 3, b = a] = [1]; // a = 1, b = 1
let [a = 3, b = a] = [1, 2]; // a = 1, b = 2
a 與 b 匹配結果爲 undefined ,觸發默認值:a = 3; b = a =3 a 正常解構賦值,匹配結果:a = 1,b 匹配結果 undefined ,觸發默認值:b = a =1 a 與 b 正常解構賦值,匹配結果:a = 1,b = 2
對象模型的解構(Object)
基本
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
let { baz : foo } = { baz : 'ddd' };
// foo = 'ddd'
可嵌套可忽略
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { y }] } = obj;
// x = 'hello'
// y = 'world'
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { }] } = obj;
// x = 'hello'
不完全解構
let obj = {p: [{y: 'world'}] };
let {p: [{ y }, x ] } = obj;
// x = undefined
// y = 'world'
剩餘運算符
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10
// b = 20
// rest = {c: 30, d: 40}
解構默認值
let {a = 10, b = 5} = {a: 3};
// a = 3; b = 5;
let {a: aa = 10, b: bb = 5} = {a: 3};
// aa = 3; bb = 5;
4 對象字面量簡寫法
屬性的簡潔表示法
ES6允許對象的屬性直接寫變量,這時候屬性名是變量名,屬性值是變量值。
const age = 12;
const name = "Amy";
const person = {age, name};
person //{age: 12, name: "Amy"}
//等同於
const person = {age: age, name: name}
方法名也可以簡寫
const person = {
sayHi(){
console.log("Hi");
}
}
person.sayHi(); //"Hi"
//等同於
const person = {
sayHi:function(){
console.log("Hi");
}
}
person.sayHi();//"Hi"
如果是Generator 函數,則要在前面加一個星號:
const obj = {
* myGenerator() {
yield 'hello world';
}
};
//等同於
const obj = {
myGenerator: function* () {
yield 'hello world';
}
};
屬性名表達式
ES6允許用表達式作爲屬性名,但是一定要將表達式放在方括號內。
const obj = {
["he"+"llo"](){
return "Hi";
}
}
obj.hello(); //"Hi"
注意點:屬性的簡潔表示法和屬性名表達式不能同時使用,否則會報錯。
const hello = "Hello";
const obj = {
[hello]
};
obj //SyntaxError: Unexpected token }
const hello = "Hello";
const obj = {
[hello+"2"]:"world"
};
obj //{Hello2: "world"}
對象的拓展運算符
拓展運算符(…)用於取出參數對象所有可遍歷屬性然後拷貝到當前對象。
- 基本用法
let person = {name: "Amy", age: 15};
let someone = { ...person };
someone; //{name: "Amy", age: 15}
可用於合併兩個對象
let age = {age: 15};
let name = {name: "Amy"};
let person = {...age, ...name};
person; //{age: 15, name: "Amy"}
- 注意點
自定義的屬性和拓展運算符對象裏面屬性的相同的時候:自定義的屬性在拓展運算符後面,則拓展運算符對象內部同名的屬性將被覆蓋掉。
let person = {name: "Amy", age: 15};
let someone = { ...person, name: "Mike", age: 17};
someone; //{name: "Mike", age: 17}
自定義的屬性在拓展運算度前面,則變成設置新對象默認屬性值。
let person = {name: "Amy", age: 15};
let someone = {name: "Mike", age: 17, ...person};
someone; //{name: "Amy", age: 15}
拓展運算符後面是空對象,沒有任何效果也不會報錯。
let a = {...{}, a: 1, b: 2};
a; //{a: 1, b: 2}
拓展運算符後面是null或者undefined,沒有效果也不會報錯。
let b = {...null, ...undefined, a: 1, b: 2};
b; //{a: 1, b: 2}
對象的新方法
Object.assign(target, source_1, ···)
用於將源對象的所有可枚舉屬性複製到目標對象中。
基本用法
let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target,object2,object3);
// 第一個參數是目標對象,後面的參數是源對象
target; // {a: 1, b: 2, c: 3
-
如果目標對象和源對象有同名屬性,或者多個源對象有同名屬性,則後面的屬性會覆蓋前面的屬性。
-
如果該函數只有一個參數,當參數爲對象時,直接返回該對象;當參數不是對象時,會先將參數轉爲對象然後返回。
Object.assign(3); // Number {3}
typeof Object.assign(3); // "object"
因爲 null 和 undefined 不能轉化爲對象,所以會報錯:
Object.assign(null); // TypeError: Cannot convert undefined or null to object
Object.assign(undefined); // TypeError: Cannot convert undefined or null to object
當參數不止一個時,null 和 undefined 不放第一個,即不爲目標對象時,會跳過 null 和 undefined ,不報錯
Object.assign(1,undefined); // Number {1}
Object.assign({a: 1},null); // {a: 1}
Object.assign(undefined,{a: 1}); // TypeError: Cannot convert undefined or null to object
- 注意點
assign 的屬性拷貝是淺拷貝:
let sourceObj = { a: { b: 1}};
let targetObj = {c: 3};
Object.assign(targetObj, sourceObj);
targetObj.a.b = 2;
sourceObj.a.b; // 2
同名屬性替換
targetObj = { a: { b: 1, c:2}};
sourceObj = { a: { b: "hh"}};
Object.assign(targetObj, sourceObj);
targetObj; // {a: {b: "hh"}}
數組的處理
Object.assign([2,3], [5]); // [5,3]
會將數組處理成對象,所以先將 [2,3] 轉爲 {0:2,1:3} ,然後再進行屬性複製,所以源對象的 0 號屬性覆蓋了目標對象的 0。
Object.is(value1, value2)
用來比較兩個值是否嚴格相等,與(===)基本類似。
- 基本用法
Object.is("q","q"); // true
Object.is(1,1); // true
Object.is([1],[1]); // false
Object.is({q:1},{q:1}); // false
與(===)的區別
//一是+0不等於-0
Object.is(+0,-0); //false
+0 === -0 //true
//二是NaN等於本身
Object.is(NaN,NaN); //true
NaN === NaN //false
5.迭代器
Iterator
lterator 是 ES6 引入的一種新的遍歷機制,迭代器有兩個核心概念:
- 迭代器是一個統一的接口,它的作用是使各種數據結構可被便捷的訪問,它是通過一個鍵爲Symbol.iterator 的方法來實現。
- 迭代器是用於遍歷數據結構元素的指針(如數據庫中的遊標)。
迭代過程
-
通過 Symbol.iterator 創建一個迭代器,指向當前數據結構的起始位置
-
隨後通過 next 方法進行向下迭代指向下一個位置, next 方法會返回當前位置的對象,對象包含了 value 和 done 兩個屬性,value 是當前屬性的值, done 用於判斷是否遍歷結束
-
當 done 爲 true 時則遍歷結束
下面通過一個簡單的例子進行說明:
const items = ["zero", "one", "two"];
const it = items[Symbol.iterator]();
it.next();
>{value: "zero", done: false}
it.next();
>{value: "one", done: false}
it.next();
>{value: "two", done: false}
it.next();
>{value: undefined, done: true}
·上面的例子,首先創建一個數組,然後通過 Symbol.iterator 方法創建一個迭代器,之後不斷的調用 next 方法對數組內部項進行訪問,當屬性 done 爲 true 時訪問結束。·
·迭代器是協議(使用它們的規則)的一部分,用於迭代。該協議的一個關鍵特性就是它是順序的:迭代器一次返回一個值。這意味着如果可迭代數據結構是非線性的(例如樹),迭代將會使其線性化。·
可迭代的數據結構
可迭代的值:
-
Array
-
String
-
Map
-
Set
-
Dom元素(正在進行中)
我們將使用 for…of 循環對數據結構進行迭代。
Array
數組 ( Array ) 和類型數組 ( TypedArray ) 他們是可迭代的。
for (let item of ["zero", "one", "two"]) {
console.log(item);
}
// output:
// zero
// one
// two
String
字符串是可迭代的,但他們遍歷的是 Unicode 碼,每個碼可能包含一個到兩個的 Javascript 字符。
for (const c of 'z\uD83D\uDC0A') {
console.log(c);
}
// output:
// z
// \uD83D\uDC0A
Map
Map 主要是迭代它們的 entries ,每個 entry 都會被編碼爲 [key, value] 的項, entries 是以確定的形勢進行迭代,其順序是與添加的順序相同。
const map = new Map();
map.set(0, "zero");
map.set(1, "one");
for (let item of map) {
console.log(item);
}
// output:
// [0, "zero"]
// [1, "one"]
注意: WeakMaps 不可迭代
Set
Set 是對其元素進行迭代,迭代的順序與其添加的順序相同
const set = new Set();
set.add("zero");
set.add("one");
for (let item of set) {
console.log(item);
}
// output:
// zero
// one
注意: WeakSets 不可迭代
arguments
arguments 目前在 ES6 中使用越來越少,但也是可遍歷的
function args() {
for (let item of arguments) {
console.log(item);
}
}
args("zero", "one");
// output:
// zero
// one
普通對象不可迭代
普通對象是由 object 創建的,不可迭代:
// TypeError
for (let item of {}) {
console.log(item);
}
for…of循環
for…of 是 ES6 新引入的循環,用於替代 for…in 和 forEach() ,並且支持新的迭代協議。它可用於迭代常規的數據類型,如 Array 、 String 、 Map 和 Set 等等。
迭代常規數據類型
Array
const nums = ["zero", "one", "two"];
for (let num of nums) {
console.log(num);
}
TypedArray
const typedArray1 = new Int8Array(6);
typedArray1[0] = 10;
typedArray1[1] = 11;
for (let item of typedArray1) {
console.log(item);
}
**String**
```javascript
const str = "zero";
for (let item of str) {
console.log(item);
}
**Map**
```javascript
let myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
myMap.set(2, "two");
// 遍歷 key 和 value
for (let [key, value] of myMap) {
console.log(key + " = " + value);
}
for (let [key, value] of myMap.entries()) {
console.log(key + " = " + value);
}
// 只遍歷 key
for (let key of myMap.keys()) {
console.log(key);
}
// 只遍歷 value
for (let value of myMap.values()) {
console.log(value);
}
Set
let mySet = new Set();
mySet.add("zero");
mySet.add("one");
mySet.add("two");
// 遍歷整個 set
for (let item of mySet) {
console.log(item);
}
// 只遍歷 key 值
for (let key of mySet.keys()) {
console.log(key);
}
// 只遍歷 value
for (let value of mySet.values()) {
console.log(value);
}
// 遍歷 key 和 value ,兩者會相等
for (let [key, value] of mySet.entries()) {
console.log(key + " = " + value);
}
for…of 循環還具有其他優勢,解決了 for 和 for…in 循環的不足之處。你可以隨時停止或退出 for…of 循環。
for (const digit of digits) {
if (digit % 2 === 0) {
continue;
}
console.log(digit);
}
不用擔心向對象中添加新的屬性。for…of 循環將只循環訪問對象中的值。
Array.prototype.decimalfy = function() {
for (i = 0; i < this.length; i++) {
this[i] = this[i].toFixed(2);
}
};
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const digit of digits) {
console.log(digit);
}
可迭代的數據結構
of 操作數必須是可迭代,這意味着如果是普通對象則無法進行迭代。如果數據結構類似於數組的形式,則可以藉助 Array.from() 方法進行轉換迭代。
const arrayLink = {length: 2, 0: "zero", 1: "one"}
// 報 TypeError 異常
for (let item of arrayLink) {
console.log(item);
}
// 正常運行
// output:
// zero
// one
for (let item of Array.from(arrayLink)) {
console.log(item);
}
let 、const 和 var 用於 for…of
如果使用 let 和 const ,每次迭代將會創建一個新的存儲空間,這可以保證作用域在迭代的內部。
const nums = ["zero", "one", "two"];
for (const num of nums) {
console.log(num);
}
// 報 ReferenceError
console.log(num);
從上面的例子我們看到,最後一句會報異常,原因 num 的作用域只在循環體內部,外部無效.使用 var 則不會出現上述情況,因爲 var 會作用於全局,迭代將不會每次都創建一個新的存儲空間。
const nums = ["zero", "one", "two"];
forv (var num of nums) {
console.log(num);
}
// output: two
console.log(num);
6.展開運算符
展開運算符(用三個連續的點 (…) 表示)是 ES6 中的新概念,使你能夠將字面量對象展開爲多個元素
const books = ["Don Quixote", "The Hobbit", "Alice in Wonderland", "Tale of Two Cities"];
console.log(...books);
Prints: Don Quixote The Hobbit Alice in Wonderland Tale of Two Cities
展開運算符的一個用途是結合數組。
如果你需要結合多個數組,在有展開運算符之前,必須使用 Array
的 concat()
方法。
const fruits = ["apples", "bananas", "pears"];
const vegetables = ["corn", "potatoes", "carrots"];
const produce = fruits.concat(vegetables);
console.log(produce);
Prints: ["apples", "bananas", "pears", "corn", "potatoes", "carrots"]
使用展開符來結合數組
const fruits = ["apples", "bananas", "pears"];
const vegetables = ["corn", "potatoes", "carrots"];
const produce = [...fruits,...vegetables];
console.log(produce);
應用:可變參數
使用展開運算符將數組展開爲多個元素, 使用剩餘參數可以將多個元素綁定到一個數組中.
可變參數也用三個連續的點 ( … ) 表示,使你能夠將不定數量的元素表示爲數組.
用途1: 將變量賦數組值時:
const order = [20.17, 18.67, 1.50, "cheese", "eggs", "milk", "bread"];
const [total, subtotal, tax, ...items] = order;
console.log(total, subtotal, tax, items);
用途2: 可變參數函數
對於參數不固定的函數,ES6之前是使用參數對象(arguments)處理:
function sum() {
let total = 0;
for(const argument of arguments) {
total += argument;
}
return total;
}
在ES6中使用剩餘參數運算符則更爲簡潔,可讀性提高:
function sum(...nums) {
let total = 0;
for(const num of nums) {
total += num;
}
return total;
}
本文參考:
https://www.jianshu.com/p/87008f4f8513
https://www.runoob.com/w3cnote/es6-tutorial.html
本次介紹就到這,代碼要跟着敲幾遍哦!學習愉快 😃