- <!-- 加載Traceur編譯器 -->
- <scriptsrc="http://google.github.io/traceur-compiler/bin/traceur.js" type="text/javascript"></script>
- <!-- 將Traceur編譯器用於網頁 -->
- <scriptsrc="http://google.github.io/traceur-compiler/src/bootstrap.js" type="text/javascript"></script>
- <!-- 打開實驗選項,否則有些特性可能編譯不成功 -->
- <script>
- traceur.options.experimental =true;
- </script>
- <scripttype="module">
- classCalc{
- constructor(){
- console.log('Calc constructor');
- }
- add(a, b){
- return a + b;
- }
- }
- var c =newCalc();
- console.log(c.add(4,5));
- </script>
注意,script標籤的type屬性的值是module(或者traceur),而不是text/javascript。這是Traceur編譯器識別ES6代碼的標識,編譯器會自動將所有type=module的代碼編譯爲ES5,然後再交給瀏覽器執行。
2.新增關鍵字
(1)let是ES6中新增關鍵字。它的作用類似於var,用來聲明變量,但是所聲明的變量,只在let命令所在的代碼塊內有效。
(2)const 聲明的是常量,一旦聲明,值將是不可變的。const 的特點:
- 具有塊級作用域
- 不能變量提升(必須先聲明後使用)
- 不可重複聲明
- const 指令指向變量所在的地址,所以對該變量進行屬性設置是可行的(未改變變量地址),如果想完全不可變化(包括屬性),那麼可以使用凍結Object.freeze
3.新增方法
(1)是否包含字符串
- includes():返回布爾值,表示是否找到了參數字符串。
- startsWith():返回布爾值,表示參數字符串是否在源字符串的頭部。
- endsWith():返回布爾值,表示參數字符串是否在源字符串的尾部。
(2)重複字符串repeat()
repeat()返回一個新字符串,表示將原字符串重複n次。
(3)模版字符串
模板字符中,支持字符串插值:
- let first ='world';
- let last='中國';
- document.write(`Hello ${first} ${last}!`);
- // Hello world 中國!
(4)String.row()
若使用String.raw 作爲模板字符串的前綴,則模板字符串可以是原始(raw)的。反斜線也不再是特殊字符,\n 也不會被解釋成換行符:
- let raw =String.raw`Not a newline: \n`;
- document.write(raw ==='Not a newline: \\n');// true
(5)isFinite(),isNaN(),isInteger()
在Number對象上,新提供了Number.isFinite()和Number.isNaN()兩個方法,用來檢查Infinite和NaN這兩個特殊值
Number.isInteger()用來判斷一個值是否爲整數。需要注意的是,在JavaScript內部,整數和浮點數是同樣的儲存方法,所以3和3.0被視爲同一個值。
(6)Math對象新增方法
- Math.trunc():去除一個數的小數部分,返回整數部分
- Math.sign():判斷一個數到底是正數、負數、還是零。它返回五種值:參數爲正數,返回+1;參數爲負數,返回-1;參數爲0,返回0;參數爲-0,返回-0;其他值,返回NaN
- Math.cbrt:計算一個數的立方根
- Math.fround:返回一個數的單精度浮點數形式
- Math.hypot:返回所有參數的平方和的平方根。
- Math.expm1(x):返回ex
- 1。
- Math.log1p(x):返回1
+ x的自然對數。如果x小於-1,返回NaN。
- Math.log10(x):返回以10爲底的x的對數。如果x小於0,則返回NaN。
- Math.log2(x):返回以2爲底的x的對數。如果x小於0,則返回NaN
- let array =Array.from({0:"a",1:"b",2:"c", length:3});
- document.write(array);// [ "a", "b" , "c" ]
- Array(3)// [undefined, undefined, undefined]
- Array.of(3)// [3]
- Array.of(3).length // 1
- let array =[1,5,10,15].find(function(value, index, arr){
- return value >9;
- })
- document.write(array);// 10
- entries()
- keys()
- values()
- for(let index of ['a','b'].keys()){
- document.write(index);//0,1
- }
- for(let elem of ['a','b'].values()){
- document.write(elem);//'a','b'
- }
- for(let [index, elem] of ['a','b'].entries()){
- document.write(index, elem);//0a,1b
- }
- var target={name:'張三'}
- var source={age:'19',sex:'男'}
- Object.assign(target, source1, source2); {"name":"張三","age":"19","sex":"男"}
注意,Symbol函數前不能使用new命令,否則會報錯。這是因爲生成的Symbol是一個原始類型的值,不是對象。
Symbol類型的值不能與其他類型的值進行運算,會報錯。但是,Symbol類型的值可以轉爲字符串。
(4).Proxy 內置的一個代理工具,使用他可以在對象處理上加一層屏障,new Proxy()表示生成一個Proxy實例,它的target參數表示所要攔截的目標對象,handler參數也是一個對象,用來定製攔截行爲。- var proxy =newProxy(target, handler)
- let log =::console.log;
- // 等同於var log = console.log.bind(console);
- foo::bar;
- // 等同於bar.call(foo);
- foo::bar(...arguments);
- // 等同於bar.apply(foo, arguments);
var array = [1, 2, 3];//傳統寫法 array.forEach(function(v, i, a) {console.log(v);}); //ES6 array.forEach(v = > console.log(v));
- 函數體內的this對象,綁定定義時所在的對象,而不是使用時所在的對象。
- 不可以當作構造函數,也就是說,不可以使用new命令,否則會拋出一個錯誤。
- 不可以使用arguments對象,該對象在函數體內不存在。
上面三點中,第一點尤其值得注意。this對象的指向是可變的,但是在箭頭函數中,它是固定的。
(1).Set結構實例的屬性:
- Set.prototype.constructor:構造函數,默認就是Set函數。
- Set.prototype.size:返回Set實例的成員總數。
(2).操作方法:
- add(value):添加某個值,返回Set結構本身。
- delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功。
- has(value):返回一個布爾值,表示該值是否爲Set的成員。
- clear():清除所有成員,沒有返回值。
(3).遍歷方法:
- keys():返回一個鍵名的遍歷器
- values():返回一個鍵值的遍歷器
- entries():返回一個鍵值對的遍歷器
- forEach():使用回調函數遍歷每個成員
- let set=newSet(['red','green','blue']);
- for( let item of set.keys()){
- document.write(item);
- }
- for( let item of set.values()){
- document.write(item);
- }
- for( let item of set.entries()){
- document.write(item);
- }//["red", "red"]["green", "green"]["blue", "blue"]
- set.forEach(function(item){
- document.write(item);
- })
8.Map
Map 是一個“超對象”,其 key 除了可以是 String 類型之外,還可以爲其他類型(如:對象),他的方法和 Set 差不多:
- size:返回成員總數。
- set(key, value):設置key所對應的鍵值,然後返回整個Map結構。如果key已經有值,則鍵值會被更新,否則就新生成該鍵。
- get(key):讀取key對應的鍵值,如果找不到key,返回undefined。。
- has(key):返回一個布爾值,表示某個鍵是否在Map數據結構中。
- delete(key):刪除某個鍵,返回true。如果刪除失敗,返回false。
- clear():清除所有成員。
- keys():返回鍵名的遍歷器。
- values():返回鍵值的遍歷器。
- entries():返回所有成員的遍歷器。
9.Iterator遍歷器
遍歷器(Iterator)就是統一的接口機制,來處理所有不同的數據結構。
Iterator的作用有三個:一是爲各種數據結構,提供一個統一的、簡便的訪問接口;二是使得數據結構的成員能夠按某種次序排列;三是ES6創造了一種新的遍歷命令for...of循環,Iterator接口主要供for...of消費。
Iterator的遍歷過程:
- 創建一個指針,指向當前數據結構的起始位置。也就是說,遍歷器的返回值是一個指針對象。
- 第一次調用指針對象的next方法,可以將指針指向數據結構的第一個成員。
- 第二次調用指針對象的next方法,指針就指向數據結構的第二個成員。
- 調用指針對象的next方法,直到它指向數據結構的結束位置。
每一次調用next方法,都會返回當前成員的信息,具體來說,就是返回一個包含value和done兩個屬性的對象。其中,value屬性是當前成員的值,done屬性是一個布爾值,表示遍歷是否結束。
遍歷器返回的指針對象除了具有next方法,還可以具有return方法和throw方法。其中,next方法是必須部署的,return方法和throw方法是否部署是可選的。
return方法的使用場合是,如果for...of循環提前退出(通常是因爲出錯,或者有break語句或continue語句),就會調用return方法。如果一個對象在完成遍歷前,需要清理或釋放資源,就可以部署return方法。
throw方法主要是配合Generator函數使用,一般的遍歷器用不到這個方法。
Generator函數是一個函數的內部狀態的遍歷器(也就是說,Generator函數是一個狀態機)。形式上,Generator函數是一個普通函數,但是有兩個特徵。
- 一是,function命令與函數名之間有一個星號*;
- 二是,函數體內部使用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 }
總結一下,調用Generator函數,返回一個部署了Iterator接口的遍歷器對象,用來操作內部指針。以後,每次調用遍歷器對象的next方法,就會返回一個有着value和done兩個屬性的對象。value屬性表示當前的內部狀態的值,是yield語句後面那個表達式的值;done屬性是一個布爾值,表示是否遍歷結束。
yield語句本身沒有返回值,或者說總是返回undefined。next方法可以帶一個參數,該參數就會被當作上一個yield語句的返回值。
for...of循環可以自動遍歷Generator函數,且此時不再需要調用next方法。注意:一旦next方法的返回對象的done屬性爲true,for...of循環就會中止,且不包含該返回對象,所以return語句返回的對象,不包括在for...of循環之中。
Generator 函數內部還可以部署錯誤處理代碼,捕獲函數體外拋出的錯誤。使用指針對象的 throw 方法拋出的錯誤,可以被函數體內的 try
... catch 代碼塊捕獲。這意味着,出錯的代碼與處理錯誤的代碼,實現了時間和空間上的分離,這對於異步編程無疑是很重要的。
11.Promise
所謂Promise,就是一個對象,用來傳遞異步操作的消息。
Promise對象有以下兩個特點:
- 對象的狀態不受外界影響。Promise對象代表一個異步操作,有三種狀態:Pending(進行中)、Resolved(已完成,又稱Fulfilled)和Rejected(已失敗)。只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。
- 一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從Pending變爲Resolved和從Pending變爲Rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對Promise對象添加回調函數,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。
有了Promise對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操作更加容易。
Promise也有一些缺點:
- 首先,無法取消Promise,一旦新建它就會立即執行,無法中途取消。
- 其次,如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部。
- 第三,當處於Pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。
Promise對象是一個構造函數,用來生成Promise實例
- //創建promise
- var promise =newPromise(function(resolve, reject){
- // 進行一些異步或耗時操作
- if(/*如果成功 */){
- resolve("Stuff worked!");
- }else{
- reject(Error("It broke"));
- }
- });
- //綁定處理程序
- promise.then(function(result){
- //promise成功的話會執行這裏
- document.write(result);// "Stuff worked!"
- },function(err){
- //promise失敗會執行這裏
- document.write(err);// Error: "It broke"
- });
- resolve函數的作用是,將Promise對象的狀態從“未完成”變爲“成功”(即從Pending變爲Resolved),在異步操作成功時調用,並將異步操作的結果,作爲參數傳遞出去;
- reject函數的作用是,將Promise對象的狀態從“未完成”變爲“失敗”(即從Pending變爲Rejected),在異步操作失敗時調用,並將異步操作報出的錯誤,作爲參數傳遞出去。
Promise實例具有then方法,也就是說,then方法是定義在原型對象,作用是爲Promise實例添加狀態改變時的回調函數。
then方法兩個參數:
- Resolved狀態的回調函數;
- Rejected狀態的回調函數(可選)。
then方法返回的是一個新的Promise實例(注意,不是原來那個Promise實例)。因此可以採用鏈式寫法,即then方法後面再調用另一個then方法。
- getJSON("/posts.json").then(function(json){
- return json.post;
- }).then(function(post){
- // ...
- });
上面的代碼使用then方法,依次指定了兩個回調函數。第一個回調函數完成以後,會將返回結果作爲參數,傳入第二個回調函數。
Promise.prototype.catch方法是.then(null, rejection)的別名,用於指定發生錯誤時的回調函數。
- getJSON("/posts.json").then(function(posts){
- // ...
- }).catch(function(error){
- // 處理前一個回調函數運行時發生的錯誤
- document.write('發生錯誤!', error);
- });
getJSON方法返回一個Promise對象,如果該對象狀態變爲Resolved,則會調用then方法指定的回調函數;如果異步操作拋出錯誤,狀態就會變爲Rejected,就會調用catch方法指定的回調函數,處理這個錯誤。
- var promise =newPromise(function(resolve, reject){
- thrownewError('test')
- });
- promise.catch(function(error){ document.write(error)});
- // Error: test
上面代碼中,Promise拋出一個錯誤,就被catch方法指定的回調函數捕獲。
Promise對象的錯誤具有“冒泡”性質,會一直向後傳遞,直到被捕獲爲止。也就是說,錯誤總是會被下一個catch語句捕獲。
- getJSON("/post/1.json").then(function(post){
- return getJSON(post.commentURL);
- }).then(function(comments){
- // some code
- }).catch(function(error){
- // 處理前面三個Promise產生的錯誤
- });
上面代碼中,一共有三個Promise對象:一個由getJSON產生,兩個由then產生。它們之中任何一個拋出的錯誤,都會被最後一個catch捕獲。
Promise.all方法用於將多個Promise實例,包裝成一個新的Promise實例。
- var p =Promise.all([p1,p2,p3]);
上面代碼中,Promise.all方法接受一個數組作爲參數,p1、p2、p3都是Promise對象的實例。(Promise.all方法的參數不一定是數組,但是必須具有iterator接口,且返回的每個成員都是Promise實例。)
p的狀態由p1、p2、p3決定,分成兩種情況。
- 只有p1、p2、p3的狀態都變成fulfilled,p的狀態纔會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回調函數。
- 只要p1、p2、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。
Promise.race方法同樣是將多個Promise實例,包裝成一個新的Promise實例。
- var p =Promise.race([p1,p2,p3]);
上面代碼中,只要p1、p2、p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的Promise實例的返回值,就傳遞給p的回調函數。
如果Promise.all方法和Promise.race方法的參數,不是Promise實例,就會先調用Promise.resolve方法,將現有對象轉爲Promise對象,如果Promise.resolve方法的參數,不是具有then方法的對象(又稱thenable對象),則返回一個新的Promise對象,且它的狀態爲Resolved。
- var p =Promise.resolve('Hello');
- p.then(function(s){
- document.write(s)
- });
- // Hello
由於字符串Hello不屬於異步操作(判斷方法是它不是具有then方法的對象),返回Promise實例的狀態從一生成就是Resolved,所以回調函數會立即執行。
Promise.reject(reason)方法也會返回一個新的Promise實例,該實例的狀態爲rejected。Promise.reject方法的參數reason,會被傳遞給實例的回調函數。
- var p =Promise.reject('出錯了');
- p.then(null,function(s){
- document.write(s)
- });// 出錯了
上面代碼生成一個Promise對象的實例p,狀態爲rejected,回調函數會立即執行。
12.Generator函數與Promise結合
使用Generator函數管理流程,遇到異步操作的時候,通常返回一個Promise對象。
- function getFoo (){
- returnnewPromise(function(resolve, reject){
- resolve('foo');
- });
- }
- var g =function*(){
- try{
- var foo =yield getFoo();
- document.write(foo);
- }catch(e){
- document.write(e);
- }
- };
- function run (generator){
- var it = generator();
- function go(result){
- if(result.done)return result.value;
- return result.value.then(function(value){
- return go(it.next(value));
- },function(error){
- return go(it.throw(value));
- });
- }
- go(it.next());
- }
- run(g);
上面代碼的Generator函數g之中,有一個異步操作getFoo,它返回的就是一個Promise對象。函數run用來處理這個Promise對象,並調用下一個next方法。
13.Class
ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念,作爲對象的模板。通過class關鍵字,可以定義類。
- //定義類
- classPoint{
- constructor(x, y){
- this.x = x;
- this.y = y;
- }
- toString(){
- return'('+this.x+', '+this.y+')';
- }
- }
上面代碼定義了一個“類”,可以看到裏面有一個constructor方法,這就是構造方法,而this關鍵字則代表實例對象。
constructor方法是類的默認方法,通過new命令生成對象實例時,自動調用該方法。
(1).class的繼承
Class之間可以通過extends關鍵字,實現繼承。子類會繼承父類的屬性和方法。
- classColorPointextendsPoint{
- constructor(x, y, color){
- this.color = color;// ReferenceError
- super(x, y);
- this.color = color;// 正確
- }
- }
注意:ColorPoint繼承了父類Point,但是它的構造函數必須調用super方法,super方法必須放在第一行。
在Class內部可以使用get和set關鍵字,對某個屬性設置存值函數和取值函數。
(2).class的靜態方法
類相當於實例的原型,所有在類中定義的方法,都會被實例繼承。如果在一個方法前,加上static關鍵字,就表示該方法不會被實例繼承,而是直接通過類來調用,這就稱爲“靜態方法”。父類的靜態方法,可以被子類繼承。
- classFoo{
- static classMethod(){
- return'hello';
- }
- }
- Foo.classMethod()// 'hello'
- var foo =newFoo();
- foo.classMethod()
- // TypeError: undefined is not a function
(3).new.target屬性
new是從構造函數生成實例的命令。ES6爲new命令引入了一個new.target屬性,(在構造函數中)返回new命令作用於的那個構造函數。如果構造函數不是通過new命令調用的,new.target會返回undefined,因此這個屬性可以用來確定構造函數是怎麼調用的。
- Class內部調用new.target,返回當前Class。
- 子類繼承父類時,new.target會返回子類。
(4).修飾器
修飾器(Decorator)是一個表達式,用來修改類的行爲。這是ES7的一個提案,目前Babel轉碼器已經支持。修飾器對類的行爲的改變,是代碼編譯時發生的,而不是在運行時。這意味着,修飾器能在編譯階段運行代碼。
- function testable(target){
- target.isTestable =true;
- }
- @testable
- classMyTestableClass{}
- console.log(MyTestableClass.isTestable)// true
上面代碼中,@testable就是一個修飾器。它修改了MyTestableClass這個類的行爲,爲它加上了靜態屬性isTestable。
基本上,修飾器的行爲就是下面這樣。
- @decorator
- class A {}
- // 等同於
- class A {}
- A = decorator(A)|| A;
修飾器函數可以接受三個參數,依次是目標函數、屬性名和該屬性的描述對象。後兩個參數可省略。testable函數的參數target,就是所要修飾的對象。如果希望修飾器的行爲,能夠根據目標對象的不同而不同,就要在外面再封裝一層函數。
- function testable(isTestable){
- returnfunction(target){
- target.isTestable = isTestable;
- }
- }
- @testable(true)classMyTestableClass(){}
- document.write(MyTestableClass.isTestable)// true
- @testable(false)classMyClass(){}
- document.write(MyClass.isTestable)// false
如果想要爲類的實例添加方法,可以在修飾器函數中,爲目標類的prototype屬性添加方法。
- function testable(target){
- target.prototype.isTestable =true;
- }
- @testable
- classMyTestableClass(){}
- let obj =newMyClass();
- document.write(obj.isTestable)// true
14.模塊(module)
模塊功能主要由兩個命令構成:export和import。
- export命令用於用戶自定義模塊,規定對外接口;
- import命令用於輸入其他模塊提供的功能,同時創造命名空間(namespace),防止函數名衝突。
ES6允許將獨立的JS文件作爲模塊,允許一個JavaScript腳本文件調用另一個腳本文件。ES6支持多重加載,即所加載的模塊中又加載其他模塊。
- // profile.js
- var firstName ='Michael';
- var lastName ='Jackson';
- var year =1958;
- export{firstName, lastName, year};
- // main.js
- import{firstName, lastName, year}from'./profile';
- function sfirsetHeader(element){
- element.textContent = firstName +' '+ lastName;
- }
import命令接受一個對象(用大括號表示),裏面指定要從其他模塊導入的變量名。大括號裏面的變量名,必須與被導入模塊(profile.js)對外接口的名稱相同。
module命令可以取代import語句,達到整體輸入模塊的作用。module命令後面跟一個變量,表示輸入的模塊定義在該變量上。
- module profile from './profile';
爲加載模塊指定默認輸出,使用export default命令。其他模塊加載該模塊時,import命令可以爲該匿名函數指定任意名字。
- // export-default.js
- exportdefaultfunction(){
- document.write('foo');
- }
- // import-default.js
- import customName from'./export-default';
- customName();// 'foo'
上面代碼的import命令,可以用任意名稱指向export-default.js輸出的方法。需要注意的是,這時import命令後面,不使用大括號。