越來越多的開源庫開始使用ES2015來構建程式碼了,大家知道ES6=ES2015,ES6在2015年被ECMAScript標準化組織approve,各大瀏覽器廠商要完全支援ES6的強大功能還須一些時日,對於喜愛新嘗試的同學難道只有乾等嗎?幸運的是有了babel,traceur等transpiler的幫助,我們根本不用等待瀏覽器原生支援ES6才開始使用新技術了!其實babel做的事情很簡單,他就是把es6的程式碼轉換成瀏覽器認識的ES5程式碼。簡單舉一個例子,比如ES6中引入的語言原生支援模組的關鍵字import, export在僅實現ES5的瀏覽器中無法執行,因此babel做的就是將import, export轉換為commonJS的模組格式require, exports, 隨後在載入到瀏覽器端的SystemJS模組載入器的幫助下(或者通過webpack,browserify Module bundler工具整合),就能完全實現了ES6模組的功能。
本文檢視整理我在學習ES6過程中遇到的一些常見重要知識點和疑惑的問題
Variable and Parameters
block scope
ES6中引入了block scope的概念,配合使用let來declare一個變數的話,該變數就只在block中可見
if (flag){ let x = 3; // x只在這個{}block中可見 } return x; // x is not defined error!
declared vs initialized
var x; // declared
x = 5 ; // initialized
'use strict'; function udpate(){ pid = 12; } let pid = null; update(); console.log(pid); //12 和var宣告的變數類似,外部scope可以被inner訪問,由於pid在全域性定義,函式內可以訪問全域性變數
let vs var
let支援塊作用域,不會像var那樣hoisted到前面
同樣let支援for block
for (let i=0;i <10; i++) { } return i // i not defined, 原因是i只在for loop中可見 for (var i=0;i <10; i++) { } return i // i==10
let在for loop中以及closure下和傳統var的區別
'use strict'; let updatefns = []; for (let i=0;i<2;i++){ updatefns.push(function(){ return i; //注意let在for loop中使用時,每一次迴圈都會在for block中定義一個新的i,所以updatefns[0]()將返回0而不是2~!! //好好體會這一點:在es5中的closure則會返回2 }); consoloe.log(updatefns[0]()); //返回0 consoloe.log(updatefns[1]()); //返回1
const(chrome,firefox支援的,貌似非es6標準)
const MAX_AGE 130 MAX_AGE = 200 //syntax error
const NOTINIT //語法錯誤,const必須初始化!
注意:const的scope和let是一致的,這是es6引入的新特性
Destructuring
let x = 2; let y = 3; [x,y] = [y,x] //[3,2] // 右邊的是array, 左邊不是array,而是destructuring,只對x,和y賦值 // destructuring assignment, 注意 let [x,y]=[2,3] //這裡對x,y兩個單個變數賦值: x=2, y=3並且在後面可以直接訪問 expect(x).toBe(2) expect(y).toBe(3); var retv = function(){ return [1,2,3]; } let [, x , y] = retv(); // [1,2,3]解構 這裡 , 號表示忽略部分結構單元 exepct(x).toBe(2) exepct(y).toBe(3) var retO = function(){ return { firstName: "alice", lastName: "zhang", social: { qq: "13442", wechat: "wechatalice" } } } let { firstName: f, lastName: l, social:{wechat: weixin}} = retO(); //f = "alice", l = "zhang" , weixin = "wechatalice" let { firstName, lastNamel, social:{wechat}} = retO(); //firstName= "alice", lastName = "zhang" , wechat = "wechatalice" //模擬一個ajax call返回結果解構: let ajax = function(url, {data, cache}{ return data; //注意這裡data是從ajax呼叫者傳入的配置物件解構出來data欄位後直接返回的 //在通常的ajax呼叫中,則從server端返回 } let result = ajax("api/test", { data: "test data for ajax", //這是ajax呼叫的傳入物件,可以被ajax解構成變數 cache: false }); expect(result).toBe("test data for ajax") // true
再看一些例子:
let salary = ['1000',3000','2000']; let [ low, ...remaining ] = salary; //在destructuring中使用rest操作符 console.log(remaining); // ["3000","2000"] let salary = ['1000',3000']; let [ low, average, hight = '8000' ] = salary; //在destructuring中使用預設引數 console.log(high); // 8000 let salary = ['1000',3000']; let low, average, hight; // 先let宣告變數 [ low, average, hight = '8000' ] = salary; //通過destructuring來賦值變數 console.log(high); // 8000 function reviewSalary([low, average], high = '8000'){ console.log(average); //在這裡通過destructure傳入的陣列來賦值average } reviewSalary(['2000','3000']); //3000
destucturing object需要注意的點:
let salary = { low: '3000', average: '4000', high: '5000' } let newLow, newAverage, newHigh; { low: newLow, average: newAverage, high: newHigh} = salary; //注意額,這個是會出現syntax error的,原因是js引擎會認為是一條語句, //解決方案是用()括起來 ({ low: newLow, average: newAverage, high: newHigh} = salary); // 5000 let [high, low] = null; //會出錯,destructure的物件必須要支援iterator
default value(預設引數值)
var doWork = function(name){ // es5中對預設name引數的處理方法:使用|| 操作符,如果不傳入name,則name就為zhangsan name = name || "zhangsan" ; return name; }; var doWork = function(name = "zhangsan"){ // es6給name引數一個預設值,如果不傳入name,則name就為zhangsan return name; }; doWork(); //預設引數同樣適用於解構: let ajax = function(url, {data = “default data”, cache}{ return data; //注意這裡data是從ajax呼叫者傳入的配置物件解構出來data欄位後直接返回的, // 如果傳入物件不含data,則取預設值 default data //在通常的ajax呼叫中,則從server端返回 } let result = ajax("api/test", { cache: false }); expect(result).toBe("default data") // true
Rest Parameters ...paraName (必須是最後一個引數)
let sum = function(name, ...numbers){ // rest parameter必須是函式的最後一個引數 let result = 0; for (let i=0;i<numbers.length;i++){ result += numbers[i]; } return result; }
let result = sum("alicechang") // 0 // 如果不傳入numbers引數,則numbers就是一個空陣列[]
let result = sum("alicechang",1,2,3) // 6 // 注意這時numbers就是一個陣列了[1,2,3] let result = sum("alicechang",1,3,5,6) //15 // 注意這時numbers就是一個陣列了[1,3,5,6]
Spread Operator ...[a1,a2,a3]
let dowork = function(x,y,z){ return x+y+z; } var result =dowork(...[1,2,3]); //將陣列中的3個數分別傳給x,y,z形參 expect(result).toBe(6) //也可以用來構建新的陣列: var a = [4,5,6]; var b = [1,2,3, ...a, 7,8,9]; // b就等於 [1,2,3,4,5,6,7,8,9]了!!
同時也可以用在對字串的操作上:
var maxCode = Math.max(..."43201"); console.log(maxCode); // 4 var codeArray = ["A",..."BCD","E"]; console.log(codeArray);// ["A","B","C","D","E"];
Template Literal: `template String ${ varName }`
let category = "music"; let id = 1234; let url = `http://myserver.com/${category}/${id}`; //這時 url中的變數就會被替換,最後的value = http://myserver.com/music/1234
更多的例子:
let invoiceNum = '1350'; console.log(`Invoice Number: ${"INV-"+invoiceNum}`); //Invoice Number: INV-1350 //插值早於函式被呼叫! function showMsg(message){ let invoiceNum = '99'; //注意這個invoiceNum不會被用到,因為插值要早於函式呼叫 } let invoiceNum = '1350'; showMsg(`Invoice Number: ${invoiceNum}`); //Invoice Number: 1350
template還支援所謂的tag, tag template literal 比如:
let upper = function(strings,...values){ let result = ""; for (var i=0; i <strings.length; i++){ result+=strings[i]; if (i<values.length){ result+= values [i]; } } return result.toUpperCase(); } var x = 1; y =3; var result = upper `${x} + ${y} is equal to ${x+y}`; // result為: 1 + 3 IS EQUAL TO 4
再看看幾個所謂tag template literal的例子:
function processInvoice(segments){ console.log(segments); } processInvoice `template` ; // ["template"] 這是我們的template literal的value //更復雜一點的例子: function processInvoice(segments, ...values){ console.log(segments); console.log(values); } let invoiceNum = '1350'; let amount = '2000'; // 在這裡tag就是processInvoice, template literal就是`Invoice......${amount}` processInvoice `Invoice: ${invoiceNum} for ${amount}`; //輸出內容 ["Invoice: "," for ", ""] //這就是所有獨立的string內容 [1350,2000] //這就是所有插值的value
可以參考: http://exploringjs.com/es6/ch_template-literals.html 來詳細瞭解所謂tag template literal的功用
Arrow Functions =>(this指向問題)
var getPrice = () => 5.99; console.log(typeof getPrice); // function var getPrice = count => count*4.00; //只有一個引數時,可以不用() console.log(getPrice(2)); // 8
arrow functions除了具有以上語法格式簡潔的優點外,更重要的是為了解決es5中的this難題:
document.addEventListener('click',function(){ console.log(this); //這裡this為#document(接收事件的哪個物件),而不 //不是程式碼執行的context(程式碼執行時function的context實際上應該是window! }); document.addEventListener('click',() => console.log(this); //這裡this為window!也就是function執行時的context );
更多關於arrow function中的this指向問題
var invoice = { number: 123, process: function(){ consoloe.log(this); } } invoice.process(); // object invoice, ES5中this is being set to the object on which the function(owns the this) is called var invoice = { number: 123, process: () =>{ consoloe.log(this); } } invoice.process(); // window, arrow function中的this指向context the code running on var invoice = { number: 123, process: function(){ return () => consoloe.log(this.number); } } involice.process()(); //123, this指向invoice var invoice = { number: 123, process: function(){ return () => consoloe.log(this.number); } } var newInvoice = { number: 456 } involice.process().bind(newInvoice)(); //123, 注意bind不能繫結一個物件到一個arrow function!甚至js會丟擲錯誤, 不能修改arrow的this involice.process().call(newInvoice); //123, 注意call也不能繫結一個物件到一個arrow function!甚至js會丟擲錯誤, 不能修改arrow的this
arrow function不具有prototype屬性(傳統ES5的函式都具有該屬性可以訪問使用)
var getPrice = () => 5.99; console.log(getPrice.hasOwnProperty('prototype') //false,因為arrow function不具有prototype屬性!
Object Literal extension
var price = 5, quantity = 30; var productView = { price, // = price: price quantity // =quantity: quantity } console.log(productView); // {price: 5, quantity: 30} var field = 'dynamicField'; var price = 5; var productView = { [field+"-001"]: price } console.log(productView); //返回{dynamicFiled-001: 5}
for ... of loops
var cates = ['hardware','software']; for (var item of cates){ cosole.log(item); // hardware software } var cates = [,,]; for (var item of cates){ cosole.log(item); // undefined undefined } var codes = "ABCDE"; var count = 0; for (var code of codes){ count++; } console.log(count); //5 for ... of可以loop字串