常用ES6-ES10知識點總結


在工作中我們會常用到的一些es6-es10的一些特性還記得多少,今天就讓我們重新復習一遍

ES6語法

 1.Let

  1.let聲明的變量具有塊級作用域,

{
  let a = 1
}
console.log(a); //undefined   a變量是在代碼塊{}中使用 let 定義的,它的作用域是這個代碼塊內部,外部無法訪問。

 

  2.let聲明的全局變量不是全局對象的屬性

var a = 1
console.log(window.a); //1

let a = 1 console.log(window.a); // undefined

 

  3.用let重定義變量會拋出一個語法錯誤

var a = 1
var a = 2

console.log(a)  //2
如果是 let ,則會報錯

let a = 1
let a = 2
// VM131:1 Uncaught SyntaxError: Identifier 'a' has already been declared
//   at <anonymous>:1:1

 2.Const

const除了具有let的塊級作用域和不會變量提升外,還有就是它定義的是常量,在用const定義變量后,我們就不能修改它了,對變量的修改會拋出異常。

const A= 123;

console.log(A);

A = 1;

console.log(A);
// Uncaught TypeError: Assignment to constant variable.

在 ES6 中新增了很多實用的原生 API,方便開發者對 Array 的操控性更強,如 for…of、from、of、fill、find、findIndex等。

3.for…of

  在es5之前遍歷數組

for (var i = 0; i < array.length; i++) {
  console.log(array[i]);
}

  在es6之前遍歷數組

for (let val of [1,2,3]) {
  console.log(val);
}
// 1,2,3

4.Array.prototype.from()

數組是開發中經常用到的數據結構,它非常好用。在 JavaScript 的世界里有些對象被理解為數組,然而缺不能使用數組的原生 API,比如函數中的 arguments、DOM中的 NodeList等。當然,還有一些可遍歷的對象,看上去都像數組卻不能直接使用數組的 API,因為它們是偽數組(Array-Like)。要想對這些對象使用數組的 API 就要想辦法把它們轉化為數組,傳統的做法是這樣的:

let args = [].slice.call(arguments);
let imgs = [].slice.call(document.querySelectorAll('img'));

基本原理是使用 call 將數組的 api 應用在新的對象上,換句話說是利用改變函數的上下文來間接使用數組的 api。在 ES6 中提供了新的 api 來解決這個問題,就是 Array.from,代碼如下:

 
let args = Array.from(arguments);
let imgs = Array.from(document.querySelectorAll('img'));

5.Array.prototype.of()

Array.of() 方法創建一個具有可變數量參數的新數組實例,而不考慮參數的數量或類型。

Array.of() 和 Array 構造函數之間的區別在於處理整數參數:Array.of(7) 創建一個具有單個元素 7 的數組,而 Array(7) 創建一個長度為7的空數組(注意:這是指一個有7個空位(empty)的數組,而不是由7個undefined組成的數組)。

Array.of(7);       // [7]
Array.of(1, 2, 3); // [1, 2, 3]

Array(7);          // [ , , , , , , ]
Array(1, 2, 3);    // [1, 2, 3]

6.Array.prototype.fill()

fill() 方法用一個固定值填充一個數組中從起始索引到終止索引內的全部元素。不包括終止索引。

 
let array = [1, 2, 3, 4]
array.fill(0, 1, 2)
// [1,0,3,4]

7.Array.prototype.find()

find() 方法返回數組中滿足提供的測試函數的第一個元素的值,否則返回 undefined。

 
let array = [5, 12, 8, 130, 44];

let found = array.find(function(element) {
  return element > 10;
});

console.log(found);
// 12

8.Array.prototype.findIndex()

findIndex()方法返回數組中滿足提供的測試函數的第一個元素的索引。否則返回-1。其實這個和 find() 是成對的,不同的是它返回的是索引而不是值。

 
let array = [5, 12, 8, 130, 44];

let found = array.findIndex(function(element) {
  return element > 10;
});

console.log(found);
// 1

9.Class

對於面向對象編程而言,更關注類的聲明、屬性、方法、靜態方法、繼承、多態、私有屬性。

首先我們要先來說明在 JavaScript 世界里如何聲明一個 “類”。在 ES6 之前大家都是這么做的:

 
let Animal = function (type) {
  this.type = type
  this.walk = function () {
    console.log(`I am walking`)
  }
}

let dog = new Animal('dog')
let monkey = new Animal('monkey')

在 ES6 中把類的聲明專業化了,不在用 function 的方式了,請看:

class Animal {
  constructor (type) {
    this.type = type
  }
  walk () {
    console.log(`I am walking`)
  }
}
let dog = new Animal('dog')
let monkey = new Animal('monkey')

10.Setters & Getters

對於類中的屬性,可以直接在 constructor 中通過 this 直接定義,還可以直接在類的頂層來定義: 

class Animal {
  constructor (type, age) {
    this.type = type
    this._age = age
  }
  get age () {
    return this._age
  }
  set age (val) {
    this._age = val
  }
}

11.靜態方法

靜態方法是面向對象最常用的功能,在 ES5 中利用 function 實現的類是這樣實現一個靜態方法的。

let Animal = function (type) {
  this.type = type
  this.walk = function () {
    console.log(`I am walking`)
  }
}

