整理了ES6經常使用的一些語法,跟你們分享(promise、generator什麼的尚未梳理清楚,後續再更新。。。)javascript
1⃣️ 變量聲明-let 與 constphp
(首先關於測試結果:這裏有個小問題,若是用let/const聲明的變量,在控制檯調試的話,第一次輸出結果後,第二次若是你還想用,要麼把代碼放入編輯器中,再打開瀏覽器看結果,要麼就把變量名更改從新輸入結果,不然是會報錯的)
let與const的做用與var 相似,都是用來聲明變量可是在實際應用中,它倆有各自的特殊用途css
(注意:ES6裏let和const沒有變量的提高,必定要聲明後再使用,但代碼編譯成ES5以後,變量提高依舊存在)html
先舉個栗子前端
var name = 'aaron'; if(true) { var name = 'peter'; console.log(name); // peter } console.log(name); //peter
咱們能夠看到,使用var聲明的變量,兩次輸出結果都是peter,那是由於ES5裏只有全局做用域和函數做用域,沒有塊級做用域,那麼咱們怎麼才能讓它兩次打印的結果分別是aaron 和 peter呢? 如今let就能夠派上用場了 java
改造一下上面的栗子node
let name = 'aaron'; if(true) { let name = 'peter'; console.log(name); // peter } console.log(name); //aaron
如今能夠看到,兩次的結果已經不相同了,let實際上爲
JavaScript新增了塊級做用域。用它所聲明的變量,只在let
命令所在的代碼塊內有效。nginx
下面再來看一發關於關於for循環的栗子,問題就是用來計數的循環變量泄露爲全局變量,會對咱們的一些操做帶來很大的影響,話很少說,來看栗子c++
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;git
在沒有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中用...
來表示展開運算符,它能夠將數組方法或者對象進行展開。上栗子
函數調用裏,將一個數組展開成多個參數,咱們會用到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);
有了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)Object.assign()
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
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))
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]}
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)
{ var globalVar = "from demo1" } { let globalLet = "from demo2"; } console.log(globalVar) console.log(globalLet)
結果:
globalVar -> from demo1 globalLet -> ReferenceError: globalLet is not defined
globalVar -> from demo1 globalLet -> ReferenceError: globalLet is not defined
可是,它不會向window分配任何內容:
let me = "go"; // 全局做用域 var i = "able"; // 全局做用域 console.log(window.me); console.log(window.i);
let me = "go"; // 全局做用域 var i = "able"; // 全局做用域 console.log(window.me); console.log(window.i);
結果:
window.me -> undefined window.i -> able
window.me -> undefined window.i -> able
不能使用let從新聲明一個變量:
let me = "foo"; let me = "bar"; console.log(me);
let me = "foo"; let me = "bar"; console.log(me);
結果:
SyntaxError: Identifier 'me' has already been declared
SyntaxError: Identifier 'me' has already been declared
var me = "foo"; var me = "bar"; console.log(me)
var me = "foo"; var me = "bar"; console.log(me)
結果:
me -> bar
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
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); }
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
"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 }
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); }
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
"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); }
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);
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
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())
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
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";
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
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) }
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
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) }
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
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) }
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]
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"}))
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
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) }
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
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);
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
proxy.words -> Nice words
提供如下 trap :
var handler = { get:..., set:..., has:..., deleteProperty:..., apply:..., construct:..., getOwnPropertyDescriptor:..., defineProperty:..., getPrototypeOf:..., setPrototypeOf:..., enumerate:..., ownKeys:..., preventExtensions:..., isExtensible:... }
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"))
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
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])
class CustomArray extends Array { } var a = new CustomArray(); a[0] = 2 console.log(a[0])
結果:
a[0] -> 2
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) }))
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
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
console.log(0b11111) console.log(0o2342) console.log(0xff); // also in es5
結果:
ES6 代碼: 0b11111 -> 31 0o2342 -> 1250 0xff -> 255
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) })
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"]
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) })
var p1 = Promise.resolve("1") var p2 = Promise.reject("2") Promise.race([p1, p2]).then((res) => { console.log(res) })
結果:
res -> 1
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) })
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
"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"));
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
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))
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
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]
變量解構賦值用途
[x, y] = [y, x];
提取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]
函數參數的默認值
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)
[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])
擴展運算符的應用
合併數組
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' ]
擴展運算符還能夠將字符串轉爲真正的數組。
[...'hello']// [ "h", "e", "l", "l", "o" ]
實現了Iterator接口的對象
var nodeList = document.querySelectorAll('div'); var array = [...nodeList];
箭頭函數
ES6容許使用「箭頭」( => )定義函數。
var f = v => v; //等同於 var f = function(v) { return v; };
箭頭函數有幾個使用注意點
對象的擴展屬性的簡潔表示法
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 方法有不少用處。
爲對象添加屬性
class Point { constructor(x, y) { Object.assign(this, {x, y}); } }
爲對象添加方法
Object.assign(SomeClass.prototype, { someMethod(arg1, arg2) { ··· }, anotherMethod() { ··· } });
克隆對象
function clone(origin) { return Object.assign({}, origin); }
合併多個對象
const merge = (target, ...sources) => Object.assign(target, ...sources);
爲屬性指定默認值
const DEFAULTS = { logLevel: 0, outputFormat: 'html' }; function processContent(options) { let options = Object.assign({}, DEFAULTS, options); }
ES6屬性的遍歷5種方法
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實例的屬性和方法
Map結構的目的和基本用法
JavaScript的對象(Object),本質上是鍵值對的集合(Hash結構),可是傳統上只能用字符串看成鍵。這給它的使用帶來了很大的限制。爲了解決這個問題,ES6提供了Map數據結構。它相似於對象,也是鍵值對的集合,可是「鍵」的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵
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 這個關鍵字,有兩種用法,含義不一樣。
Class的靜態方法
類至關於實例的原型,全部在類中定義的方法,都會被實例繼承。若是在一個方法前,加上 static 關鍵字,就表示該方法不會被實例繼承,而是直接經過類來調用,這就稱爲「靜態方法」。
編程風格
單行定義的對象,最後一個成員不以逗號結尾。多行定義的對象,最後一個成員以
逗號結尾。
const a = { k1: v1, k2: v2 }; const b = { k1: v1, k2: v2, };
數組
使用擴展運算符(...)拷貝數組。const itemsCopy = [...items];
使用Array.from方法,將相似數組的對象轉爲數組。
const foo = document.querySelectorAll('.foo'); const nodes = Array.from(foo);
函數
當即執行函數能夠寫成箭頭函數的形式。
(() => { console.log('Welcome to the Internet.'); })();
老是用Class,取代須要prototype的操做。由於Class的寫法更簡潔,更易於理解。
這些筆記只是看第一遍消化的,還有更多的內容須要對研究幾遍才行。