es6 新特性整理


整理了ES6常用的一些語法,跟大家分享(promise、generator什么的還沒有梳理清楚,后續再更新。。。)

1⃣️ 變量聲明-let 與 const

(首先關於測試結果:這里有個小問題,如果用let/const聲明的變量,在控制台調試的話,第一次輸出結果后,第二次如果你還想用,要么把代碼放入編輯器中,再打開瀏覽器看結果,要么就把變量名更改重新輸入結果,否則是會報錯的)
  let與const的作用與var 類似,都是用來聲明變量但是在實際應用中,它倆有各自的特殊用途

  (注意:ES6里let和const沒有變量的提升,一定要聲明后再使用,但代碼編譯成ES5之后,變量提升依舊存在)

  先舉個栗子

 

var name = 'aaron';

if(true) {
    var name = 'peter';
    console.log(name); // peter
}

console.log(name);  //peter

 

 

  我們可以看到,使用var聲明的變量,兩次輸出結果都是peter,那是因為ES5里只有全局作用域和函數作用域,沒有塊級作用域,那么我們怎么才能讓它兩次打印的結果分別是aaron 和 peter呢? 現在let就可以派上用場了  

  改造一下上面的栗子

let name = 'aaron';

if(true) {
    let name = 'peter';
    console.log(name); // peter
}

console.log(name);  //aaron

 

  現在可以看到,兩次的結果已經不相同了,let實際上為JavaScript新增了塊級作用域。用它所聲明的變量,只在let命令所在的代碼塊內有效。

  下面再來看一發關於關於for循環的栗子,問題就是用來計數的循環變量泄露為全局變量,會對我們的一些操作帶來很大的影響,話不多說,來看栗子

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function() {
        console.log(i)
    };
}
a[6](); //10

 

   原本我們的需求是 a[6](),結果可以輸出6,但無論我們寫的是a[i](),最終輸出的結果都是10,因為for循環結束之后,i的值已經變成了10,而i又是全局變量,當函數之行的時候,首先在函數體內部沒有i這么一個變量,所以它會去上一級作用域去尋找,本栗中它的上一級作用域就是全局作用域,所以也就找到了已經變為10的全局變量i,所以a[i]();無論你[]內寫0~9哪個數字,最終輸出結果都是10;

  在沒有ES6之前,如果我們想讓它的輸出就過就是6,就要使用到閉包(閉包這東西,個人的理解,用大白話說就是把你想要實現功能的方法,外面再給它包一個函數。內部return你要實現功能的方法,由於函數的作用域,這樣可以避免變量泄露變成全局變量,從而帶來一些我們不想看到的結果)

var a = [];
function fn(i){
    function inner() {
        console.log(i);
    }
    return inner;
}
for (var i = 0; i < 10; i++) {
  a[i] = fn (i);
}
a[6](); //6

 

  講真,這樣很麻煩有沒有?現在有了ES6 的let ,完全可以不用這么寫了

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function() {
        console.log(i)
    };
}
a[6](); //6

 

   只是改了幾個字母,var改成了let,已經實現了我們的需求,很方便有沒有?!

  const也用來聲明變量,但是聲明的是常量。一旦聲明,常量的值就不能改變。改變的話,瀏覽器會報錯

  栗子

const A = 1;
A = 2; // Uncaught TypeError: Assignment to constant variable.
針對const的這個特性,我們可以在引用第三方庫的時,把需要聲明的變量,用const來聲明,這樣可以避免未來不小心重命名而導致出現bug
const xxx = require('xxxxx');
注意:當值為基礎數據類型時,那么這里的值,就是指值本身。

    而當值對應的為引用數據類型時,那么這里的值,則表示指向該對象的引用。這里需要注意,正因為該值為一個引用,只需要保證引用不變就可以,我們仍然可以改變該引用所指向的對象。

  栗子

const obj = {
    a: 20,
    b: 30
}

obj.a = 30;
obj.c = 40;

console.log(obj); // Object {a: 30, b: 30,c:40}

 

這種情況下只要不是直接覆蓋obj的值,只改變屬性什么的還是還可以

 

 2⃣️模版字符串

使用 反引號``(鍵盤上esc下面那個鍵) 將整個字符串包裹起來,而在其中使用 ${} 來包裹一個變量或者一個表達式
// es5
var a = 20;
var b = 30;
var string = a + "+" + b + "=" + (a + b);

// es6
const a = 20;
const b = 30;
const string = `${a}+${b}=${a+b}`;

 

 3⃣️解構(destructuring)賦值

  數組以序列號一一對應,這是一個有序的對應關系。
  對象根據屬性名一一對應,這是一個無序的對應關系。

  為了更好的理解,直接上栗子吧
 數組的解構賦值
// es5
var arr = [1, 2, 3];
var a = arr[0];
var b = arr[1];
var c = arr[2];

// es6
const arr = [1, 2, 3];
const [a, b, c] = arr;
console.log(a,b,c) // 1,2,3

 

  對象的解構賦值
const MYOBJ = {
    className: 'trigger-button',
    loading: false,
    clicked: true,
    disabled: 'disabled'
}

 

現在我們想要取得其中的2個值:loading與clicked:

// es5
var loading = MYOBJ.loading;
var clicked = MYOBJ.clicked;

// es6
const { loading, clicked } = MYOBJ;
console.log(loading);// false

// 還可以給一個默認值,當props對象中找不到loading時,loading就等於該默認值
const { loadings = false, clicked } = MYOBJ;
console.log(loadings);// false

 

4⃣️展開運算符(spread operater)

   在ES6中用...來表示展開運算符,它可以將數組方法或者對象進行展開。上栗子

1.函數調用中使用展開運算符

  函數調用里,將一個數組展開成多個參數,我們會用到apply:

function test(a, b, c) { }
var args = [0, 1, 2];
test.apply(null, args);
  在ES6里可以這樣寫

function test(a,b,c) { }
var args = [0,1,2];
test(...args);

 

2.數組字面量中使用展開運算符

  有了ES6,我們可以直接加一個數組直接合並到另外一個數組當中

const arr1 = [1, 2, 3];
const arr2 = [...arr1, 10, 20, 30];

// 這樣,arr2 就變成了[1, 2, 3, 10, 20, 30];

 

  展開運算符也可以用在push函數中,可以不用再用apply()函數來合並兩個數組:

var arr1=['a','b','c'];
var arr2=['d','e'];
arr1.push(...arr2); //['a','b','c','d','e']

 

3.用於解構賦值

let [arg1,arg2,...arg3] = [1, 2, 3, 4];
arg1 //1
arg2 //2
arg3 //['3','4']

 

  注意:解構賦值中展開運算符只能用在最后,否則會報錯

4.展開運算符可以將偽數組變成真正的數組

var list=document.querySelectorAll('div');
var arr=[..list];
Object.prototype.toString.apply(list) // "[object NodeList]"

  Object.prototype.toString.apply(arr) // "[object Array]"

 

 

