【學習筆記】阮一峯的ES6入門

let、const

塊做用域javascript

ES6引入塊做用域java

考慮到環境致使的行爲差別太大,應該避免在塊級做用域內聲明函數。若是確實須要,也應該寫成函數表達式,而不是函數聲明語句。node

letjquery

  • let聲明的變量只在它所在的代碼塊有效。
  • let不容許在相同做用域內重複聲明同一個變量
  • let聲明不存在變量提高,會產生暫時性死區

constes6

  • 聲明並賦值一個只讀的變量
  • const實際上保證的,並非變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動,所以值類型變量不可變,而引用類型變量能夠更改實體數據內容。
  • 其餘特性和let一致

頂層對象編程

let、const、class命令聲明的全局變量,不屬於頂層對象的屬性數組

解構

ES6 容許按照必定模式,從數組和對象中提取值,對變量進行賦值,這被稱爲解構

實質是模式匹配promise

等號右邊的對象須要具有 Iterator 接口瀏覽器

// 基本模式匹配
let [a, b, , c] = [1, 2, 3]; // a = 1; b = 2; c = undefined;
// 複雜模式匹配
[a, b, c] = [1, [2], 3]; // a = 1; b = [2]; c = 3;
[a, [b], c] = [1, [2], 3]; // a = 1; b = 2; c = 3;
[a, [b], c] = [1, [2, 3], 4]; // a = 1; b = 2; c = 4;
[a, [b], c] = [1, 2, 3]; // Uncaught TypeError: undefined is not a function

// 與rest參數結合使用
let [head, ...tail] = [1, 2, 3, 4]; // head = 1; tail = [2, 3, 4]

// 設置默認值,ES6 內部使用嚴格相等運算符(===),判斷一個位置是否有值。因此,只有當一個數組成員嚴格等於undefined,默認值纔會生效。
let [foo = 1] = []; // foo = 1;
let [foo = 1] = [null]; // foo = null;

// 默認值是表達式的狀況,表達式惰性求值
function f() {
  console.log('aaa');
}
let [x = f()] = [1];

// 默認值爲變量的狀況,變量必須已聲明
let [x = 1, y = x] = []; 
let [x = y, y = 1] = []; // ReferenceError: y is not defined

// 解構字符串
const [a, b, c, d, e] = 'hello'; // a = 'h'; b = 'e'; c = 'l'; d = 'l'; e = 'o'
let { foo, bar } = { foo: "aaa", bar: "bbb" }; // foo = 'aaa'; bar = 'bbb';

// 匹配的模式: 變量
let { foo: f, bar: b } = { foo: "aaa", bar: "bbb" }; // foo = undefined; f = "aaa"; b = "bbb"

// 默認值
let { baz = {} } = { foo: "aaa", bar: "bbb" }; // baz = {};
let {x: y = 3} = {x: 5}; // y = 5;

// 混合解構
let { items: [{name}] } = { id: '1', items: [{name: 'joke'}, {name: 'tony'}] }; // name = 'joke'
let [{name, habbits: [habbit]}] = [{id: '1', name: 'kelly', habbits: ['piano']}, {id: '2', name: 'tony'}]; // name = 'kelly'; habbit = 'piano'

// 嵌套賦值
let obj = {};
let arr = [];
({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });
obj // {prop:123}
arr // [true]

// 在直接使用賦值表達式時,須要使用圓括號包住賦值表達式,大括號寫在行首會被解釋爲代碼塊
let x;
{x} = {x: 1}; // SyntaxError: syntax error
({x} = {x: 1}); // x = 1;

