淺析ES6新特性

zhanzhan_wu發表於2018-01-11

在近期的Vue開發中,使用了大量的ES6語法,因此覺得有必要找個時間來整理下近期的學習筆記。ES6相對ES5增加了許多新的特性,給我們的開發提供了方便。並且ES6也是未來開發的潮流,所以我覺得大家很有必要去掌握這一技能。

在我們正式學習ES6前,我們需要先去學習下轉碼器。常用的轉碼器有Babel和Traceur。這裡推薦一篇常見的ES6轉碼工具有興趣的可以去看看。

接下來我們就一起去見證ES6的神奇之處吧!

1.let和const

let與var類似是用來宣告變數的,const用來宣告常量。在實際用途中它們存在著許多區別,廢話不多說,直接看程式碼。

  {
      var a = 100;
      let b = 200;
  }
  console.log(a); //100
  console.log(b); //b is not defined -- Error
複製程式碼

是不是感覺很奇怪,為什麼a有值而b卻not defined呢?因為ES5只有全域性作用域和函式作用域,沒有塊級作用域。而let則實際上為JavaScript新增了塊級作用域。用它所宣告的變數,只在let命令所在的程式碼塊內有效。

let不存在變數提升。那麼什麼是變數提升呢?簡單來說就是無論宣告在什麼地方,都會被視為宣告在頂部。下面來看個例子。

//ES5
	console.log("ES5:");
	var a = [];
	for (var i = 0; i < 10; i++) {
		var c = i;
		a[i] = function () {
			console.log(c);
		};
	};
	a[5]();	//結果:9
複製程式碼

是不是有許多小夥伴在疑惑為什麼輸出的不是5呢?變數i是var宣告的,在全域性範圍內都有效。所以每一次迴圈,新的i值都會覆蓋舊值,導致最後輸出的是最後一輪的i的值。而使用let則不會出現這個問題。

//ES6
	console.log("ES6:");
	var b = [];
	for (var j = 0; j < 10; j++) {
		let d = j;
		b[j] = function () {
			console.log(d);
		};
	};
	b[5](); 結果://5
複製程式碼

同時let也是不允許重複宣告的。

        {
		var a = 100;
		var a = 200;
		console.log(a); //200
	}
	// 模組內部不允許用let命令重複宣告
	{
		var a = 1;
		let a = 2;
		console.log(a); // 報錯
	}
複製程式碼

下面再來說說 const。const也用來宣告變數,但是宣告的是常量,一旦宣告,常量的值就不能改變。它和let一樣只在宣告的區域內有用。

        {
		var a   = 100;
		const a = 200;
		console.log(a); // 報錯
	}
複製程式碼

const宣告物件

        const person = {};
	person.name  = "Zhangsan";
	person.age   = 30;

	console.log(person.name);	//Zhangsan
	console.log(person.age);	//30
	console.log(person);		//Object {name: "Zhangsan", age: 30}
複製程式碼

const物件凍結

        const person = Object.freeze({});
	person.name  = "Zhangsan";
	person.age   = 30;

	console.log(person.name);	//undefined
	console.log(person.age);	//undefined
	console.log(person);		//Object
複製程式碼

2.模板字串

相對於es5的' '+變數這種字串拼接方法,es6的模板字串要方便許多。

     var a = '張三';
     var age = 18;
     var b = '我的名字是'+a+'我今年'+age+'歲了'; // es5
     var c = `我的名字是${a}我今年${age}歲了`; // es6
複製程式碼

當然簡單的兩行程式碼可能它的優勢沒有那麼的明顯,一旦程式碼量變多,你就能體會到它的便利了。

3.函式

函式預設引數
     function num(n) {
        n = n || 200; //當傳入n時,n為傳入的值,沒有則預設200
        return n;
     }
複製程式碼

es6為引數提供了預設值。在定義函式時便初始化了這個引數,直接看程式碼。

     function num(n = 200) {
        return n;
     }
     console.log(n()); // 200
     console.log(n(100)); // 100