Animal.eat = function (food) {
  console.log(`I am eating`);
}

在 ES6 中使用 static 的標記是不是靜態方法,代碼如下:

 
class Animal {
  constructor (type) {
    this.type = type
  }
  walk () {
    console.log(`I am walking`)
  }
  static eat () {
    console.log(`I am eating`)
  }
}

12.繼承

面向對象只所以可以應對復雜的項目實現,很大程度上要歸功於繼承。如果對繼承概念不熟悉的同學,可以自行查詢。在 ES5 中怎么實現繼承呢?

// 定義父類
let Animal = function (type) {
  this.type = type
}
// 定義方法
Animal.prototype.walk = function () {
  console.log(`I am walking`)
}
// 定義靜態方法
Animal.eat = function (food) {
  console.log(`I am eating`)
}
// 定義子類
let Dog = function () {
  // 初始化父類
  Animal.call(this, 'dog')
  this.run = function () {
    console.log('I can run')
  }
}
// 繼承
Dog.prototype = Animal.prototype

從代碼上看,是不是很繁瑣?而且閱讀性也較差。再看看 ES6 是怎么解決這些問題的:

class Animal {
  constructor (type) {
    this.type = type
  }
  walk () {
    console.log(`I am walking`)
  }
  static eat () {
    console.log(`I am eating`)
  }
}

class Dog extends Animal {
  constructor () {
    super('dog')
  }
  run () {
    console.log('I can run')
  }
}

 13.默認參數

 

對於函數而言,經常會用到參數,關於參數的默認值通常都是寫在函數體中,如在 ES5 的時候大家都會這么寫:

function f (x, y, z) {
    if (y === undefined)
        y = 7;
    if (z === undefined)
        z = 42;
    return x + y + z;
};
f(1) === 50;

當一個函數有很多參數涉及初始化的時候,這樣寫代碼極其丑陋,所以在 ES6 中改變了對這種知識的寫法:

function f (x, y = 7, z = 42) {
    return x + y + z
}
f(1) === 50

 14.Rest Parameter

在寫函數的時候,部分情況我們不是很確定參數有多少個,比如求和運算,之前都是這么做的:

function sum () {
  let num = 0
  Array.prototype.forEach.call(arguments, function (item) {
    num += item * 1
  })
  return num
}

console.log(sum(1, 2, 3))// 6
console.log(sum(1, 2, 3, 4))// 10

其實在上面說過,這個代碼在 ES5 中可以這么寫,在 ES6 就不能這么寫了,因為 arguments 的問題。現在需要這樣寫:

function sum (...nums) {
  let num = 0
  nums.forEach(function (item) {
    num += item * 1
  })
  return num
}

console.log(sum(1, 2, 3))// 6
console.log(sum(1, 2, 3, 4))// 10

當然,Rest Parameter 也可以和其他參數一起來用,比如:

function sum (base, ...nums) {
  let num = base
  nums.forEach(function (item) {
    num += item * 1
  })
  return num
}

console.log(sum(30, 1, 2, 3))// 36
console.log(sum(30, 1, 2, 3, 4))// 40

 15.箭頭函數

箭頭函數可以說是 ES6 很大的福利了,不管你是函數式愛好者還是面向對象開發者,函數是必須要用到的東西。之前聲明函數需要使用 function,如下:

function hello () {
  console.log('say hello')
}
//

let hello = function () {
  console.log('say hello')
}

現在可以這樣做了:

let hello = () => {
  console.log('say hello')
}

如果帶參數該怎么做呢?

let hello = (name) => {
  console.log('say hello', name)
}
// 或者

let hello = name => {
  console.log('say hello', name)
}

16.Enhanced Object Properties(增強對象屬性)

在 ES6 之前 Object 的屬性必須是 key-value 形式,如下:

var x = 0, y = 0;
obj = { x: x, y: y };

在 ES6 之后是可以用簡寫的形式來表達:

var x = 0, y = 0
obj = { x, y }

 17.Object.assign()

Object.assign() 方法用於將所有可枚舉屬性的值從一個或多個源對象復制到目標對象,它將返回目標對象。

 
const target = { a: 1, b: 2 }
const source = { b: 4, c: 5 }

const returnedTarget = Object.assign(target, source)

console.log(target)
// expected output: Object { a: 1, b: 4, c: 5 }

console.log(returnedTarget)
// expected output: Object { a: 1, b: 4, c: 5 }

從語法上可以看出源對象的個數是不限制的(零個或多個),如果是零個直接返回目的對象,如果是多個相同屬性的會被后邊的源對象的屬相覆蓋。

let s = Object.assign({ a: 1 })
// {a: 1}
18.RegExp
  
Sticky

除了u修飾符,ES6還為正則表達式添加了y修飾符,叫做“粘連”(sticky)修飾符。

y修飾符的作用與g修飾符類似,也是全局匹配,后一次匹配都從上一次匹配成功的下一個位置開始。不同之處在於,g修飾符只要剩余位置中存在匹配就可,而y修飾符確保匹配必須從剩余的第一個位置開始,這也就是“粘連”的涵義。

const s = 'aaa_aa_a'
const r1 = /a+/g
const r2 = /a+/y