關於對象展開
好像目前ES6還不支持這樣,現在這樣寫瀏覽器會報錯,ES7草案里貌似有提到,所以對象展開這里先了解一下就好了
const obj1 = {
  a: 1,
  b: 2, 
  c: 3
}

const obj2 = {
  ...obj1,
  d: 4,
  e: 5,
  f: 6
}

// 結果類似於 const obj2 = Object.assign({}, obj1, {d: 4,e:5,f:6})

 


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

 

5⃣️ arrow function箭頭函數

  函數的快捷寫法,不需要通過 function 關鍵字創建函數,並且還可以省略 return 關鍵字。(注意:箭頭函數本身沒有this,如果在箭頭函數內使用this,這個this一定是它上級的this,再有就是箭頭函數可以代替函數表達式,但代替不了函數聲明,它還是需要聲明才能使用的)。

 
(parma)=>{expression},箭頭函數根據parma個數的不同,寫法上還可以做如下改變
() => { expression } // 零個參數用 () 表示
x => { expression } // 一個參數可以省略 ()
(x, y) => { expression } // 多參數不能省略 ()

 

注意: 在ES6中,會默認采用嚴格模式,因此this也不會自動指向window對象了,而箭頭函數本身並沒有this,因此this就只能是undefined,這一點,在使用的時候,一定要慎重慎重再慎重,不然踩了坑你都不知道自己錯在哪!這種情況,如果你還想用this,就不要用使用箭頭函數的寫法。

  栗子

var person = {
    name: 'tom',
    getName: function() {
        return this.name;
    }
}

// 用ES6的寫法來重構上面的對象
const person = {
    name: 'tom',
    getName: () => this.name
}

// 但是編譯結果卻是
var person = {
    name: 'tom',
    getName: function getName() {
        return undefined.name;
    }
};

 

對上面的代碼稍作改動

const person = {
    name: 'tom',
    getName: function() {
        return setTimeout(() => this.name, 1000);
    }
}

// 編譯之后變成
var person = {
    name: 'tom',
    getName: function getName() {
        var _this = this;  // 使用了我們在es5時常用的方式保存this引用

        return setTimeout(function () {
            return _this.name;
        }, 1000);
    }
};

 

6⃣️函數參數的默認值

之前我們想要保證傳入函數的參數有一個默認值,通常需要這么寫
function add(x) {
    var x = x || 20;
    return x;
}

console.log(add()); // 20

 

但這種方法是有缺陷的,比如說我們如果傳入一個false

function add(x) {
    var x = x || 20;
    return x;
}

console.log(add(false)); // 20

 

打印結果是20 而不是fasle,顯然合格結果不是我們想要的,如果我們想要打印出false,就還要再做什么if判斷等一些列操作,很麻煩,現在有了es6,我們可以很容易解決這個問題,下面我們來看一下es6的寫法

function add(x = 20) {
    return x ;
}

console.log(add());// 20
console.log(add(false)); // false

 

可以看到,es6很容易就解決了這個問題。
7⃣️對象字面量({})擴展
ES6針對對象字面量做了許多簡化語法的處理

1)精簡屬性:

const name = 'Jane';
const age = 20

// es6寫法
const person = {
  name,
  age
}

// es5寫法
var person = {
  name: name,
  age: age
};

 

2)精簡方法:

// es6寫法
const person = {
  name,
  age,
  getName() { // 只要不使用箭頭函數,this就還是我們熟悉的this
    return this.name
  }
}

// es5寫法
var person = {
  name: name,
  age: age,
  getName: function getName() {
    return this.name;
  }
};

 

3)屬性名表達式:(這里有點兒惡心,經過幾次代碼測試,最終確定下面這樣解釋的話,可能會容易理解一些)

  在對象字面量中可以使用中括號作為屬性,表示屬性也能是一個變量了,而且這個變量的值還可以改變

const name = 'Jane';

const person = {
  [name]: true,
  ['a'+'ge']: true
}
注意:上面的對象{}內寫了兩個[]屬性 ,切記[]里面如果是一個變量的話,那么這個變量一定要是一個已經聲明過的,否則結果就是undefined,[]如果是表達式,那么訪問的時候,要向下面這樣寫
console.log(person['a'+'ge'])/console.log(person['age'])/console.log(oerson.age) // true
console.log(person[name]);// true  

注意:對象內用[變量]當作屬性時,訪問該屬性只能用[]語法,對象內給這個屬性賦什么值,它就會變成什么值,而且這個值也可以通過[]里面傳入訪問變量最初設置的值,也可以訪問到,寫就是說像下面這樣寫 和上面結果是一樣的
 console.log(person['Jane']);  // true

 

對象的方法也可以這樣寫

let obj = {  
    ['h'+'ello']() {  
        return 'hi';  
     }  
};  
console.log(obj.hello()); // hi  

 

 8⃣️class、extend、super

class、extend、super這三個特性涉及了ES5中最令人頭疼的的幾個部分:構造函數、繼承,原型...

ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念。新的class寫法讓對象原型的寫法更加清晰、更像面向對象編程的語法,也更加通俗易懂。

新舊語法對比

 class

// ES5
// 構造函數
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 原型方法
Person.prototype.getName = function() {
  return this.name
}

// ES6
class Person {
  constructor(name, age) {  // 構造函數
    this.name = name;
    this.age = age;
  }

  getName() {  // 原型方法
    return this.name
  }
}

 

上面代碼首先用class定義了一個“類”,可以看到里面有一個constructor方法,這就是構造方法,而this關鍵字則代表實例對象。

簡單地說,constructor內定義的方法和屬性是實例對象自己的,而constructor外定義的方法和屬性則是所有實例對象可以共享的。這個和ES5里的構造函數是差不多的意思,相當於把方法定義在構造函數里是私有的,而把方法定義到原型中,所有實例共享

extend繼承

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  getName() {
    return this.name
  }
}

// Student類繼承Person類
class Student extends Person {
  constructor(name, age, gender, classes) {
    super(name, age);
    this.gender = gender;
    this.classes = classes;
  }

  getGender() {
    return this.gender;
  }
}

 

Class之間可以通過extends關鍵字實現繼承,這比ES5的通過修改原型鏈實現繼承,要清晰和方便很多。上面定義了一個Cat類,該類通過extends關鍵字,繼承了Animal類的所有屬性和方法。

super關鍵字,它指代父類的實例(即父類的this對象)。子類必須在constructor方法中調用super方法,否則新建實例時會報錯。這是因為子類沒有自己的this對象,而是繼承父類的this對象,然后可以對其進行加工。如果不調用super方法,子類就得不到this對象。

關於 super,像上面的栗子

// 構造函數中
// es5寫法
Person.call(this);
// es6寫法 
super(name, age); 

 

class、extend/super三者的綜合實栗

class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        console.log(this.type + ' says ' + say)
    }
}