const arr = [1,2,3,4]
let {0 : first, [arr.length - 1] : last, length} = arr; // first = 1; last = 4; length = 4
// 函數參數的解構賦值
function move({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

字符串

  • 模板字符串

    ${name}緩存

  • 擴展方法

    • startsWith

      let s = 'Hello world!';
      s.startsWith('world') // true
      s.startsWith('world', 6) // true, 第二個參數針對第n個位置直到結束
    • endsWith

      let s = 'Hello world!';
      s.endsWith('world') // true
      s.endsWith('Hello', 5) // true, 第二參數針對前n個字符
    • includes

      let s = 'Hello world!';
      s.includes('world') // true
      s.includes('Hello', 6) // false, 第二參數針對第n個位置直到結束
    • repeat

      'hello'.repeat(2) // hellohello,repeat重複字符串操做
    • padStart, padEnd

      // 補全字符串方法,接受兩個參數,第一個參數指定字符串長度,第二個參數指定補全內容
      'x'.padStart(5, 'ab') // ababx
      'x'.padEnd(3, 'a') // xaa

數值

  • 在Number原型上新增了isFinite(), isNaN(), parseInt(), parseFloat(), isInteger()方法,用來代替全局方法
  • 擴展方法

    • Math.trunc - 去除小數部分
    • Math.sign - 判斷一個數究竟是正數、負數、仍是零
    • Math.cbrt - 計算立方根

函數

  • 默認值

    • 參數全等於undefined時使用默認值
    • 默認值爲函數時惰性執行
    • 默認值爲表達式時惰性執行,並不會緩存下計算值
  • rest參數 - 獲取函數的多餘參數, 代替arguments使用
  • name屬性
  • 箭頭函數

    • 箭頭函數自己不存在上下文
    • this在定義時肯定
    • 不能用做構造函數
    • 沒有arguments,用rest代替
    • 不能使用yield命令

數組

  • 解構
  • 擴展運算符
  • 擴展方法

    • Array.from - 將類數組對象或可遍歷對象轉換爲數組
    • Array.of - 將一組值轉換爲數組
    • copyWithin
    • find
    • findIndex
    • fill
    • entries
    • keys
    • values
    • includes - 代替indexOf,更加直觀,避免NaN判斷的問題
    • flat

      [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]
    • flatMap - flat和map的結合

對象

  • 屬性簡寫
  • 屬性名錶達式

    let userName = 'joe';
    let firstName = 'curly';
    let lastName = 'water';
[userName]: 23,
  [firstName + lastName]: 25

};

- 方法的name屬性

- 屬性遍歷

- for...in - 遍歷原型鏈,遍歷自身和繼承的可枚舉屬性(不含Symbol屬性)
- Object.keys - 返回一個數組,包括自身的全部可枚舉屬性(不含 Symbol 屬性)的鍵名
- Object.getOwnPropertyNames - 返回一個數組,包含對象自身的全部屬性(不含 Symbol 屬性)的鍵名
- Object.getOwnPropertySymbols - 返回一個數組,包含對象自身的全部Symbol屬性的鍵名
- Reflect.ownKeys - 返回一個數組,包含對象自身的全部鍵名

- super關鍵字 - 指向當前對象的原型對象,只能用在簡寫的對象方法中

- 解構

- 擴展運算符

- 擴展方法

- Object.is() - 嚴格相等,解決全等運算符`NaN`不等於自身,以及`+0`等於`-0`等問題
- Object.assign() - 用於對象的合併,將源對象(source)的全部可枚舉屬性,複製到目標對象,淺拷貝
- Object.getOwnPropertyDescriptors() - 返回對象屬性的描述對象
- Object.setPrototypeOf() - 設置原型對象
- Object.getPrototypeOf() - 讀取原型對象
- Object.keys() - 返回一個數組,包括自身的全部可枚舉屬性(不含 Symbol 屬性)的鍵名
- Object.values() - 返回一個數組,包括自身的全部可枚舉屬性(不含 Symbol 屬性)的鍵值
- Object.entries() - 方法返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵值對數組
- Object.fromEntries() - Object.entries的逆操做



# Symbol

ES6新加入的類型值,表示獨一無二的值

let s = Symbol();

- `Symbol`函數的參數只是表示對當前 Symbol 值的描述

- 不能與其餘類型的值進行運算

- 做爲屬性名,不能使用點運算符

let mySymbol = Symbol();

// 第一種寫法
let a = {};
a[mySymbol] = 'Hello!';

// 第二種寫法
let a = {

[mySymbol]: 'Hello!'

};

// 第三種寫法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上寫法都獲得一樣結果
a[mySymbol] // "Hello!"

- 能夠用於定義一組常量,保證這組常量的值都是不相等的

const log = {};

log.levels = {

DEBUG: Symbol('debug'),
INFO: Symbol('info'),
WARN: Symbol('warn')

};
console.log(log.levels.DEBUG, 'debug message');
console.log(log.levels.INFO, 'info message');

# Set 和 Map

**Set**

ES6 提供了新的數據結構 Set。它相似於數組,可是成員的值都是惟一的,沒有重複的值。

`Set`自己是一個構造函數,用來生成 Set 數據結構。

const s = new Set([1, 2, 3]);
s.add(4);
s.delete(4);
s.has(4);
s.clear();
s.size;
s.keys();
s.values();
s.entries();
s.forEach((value, key) => console.log(key + ":" + value));

**WeakSet**

WeakSet 結構與 Set 相似,也是不重複的值的集合。

WeakSet 的成員只能是對象。

WeakSet 中的對象都是弱引用,即垃圾回收機制不考慮 WeakSet 對該對象的引用,也就是說,若是其餘對象都再也不引用該對象,那麼垃圾回收機制會自動回收該對象所佔用的內存,不考慮該對象還存在於 WeakSet 之中。

**Map**

ES6 提供了 Map 數據結構。它相似於對象,也是鍵值對的集合,可是「鍵」的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵。

const map = new Map();
map.set('foo', true);
map.set('bar', false);
map.set([1], false);

map.size // 2
map.get('foo');
map.has('foo');
map.delete('foo');
map.clear();

for (let key of map.keys()) {
console.log(key);
}

for (let value of map.values()) {
console.log(value);
}

for (let item of map.entries()) {
console.log(item[0], item[1]);
}

// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}

// 等同於使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}

**WeakMap**

`WeakMap`結構與`Map`結構相似,也是用於生成鍵值對的集合。

`WeakMap`只接受對象做爲鍵名(`null`除外)

`WeakMap`的鍵名所指向的對象,不計入垃圾回收機制。



# Proxy

Proxy 是一個構造函數,能夠理解成,在目標對象以前架設一層「攔截」,外界對該對象的訪問,都必須先經過這層攔截,所以提供了一種機制,能夠對外界的訪問進行過濾和改寫。



# Reflect

保存Object對象的一些屬於語言內部的方法,好比說`defineProperty`/`get`/`apply`

好處在於:讓`Object`操做都變成函數行爲,在Proxy中能夠獲取對象的默認行爲

> - Reflect.apply(target, thisArg, args)
> - Reflect.construct(target, args)
> - Reflect.get(target, name, receiver)
> - Reflect.set(target, name, value, receiver)
> - Reflect.defineProperty(target, name, desc)
> - Reflect.deleteProperty(target, name)
> - Reflect.has(target, name)
> - Reflect.ownKeys(target)
> - Reflect.isExtensible(target)
> - Reflect.preventExtensions(target)
> - Reflect.getOwnPropertyDescriptor(target, name)
> - Reflect.getPrototypeOf(target)
> - Reflect.setPrototypeOf(target, prototype)



# Promise

所謂`Promise`,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。

`Promise`對象是一個構造函數,用來生成`Promise`實例。

狀態:`pending`(進行中)、`fulfilled`(已成功)和`rejected`(已失敗)

- `Promise` 新建後就會當即執行。
- 調用`resolve`或`reject`並不會終結 `Promise` 的參數函數的執行。
- `then`方法返回的是一個新的`Promise`實例
- `catch`方法會捕獲狀態肯定前的全部錯誤,包括在then回調函數中的錯誤
- `Promise` 會吃掉錯誤,不會對後續代碼執行產生影響
- `finally`方法,無論 `Promise` 對象最後狀態如何,都會執行的操做
- `Promise.all`方法用於將多個 `Promise` 實例,包裝成一個新的 `Promise` 實例。多個`Promise`實例都改變狀態,纔會調用新`Promise`實例的回調
- 若是做爲參數的 `Promise` 實例,本身定義了`catch`方法,那麼它一旦被`rejected`,並不會觸發`Promise.all()`的`catch`方法。
- `Promise.race`方法用於將多個 `Promise` 實例,包裝成一個新的 `Promise` 實例。第一個`Promise`實例都改變狀態,進入新`Promise`實例的回調
- `Promise.resolve(reason)`方法也會返回一個新的 `Promise` 實例,該實例的狀態爲`fulfilled`。
- `Promise.reject(reason)`方法也會返回一個新的 `Promise` 實例,該實例的狀態爲`rejected`。

const promise = new Promise(function(resolve, reject) {
// ... some code

if (/ 異步操做成功 /){

resolve(value);

} else {

reject(error);

}
});

promise
.then(function(value) { console.log(value) }, function (err) {console.log(err);})
.catch(function(error) { console.log(error) });

# Iterator

Iterator(遍歷器)是一種接口,爲各類不一樣的數據結構提供統一的訪問機制。任何數據結構只要部署 Iterator 接口,就能夠完成遍歷操做。

var it = makeIterator(['a', 'b']);

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }

function makeIterator(array) {
var nextIndex = 0;
return {

next: function() {
  return nextIndex < array.length ?
    {value: array[nextIndex++], done: false} :
    {value: undefined, done: true};
}

};
}

ES6 規定,默認的 Iterator 接口部署在數據結構的`Symbol.iterator`屬性,或者說,一個數據結構只要具備`Symbol.iterator`屬性,就能夠認爲是「可遍歷的」(iterable)

原生具有Iterator接口的數據結構

- Array
- Map
- Set
- String
- TypedArray
- 函數的 arguments 對象
- NodeList 對象

`for...of`循環調用遍歷器接口,做爲遍歷全部數據結構的統一的方法。

`for...in`循環主要是爲遍歷對象而設計的。

`forEach`沒法跳出循環

`for...of`可跳出循環,嚴格按照順序遍歷



# Generator

Generator 函數是一個狀態機,封裝了多個內部狀態。

執行 Generator 函數會返回一個遍歷器對象,也就是說,Generator 函數除了狀態機,仍是一個遍歷器對象生成函數。返回的遍歷器對象,能夠依次遍歷 Generator 函數內部的每個狀態。

function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}

var hw = helloWorldGenerator();

hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }

- `yield`表達式後面的表達式,只有當調用`next`方法、內部指針指向該語句時纔會執行
- 只有調用`next`方法時,Genarator函數纔會執行
- `yield`只能被Genarator函數包裹,普通函數不行
- `yield*`表達式,用來在一個 Generator 函數裏面執行另外一個 Generator 函數