r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]

r1.exec(s) // ["aa"]
r2.exec(s) // null
 
                    

19.Unicode

ES6對正則表達式添加了u修飾符,含義為“Unicode模式”,用來正確處理大於 \uFFFF 的Unicode字符。也就是說,會正確處理四個字節的UTF-16編碼。

/^\uD83D/u.test('\uD83D\uDC2A')
// false
/^\uD83D/.test('\uD83D\uDC2A')
// true

上面代碼中,\uD83D\uDC2A是一個四個字節的UTF-16編碼,代表一個字符 “?”。但是,ES5不支持四個字節的UTF-16編碼,會將其識別為兩個字符,導致第二行代碼結果為true。加了u修飾符以后,ES6就會識別其為一個字符,所以第一行代碼結果為false。

  (1)電字符

    點(.)字符在正則表達式中,含義是除了換行符以外的任意單個字符。對於碼點大於 0xFFFF 的 Unicode 字符,點字符不能識別,必須加上u修飾符。

let s = '?'; //

/^.$/.test(s) // false
/^.$/u.test(s) // true

  (2).Unicode字符表示法

    ES6新增了使用大括號表示Unicode字符,這種表示法在正則表達式中必須加上u修飾符,才能識別。

/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('?') // true

  (3)量詞

    使用u修飾符后,所有量詞都會正確識別碼點大於0xFFFF的Unicode字符。

/a{2}/.test('aa') // true
/a{2}/u.test('aa') // true
/?{2}/.test('??') // false
/?{2}/u.test('??') // true

  (4) 預定義模式 

    u修飾符也影響到預定義模式,能否正確識別碼點大於0xFFFF的Unicode字符。

/^\S$/.test('?') // false
/^\S$/u.test('?') // true

上面代碼的\S是預定義模式,匹配所有不是空格的字符。只有加了u修飾符,它才能正確匹配碼點大於0xFFFF的Unicode字符。

利用這一點,可以寫出一個正確返回字符串長度的函數。

function codePointLength(text) {
  const result = text.match(/[\s\S]/gu);
  return result ? result.length : 0;
}

const s = '??';

s.length // 4
codePointLength(s) // 2
 
                    

  (5) i修飾符

    有些Unicode字符的編碼不同,但是字型很相近,比如,\u004B與\u212A都是大寫的K。

/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true
 20.Template 模板字符串 

  在 ES6 之前對字符串的處理是相當的麻煩,看如下場景:

  1. 字符串很長要換行

    字符串很長包括幾種情形一個是開發時輸入的文本內容,一個是接口數據返回的文本內容。如果對換行符處理不當,就會帶來異常。

  2. 字符串中有變量或者表達式

    如果字符串不是靜態內容,往往是需要加載變量或者表達式,這個也是很常見的需求。之前的做法是字符串拼接:

var a = 20
var b = 10
var c = 'JavaScript'
var str = 'My age is ' + (a + b) + ' and I love ' + c
console.log(str)

從 ES6 開始可以這樣定義字符串了。

`string text`

`string text line 1
 string text line 2`

`string text ${expression} string text`
在這里你可以任意插入變量或者表達式,只要用 ${} 包起來就好。

 21解構賦值

在 ES6 中新增了變量賦值的方式:解構賦值。如果對這個概念不了解,我們可以快速展示一個小示例一睹風采:

let arr = ['Ilya', 'Kantor']
let firstName = arr[0]
let surname = arr[1]

想從數組中找出有意義的項要單獨賦值給變量(一一的寫),在 ES6 中就可以這樣寫了:

let [firstName, surname] = ['Ilya', 'Kantor']
console.log(firstName) // Ilya
console.log(surname) // Kantor

22.Promise

首先要說明 Promise 就是為了解決“回調地獄”問題的,它可以將異步操作的處理變得很優雅。如果我們使用 Promise 來解決上面那個問題該怎么做呢?

function loadScript (src) {
  return new Promise((resolve, reject) => {
    let script = document.createElement('script')
    script.src = src
    script.onload = () => resolve(script)
    script.onerror = (err) => reject(err)
    document.head.append(script)
  })
}

loadScript('1.js')
  .then(loadScript('2.js'), (err) => {
    console.log(err)
  })
  .then(loadScript('3.js'), (err) => {
    console.log(err)
  })

   通過創建 Promise 對象開啟一個異步操作的過程,一般用幾步完成多次異步操作:

  1. new Promise(fn) 返回一個Promise 對象
  2. 在fn 中指定異步等處理
  3. 處理結果正常的話,調用resolve(處理結果值)
  4. 處理結果錯誤的話,調用reject(Error對象)

  1.Promise.prototype.then()

var promise = new Promise(function (resolve, reject) {
  resolve('傳遞給then的值')
})
promise.then(function (value) {
  console.log(value)
}, function (error) {
  console.error(error)
})

    這段代碼創建一個 Promise 對象,定義了處理 onFulfilled 和 onRejected 的函數(handler),然后返回這個 Promise 對象。

    這個 Promise 對象會在變為 resolve 或者 reject 的時候分別調用相應注冊的回調函數。

      當 handler 返回一個正常值的時候,這個值會傳遞給 Promise 對象的 onFulfilled 方法。

      定義的 handler 中產生異常的時候,這個值則會傳遞給 Promise 對象的 onRejected 方法

  2.Promise.resolve() 

    一般情況下我們都會使用 new Promise() 來創建 Promise 對象,但是除此之外我們也可以使用其他方法。

 

