ES5和ES6那些你必須知道的事兒(三)
ES5和ES6那些你必須知道的事兒
ES6新增的東西
一、塊級作用域:關鍵字let,常量const
二、物件字面量屬性賦值簡寫
var name = “我是name”; var age = 18; var obj = { name, age } //此處的物件的key值與value值是一樣的,所以簡寫 //不簡寫的話就是 var obj = { name:name, //即name:“我是name” age:age //即age:18 }
三、解構賦值
let singer = { first: "Bob", last: "Dylan" }; let { first: f, last: l }= singer; // 相當於 f = "Bob", l = "Dylan" let [all, year, month, day] = /^(\d\d\d\d)-(\d\d)-(\d\d)$/.exec("2015-10-25"); let [x, y] = [1, 2, 3]; // x = 1, y = 2
四、展開運算子...
var arr=[1,2,3,4]; console.log(...arr) //打印出來不是一個數組, //而是將數組裡的內容分開列印 //1 2 3 4
五、箭頭函式
//正常函式的寫法: function a(){//內容 } //如果改成箭頭函式 a()=>{ //內容 }
//箭頭函式的簡寫 x => x * x //上面的程式碼相當於下面的程式碼 function (x) { return x * x; } //如果引數不是一個,就需要用括號()括起來: // 兩個引數: (x, y) => x * x + y * y // 無引數: () => 3.14 // 可變引數: (x, y, ...rest) => { var i, sum = x + y; for (i=0; i<rest.length; i++) { sum+= rest[i]; } return sum; } //如果要返回一個物件,就要注意,寫成下面這樣 x => ({ foo: x })
要注意箭頭函式的this
普通函式下的this:
- 在普通函式中的this總是代表它的直接呼叫者,在預設情況下,this指的是window,
- 在嚴格模式下,沒有直接呼叫者的函式中的this是 undefined使用
- call,apply,bind(ES5新增)繫結的,this指的是 繫結的物件
箭頭函式中的this:
- 箭頭函式沒有自己的this, 它的this是繼承而來; 預設指向在定義它時所處的物件(宿主物件),
- 而不是執行時的物件, 定義它的時候,可能環境是window,也有可能是其他的。
看下面這段程式碼:
function a() { console.log(this); //window } a();
因為a是一個全域性函式,也就是掛載在window物件下的,所以a(),等價於window.a();
var obj = { say: function () { setTimeout(function () { console.log(this); //window }); } } obj.say();
定時器中的函式,由於沒有預設的宿主物件,所以this指向window
var obj = { func: function() {}, say: function () { console.log(this);//obj,此時的this是obj物件 setTimeout(function () { console.log(this); //window that.func(); }); } } obj.say();
此時say的宿主環境是obj,所以say裡面的this是obj,定時器中的函式, 由於沒有預設的宿主物件,所以預設this指向window
箭頭函式中的this:
var obj = { say: function () { setTimeout(() => { console.log(this);// obj }); } } obj.say();
此時的 this繼承自obj, 指的是定義它的物件obj, 而不是 window!
var obj = { say: function () { var f1 = () => { console.log(this); // obj setTimeout(() => { console.log(this); // obj }) } f1(); } } obj.say()
因為f1定義時所處的函式 中的 this是指的 obj, setTimeout中的箭頭函式this繼承自f1,所以不管有多層巢狀,都是 obj
var obj = { say: function () { var f1 = function () { console.log(this); // window, f1呼叫時,沒有宿主物件,預設是window setTimeout(() => { console.log(this); // window }) }; f1(); } } obj.say()
結果: 都是 window,因為 箭頭函式在定義的時候它所處的環境相當於是window, 所以在箭頭函式內部的this函式window
換個理解方式:因為箭頭函式沒有this,當需要判斷當前this是什麼的時候,把箭頭函式去掉,看剩下的程式碼此處的this指向哪裡即可。
六、字串模版` `
var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?` // return "Hello Bob, how are you today?"
七、迭代器
八、生成器
九、class
Class,有constructor、extends、super,但本質上是語法糖(對語言的功能並沒有影響,但是更方便程式設計師使用)。
class Artist { constructor(name) { this.name = name; } perform() { return this.name + " performs "; } } class Singer extends Artist { constructor(name, song) { super.constructor(name); this.song = song; } perform() { return super.perform() + "[" + this.song + "]"; } } let james = new Singer("Etta James", "At last"); james instanceof Artist; // true james instanceof Singer; // true james.perform(); // "Etta James performs [At last]"
十一、Map() 和 Set()
// Sets var s = new Set(); s.add("hello").add("goodbye").add("hello"); s.size === 2; s.has("hello") === true; // Maps var m = new Map(); m.set("hello", 42); m.set(s, 34); m.get(s) == 34;
十二、一些新的API
一些新的API Number.EPSILON Number.isInteger(Infinity) // false Number.isNaN("NaN") // false Math.acosh(3) // 1.762747174039086 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.from(document.querySelectorAll('*')) // Returns a real Array Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior [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() // iterator [0, "a"], [1,"b"], [2,"c"] ["a", "b", "c"].keys() // iterator 0, 1, 2 ["a", "b", "c"].values() // iterator "a", "b", "c" Object.assign(Point, { origin: new Point(0,0) })
十三、proxy
Proxy 讓我們能夠以簡潔易懂的方式控制外部對物件的訪問。其功能非常類似於設計模式中的代理模式。
使用 Proxy 的好處是:物件只需關注於核心邏輯,一些非核心的邏輯(如:讀取或設定物件的某些屬性前記錄日誌;設定物件的某些屬性值前,需要驗證;某些屬性的訪問控制等)可以讓 Proxy 來做。從而達到關注點分離,降級物件複雜度的目的。
var p = new Proxy(target, handler);
其中,target 為被代理物件。handler 是一個物件,其聲明瞭代理 target 的一些操作。p 是代理後的物件。
當外界每次對 p 進行操作時,就會執行 handler 物件上的一些方法。handler 能代理的一些常用的方法如下:
- get:讀取
- set:修改
- has:判斷物件是否有該屬性
- construct:建構函式
- ...
var target = { name: 'obj' }; var logHandler = { get: function(target, key) { console.log(`${key} 被讀取`); return target[key]; }, set: function(target, key, value) { console.log(`${key} 被設定為 ${value}`); target[key] = value; } } var targetWithLog = new Proxy(target, logHandler); targetWithLog.name; // 控制檯輸出:name 被讀取,執行的get targetWithLog.name = 'others'; // 控制檯輸出:name 被設定為 others,執行了set console.log(target.name); // 控制檯輸出: others
- targetWithLog 讀取屬性的值時,實際上執行的是 logHandler.get :在控制檯輸出資訊,並且讀取被代理物件 target 的屬性。
- 在 targetWithLog 設定屬性值時,實際上執行的是 logHandler.set :在控制檯輸出資訊,並且設定被代理物件 target 的屬性的值。
_
開頭的變數都認為是私有的。
var api = { _secret: 'xxxx', _otherSec: 'bbb', ver: 'v0.0.1' }; api = new Proxy(api, { get: function(target, key) { // 以 _ 下劃線開頭的都認為是 私有的 if (key.startsWith('_')) { console.log('私有變數不能被訪問'); return false; } return target[key]; }, set: function(target, key, value) { if (key.startsWith('_')) { console.log('私有變數不能被修改'); return false; } target[key] = value; }, has: function(target, key) { return key.startsWith('_') ? false : (key in target); } }); api._secret; // 私有變數不能被訪問 console.log(api.ver); // v0.0.1 api._otherSec = 3; // 私有變數不能被修改 console.log('_secret' in api); //false console.log('ver' in api); //true
使用代理(Proxy)監聽物件的操作,然後可以做一些相應事情。
可監聽的操作: get、set、has、deleteProperty、apply、construct、getOwnPropertyDescriptor、defineProperty、getPrototypeOf、setPrototypeOf、enumerate、ownKeys、preventExtensions、isExtensible。
十四、Symbol
Symbol是一種基本型別。Symbol 通過呼叫symbol函式產生,它接收一個可選的名字引數,該函式返回的symbol是唯一的。
var key = Symbol("key"); var key2 = Symbol("key"); key == key2 //false
十五、promise
Promises是處理非同步操作的物件,使用了 Promise 物件之後可以用一種鏈式呼叫的方式來組織程式碼,讓程式碼更加直觀
function fakeAjax(url) { return new Promise(function (resolve, reject) { // setTimeouts are for effect, typically we would handle XHR if (!url) { return setTimeout(reject, 1000); } return setTimeout(resolve, 1000); }); } // no url, promise rejected fakeAjax().then(function () { console.log('success'); },function () { console.log('fail'); });
非同步操作的同步程式碼