# async

Generator 函數的語法糖

`async`表示函數裏有異步操做,`await`表示緊跟在後面的表達式須要等待結果。

`await`命令後面能夠是`Promise`對象

`async`函數的返回值是 Promise 對象



# Class

class MyClass {
constructor() {

this.name = name;

}
get prop() {

return 'getter';

}
set prop(value) {

console.log('setter: '+value);

}
}

- 類和模塊的內部,默認就是嚴格模式,因此不須要使用`use strict`指定運行模式

- 類不存在變量提高

new Foo(); // Error
class Foo {

print () { console.log('Hello'); }

}

- name 屬性

- 可使用Generator實現Symbol.iterator遍歷器

- 類的方法內部若是含有`this`,它默認指向類的實例。可是若是單獨提取方法出來用,容易報錯

class Logger {

printName(name = 'there') {
  this.print(`Hello ${name}`);
}

print(text) {
  console.log(text);
}

}

const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined

解決方法

class Logger {

constructor() {
  this.printName = this.printName.bind(this);
}

// ...

}

class Logger {

prinitName () {
    this.print(`Hello ${name}`)    
}
// ...

}

- 若是在一個方法前,加上`static`關鍵字,就表示該方法不會被實例繼承,而是直接經過類來調用,這就稱爲「靜態方法」。

class Foo {

static classMethod() {
  return 'hello';
}

}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

若是靜態方法包含`this`關鍵字,這個`this`指的是類,而不是實例。

父類的靜態方法,能夠被子類繼承。

- 實例屬性除了在`constructor()`方法裏面定義,也能夠直接寫在類的最頂層。

class IncreasingCounter {

_count = 0;
get value() {
  console.log('Getting the current value!');
  return this._count;
}
increment() {
  this._count++;
}

}

- 靜態屬性

class Foo {

static prop = 1;

}

- 私有屬性

- 使用_約定

- 結合Symbol使用避免被覆蓋

- 使用#表明私有屬性
class IncreasingCounter {
  #count = 0;
  get value() {
    console.log('Getting the current value!');
    return this.#count;
  }
  increment() {
    this.#count++;
  }
}
```
  • new.target - 該屬性通常用在構造函數之中,返回new命令做用於的那個構造函數。
  • 類繼承 - 子類本身的this對象,必須先經過父類的構造函數完成塑造,獲得與父類一樣的實例屬性和方法,而後再對其進行加工,加上子類本身的實例屬性和方法。若是不調用super方法,子類就得不到this對象。

Decorator

修飾器是一個對類進行處理的函數

@testable
class MyTestableClass {
  // ...
}

function testable(target) {
  target.isTestable = true;
}

MyTestableClass.isTestable // true
function testable(isTestable) {
  return function(target) {
    target.isTestable = isTestable;
  }
}

@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true

@testable(false)
class MyClass {}
MyClass.isTestable // false

修飾器對類的行爲的改變,是代碼編譯時發生的,而不是在運行時。這意味着,修飾器能在編譯階段運行代碼。也就是說,修飾器本質就是編譯時執行的函數。

@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {}

修飾對象屬性

class Person {
  @readonly
  name() { return `${this.first} ${this.last}` }
}

function readonly(target, name, descriptor){
  // target要修改的對象
  // name要修飾的屬性名
  // descriptor要修飾的屬性的描述對象
  descriptor.writable = false;
  return descriptor;
}

修飾器只能用於類和類的方法,不能用於函數,由於存在函數提高。

Module

CommonJS規範

  • 始於nodejs
  • 特色:同步加載模塊,運行時加載
  • 接口:

    // moduleA.js
    module.exports = function( value ){
        return value * 2;
    }
    // moduleB.js
    var multiplyBy2 = require('./moduleA');
    var result = multiplyBy2(4);
  • 原理:經過require讀取並執行一個JS文件返回該模塊的exports對象的拷貝,一旦輸出一個值,模塊內部的變化就影響不到這個值。CommonJS 的一個模塊,就是一個腳本文件。require命令第一次加載該腳本,就會執行整個腳本,而後在內存生成一個對象緩存。

AMD規範

  • 爲瀏覽器設計,經常使用requirejs實現
  • 特色:異步加載模塊,運行時加載
  • 接口:

    define('myModule', ['jquery'], function($) {
        // $ 是 jquery 模塊的輸出
        $('body').text('hello world');
    });
    
    require(['myModule'], function(myModule) {});
    
    // 未使用模塊名,類CommonJS使用
    define(function(require, exports, module) {})

ES6 模塊

  • 瀏覽器與服務器通用
  • 特色:ES6 模塊的設計思想是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運行時肯定這些東西。
  • 原理:經過export命令顯式指定輸出的代碼(並不是輸出對象),再經過import動態引用。
  • 接口:

    • export命令用於規定模塊的對外接口
    • import命令用於輸入接口
    • export default規定模塊默認接口,本質是輸出一個叫default的變量,因此在模塊中只能惟一存在,而且不可更改,不能跟聲明語句
    • export 其餘模塊,export和import的複合寫法,實際上並無導入當前模塊,只是轉發
    • import命令會被 JavaScript 引擎靜態分析,先於模塊內的其餘語句執行,importexport命令只能在模塊的頂層,不能在代碼塊之中
    • import()函數完成動態加載,返回一個Promise對象
// Module1
var m = 1;
export {
    m
    n as N
};
// 輸出一個default變量,將變量m的值賦給變量default
export default m;

// Module2
import {m, N} from "Module1"; // 導入m和N接口
import { m as M } from "Module1"; // 導入m接口,重命名爲M
import module1 from "Module1"; // 導入默認接口
import * as module1 from "Module1"; // 導入全部接口
export * from "Module1"; // 再輸出,export *命令會忽略Module1模塊的default方法。
  • 加載規則

    瀏覽器對於帶有type="module"<script>,都是異步加載,不會形成堵塞瀏覽器,即等到整個頁面渲染完,再執行模塊腳本,等同於打開了<script>標籤的defer屬性。

    外部ES6模塊腳本特性:

    • 代碼是在模塊做用域之中運行,而不是在全局做用域運行。模塊內部的頂層變量,外部不可見。
    • 模塊腳本自動採用嚴格模式,無論有沒有聲明use strict
    • 模塊之中,可使用import命令加載其餘模塊(.js後綴不可省略,須要提供絕對 URL 或相對 URL),也可使用export命令輸出對外接口。
    • 模塊之中,頂層的this關鍵字返回undefined,而不是指向window。也就是說,在模塊頂層使用this關鍵字,是無心義的。
    • 同一個模塊若是加載屢次,將只執行一次。
  • 解決循環加載問題:使用函數聲明作提高

編程風格

letconst之間,建議優先使用const

  1. 常量表示,便於理解
  2. 有利於將來多線程編寫
  3. JavaScript 編譯器會對const進行優化,因此多使用const,有利於提升程序的運行效率

靜態字符串一概使用單引號或反引號,不使用雙引號。動態字符串使用反引號。

const a = 'apple';
let b = "banana";
b = "batman";

優先使用解構賦值

若是函數返回多個值,優先使用對象的解構賦值,而不是數組的解構賦值。這樣便於之後添加返回值,以及更改返回值的順序。

對象

  • 單行定義的對象,最後一個成員不以逗號結尾。多行定義的對象,最後一個成員以逗號結尾。
  • 對象儘可能靜態化,一旦定義,就不得隨意添加新的屬性。若是添加屬性不可避免,要使用Object.assign方法。
  • 使用屬性表達式定義動態屬性名
  • 對象的屬性和方法儘可能使用簡寫

數組

使用擴展運算符拷貝數組

使用Array.from將類數組對象轉爲數組

函數

全部配置項都應該集中在一個對象,放在最後一個參數

rest運算符代替arguments變量

使用默認值語法設置函數參數的默認值。

Map

注意區分 Object 和 Map,若是隻是須要key: value的數據結構,使用 Map 結構。由於 Map 有內建的遍歷機制。

模塊

若是模塊只有一個輸出值,就使用export default,若是模塊有多個輸出值,就不使用export defaultexport default與普通的export不要同時使用。

若是模塊默認輸出一個函數,函數名的首字母應該小寫。

function makeStyleGuide() {
}

export default makeStyleGuide;

若是模塊默認輸出一個對象,對象名的首字母應該大寫。

const StyleGuide = {
  es6: {
  }
};

export default StyleGuide;
相關文章
相關標籤/搜索