Promise.resolve(42).then(function (value) {
  console.log(value)
})

在這段代碼中的 resolve(42) 會讓這個 Promise 對象立即進入確定(即resolved)狀態,並將 42 傳遞給后面 then 里所指定的 onFulfilled 函數。

  3.Promise.reject()  

    Promise.reject(error) 是和 Promise.resolve(value) 類似的靜態方法,是 new Promise() 方法的快捷方式。

new Promise(function (resolve, reject) {
  reject(new Error('出錯了'))
})

  4.Promise.prototype.catch()  

    捕獲異常是程序質量保障最基本的要求,可以使用 Promise 對象的 catch 方法來捕獲異步操作過程中出現的任何異常。

function test () {
  return new Promise((resolve, reject) => {
    reject(new Error('es'))
  })
}

test().catch((e) => {
  console.log(e.message) // es
})

  5.Promise.all()

var p1 = Promise.resolve(1)
var p2 = Promise.resolve(2)
var p3 = Promise.resolve(3)
Promise.all([p1, p2, p3]).then(function (results) {
  console.log(results) // [1, 2, 3]
})

Promise.all 生成並返回一個新的 Promise 對象,所以它可以使用 Promise 實例的所有方法。參數傳遞promise數組中所有的 Promise 對象都變為resolve的時候,該方法才會返回, 新創建的 Promise 則會使用這些 promise 的值。

如果參數中的任何一個promise為reject的話,則整個Promise.all調用會立即終止,並返回一個reject的新的 Promise 對象。

由於參數數組中的每個元素都是由 Promise.resolve 包裝(wrap)的,所以Paomise.all 可以處理不同類型的 promose對

  6.Promise.race()

var p1 = Promise.resolve(1)
var p2 = Promise.resolve(2)
var p3 = Promise.resolve(3)
Promise.race([p1, p2, p3]).then(function (value) {
  console.log(value) // 1
})

Promise.race 生成並返回一個新的 Promise 對象。

參數 promise 數組中的任何一個 Promise 對象如果變為 resolve 或者 reject 的話, 該函數就會返回,並使用這個 Promise 對象的值進行 resolve 或者 reject。

 

 23.Reflect

Reflect 是一個內置的對象,它提供攔截 JavaScript 操作的方法,這些方法與處理器對象的方法相同。Reflect不是一個函數對象,因此它是不可構造的。

    1.Reflect.apply()
    
Reflect.apply(Math.floor, undefined, [1.75])
// 1;

Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111])
// "hello"

Reflect.apply(RegExp.prototype.exec, /ab/, ['confabulation']).index
// 4

Reflect.apply(''.charAt, 'ponies', [3])
// "i"

該方法與ES5中Function.prototype.apply()方法類似:調用一個方法並且顯式地指定this變量和參數列表(arguments) ,參數列表可以是數組,或類似數組的對象。

Function.prototype.apply.call(Math.floor, undefined, [1.75]);

  2.Reflect.construct() 

    Reflect.construct() 方法的行為有點像 new 操作符 構造函數 , 相當於運行 new target(…args).

var d = Reflect.construct(Date, [1776, 6, 4])
d instanceof Date // true
d.getFullYear() // 1776

  3.Reflect.define​Property()

    靜態方法 Reflect.defineProperty() 基本等同於 Object.defineProperty() 方法,唯一不同是返回 Boolean 值。

const student = {}
Reflect.defineProperty(student, 'name', { value: 'Mike' }) // true
student.name // "Mike"

  4.Deflect.delete​Property()

    Reflect.deleteProperty 允許你刪除一個對象上的屬性。返回一個 Boolean 值表示該屬性是否被成功刪除。它幾乎與非嚴格的 delete operator 相同。

var obj = { x: 1, y: 2 };
Reflect.deleteProperty(obj, "x"); // true
obj; // { y: 2 }

var arr = [1, 2, 3, 4, 5];
Reflect.deleteProperty(arr, "3"); // true
arr; // [1, 2, 3, , 5]

// 如果屬性不存在,返回 true
Reflect.deleteProperty({}, "foo"); // true

// 如果屬性不可配置,返回 false
Reflect.deleteProperty(Object.freeze({foo: 1}), "foo"); // false

  5.Reflect.get()

    Reflect.get() 方法的工作方式,就像從 object (target[propertyKey]) 中獲取屬性,但它是作為一個函數執行的。

// Object
var obj = { x: 1, y: 2 }
Reflect.get(obj, 'x') // 1

// Array
Reflect.get(['zero', 'one'], 1) // "one"

// Proxy with a get handler
var x = { p: 1 }
var obj = new Proxy(x, {
  get (t, k, r) { return k + 'bar' }
})
Reflect.get(obj, 'foo') // "foobar"

  6.Reflect.get​OwnProperty​Descriptor()  

    靜態方法 Reflect.getOwnPropertyDescriptor() 與 Object.getOwnPropertyDescriptor() 方法相似。如果在對象中存在,則返回給定的屬性的屬性描述符,否則返回 undefined。