let animal = new Animal()
animal.says('hello') //animal says hello

class Cat extends Animal {
    constructor(){
        super()
        this.type = 'cat'
    }
}

let cat = new Cat()
cat.says('hello') //cat says hello

 

9⃣️模塊的 Import 和 Export

import 用於引入模塊,export 用於導出模塊。

// 引入整個文件
import dva from 'dva';

// 引入函數(可以是一個或多個)
import { connect } from 'dva';
import { Link, Route } from 'dva/router';

// 引入全部並作為 github 對象
import * as github from './services/github';

// 導出默認
export default App;
// 部分導出,復合寫法是 export { App } from './file';
  等價於import { App } from './file;export{App}

 

 

 

ECMAScript6(ECMAScript 2015 ,ES5,ES2016)技術已經在前端圈子很流行了,他給前端開發人員帶來了很多驚喜,提供的語法糖使復雜的操作變得簡單。

本文沒有詳細描述這些新特性,因為網上都已經有很多相關的介紹了。主要針對ES6 新特性收集了相關范例代碼,他可以讓你快速了解這個新的javascript規范。

箭頭函數

function() 函數的簡寫表示法,但它不綁定 this。

ES6 代碼:

var odds = evens.map(v => v + 1);  // no parentes and no brackets
var nums = evens.map((v, i) => v + i);
var pairs = evens.map(v => ({even: v, odd: v + 1}));
 
// Statement bodies
nums.forEach(v => {
  if (v % 5 === 0)
    fives.push(v);
});

 

this 是如何工作的?

ES6 代碼:

var object = {
    name: "Name", 
    arrowGetName: () => this.name,
    regularGetName: function() { return this.name },
    arrowGetThis: () => this,
    regularGetThis: function() { return this }
}
console.log(object);
console.log(object.name)
console.log(object.arrowGetName());
console.log(object.arrowGetThis());
console.log(this);
console.log(object.regularGetName());
console.log(object.regularGetThis());

 

結果:
console.log(object);  =>
Object {
    name: "Name",
    arrowGetName: function,
    regularGetName: function,
    arrowGetThis: function,
    regularGetThis: function
}

console.log(object.name) => 
Name

console.log(object.arrowGetName());  =>
undefined

console.log(object.arrowGetThis());  =>  [object Window] 
Window {
    stop: function,
    open: function,
    alert: function,
    confirm: function,
    prompt: function,
    ...
}

console.log(this);  => 同上

console.log(object.regularGetName());   =>
Name

console.log(object.regularGetThis());  =>
Object {
    name: "Name",
    arrowGetName: function,
    regularGetName: function,
    arrowGetThis: function,
    regularGetThis: function
}

 

我們知道“真正”語言中的類(Classes)。在 ES6 中類(Classes)其實是原型繼承的語法糖。

ES6 代碼:

class SkinnedMesh extends THREE.Mesh {
  constructor(geometry, materials) {
    super(geometry, materials);
 
    this.idMatrix = SkinnedMesh.defaultMatrix();
    this.bones = [];
    this.boneMatrices = [];
    //...
  }
  update(camera) {
    //...
    super.update();
  }
  get boneCount() {
    return this.bones.length;
  }
  set matrixType(matrixType) {
    this.idMatrix = SkinnedMesh[matrixType]();
  }
  static defaultMatrix() {
    return new THREE.Matrix4();
  }
}

 

增強的對象字面量

ES6 代碼:

var theProtoObj = {
  toString: function() {
    return "The ProtoOBject To string"
  }
}
var handler = () => "handler"
var obj = {
    // __proto__
    __proto__: theProtoObj,
    // Shorthand for ‘handler: handler’
    handler,
    // Methods
    toString() {
     // Super calls
     return "d " + super.toString();
    },
    // Computed (dynamic) property names
    [ "prop_" + (() => 42)() ]: 42
};
 
console.log(obj.handler)
console.log(obj.handler())
console.log(obj.toString())
console.log(obj.prop_42)

 

結果:
obj.handler -> () => "handler"
obj.handler() -> handler
obj.toString() -> d The ProtoOBject To string
obj.prop_42 -> 
(() => 42)()  是一個立即執行函數 (() => 42)() 相當於
(function() {
    return 42;
})()
42

 

字符串插值
var name = "Bob", time = "today";
 
var multiLine = `This Line Spans Multiple Lines`;
 
console.log(`Hello ${name},how are you ${time}?`)
console.log(multiLine)

 

結果:
`Hello ${name},how are you ${time}?` -> Hello Bob,how are you today?
multiLine -> This Line Spans Multiple Lines

 

解構 Destructuring
// list "matching"
var [a, , b] = [1,2,3];
console.log(a);
console.log(b);

 

結果:
a -> 1
b -> 3

 

對象也能很好的解構

nodes = () => { return {op: "a", lhs: "b", rhs: "c"}}
var { op: a, lhs: b , rhs: c } = nodes()
console.log(a)
console.log(b)
console.log(c)

 

結果:
a -> a
b -> b
c -> c

 

使用速記表示法。
nodes = () => { return {lhs: "a", op: "b", rhs: "c"}}
 
// binds `op`, `lhs` and `rhs` in scope
var {op, lhs, rhs} = nodes()
 
console.log(op)
console.log(lhs)
console.log(rhs)

 

結果:
op -> b
lhs -> a
rhs -> c

 

可在參數位置使用
function g({name: x}) {
  return x
}
 
function m({name}) {
  return name
}
 
console.log(g({name: 5}))
console.log(m({name: 5}))

 

結果:
g({name: 5}) -> 5
m({name: 5}) -> 5

 

故障弱化解構
var [a] = []
var [b = 1] = []
var c = [];
console.log(a)
console.log(b);
console.log(c);

 

結果:
a -> undefined
b -> 1
c -> []

 

參數默認值(Default)
function f(x, y=12) {
  return x + y;
}
 
console.log(f(3))
console.log(f(3,2))

 

結果:
f(3) -> 15
f(3,2) -> 5

 

擴展(Spread)
在函數中:

function f(x, y, z) {
  return x + y + z;
}
// 傳遞數組的每個元素作為參數
console.log(f(...[1,2,3]))

 

結果:
f(...[1,2,3]) -> 6

 

在數組中:
var parts = ["shoulders", "knees"];
var lyrics = ["head", ...parts, "and", "toes"]; 
 
console.log(lyrics)

 

結果:
lyrics -> ["head","shoulders","knees","and","toes"]

 

擴展 + 對象字面量

我們可以使用這個創造很酷的對象。

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; console.log(x); // 1 console.log(y); // 2 console.log(z); // { a: 3, b: 4 } // Spread properties let n = { x, y, ...z }; console.log(n); // { x: 1, y: 2, a: 3, b: 4 } console.log(obj) 可惜的是它還不支持: npm install --save-dev babel-plugin-transform-object-rest-spread 
Rest

