ES6的新特性??_ES6是什麼+30分鐘帶你瞭解ES6核心內容(上)

一.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函數之前,所有變量都會被提升, 提升到函數作用域頂部.

letconst聲明的變量解決了這種問題,因爲他們是塊級作用域, 在代碼塊(用{}表示)中使用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

展開運算符的一個用途是結合數組。

如果你需要結合多個數組,在有展開運算符之前,必須使用 Arrayconcat() 方法。

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

本次介紹就到這,代碼要跟着敲幾遍哦!學習愉快 😃


ES6的新特性??_ES6是什麼+30分鐘帶你瞭解ES6核心內容(下)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章