Reflect.getOwnPropertyDescriptor({ x: 'hello' }, 'x')
// {value: "hello", writable: true, enumerable: true, configurable: true}

Reflect.getOwnPropertyDescriptor({ x: 'hello' }, 'y')
// undefined

Reflect.getOwnPropertyDescriptor([], 'length')
// {value: 0, writable: true, enumerable: false, configurable: false}

  7.Reflect.get​PrototypeOf()

    靜態方法 Reflect.getPrototypeOf() 與 Object.getPrototypeOf() 方法是一樣的。都是返回指定對象的原型(即,內部的 [[Prototype]] 屬性的值)。

   8.Reflect.has() 

    Reflect.has 用於檢查一個對象是否擁有某個屬性, 相當於in 操作符

   9.Reflect.set()

    Reflect.set 方法允許你在對象上設置屬性。它的作用是給屬性賦值並且就像 property accessor 語法一樣,但是它是以函數的方式。

// Object
var obj = {};
Reflect.set(obj, "prop", "value"); // true
obj.prop; // "value"

// Array
var arr = ["duck", "duck", "duck"];
Reflect.set(arr, 2, "goose"); // true
arr[2]; // "goose"

// It can truncate an array.
Reflect.set(arr, "length", 1); // true
arr; // ["duck"];

// With just one argument, propertyKey and value are "undefined".
var obj = {};
Reflect.set(obj); // true
Reflect.getOwnPropertyDescriptor(obj, "undefined");
// { value: undefined, writable: true, enumerable: true, configurable: true }

  10.Reflect.set​PrototypeOf()

    Reflect.setPrototypeOf 方法改變指定對象的原型 (即,內部的 [[Prototype]] 屬性值)

Reflect.setPrototypeOf({}, Object.prototype); // true

// It can change an object's [[Prototype]] to null.
Reflect.setPrototypeOf({}, null); // true

// Returns false if target is not extensible.
Reflect.setPrototypeOf(Object.freeze({}), null); // false

// Returns false if it cause a prototype chain cycle.
var target = {};
var proto = Object.create(target);
Reflect.setPrototypeOf(target, proto); // false

 24.Proxy

   在 ES6 標准中新增的一個非常強大的功能是 Proxy,它可以自定義一些常用行為如查找、賦值、枚舉、函數調用等。通過 Proxy 這個名稱也可以看出來它包含了“代理”的含義,只要有“代理”的訴求都可以考慮使用 Proxy 來實現。
let p = new Proxy(target, handler)

  從服務端獲取的數據希望是只讀,不允許在任何一個環節被修改。

// response.data 是 JSON 格式的數據,來自服務端的響應
// 在 ES5 中只能通過遍歷把所有的屬性設置為只讀
for (let [key] of Object.entries(response.data)) {
  Object.defineProperty(response.data, key, {
    writable: false
  })
}

  如果我們使用 Proxy 就簡單很多了:

let data = new Proxy(response.data, {
  set(obj, key, value) {
    return false
  }
})

 25.Generator

什么是 JavaScript Generators 呢?通俗的講 Generators 是可以用來控制迭代器的函數。它們可以暫停,然后在任何時候恢復。如果這句話不好理解,可以看下接下來的示例。

 ES5之前做法  

for (let i = 0; i < 5; i += 1) {
  console.log(i)
}
// this will return immediately 0 -> 1 -> 2 -> 3 -> 4

用 Generator

function * generatorForLoop () {
  for (let i = 0; i < 5; i += 1) {
    yield console.log(i)
  }
}

const genForLoop = generatorForLoop()

console.log(genForLoop.next()) // first console.log - 0
console.log(genForLoop.next()) // 1
console.log(genForLoop.next()) // 2
console.log(genForLoop.next()) // 3

對比下代碼,常規的循環只能一次遍歷完所有值,Generator 可以通過調用 next 方法拿到依次遍歷的值,讓遍歷的執行變得“可控”。

  語法

function * gen () {
  yield 1
  yield 2
  yield 3
}

let g = gen()
// "Generator { }"

  這個是 Generator 的定義方法,有幾個點值得注意:

  1. 比普通函數多一個 *
  2. 函數內部用 yield 來控制程序的執行的“暫停”
  3. 函數的返回值通過調用 next 來“恢復”程序執行

  yield 表達式

     yield 表達式的返回值是 undefined,但是遍歷器對象的 next 方法可以修改這個默認值

function * gen () {
  let val
  val = yield 1
  console.log(`1:${val}`) // 1:undefined
  val = yield 2
  console.log(`2:${val}`) // 2:undefined
  val = yield 3
  console.log(`3:${val}`) // 3:undefined
}

var g = gen()

console.log(g.next()) // {value: 1, done: false}
console.log(g.next()) // {value: 2, done: false}
console.log(g.next()) // {value: 3, done: false}
console.log(g.next()) // {value: undefined, done: true}

  yeild * 是委托給另一個遍歷器對象或者可遍歷對象

