首先要區分語言和平臺之間的關係,語言自己是指ECMAScript,平臺是指瀏覽器或者node,在平時咱們瀏覽器開發裏js就是ECMAScript。
瀏覽器的組成部分
node.js的組成部分
在ES5.1以後版本咱們統稱爲ES6
主要說明以下:
**• let 和 const
• Arrow functions
• Classes(類)
• Generators
• Map 和 Set
• for ... of 循環
• Symbol
• Modules
• Template literals(模板字⾯量)
• Default parameters(默認參數)
• Enhanced object literals(對象字⾯量加強)
• Destructuring assignments(解構分配)
• Spread operator(展開操做符)
• Proxy和Reflect**前端
這些新增API和對象主要是進行:
**• 對原有語法進⾏加強
• 解決原有語法上的⼀些問題或者缺陷
• 全新的對象、全新的⽅法、全新的功能
• 全新的數據類型和數據結構**node
let 聲明的成員只會在所聲明的塊中生效
if (true) {
let foo = 'zce'
console.log(foo)
}es6
let經典應用場景算法
var elements = [{}, {}, {}] for (let i = 0; i < elements.length; i++) { elements[i].onclick = function () { console.log(i) } }
由於產生本身的循環體的塊級做用域,因此能夠代替閉包。api
咱們看下面例子來解析一下運行
for (let i = 0; i < 3; i++) {
let i = 'foo'
console.log(i)
}數組
以下:
let i = 0瀏覽器
if (i < 3) {
let i = 'foo'
console.log(i)
}數據結構
i++閉包
if (i < 3) {
let i = 'foo'
console.log(i)
}dom
i++
if (i < 3) {
let i = 'foo'
console.log(i)
}
i++
實際上是這樣的,因此咱們也就能夠解釋在這其中for 循環會產生兩層做用域。
let 還修復了變量聲明提高現象。
const 總體和let相似,
const name
name = 'zce' //會報錯
箭頭函數的 簡易寫法,能夠去看下阮一峯老師的es6,這裏主要標明幾個箭頭函數的特徵。
class Super{ sayName =()=>{ //do some thing here } } var a = new Super() var b = new Super() a.sayName === b.sayName //false
es6裏實現class關鍵字,來代替es5裏的構造函數的方式生成類。
es6的extends關鍵詞來進行繼承,底層是使用es5寄生組合式繼承的封裝。
使用extends來繼承,constructor中使用this必需要先實例化父類,使用super關鍵詞實例化,其餘方法裏也可經過super調用父類成員。
class裏的constructor至關於es5的實例化,而後以外定義的屬性函數,至關於定義在原型上(不包含箭頭函數)
static關鍵詞 靜態屬性,能夠不實例化類可直接訪問,裏面的this指向類自己,不是實例化後的對象。
// extends 繼承 class Person { constructor (name) { this.name = name } say () { console.log(`hi, my name is ${this.name}`) } } class Student extends Person { constructor (name, number) { super(name) // 父類構造函數 this.number = number } hello () { super.say() // 調用父類成員 console.log(`my school number is ${this.number}`) } } const s = new Student('jack', '100') s.hello()
Generators函數調用會返回一個iterator對象,能夠繼續調用next()方法來依次返回Generators函數yield關鍵詞以後的表達式結果,結果是個對象{value:'結果值',done:布爾值(是否結束true/false)},yield能夠暫停當前函數,而後再經過外面iterator調用next方法繼續往下走。
代碼以下:
function * createIdMaker () { let id = 1 while (true) { yield id++ } } const idMaker = createIdMaker() console.log(idMaker.next().value) console.log(idMaker.next().value) console.log(idMaker.next().value) console.log(idMaker.next().value)
Map
對象保存鍵值對。任何值(對象或者原始值) 均可以做爲一個鍵或一個值。構造函數Map
能夠接受一個數組做爲參數。
map和object區別
Object
的鍵只能是字符串或者 Symbols
,但一個Map
的鍵能夠是任意值。Map
中的鍵值是有序的(FIFO 原則),而添加到對象中的鍵則不是。Map
的鍵值對個數能夠從 size 屬性獲取,而 Object
的鍵值對個數只能手動計算。Map
支持for of 循環,obj須要本身添加Symbol.iterator經常使用api
遍歷方法
keys()
:返回鍵名的遍歷器values()
:返回鍵值的遍歷器entries()
:返回鍵值對的遍歷器forEach()
:使用回調函數遍歷每一個成員map支持 for of 遍歷,內部的Symbol.iterator是entries
Set
對象容許你存儲任何類型的值,不管是原始值或者是對象引用。它相似於數組,可是成員的值都是惟一的,沒有重複的值。
Set
自己是一個構造函數,用來生成Set
數據結構。Set
函數能夠接受一個數組(或者具備 iterable 接口(迭代器)的其餘數據結構)做爲參數,用來初始化(帶有Symbol.iterator)。Set
對象存儲的值老是惟一的,因此須要判斷兩個值是否恆等。有幾個特殊值須要特殊對待:
set經常使用方法
add(value)
:添加某個值,返回 Set 結構自己(能夠鏈式調用)。delete(value)
:刪除某個值,刪除成功返回true
,不然返回false
。has(value)
:返回一個布爾值,表示該值是否爲Set的成員。clear()
:清除全部成員,沒有返回值。遍歷方法
keys()
:返回鍵名的遍歷器。values()
:返回鍵值的遍歷器。entries()
:返回鍵值對的遍歷器。forEach()
:使用回調函數遍歷每一個成員。因爲Set
結構沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值),因此keys方法和values方法的行爲徹底一致。
for of 循環本意是統一 js中循環的標準,咱們查看可使用for of循環的對象,
解釋:裏面都有一個Symbol.iterator方法,這個of循環首先調用這個方法,它返回一個對象(而後就是循環對象),這個對象內部有next方法,方法返回一個對象{value:'值',done:布爾值//是否結束},每次循環就是調用這個next方法,等done爲true的時候循環結束。
這是否是很像Generator函數,因此說Generator調用的結果也是能夠用of循環的,好比map和set結構都有這個Symbol.iterator方法,,因此它也是能夠循環的,可是對象是沒有這個方法的,因此須要咱們手動添加一下
const todos = { life: ['吃飯', '睡覺', '打豆豆'], learn: ['語文', '數學', '外語'], work: ['喝茶'], [Symbol.iterator]: function * () { const all = [...this.life, ...this.learn, ...this.work] for (const item of all) { yield item } } } for (const item of todos) { console.log(item) }
由於咱們看到 它和Generator函數很像,因此咱們用它輔助實現一下,
給[Symbol.iterator]定義一個生成器函數,返回一個可迭代對象,而後咱們使用 yield 函數來輔助實現,由於它返回的和咱們要的那個對象是同樣的。
symbol 是一種基本數據類型 (primitive data type)。Symbol()
函數會返回symbol類型的值,該類型具備靜態屬性和靜態方法。它的靜態屬性會暴露幾個內建的成員對象;它的靜態方法會暴露全局的symbol註冊,且相似於內建對象類,但做爲構造函數來講它並不完整,由於它不支持語法:"new Symbol()
"。
每一個從Symbol()
返回的symbol值都是惟一的。一個symbol值能做爲對象屬性的標識符;這是該數據類型僅有的目的。更進一步的解析見—— glossary entry for Symbol。
使用場景:擴展對象,屬性名衝突問題
通常使用第三方庫擴展時,咱們預防覆蓋方法名,可使用Symbol對象作key,由於兩個 Symbol 永遠不會相等。
因爲沒法建立出同樣的 Symbol 值, 因此咱們能夠用它建立「私有」成員
更多的方法能夠查看mdn
Symbol
模塊 es6中的import語句爲變量,函數,和類建立的是隻讀綁定,標識符只有在導出的模塊中能夠修改,即便是導入綁定的模塊也沒法更改綁定的值。
一些簡單的語法能夠在mdn上查看
這裏貼一個之前的異步模塊
var me =(function hello(name){ function greeting(){ console.log('hello'+name) } return {//public API greeting:greeting } })('kyle') console.log(me.greeting())
// 反引號包裹
const str = hello es2015, this is a string
;
容許換行,能夠經過${}插入表達式,返回得結果會輸出到對應位置
xxx${...}xxx
**帶標籤的模板字符串,模板字符串的標籤就是一個特殊的函數,
使用這個標籤就是調用這個函數**
const name = 'tom' const gender = false function myTagFunc (strings, name, gender) { // console.log(strings, name, gender) // return '123' const sex = gender ? 'man' : 'woman' return strings[0] + name + strings[1] + sex + strings[2] } const result = myTagFunc`hey, ${name} is a ${gender}.` console.log(result) hey, tom is a woman.
就是把字符串根據變量分割成一個數組,而後後續就是字符串中的變量依次傳遞.
在es5中給函數的默認參數只能
function foo (enable) { // 短路運算不少狀況下是不適合判斷默認參數的,例如 0 '' false null // enable = enable || true enable = enable === undefined ? true : enable console.log('foo invoked - enable: ') console.log(enable) } //這樣去賦值如今es6中咱們能夠 function foo (enable = true) { console.log('foo invoked - enable: ') console.log(enable) } //包括解構的時候能夠 const {sex='默認'}=props; 甚至函數能夠 function foo({sex='1'}={}){ console.log(sex) }
這一篇比較簡單看代碼把
// 對象字面量 const bar = '345' const obj = { foo: 123, // bar: bar // 屬性名與變量名相同,能夠省略 : bar bar, // method1: function () { // console.log('method111') // } // 方法能夠省略 : function method1 () { console.log('method111') // 這種方法就是普通的函數,一樣影響 this 指向。 console.log(this) }, // Math.random(): 123 // 不容許 // 經過 [] 讓表達式的結果做爲屬性名 [bar]: 123 } // obj[Math.random()] = 123 console.log(obj) obj.method1() // Object.assign 方法 // const source1 = { // a: 123, // b: 123 // } // const source2 = { // b: 789, // d: 789 // } // const target = { // a: 456, // c: 456 // } // const result = Object.assign(target, source1, source2) // console.log(target) // console.log(result === target) // 應用場景 function func (obj) { // obj.name = 'func obj' // console.log(obj) const funcObj = Object.assign({}, obj) funcObj.name = 'func obj' console.log(funcObj) } const obj = { name: 'global obj' } func(obj) console.log(obj) // Object.is console.log( // 0 == false // => true // 0 === false // => false // +0 === -0 // => true // NaN === NaN // => false // Object.is(+0, -0) // => false // Object.is(NaN, NaN) // => true )
數組的解構
const arr = [100, 200, 300]
const [foo, bar, baz] = arr;
能夠直接這樣去解構,對應下表0,1,2
若是不要前面的能夠
[,,baz]
這樣就是隻取2位置得了
經典的排序算法 交換位置能夠:
const [arr[1],arr[0]]=[arr[0],arr[1]]
而後展開操做符
const [foo, ...rest] = arr;
取第一個 參數以後全部的返回一個數組。
展開操做符只能放在最後。
取出來的值也能夠給定默認值
例如
const [foo, bar, baz = 123, more = 'default value'] = arr;
對象的解構:
const obj = { name: 'zce', age: 18 }
const { name } = obj;
至關於{name:name}
能夠這樣去解構,包括對象當參數傳遞時也能夠這樣解構
而後也能夠給定默認值,前面有例子。
這個解構變量名字的聲明和正常聲明相反,是在右邊,以下
const { name: objName = 'jack' } = obj;
name是從obj解構出來的屬性名,objName是咱們新的變量名來接收這個值,而且給了它一個默認值。
而後就是展開操做
const {name,...args}=obj;
不要在意這些報錯
這個解構展開就是說,除了解構指明的屬性名以外,剩餘的屬性組成一個新對象,用剩餘參數名稱接受。
代理對象proxy和defineProperty的對比
優點:
一個簡單的例子
const person2 = { name: 'zce', age: 20 } const personProxy = new Proxy(person2, { get (target, property) { console.log('get', property) return target[property] }, set (target, property, value) { console.log('set', property, value) target[property] = value } }) personProxy.name = 'jack' console.log(personProxy.name)
更多的api的使用方式能夠在mdn中查看
Proxy的使用方式
Reflect 是一個內置的對象,它提供攔截 JavaScript 操做的方法。這些方法與proxy handlers的方法相同。Reflect
不是一個函數對象,所以它是不可構造的。
它提供了對對象的統一操做,以前對象的操做不統一,
const obj = {
name: 'zce',
age: 18
}
console.log('name' in obj)
console.log(delete obj['age'])
console.log(Object.keys(obj))
console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))
具體提供了哪些方法,也能夠直接去mdn中查看
Reflect
該內容借鑑於 拉鉤大前端訓練營