ES6/ES2015常用知識點和概念

世有因果知因求果發表於2016-08-23

越來越多的開源庫開始使用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字串

 

相關文章