function * gen () {
  let val
  val = yield 1
  console.log(`1:${val}`) // 1:undefined
  val = yield 2
  console.log(`2:${val}`) // 2:undefined
  val = yield [3, 4, 5]
  console.log(`3:${val}`) // 3:undefined
}

enerator 對象的 next 方法,遇到 yield 就暫停,並返回一個對象,這個對象包括兩個屬性:value 和 done。

  26.module

  在 ES6 之前,JS 文件之間的導入、導出是需要借助 require.js、sea.js。現在,大家可以使用 import、export 來實現原生 JavaScript 的導入、導出了。

  export

  導出變量或者常量

export const name = 'hello'
export let addr = 'BeiJing City'
export var list = [1, 2 , 3]

  import

const name = 'hello'
let addr = 'BeiJing City'
var list = [1, 2 , 3]
export {
  name as cname,
  addr as caddr
}
export default list

 

 ES7語法

 1.Array.prototype.includes

    Array.prototype.includes() 方法用來判斷一個數組是否包含一個指定的值,根據情況,如果包含則返回 true,否則返回false。

let array1 = [1, 2, 3]

console.log(array1.includes(2))
// expected output: true

var pets = ['cat', 'dog', 'bat']

console.log(pets.includes('cat'))
// expected output: true

console.log(pets.includes('at'))
// expected output: false

在 ES7 之前想判斷數組中是否包含一個元素,基本可以這樣寫:

 
console.log(array1.find(function (item) { return item === 2 }))

 2.Math.pow

 我們一直都是這樣用的:

console.log(Math.pow(2, 3)) // 8(2的三次方)

  在 ES7 可以這樣寫了:

console.log(2 ** 3)

 

ES8語法

 1.Async/Await

async 和 await 是 Promise 的拓展,如果對 Promise 還不了解的話,請移步 Promise 章節進行學習。

async function firstAsync () {
  return 27
}
firstAsync().then(console.log) // 27

也就是說上面的代碼等同於:

async function firstAsync () {
  return Promise.resolve(27)
}

async 的作用我們清楚了,await呢?話不多說:

async function firstAsync () {
  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("Now it's done!"), 1000)
  })

  // wait until the promise returns us a value
  let result = await promise

  // "Now it's done!"
  return result
}

firstAsync().then(console.log)

這段代碼使用了 await,從這個字面的意思看就是“等待”,它等什么呢?很簡單,它等 Promise 返回結果。上面代碼的意思是 async 開啟了一個 Promise 對象,這個函數內部嵌套了一個 Promise 操作,這個操作需要等 1 秒才返回 “Now it’s done!” 這個結果。也就是說 await 在拿到這個結果之前不會繼續執行,一直等到結果才往后繼續執行,也就是 async function 聲明的 Promise 才會響應(console.log才執行)。

   1.await 后面一定是 Promise 對象,如果不是會自動包裝成 Promise 對象
  
常規
async function firstAsync () { let result = await 27 return result } firstAsync().then(console.log) // 27

 

對象
async function firstAsync () { let result = await { a: 1 } return result } firstAsync().then(console.log) // { a: 1 }

 非 Promise 對象將用 Promise.resolve 來包裝,而 await 后邊的值作為 resolve 的參數。

   2. await 不可以脫離 async 單獨使用

function firstAsync() {
  let promise = Promise.resolve(10);
  let result = await promise; // Syntax error
}

 2.Object.values()

   Object.values() 返回一個數組,其元素是在對象上找到的可枚舉屬性值。屬性的順序與通過手動循環對象的屬性值所給出的順序相同(for…in,但是for…in還會遍歷原型上的屬性值)。

let grade = {
  'lilei': 98,
  'hanmei': 87
}

console.log(Object.values(grade)) // [98, 87]

 3.Object.entries

  Object.entries()方法返回一個給定對象自身可枚舉屬性的鍵值對數組,其排列與使用 for…in 循環遍歷該對象時返回的順序一致。(區別在於 for-in 循環也枚舉原型鏈中的屬性)

let grade = {
  'lilei': 98,
  'hanmei': 87
}

for (let [key, value] of grade) {
  console.log(key, value) // Uncaught TypeError: grade is not iterable
}

我們知道 Object 是不可直接遍歷的,上述代碼足以說明直接遍歷觸發了錯誤。如果使用 Object.entries() 則可以完成遍歷任務。

 
let grade = {
  'lilei': 98,
  'hanmei': 87
}

for (let [k, v] of Object.entries(grade)) {
  console.log(k, v)
  // lilei 98
  // hanmei 87
}

4.String padding

在 ES8 中 String 新增了兩個實例函數 String.prototype.padStart 和 String.prototype.padEnd,允許將空字符串或其他字符串添加到原始字符串的開頭或結尾。

這兩個方法有什么用武之地呢?回憶下我們處理日期的時候經常要格式化,比如 01、02、10等。

for (let i = 1; i < 31; i++) {
  if (i < 10) {
    console.log(`0${i}`)
  } else {
    console.log(i)
  }
}

之前輸出1號到31號,需要手動判斷是否小於10號,小於的在前面增加 0。現在可以這樣做了:

for (let i = 1; i < 31; i++) {
  console.log(i.toString().padStart(2, '0'))
}

String.prototype.padStart 和 String.prototype.padEnd 用法是相同的,只是一個在開頭補白一個在結尾。