我們可以使用 rest 操作符來允許無限參數。

function demo(part1, ...part2) { return {part1, part2} } console.log(demo(1,2,3,4,5,6))
結果:
demo(1,2,3,4,5,6) -> {"part1":1,"part2":[2,3,4,5,6]}
Let

let是新的var。 因為它有塊級作用域。

{ var globalVar = "from demo1" } { let globalLet = "from demo2"; } console.log(globalVar) console.log(globalLet)

結果:

globalVar -> from demo1 globalLet -> ReferenceError: globalLet is not defined

但是,它不會向window分配任何內容:

let me = "go"; // 全局作用域 var i = "able"; // 全局作用域 console.log(window.me); console.log(window.i);
結果:
window.me -> undefined window.i -> able

不能使用let重新聲明一個變量:

let me = "foo"; let me = "bar"; console.log(me);
結果:
SyntaxError: Identifier 'me' has already been declared
var me = "foo"; var me = "bar"; console.log(me)
結果:
me -> bar
Const

const 是只讀變量。

const a = "b"
a = "a"

 

結果:
TypeError: Assignment to constant variable.

 

應該注意,const 對象仍然可以被改變的。

const a = { a: "a" }
a.a = "b"
console.log(a)

 

結果:
a -> {"a":"b"}

 

For..of

迭代器的新類型,可以替代for..in。 它返回的是值而不是keys。

let list = [4, 5, 6];
console.log(list)
for (let i in list) {
   console.log(i);
}

 

結果:
list -> [4,5,6]
i -> 0
i -> 1
i -> 2
let list = [4, 5, 6];
console.log(list)
for (let i of list) {
   console.log(i); 
}

 

結果:
list -> [4,5,6] i -> 4 i -> 5 i -> 6
迭代器(Iterators)

迭代器是一個比數組更動態的類型。

let infinite = { [Symbol.iterator]() { let c = 0; return { next() { c++; return { done: false, value: c } } } } } console.log("start"); for (var n of infinite) { // truncate the sequence at 1000 if (n > 10) break; console.log(n); }
結果:
"start" -> start n -> 1 n -> 2 n -> 3 n -> 4 n -> 5 n -> 6 n -> 7 n -> 8 n -> 9 n -> 10

使用Typescript,我們可以看到它接口的樣子:

Typescript 代碼: interface IteratorResult { done: boolean; value: any; } interface Iterator { next(): IteratorResult; } interface Iterable { [Symbol.iterator](): Iterator }
生成器(Generators)

生成器創建迭代器,並且比迭代器更具動態性。他們不必以相同的方式跟蹤狀態 並不支持 done 的概念。

var infinity = { [Symbol.iterator]: function*() { var c = 1; for (;;) { yield c++; } } } console.log("start") for (var n of infinity) { // truncate the sequence at 1000 if (n > 10) break; console.log(n); }
結果:
"start" -> start n -> 1 n -> 2 n -> 3 n -> 4 n -> 5 n -> 6 n -> 7 n -> 8 n -> 9 n -> 10

使用Typescript 再次顯示接口:

Typescript 代碼:

interface Generator extends Iterator { next(value?: any): IteratorResult; throw(exception: any); }
function* Iterators and generator

一個產量的例子*

function* anotherGenerator(i) { yield i + 1; yield i + 2; yield i + 3; } function* generator(i) { yield i; yield* anotherGenerator(i); yield i + 10; } var gen = generator(10); console.log(gen.next().value); console.log(gen.next().value); console.log(gen.next().value); console.log(gen.next().value); console.log(gen.next().value);
結果:
gen.next().value -> 10 gen.next().value -> 11 gen.next().value -> 12 gen.next().value -> 13 gen.next().value -> 20
Unicode

ES6 為Unicode 提供了更好的支持。

var regex = new RegExp('\u{61}', 'u'); console.log(regex.unicode) console.log("\uD842\uDFD7") console.log("\uD842\uDFD7".codePointAt())
結果:
regex.unicode -> true "" -> "".codePointAt() -> 134103
模塊和模塊加載器

原生支持模塊。

import defaultMember from "module-name"; import * as name from "module-name"; import { member } from "module-name"; import { member as alias } from "module-name"; import { member1 , member2 } from "module-name"; import { member1 , member2 as alias2 , [...] } from "module-name"; import defaultMember, { member [ , [...] ] } from "module-name"; import defaultMember, * as name from "module-name"; import "module-name";
export { name1, name2, …, nameN }; export { variable1 as name1, variable2 as name2, …, nameN }; export let name1, name2, …, nameN; // also var export let name1 = …, name2 = …, …, nameN; // also var, const export expression; export default expression; export default function (…) { … } // also class, function* export default function name1(…) { … } // also class, function* export { name1 as default, … }; export * from …; export { name1, name2, …, nameN } from …; export { import1 as name1, import2 as name2, …, nameN } from …; Import Export
Set

Set 為數學對應,其中所有項目都是唯一的。對於知道SQL的人來說,這相當於distinct。

var set = new Set(); set.add("Potato").add("Tomato").add("Tomato"); console.log(set.size) console.log(set.has("Tomato")) for(var item of set) { console.log(item) }
結果:
set.size -> 2 set.has("Tomato") -> true item -> Potato item -> Tomato
WeakSet

WeakSet對象允許您在集合中存儲弱持有的對象。沒有引用的對象將被垃圾回收。

var item = { a:"Potato"} var set = new WeakSet(); set.add({ a:"Potato"}).add(item).add({ a:"Tomato"}).add({ a:"Tomato"}); console.log(set.size) console.log(set.has({a:"Tomato"})) console.log(set.has(item)) for(let item of set) { console.log(item) }
結果:
set.size -> undefined set.has({a:"Tomato"}) -> false set.has(item) -> true TypeError: set[Symbol.iterator] is not a function
Map

Map 也稱為詞典。

var map = new Map(); map.set("Potato", 12); map.set("Tomato", 34); console.log(map.get("Potato")) for(let item of map) { console.log(item) } for(let item in map) { console.log(item) }
結果:
map.get("Potato") -> 12 item -> ["Potato",12] item -> ["Tomato",34]

可以使用除字符串之外的其他類型。

var map = new Map(); var key = {a: "a"} map.set(key, 12); console.log(map.get(key)) console.log(map.get({a: "a"}))
結果:
map.get(key) -> 12 map.get({a: "a"}) -> undefined
WeakMap

使用鍵的對象,並且只保留對鍵的弱引用。

var wm = new WeakMap(); var o1 = {} var o2 = {} var o3 = {} wm.set(o1, 1); wm.set(o2, 2); wm.set(o3, {a: "a"}); wm.set({}, 4); console.log(wm.get(o2)); console.log(wm.has({})) delete o2; console.log(wm.get(o3)); for(let item in wm) { console.log(item) } for(let item of wm) { console.log(item) }
結果:
wm.get(o2) -> 2 wm.has({}) -> false wm.get(o3) -> {"a":"a"} TypeError: wm[Symbol.iterator] is not a function
代理(Proxy)

代理可以用來改變對象的行為。 它們允許我們定義 trap 。

var obj = function ProfanityGenerator() { return { words: "Horrible words" } }() var handler = function CensoringHandler() { return { get: function (target, key) { return target[key].replace("Horrible", "Nice"); }, } }() var proxy = new Proxy(obj, handler); console.log(proxy.words);
結果:
proxy.words -> Nice words
提供以下 trap :
var handler = {  get:...,  set:...,  has:...,  deleteProperty:...,  apply:...,  construct:...,  getOwnPropertyDescriptor:...,  defineProperty:...,  getPrototypeOf:...,  setPrototypeOf:...,  enumerate:...,  ownKeys:...,  preventExtensions:...,  isExtensible:... }

Symbols 是一個新類型。 可用於創建匿名屬性。

var typeSymbol = Symbol("type"); class Pet { constructor(type) { this[typeSymbol] = type; } getType() { return this[typeSymbol]; } } var a = new Pet("dog"); console.log(a.getType()); console.log(Object.getOwnPropertyNames(a)) console.log(Symbol("a") === Symbol("a"))
結果:
a.getType() -> dog Object.getOwnPropertyNames(a) -> [] Symbol("a") === Symbol("a") -> false
可繼承內置函數

我們現在可以繼承原生類。

class CustomArray extends Array { } var a = new CustomArray(); a[0] = 2 console.log(a[0])
結果:
a[0] -> 2
不能使用數組的代理(Proxy)來覆蓋getter函數。
新類庫
各種新的方法和常量
console.log(Number.EPSILON) console.log(Number.isInteger(Infinity)) console.log(Number.isNaN("NaN")) console.log(Math.acosh(3)) console.log(Math.hypot(3, 4)) console.log(Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2)) console.log("abcde".includes("cd") ) console.log("abc".repeat(3) ) console.log(Array.of(1, 2, 3) ) console.log([0, 0, 0].fill(7, 1) ) console.log([1, 2, 3].find(x => x == 3) ) console.log([1, 2, 3].findIndex(x => x == 2)) console.log([1, 2, 3, 4, 5].copyWithin(3, 0)) console.log(["a", "b", "c"].entries() ) console.log(["a", "b", "c"].keys() ) console.log(["a", "b", "c"].values() ) console.log(Object.assign({}, { origin: new Point(0,0) }))
結果:
Number.EPSILON -> 2.220446049250313e-16 Number.isInteger(Infinity) -> false Number.isNaN("NaN") -> false Math.acosh(3) -> 1.7627471740390859 Math.hypot(3, 4) -> 5 Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) -> 2 "abcde".includes("cd") -> true "abc".repeat(3) -> abcabcabc Array.of(1, 2, 3) -> [1,2,3] [0, 0, 0].fill(7, 1) -> [0,7,7] [1, 2, 3].find(x => x == 3) -> 3 [1, 2, 3].findIndex(x => x == 2) -> 1 [1, 2, 3, 4, 5].copyWithin(3, 0) -> [1,2,3,1,2] ["a", "b", "c"].entries() -> {} ["a", "b", "c"].keys() -> {} ["a", "b", "c"].values() -> TypeError: ["a","b","c"].values is not a function Object.assign({}, { origin: new Point(0,0) }) -> ReferenceError: Point is not defined 文檔: Number, Math, Array.from, Array.of, Array.prototype.copyWithin, Object.assign
二進制和八進制