複製程式碼
箭頭函式

箭頭函式的基本用法。

// es5
     function breakfast(dessert,drink){
 	    return dessert+drink;
     }
// es6
     let breakfast = (dessert,drink) => dessert + ' ' + drink;
     console.log(breakfast('麵包','牛奶'));
複製程式碼

箭頭函式的this指向繫結定義是所在的作用域,而普通函式this指向執行時所在的作用域(全域性物件),箭頭函式沒有自己的this而是引用外層的this。

// es5
     cartView: function() {
          var _this = this;
          this.$http.get("data/cartData.json", {"id": 123}).then(function(res) {
              _this.productList = res.data.result.list;
              console.log(_this.productList);
        });
      }
// es6
      cartView(){
          this.$http.get("data/cartData.json", {"id": 123}).then((res) => {
              this.productList = res.data.result.list;
              console.log(this.productList);
        });
      }
複製程式碼

4.解構

陣列和物件是JS中最常用也是最重要表示形式。為了簡化提取資訊,ES6新增瞭解構,這是將一個資料結構分解為更小的部分的過程。

// es5提取物件
    let people = {
        name : 'json',
        age : 18,
        sex : 'male'
    };
    let name = people.name;
    let age = people.age;
    ...
 // es6
    let people = {
        name : 'json',
        age : 18,
        sex : 'male'
    };
    let {name, age, sex} = people;
複製程式碼

相比於es5是不是簡便了許多,當然陣列的方法與物件的類似。下面我們再來舉個栗子,函式引數解構賦值的預設值。

    fun ({x, y} = { x: 0, y: 0 }) {
		return [x, y];
	};

	console.log(fun({x: 100, y: 200}));	        //[100, 200]
	console.log(fun({x: 100}));	                //[100, undefined]
	console.log(fun({}));				//[undefined, undefined]
	console.log(fun());				//[0, 0]
複製程式碼

講了這麼多,可能有人就會問了這個解構到底有什麼用呢?其實它適合於各種與陣列,物件,函式打交道的場景。下面我們來舉個運用最廣的例子—變數交換。直接上程式碼。

//ES5
	console.log("ES5:");
	var a = 100;
	var b = 200;
	console.log("交換前:");
	console.log("a = " + a);	//a = 100
	console.log("b = " + b);	//b = 200
	var temp;
	temp = a;
	a = b;
	b = temp;
	console.log("交換後:");
	console.log("a = " + a);	//a = 200
	console.log("b = " + b);	//b = 100

	//ES6
	console.log("ES6:");
	var x = 100;
	var y = 200;
	console.log("交換前:");
	console.log("x = " + x);	//x = 100
	console.log("y = " + y);	//y = 200
	[x, y] = [y, x];
	console.log("交換後:");
	console.log("x = " + x);	//x = 200
	console.log("y = " + y);	//y = 100
複製程式碼

5. ...操作符

  • 展開操作符
    let str2 = ['蘋果','梨子'];
    console.log(str2);//["蘋果", "梨子"]
    console.log(...str2);//蘋果 梨子
複製程式碼
  • 剩餘操作符
    fun(a,b,...c){
        console.log(a,b,...c);//...c指展開陣列
    }
    fun('蘋果','香蕉','橘子','梨子','李子');//蘋果 香蕉 橘子 梨子 李子
複製程式碼

6.class、 extends、 super

這三個特性涉及了ES5中最令人頭疼的的幾個部分:原型、建構函式,繼承...看到它們是不是開始懷疑人生了。

別怕有來了ES6我們以後再懷疑吧!

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

class MyClass {
  constructor() {  // 建構函式
    // ...
  }
  get prop() {  // 取值
    return 'getter';
  }
  set prop(value) { // 存值
    console.log('setter: '+value);
  }
}

let inst = new MyClass();
inst.prop = 123;
// setter: 123
console.log(inst.prop);
// 'getter'
複製程式碼

extends用法

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

class ColorPoint extends Point {
  constructor(x, y, color) {
    this.color = color; // ReferenceError
    super(x, y);
    this.color = color; // 正確
  }
}
複製程式碼

在子類的建構函式中,只有呼叫super之後,才可以使用this關鍵字,否則會報錯。這是因為子類例項的構建,是基於對父類例項加工,只有super方法才能返回父類例項。父類的靜態方法,也會被子類繼承。

注意,super雖然代表了父類Point的建構函式,但是返回的是子類ColorPoint的例項,即super內部的this指的是ColorPoint,因此super()在這裡相當於Point.prototype.constructor.call(this)。

super這個關鍵字,既可以當作函式使用,也可以當作物件使用。在這兩種情況下,它的用法完全不同。 作為函式時,super()只能用在子類的建構函式之中,用在其他地方就會報錯。

    class A {}
    class B extends A {
	  m() {
	     super(); // 報錯
	  }
	}
複製程式碼

第二種情況,super作為物件時,在普通方法中,指向父類的原型物件;在靜態方法中,指向父類。

     class A {
        p() {
           return 2;
         }
     }
     class B extends A {
        constructor() {
            super();
            console.log(super.p()); // 2
        }
         
     }
     let b = new B();
複製程式碼

上面程式碼中,子類B當中的super.p()就是將super當作一個物件使用。這時,super在普通方法之中,指向A.prototype,所以super.p()就相當於A.prototype.p()。這裡需要注意,由於super指向父類的原型物件,所以定義在父類例項上的方法或屬性,是無法通過super呼叫的。

7.Promise

在promise之前程式碼過多的回撥或者巢狀,可讀性差、耦合度高、擴充套件性低。通過Promise機制,大大提高了程式碼可讀性;用同步程式設計的方式來編寫非同步程式碼,儲存線性的程式碼邏輯,極大的降低了程式碼耦合性而提高了程式的可擴充套件性。

this.$http('/api/getData').then((res) => {
res = res.data;
this.dataList = res.result;
}).catch((err) => {
...
});
複製程式碼

這是一個vue的非同步請求,用的就是promise機制,這樣的好處是不會因為多層的回撥而降低程式碼的可讀性。

當然這只是promise的冰山一角,想要更好的掌握它還是需要更加深入的去學習它。

8.Set

例項的方法分為兩大類:操作方法(用於運算元據)和遍歷方法(用於遍歷成員)。下面先介紹四個操作方法。

  • 操作方法: add(value):新增某個值,返回Set結構本身。 delete(value):刪除某個值,返回一個布林值,表示刪除是否成功。 has(value):返回一個布林值,表示該值是否為Set的成員。 clear():清除所有成員,沒有返回值。
  • 遍歷方法: keys():返回鍵名的遍歷器 values():返回鍵值的遍歷器 entries():返回鍵值對的遍歷器 forEach():使用回撥函式遍歷每個成員 由於 Set 結構沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值)
let str = [1,2,3,2,4,3,5,6,4,1,7];
console.log('str的長度為:'+str.length); // 長度為11
let s = new Set(str);
console.log(s);
console.log('去重後的長度為:'+s.size);//長度為7
console.log( Array.from(s));//Array.from 將Set轉換為陣列形式

let set = new Set(['red', 'green', 'blue']);
var arr = new Set();
for (let item of set.keys()) {
  console.log(item);// red green blue
  arr.add(item);//將item新增到arr set結構中
}
console.log(arr);
arr.forEach((value, key) => console.log(key + ' : ' + value));
複製程式碼

9.import 和 export

//全部匯入
import mallHeader from '../components/header.vue'
//匯入部分
import {name, age} from './example'
// 匯出預設, 有且只有一個預設
export default App
// 部分匯出
export class App extend Component {};

複製程式碼

小結

這些僅僅是es6家族中的冰山一角,想要更加全面的去掌握這一技能,就需要我們更加系統的去學習它。感謝花了這麼長時間,閱讀文章的小夥伴們,希望能對你們帶來幫助。有錯誤的地方希望大佬們多多包涵,給我反饋!

相關文章