5.Object.getOwnPropertyDescriptors()

想理解 Object.getOwnPropertyDescriptors 這個方法之前,首先要弄懂什么是描述符(descriptor)?

 
const data = {
  Portland: '78/50',
  Dublin: '88/52',
  Lima: '58/40'
}

還是上述那個對象,這里有 key 和 value,上邊的代碼把所有的 key、value 遍歷出來,如果我們不想讓 Lima 這個屬性和值被枚舉怎么辦?

Object.defineProperty(data, 'Lima', {
  enumerable: false
})

Object.entries(data).map(([city, temp]) => {
  console.log(`City: ${city.padEnd(16)} Weather: ${temp}`)
  // City: Portland         Weather: 78/50
  // City: Dublin           Weather: 88/52
})

  很成功,Lima 沒有被遍歷出來,那么 defineProperty 的第三個參數就是描述符(descriptor)。這個描述符包括幾個屬性:

  • value [屬性的值]
  • writable [屬性的值是否可被改變]
  • enumerable [屬性的值是否可被枚舉]
  • configurable [描述符本身是否可被修改,屬性是否可被刪除]

 ES9語法

   1.for await of

   我們知道 for…of 是同步運行的,有時候一些任務集合是異步的,那這種遍歷怎么辦呢?

function Gen (time) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve(time)
    }, time)
  })
}

async function test () {
  let arr = [Gen(2000), Gen(100), Gen(3000)]
  for (let item of arr) {
    console.log(Date.now(), item.then(console.log))
  }
}

test()
// 1560090138232 Promise {<pending>}
// 1560090138234 Promise {<pending>}
// 1560090138235 Promise {<pending>}
// 100
// 2000
// 3000

await 的作用,它可以中斷程序的執行直到這個 Promise 對象的狀態發生改變,

function Gen (time) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve(time)
    }, time)
  })
}

async function test () {
  let arr = [Gen(2000), Gen(100), Gen(3000)]
  for (let item of arr) {
    console.log(Date.now(), await item.then(console.log))
  }
}

test()
// 2000
// 1560091834772 undefined
// 100
// 1560091836774 undefined
// 3000
// 1560091836775 undefined

從返回值看確實是按照任務的先后順序進行的,其中原理也有說明是利用了 await 中斷程序的功能。不過,在 ES9 中也可以用 for…await…of 的語法來操作:

 
function Gen (time) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve(time)
    }, time)
  })
}

async function test () {
  let arr = [Gen(2000), Gen(100), Gen(3000)]
  for await (let item of arr) {
    console.log(Date.now(), item)
  }
}

test()
// 1560092345730 2000
// 1560092345730 100
// 1560092346336 3000

  2.Promise.prototype.finally()

   Promise.prototype.finally() 方法返回一個Promise,在promise執行結束時,無論結果是fulfilled或者是rejected,在執行then()和catch()后,都會執行finally指定的回調函數。這為指定執行完promise后,無論結果是fulfilled還是rejected都需要執行的代碼提供了一種方式,避免同樣的語句需要在then()和catch()中各寫一次的情況。

let connection;
db.open()
.then(conn => {
    connection = conn;
    return connection.select({ name: 'Jane' });
})
.then(result => {
    // Process result
    // Use `connection` to make more queries
})
···
.catch(error => {
    // handle errors
})
.finally(() => {
    connection.close();
});

  

  3.Object Rest Spread

前面在講 function 的 Rest & Spread 方法,忘卻的同學可以去復習下。在 ES9 新增 Object 的 Rest & Spread 方法,直接看下示例:

const input = {
  a: 1,
  b: 2
}

const output = {
  ...input,
  c: 3
}

console.log(output) // {a: 1, b: 2, c: 3}

 

ES10語法

  1.JSON.stringify()

  JSON.stringify 在 ES10 修復了對於一些超出范圍的 Unicode 展示錯誤的問題。因為 JSON 都是被編碼成 UTF-8,所以遇到 0xD800–0xDFFF 之內的字符會因為無法編碼成 UTF-8 進而導致顯示錯誤。在 ES10 它會用轉義字符的方式來處理這部分字符而非編碼的方式,這樣就會正常顯示了。

JSON.stringify('\u{D800}') === '"\\ud800"' // true

  2.Array.prototype.flat()

  flat() 方法會按照一個可指定的深度遞歸遍歷數組,並將所有元素與遍歷到的子數組中的元素合並為一個新數組返回。

const newArray = arr.flat(depth)  語法
const numbers = [1, 2, [3, 4, [5, 6]]]
console.log(numbers.flat())
// [1, 2, 3, 4, [5, 6]]

此時 flat 的參數沒有設置,取默認值 1,也就是說只扁平化向下一級,遇到 [3, 4, [5, 6]] 這個數組會扁平會處理,不會再繼續遍歷內部的元素是否還有數組

const numbers = [1, 2, [3, 4, [5, 6]]]
console.log(numbers.flat(2))
// [1, 2, 3, 4, 5, 6]

3.Array.prototype.flatMap()

  flatMap() 方法首先使用映射函數映射每個元素,然后將結果壓縮成一個新數組。從方法的名字上也可以看出來它包含兩部分功能一個是 map,一個是 flat(深度為1)。