二進制和八進制數字的字面量。

console.log(0b11111) console.log(0o2342) console.log(0xff); // also in es5
結果:
ES6 代碼: 0b11111 -> 31 0o2342 -> 1250 0xff -> 255
Promises

異步編程。

var p1 = new Promise((resolve, reject) => { setTimeout(() => resolve("1"), 101) }) var p2 = new Promise((resolve, reject) => { setTimeout(() => resolve("2"), 100) }) Promise.race([p1, p2]).then((res) => { console.log(res) }) Promise.all([p1, p2]).then((res) => { console.log(res) })
結果:
res -> 2 res -> ["1","2"]
快速的 Promise
var p1 = Promise.resolve("1") var p2 = Promise.reject("2") Promise.race([p1, p2]).then((res) => { console.log(res) })
結果:
res -> 1
快速失敗

如果一個 promise 失敗,all和race也將 reject(拒絕)。

var p1 = new Promise((resolve, reject) => { setTimeout(() => resolve("1"), 1001) }) var p2 = new Promise((resolve, reject) => { setTimeout(() => reject("2"), 1) }) Promise.race([p1, p2]).then((res) => { console.log("success" + res) }, res => { console.log("error " + res) }) Promise.all([p1, p2]).then((res) => { console.log("success" + res) }, res => { console.log("error " + res) })
結果:
"error " + res -> error 2 "error " + res -> error 2
反射(Reflect)

新類型的元編程與新的API現有的還有一些新的方法。

var z = {w: "Super Hello"} var y = {x: "hello", __proto__: z}; console.log(Reflect.getOwnPropertyDescriptor(y, "x")); console.log(Reflect.has(y, "w")); console.log(Reflect.ownKeys(y, "w")); console.log(Reflect.has(y, "x")); console.log(Reflect.deleteProperty(y,"x")) console.log(Reflect.has(y, "x"));
結果:
Reflect.getOwnPropertyDescriptor(y, "x") -> {"value":"hello","writable":true,"enumerable":true,"configurable":true} Reflect.has(y, "w") -> true Reflect.ownKeys(y, "w") -> ["x"] Reflect.has(y, "x") -> true Reflect.deleteProperty(y,"x") -> true Reflect.has(y, "x") -> false
尾調用(Tail Call)優化

尾調用的概念非常簡單,一句話就能說清楚,就是指某個函數的最后一步是調用另一個函數。

ES6可以確保尾調用不會造成堆棧溢出。 (不是所有的實現工作)。

function factorial(n, acc = 1) { if (n <= 1) return acc; return factorial(n - 1, n * acc); } console.log(factorial(10)) console.log(factorial(100)) console.log(factorial(1000)) console.log(factorial(10000)) console.log(factorial(100000)) console.log(factorial(1000000))
結果:
factorial(10) -> 3628800 factorial(100) -> 9.332621544394418e+157 factorial(1000) -> Infinity factorial(10000) -> Infinity factorial(100000) -> RangeError: Maximum call stack size exceeded factorial(1000000) -> RangeError: Maximum call stack size exceeded

原文:ES6 Features

 

 

 

ES6的各種新特性的兼容性查詢http://kangax.github.io/compat-table/es6/
盡管我們的瀏覽器還不一定完全支持ES6代碼,我們可以使用Babel轉碼器,在這里我們使用命令行轉碼babel-cli,命令行$ npm install --global babel-cli安裝babel-cli

let和const命令let命令

ES6新增了 let 命令,用來聲明變量。它的用法類似於 var ,但是所聲明的變量,只在 let 命令所在的代碼塊內有效。let不像var那樣會發生“變量提升”現象。所以,變量一定要在聲明后使用,否則報錯。

{ let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1

ES6明確規定,如果區塊中存在 let 和 const 命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。
總之,在代碼塊內,使用let命令聲明變量之前,該變量都是不可用的。這在語法上,稱為“暫時性死區”(temporal dead zone,簡稱TDZ)。
let不允許在相同作用域內,重復聲明同一個變量。let 實際上為JavaScript新增了塊級作用域。 ES6引入了塊級作用域,明確允許在塊級作用域之中聲明函數。

const命令

const 聲明一個只讀的常量。一旦聲明,常量的值就不能改變。
const 命令聲明的常量也是不提升,同樣存在暫時性死區,只能在聲明的位置后面使用。

if (true) { console.log(MAX); // ReferenceError const MAX = 5; }

ES6規定var 命令和 function 命令聲明的全局變量,依舊是全局對象的屬性;let 命令、 const 命令、 class 命令聲明的全局變量,不屬於全局對象的屬性。

變量的解構賦值數組的解構賦值

let [foo, [[bar], baz]] = [1, [[2], 3]]; foo // 1 bar // 2 baz // 3

解構賦值允許指定默認值。

[x, y = 'b'] = ['a']; // x='a', y='b' [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

注意,ES6內部使用嚴格相等運算符( === ),判斷一個位置是否有值。所以,如果一個數組成員不嚴格等於 undefined ,默認值是不會生效的。

對象的解構賦值

var { foo, bar } = { foo: "aaa", bar: "bbb" }; foo // "aaa" bar // "bbb" var { foo: baz } = { foo: "aaa", bar: "bbb" }; baz // "aaa" foo // error: foo is not defined

對象的解構與數組有一個重要的不同。數組的元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。

函數參數的解構賦值

[[1, 2], [3, 4]].map(function([a,b]){ return a + b; }) //[3,7]

變量解構賦值用途

  1. 交換變量的值[x, y] = [y, x];
  2. 提取JSON數據

    var jsonData = {
     id: 42,  status: "OK",  data: [867, 5309] }; let { id, status, data: number } = jsonData; console.log(id, status, number);// 42, "OK", [867, 5309]
  3. 函數參數的默認值

    jQuery.ajax = function (url, { async = true, beforeSend = function () {}, cache = true, complete = function () {}, crossDomain = false, global = true }) { // ... do stuff };

字符串的擴展includes(), startsWith(), endsWith()

includes():返回布爾值,表示是否找到了參數字符串。
startsWith():返回布爾值,表示參數字符串是否在源字符串的頭部。
endsWith():返回布爾值,表示參數字符串是否在源字符串的尾部。

var s = 'Hello world!'; s.startsWith('world', 6); // true s.endsWith('Hello', 5); // true s.includes('Hello', 6); // false

使用第二個參數 n 時, endsWith 的行為與其他兩個方法有所不同。它針對前 n 個字符,而其他兩個方法針對從第 n 個位置直到字符串結束。

repeat()

返回一個新字符串,表示將原字符串重復 n 次。

'hello'.repeat(2) // "hellohello"

padStart(),padEnd()

padStart 用於頭部補全, padEnd 用於尾部補全。

'x'.padStart(5, 'ab') // 'ababx' 'x'.padStart(4, 'ab') // 'abax' 'x'.padEnd(5, 'ab') // 'xabab' 'x'.padEnd(4, 'ab') // 'xaba'

padStart 和 padEnd 一共接受兩個參數,第一個參數用來指定字符串的最小長度,第二個參數是用來補全的字符串。

模板字符串

模板字符串(template string)是增強版的字符串,用反引號標識。它可以當作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。

var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?`

數值的擴展

從ES5開始,在嚴格模式之中,八進制就不再允許使用前綴 0 表示,ES6進一步明確,要使用前綴 0o 表示。

Number.isFinite()

Number.isFinite() 用來檢查一個數值是否非無窮(infinity)。

Number.isFinite(0.8); // true Number.isFinite(NaN); // false Number.isFinite(Infinity); // false

Number.isNaN()

Number.isNaN() 用來檢查一個值是否為 NaN 。

Number.isNaN(NaN) // true Number.isNaN(15) // false

它們與傳統的全局方法 isFinite() 和 isNaN() 的區別在於,傳統方法先調用 Number() 將非數值的值轉為數值,再進行判斷,而這兩個新方法只對數值有效,非數值一律返回 false 。

Number.parseInt(), Number.parseFloat()

ES6將全局方法 parseInt() 和 parseFloat() ,移植到Number對象上面,行為完全保持不變。

Number.isInteger()

Number.isInteger() 用來判斷一個值是否為整數。需要注意的是,在JavaScript內部,整數和浮點數是同樣的儲存方法,所以3和3.0被視為同一個值。

安全整數和Number.isSafeInteger()

JavaScript能夠准確表示的整數范圍在 -2^53 到 2^53 之間(不含兩個端點),超過這個范圍,無法精確表示這個值。
ES6引入了 Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER 這兩個
常量,用來表示這個范圍的上下限。
Number.isSafeInteger() 則是用來判斷一個整數是否落在這個范圍之內。

Math對象的擴展

Math.trunc 方法用於去除一個數的小數部分,返回整數部分。
Math.sign 方法用來判斷一個數到底是正數、負數、還是零。
Math.cbrt 方法用於計算一個數的立方根。
Math.fround方法返回一個數的單精度浮點數形式。
Math.hypot 方法返回所有參數的平方和的平方根。

數組的擴展Array.from()

Array.from 方法用於將兩類對象轉為真正的數組:類似數組的對象(array-like object)和可遍歷(iterable)的對象(包括ES6新增的數據結構Set和Map)。
實際應用中,常見的類似數組的對象是DOM操作返回的NodeList集合,以及函數內部的 arguments 對象。 Array.from 都可以將它們轉為真正的數組。

// NodeList對象 let ps = document.querySelectorAll('p'); Array.from(ps).forEach(function (p) { console.log(p); }); // arguments對象 function foo() { var args = Array.from(arguments); // ... }

Array.of()

Array.of 方法用於將一組值,轉換為數組。

Array.of(3, 11, 8) // [3,11,8]

數組實例的copyWithin()

Array.prototype.copyWithin(target, start = 0, end = this.length)

  • target(必需):從該位置開始替換數據。
  • start(可選):從該位置開始讀取數據,默認為0。如果為負值,表示倒數。
  • end(可選):到該位置前停止讀取數據,默認等於數組長度。如果為負值,表示倒數。
[1, 2, 3, 4, 5].copyWithin(0, 3, 4) // [4, 2, 3, 4, 5]

數組實例的find()和findIndex()

數組實例的 find 方法,用於找出第一個符合條件的數組成員。它的參數是一個回調函數,所有數組成員依次執行該回調函數,直到找出第一個返回值為 true 的成員,然后返回該成員。如果沒有符合條件的成員,則返回 undefined 。

[1, 4, -5, 10].find((n) => n < 0) // -5

數組實例的 findIndex 方法的用法與 find 方法非常類似,返回第一個符合條件的數組成員的位置,如果所有成員都不符合條件,則返回 -1 。

數組實例的fill()

fill 方法使用給定值,填充一個數組。

['a', 'b', 'c'].fill(7)// [7, 7, 7]

數組實例的entries(),keys()和values()

ES6提供三個新的方法—— entries() , keys() 和 values() ——用於遍歷數組。唯一的區別是 keys() 是對鍵名的遍歷、 values() 是對鍵值的遍歷, entries() 是對鍵值對的遍歷。

函數的擴展函數參數的默認值

ES6允許為函數的參數設置默認值,即直接寫在參數定義的后面。

function log(x, y = 'World') { console.log(x, y); }

函數的length屬性

指定了默認值以后,函數的 length 屬性,將返回沒有指定默認值的參數個數。也就是說,指定了默認值后, length 屬性將失真。

(function (a) {}).length // 1 (function (a = 5) {}).length // 0 (function (a, b, c = 5) {}).length // 2

rest參數

ES6引入rest參數(形式為“...變量名”),用於獲取函數的多余參數,這樣就不需要使用arguments對象了。rest參數搭配的變量是一個數組,該變量將多余的參數放入
數組中。

function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(2, 5, 3) // 10

注意,rest參數之后不能再有其他參數(即只能是最后一個參數),否則會報錯。

擴展運算符

擴展運算符(spread)是三個點( ... )。它好比rest參數的逆運算,將一個數組轉為用逗號分隔的參數序列。

console.log(1, ...[2, 3, 4], 5)// 1 2 3 4 5 // ES5的寫法 Math.max.apply(null, [14, 3, 77]) // ES6的寫法 Math.max(...[14, 3, 77])

擴展運算符的應用

  1. 合並數組

    var arr1 = ['a', 'b']; var arr2 = ['c']; var arr3 = ['d', 'e']; // ES5的合並數組 arr1.concat(arr2, arr3);// [ 'a', 'b', 'c', 'd', 'e' ] // ES6的合並數組 [...arr1, ...arr2, ...arr3]// [ 'a', 'b', 'c', 'd', 'e' ]
  2. 擴展運算符還可以將字符串轉為真正的數組。

    [...'hello']// [ "h", "e", "l", "l", "o" ]
  3. 實現了Iterator接口的對象

    var nodeList = document.querySelectorAll('div'); var array = [...nodeList];

箭頭函數

ES6允許使用“箭頭”( => )定義函數。

var f = v => v; //等同於 var f = function(v) { return v; };

箭頭函數有幾個使用注意點

  1. 函數體內的 this 對象,就是定義時所在的對象,而不是使用時所在的對象。
  2. 不可以當作構造函數,也就是說,不可以使用 new 命令,否則會拋出一個錯誤。
  3. 不可以使用 arguments 對象,該對象在函數體內不存在。如果要用,可以用Rest參數代替。
  4. 不可以使用 yield 命令,因此箭頭函數不能用作Generator函數。

對象的擴展屬性的簡潔表示法

ES6允許在對象之中,只寫屬性名,不寫屬性值。這時,屬性值等
於屬性名所代表的變量。

var Person = { name: '張三', //等同於birth: birth birth, // 等同於hello: function ()... hello() { console.log('我的名字是', this.name); } };

Object.assign()

Object.assign 方法用於對象的合並,將源對象(source)的所有可枚舉屬性,
復制到目標對象(target)。

var target = { a: 1 }; var source1 = { b: 2 }; var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}

如果目標對象與源對象有同名屬性,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性
Object.assign 方法實行的是淺拷貝,而不是深拷貝。也就是說,如果源對象某個屬性的值是對象,那么目標對象拷貝得到的是這個對象的引用。
Object.assign 方法有很多用處。

  1. 為對象添加屬性

    class Point { constructor(x, y) { Object.assign(this, {x, y}); } }
  2. 為對象添加方法

    Object.assign(SomeClass.prototype, { someMethod(arg1, arg2) { ··· }, anotherMethod() { ··· } }); 
  3. 克隆對象

    function clone(origin) { return Object.assign({}, origin); }
  4. 合並多個對象

    const merge = (target, ...sources) => Object.assign(target, ...sources);
  5. 為屬性指定默認值

    const DEFAULTS = { logLevel: 0, outputFormat: 'html' }; function processContent(options) { let options = Object.assign({}, DEFAULTS, options); }

ES6屬性的遍歷5種方法

  1. for...in 循環遍歷對象自身的和繼承的可枚舉屬性(不含Symbol屬性)。
  2. Object.keys(obj)返回一個數組,包括對象自身的(不含繼承的)所有可枚舉屬性(不含Symbol屬性)。
  3. Object.getOwnPropertyNames(obj)返回一個數組,包含對象自身的所有屬性(不含Symbol屬性,但是包括不可枚舉屬性)。
  4. Object.getOwnPropertySymbols(obj)返回一個數組,包含對象自身的所有Symbol屬性。
  5. Reflect.ownKeys(obj)返回一個數組,包含對象自身的所有屬性,不管是屬性名是Symbol或字符串,也不管是否可枚舉。

Symbol

ES5的對象屬性名都是字符串,這容易造成屬性名的沖突。如果有一種機制,保證每個屬性的名字都是獨一無二的就好了,這樣就從根本上防止屬性名的沖突。這就是ES6引入Symbol的原因。
注意,Symbol值作為對象屬性名時,不能用點運算符。

var mySymbol = Symbol(); var a = {}; a.mySymbol = 'Hello!'; a[mySymbol] // undefined a['mySymbol'] // "Hello!"

因為點運算符后面總是字符串,所以不會讀取 mySymbol 作為標識名所指代的那個值,導致 a 的屬性名實際上是一個字符串,而不是一個Symbol值。
Symbol作為屬性名,該屬性不會出現在 for...in 、 for...of 循環中,也不會被 Object.keys() 、 Object.getOwnPropertyNames() 返回。但是,它也不是私有屬性,有一個Object.getOwnPropertySymbols方法,可以獲取指定對象的所有Symbol屬性名。
Reflect.ownKeys 方法可以返回所有類型的鍵名,包括常規鍵名和Symbol鍵名。

let obj = { [Symbol('my_key')]: 1, enum: 2, nonEnum: 3 }; Reflect.ownKeys(obj)// [Symbol(my_key), 'enum', 'nonEnum']

Symbol.for(),Symbol.keyFor()

有時,我們希望重新使用同一個Symbol值, Symbol.for 方法可以做到這一點。它接受一個字符串作為參數,然后搜索有沒有以該參數作為名稱的Symbol值。如果有,就返回這個Symbol值,否則就新建並返回一個以該字符串為名稱的Symbol值。

Symbol.for("bar") === Symbol.for("bar")// true Symbol("bar") === Symbol("bar")// false var s1 = Symbol.for("foo"); Symbol.keyFor(s1) // "foo" var s2 = Symbol("foo"); Symbol.keyFor(s2) // undefined

Set和Map數據結構

ES6提供了新的數據結構Set。它類似於數組,但是成員的值都是唯一的,沒有重復的值。Set本身是一個構造函數,用來生成Set數據結構。

var s = new Set(); [2, 3, 5, 4, 5, 2, 2].map(x => s.add(x)); for (let i of s) { console.log(i); } // 2 3 5 4

Set實例的屬性和方法

  1. Set.prototype.constructor :構造函數,默認就是 Set 函數。
  2. Set.prototype.size :返回 Set 實例的成員總數。
  3. add(value) :添加某個值,返回Set結構本身。
  4. delete(value) :刪除某個值,返回一個布爾值,表示刪除是否成功。
  5. has(value) :返回一個布爾值,表示該值是否為 Set 的成員。
  6. clear() :清除所有成員,沒有返回值。

Map結構的目的和基本用法

JavaScript的對象(Object),本質上是鍵值對的集合(Hash結構),但是傳統上只能用字符串當作鍵。這給它的使用帶來了很大的限制。為了解決這個問題,ES6提供了Map數據結構。它類似於對象,也是鍵值對的集合,但是“鍵”的范圍不限於字符串,各種類型的值(包括對象)都可以當作鍵
Map原生提供三個遍歷器生成函數和一個遍歷方法。

  1. keys() :返回鍵名的遍歷器。
  2. values() :返回鍵值的遍歷器。
  3. entries() :返回所有成員的遍歷器。
  4. forEach() :遍歷Map的所有成員。

Generator 函數

Generator函數有多種理解角度。從語法上,首先可以把它理解成,Generator函數是一個狀態機,封裝了多個內部狀態。
執行Generator函數會返回一個遍歷器對象,也就是說,Generator函數除了狀態機,還是一個遍歷器對象生成函數。返回的遍歷器對象,可以依次遍歷Generator函數內部的每一個狀態。
形式上,Generator函數是一個普通函數,但是有兩個特征。一是, function關鍵字與函數名之間有一個星號;二是,函數體內部使用 yield語句,定義不同的內部狀態(yield語句在英語里的意思就是“產出”)。

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 }

Promise對象基本用法

var promise = new Promise(function(resolve, reject) { // ... some code if (/* 異步操作成功 */){ resolve(value); } else { reject(error); } });

Promise實例生成以后,可以用 then 方法分別指定 Resolved 狀態和 Reject 狀態的回調函數。

var getJSON = function(url) { var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if (this.readyState !== 4) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; }); return promise; }; getJSON("/posts.json").then(function(json) { console.log('Contents: ' + json); }, function(error) { console.error('出錯了', error); });

Promise.prototype.then()

then 方法可以接受兩個回調函數作為參數。第一個回調函數是Promise對象的狀態變為Resolved時調用,第二個回調函數是Promise對象的狀態變為Reject時調用。其中,第二個函數是可選的,不一定要提供。這兩個函數都接受Promise對象傳出的值作為參數。
then 方法返回的是一個新的Promise實例(注意,不是原來那個Promise實例)。因此可以采用鏈式寫法,即 then 方法后面再調用另一個 then 方法。

Promise.prototype.catch()

Promise.prototype.catch 方法是 .then(null, rejection) 的別名,用於指定發生錯誤時的回調函數。
一般來說,不要在 then 方法里面定義Reject狀態的回調函數(即 then 的第二個參數),總是使用 catch 方法。

Class

class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } }

上面代碼定義了一個“類”,可以看到里面有一個 constructor 方法,這就是構造方法,而 this 關鍵字則代表實例對象。也就是說,ES5的構造函數 Point ,對應ES6的 Point 類的構造方法。
由於類的方法都定義在 prototype 對象上面,所以類的新方法可以添加在 prototype 對象上面。 Object.assign 方法可以很方便地一次向類添加多個方法。

class Point { constructor(){ // ... } } Object.assign(Point.prototype, { toString(){}, toValue(){} });

另外,類的內部所有定義的方法,都是不可枚舉的(non-enumerable)。

constructor方法

constructor 方法是類的默認方法,通過 new 命令生成對象實例時,自動調用該方法。一個類必須有 constructor 方法,如果沒有顯式定義,一個空的 constructor 方法會被默認添加。constructor 方法默認返回實例對象(即 this ),完全可以指定返回另外一個
對象。

Class的繼承

Class之間可以通過 extends 關鍵字實現繼承

class ColorPoint extends Point {}

另一個需要注意的地方是,在子類的構造函數中,只有調用 super 之后,才可以
使用 this 關鍵字,否則會報錯。
Class不存在變量提升(hoist),這一點與ES5完全不同。

new Foo(); // ReferenceError class Foo {}

Object.getPrototypeOf()

Object.getPrototypeOf 方法可以用來從子類上獲取父類。
因此,可以使用這個方法判斷,一個類是否繼承了另一個類。

super關鍵字

super 這個關鍵字,有兩種用法,含義不同。

  1. 作為函數調用時(即 super(...args) ), super 代表父類的構造函數。
  2. 作為對象調用時(即 super.prop 或 super.method() ), super 代表父
    類。注意,此時 super 即可以引用父類實例的屬性和方法,也可以引用父類的靜
    態方法。

Class的靜態方法

類相當於實例的原型,所有在類中定義的方法,都會被實例繼承。如果在一個方法前,加上 static 關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,這就稱為“靜態方法”。

編程風格

  1. let取代var
  2. 靜態字符串一律使用單引號或反引號,不使用雙引號。動態字符串使用反引號。
  3. 函數的參數如果是對象的成員,優先使用解構賦值。
  4. 單行定義的對象,最后一個成員不以逗號結尾。多行定義的對象,最后一個成員以
    逗號結尾。

    const a = { k1: v1, k2: v2 }; const b = { k1: v1, k2: v2, };
  5. 數組
    使用擴展運算符(...)拷貝數組。const itemsCopy = [...items];
    使用Array.from方法,將類似數組的對象轉為數組。

        const foo = document.querySelectorAll('.foo'); const nodes = Array.from(foo);
  6. 函數
    立即執行函數可以寫成箭頭函數的形式。

    (() => { console.log('Welcome to the Internet.'); })();

    使用默認值語法設置函數參數的默認值。
  7. 總是用Class,取代需要prototype的操作。因為Class的寫法更簡潔,更易於理解。

這些筆記只是看第一遍消化的,還有更多的內容需要對研究幾遍才行。


免責聲明!

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



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