const new_array = arr.flatMap(function callback(currentValue[, index[, array]]) {
// 返回新數組的元素
}[, thisArg])
參數 含義 必選
callback 可以生產一個新數組中的元素的函數,可以傳三個參數,currentValue,index,array y
thisArg 遍歷函數this的指向 N

 

const numbers = [1, 2, 3]
numbers.map(x => [x * 2]) // [[2], [4], [6]]
numbers.flatMap(x => [x * 2]) // [2, 4, 6]

這個小示例可以簡單對比下 map 和 flatMap 的區別。當然還可以看下 MDN 的示例:

let arr = ['今天天氣不錯', '', '早上好']
arr.map(s => s.split(''))
// [["今", "天", "天", "氣", "不", "錯"],[""],["早", "上", "好"]]
arr.flatMap(s => s.split(''))
// ["今", "天", "天", "氣", "不", "錯", "", "早", "上", "好"]

4.String.prototype.trimStart()

   trimStart() 方法從字符串的開頭刪除空格,trimLeft()是此方法的別名。

let str = '   foo  '
console.log(str.length) // 8
str = str.trimStart()
console.log(str.length) // 5

5.String.prototype.trimEnd()

  trimEnd() 方法從一個字符串的右端移除空白字符,trimRight 是 trimEnd 的別名。

let str = '   foo  '
console.log(str.length) // 8
str = str.trimEnd()
console.log(str.length) // 6

6.String.prototype.matchAll()

matchAll() 方法返回一個包含所有匹配正則表達式及分組捕獲結果的迭代器

在了解 matchAll 之前,我們回顧下 ES10 之前一共有多少種正則全部遍歷的方法。

  1. RegExp.prototype.exec() with /g

function collectGroup1 (regExp, str) {
  const matches = []
  while (true) {
    const match = regExp.exec(str)
    if (match === null) break
    // Add capture of group 1 to `matches`
    matches.push(match[1])
  }
  return matches
}

collectGroup1(/"([^"]*)"/g, `"foo" and "bar" and "baz"`)
// [ 'foo', 'bar', 'baz' ]

  2. String.prototype.match() with /g

如果用 .match 方法結合 /g 的正則模式,將會把所有的匹配打包成一個數組返回,換句話說所有的捕獲被忽略。

'abeabd'.match(/(a)b(?=e)/g)
// ["ab"]
不過如果沒有使用 /g 的正則模式,.match 的效果和 RegExp.prototype.exec() 是一致的。

  3. String.prototype.replace()

function collectGroup1 (regExp, str) {
  const matches = []
  function replacementFunc (all, first) {
    matches.push(first)
  }
  str.replace(regExp, replacementFunc)
  return matches
}

collectGroup1(/"([^"]*)"/ug, `"foo" and "bar" and "baz"`)
// ["foo", "bar", "baz"]

 

現在看下 .matchAll 方法,可以這樣做:

function collectGroup1 (regExp, str) {
  let results = []
  for (const match of str.matchAll(regExp)) {
    results.push(match[1])
  }
  return results
}
collectGroup1(/"([^"]*)"/g, `"foo" and "bar" and "baz"`)
// ["foo", "bar", "baz"]

 7.Object.fromEntries()

方法 Object.fromEntries() 把鍵值對列表轉換為一個對象,這個方法是和 Object.entries() 相對的。

Object.fromEntries([['foo', 1], ['bar', 2]])
// {foo: 1, bar: 2}

示例

let obj = { abc: 1, def: 2, ghij: 3 }
let res = Object.fromEntries(
  Object.entries(obj)
    .filter(([ key, val ]) => key.length === 3)
    .map(([ key, val ]) => [ key, val * 2 ])
)
console.log(res)
// res is { 'abc': 2, 'def': 4 }

8.Symbol.prototype.description

  我們知道,Symbol 的描述只被存儲在內部的 [[Description]],沒有直接對外暴露,我們只有調用 Symbol 的 toString() 時才可以讀取這個屬性:

const name = Symbol('My name is axuebin')
console.log(name.toString()) // Symbol(My name is axuebin)
console.log(name) // Symbol(My name is axuebin)
console.log(name === 'Symbol(My name is axuebin)') // false
console.log(name.toString() === 'Symbol(My name is axuebin)') // true

9.Function.prototype.toString()

  toString() 方法返回一個表示當前函數源代碼的字符串

function hello (msg) {
  console.log('hello')
}
console.log(hello.toString())
// function hello (msg) {
//   console.log('hello')
// }

10.try…catch

  在 ES10 之前我們都是這樣捕獲異常的:

try {
  // tryCode
} catch (err) {
  // catchCode
}

在這里 err 是必須的參數,在 ES10 可以省略這個參數:

try {
  console.log('Foobar')
} catch {
  console.error('Bar')
}

11.BigInt

在 ES10 增加了一個數據類型:BigInt,用於處理超過 2^53 的數字。

 
const aBigInt = 11n;

const aNumber = 156;
const aBigInt = BigInt(aNumber);
aBigInt === 156n // true
typeof aBigInt === bigint // true

以上就是在工作中比較常用的es6-es10語法知識點


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM