摘要:常用知識總結之前總結了中的一些知識點。在年正式發布了,簡稱,又稱為。作為構造函數的語法糖,同時有屬性和屬性,因此同時存在兩條繼承鏈。子類的屬性,表示構造函數的繼承,總是指向父類。
ES6常用知識總結
之前總結了es5中js的一些知識點。這段時間看了石川blue老師講解的es6課程,結合阮一峰老師的es6教程,隨手做了一些筆記和總結分享給大家。內容還是es6主要的知識點,基本沒有什么創新點,主要是石川blue對里面一些難懂的知識點分析的挺好的,對我個人理解es6很有幫助,也希望對大家能有所幫助。
石川blue老師es6講解視頻百度云地址:https://pan.baidu.com/s/1qZpUeni 密碼:2m9t
es6 是什么首先弄明白ECMA和js的關系。ECMA是標準,Javascript是ECMA的實現。因為js也是一種語言,但凡語言都有一套標準,而ECMA就是javascript的標準。
在2015年正式發布了ECMAscript6.0,簡稱ES6,又稱為ECMAscript2015。
在es6之前,定義變量都是使用var,但是var存在一些問題,比如可以重復聲明,僅支持函數作用域問題。所以es6設計了let和const來彌補不足的地方,下面看一下let和const具備哪些特性
let
不能重復聲明
塊級作用域
可修改let變量的值
const
不可重復聲明
塊級作用域
不可修改const變量的值
2. 箭頭函數箭頭函數在寫法上對es5做了一些修整,代碼看起來更顯得簡潔
如果只有一個參數,圓括號"()"可以省略
函數體如果只有一句return語句,花括號也可以省略
// 定義一個箭頭函數 let a = (arg)=>{ // 這里=>符號就相當于function關鍵字 return arg+=1 } // 也可以簡寫為 let a = arg => arg+=1
箭頭函數也對this的指向做了修整
es6之前的函數的this指向調用函數時所在的對象,而箭頭函數的this指向函數定義時所在的對象
// 普通函數 var obj = { say: function () { setTimeout(function() { console.log(this) }); } } // 箭頭函數 var obj = { say: function () { setTimeout(() => { console.log(this) }); } } obj.say(); // obj3. 解構賦值
允許按照一定模式,從數組和對象中提取值,對變量進行賦值,這被稱為解構。比如:
var [a,b] = [1,2] // a=1 b=2
解構賦值必須符合下面三條規則:
左右結構必須一樣
右邊必須是一個合法的數據
聲明和賦值必須一句話完成,不能把聲明與賦值分開
let [a, b] = [1, 2] // 左右都是數組,可以解構賦值 let {a, b} = {a:1, b:2} // 左右都是對象,可以解構賦值 let [obj, arr] = [{a:1}, [1, 2]] // 左右都是對象,可以解構賦值 let [a, b] = {a:1, b:2} // err 左右結構不一樣,不可以解構賦值 let {a,b} = {1, 2} // err 右邊不是一個合法的數據,不能解構賦值 let [a, b]; [a, b] = [1, 2] // err 聲明與賦值分開,不能解構賦值4. 字符串
1.字符轉模板
字符轉模板使用反引號(``)來定義字符串,字符串模板支持斷行,也可以在字符串嵌入變量,非常方便,可謂是前端的福利。
let name = "Jone" `hello ${name}, how are you `
2. startsWith(), endsWith(), includes()
startsWith() 表示參數字符串是否在原字符串的頭部,返回布爾值
endsWith() 表示參數字符串是否在原字符串的尾部,返回布爾值
includes() 表示是否在原字符串找到了參數字符串,返回布爾值
這三個方法都支持第二個參數,表示開始搜索的位置。
let s = "Hello world!"; s.startsWith("world", 6) // true s.endsWith("Hello", 5) // true s.includes("Hello", 6) // false5. 數組
石川blue老師講的是es6對數組擴展了4個方法:map、reduce、filter、forEach。我認為這四個方法好像在es5就有了(個人感覺,有可能不對),不管這四個方法是es5還是es6添加的方法了,大家對這幾個函數應該都挺熟悉,就不多做介紹了,下面說一下es6對數組擴展的其他的新特性。
擴展運算符
擴展運算符(spread)是三個點(...),它可以將一個數組拆分為參數序列,也可以收集剩下的所有的參數或者數組元素。
// 將一個數組拆分為參數序列 let arr = [1,2] function add (a,b) { return a+b } add(...arr) // 收集剩下的所有的參數 function f(a, ...arr) { console.log(...arr) // 2 3 } f(1,2,3) // 用于數組復制 let arr1 = [1,2] let arr2 = [...arr1] // 或者let [...arr2] = arr16. 面向對象 class
ES6 提供了更接近傳統語言的寫法,引入了 Class(類)這個概念,作為對象的模板。通過class關鍵字,可以定義類。
先看如何定義一個class類:
class User { constructor(name) { // 構造器,相當于es5中的構造函數 this.name = name // 實例屬性 } showName(){ // 定義類的方法,不能使用function關鍵字,不能使用逗號分隔 console.log(this.name) } } var foo = new User("foo")(1)constructor
es6中class類專用的構造器,相當于之前定義的構造函數,每個類都必須有constructor,如果沒有則自動添加一個空的constructor構造器。
創建實例的時候自動執行constructor函數
constructor中的this指向實例,并且默認返回this(實例)
(2)class類的prototype其實class的基本類型就是函數(typeof User = "function"),既然是函數,那么就會有prototype屬性。
類的所有方法都是定義在prototype上
class User { constructor() { // ... } toString() { // ... } toValue() { // ... } } User.toValue() // err User.toValue is not a function User.prototype.toValue() // 可以調用toValue方法 // 等同于 User.prototype = { constructor() {}, toString() {}, toValue() {}, };(3)類的實例
類的實例只能通過new來創建
除了靜態方法,定義在類上的所有的方法都會被實例繼承
除非定義在類的this對象上才是實例屬性,否則都是定義在類的原型(prototype)上
//定義類 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return "(" + this.x + ", " + this.y + ")"; } } var point = new Point(2, 3); point.toString() // (2, 3) point.hasOwnProperty("x") // true point.hasOwnProperty("y") // true point.hasOwnProperty("toString") // false point.__proto__.hasOwnProperty("toString") // true(4)靜態方法
如果在類中定義一個方法的前面加上static關鍵字,就表示定義一個靜態方法,靜態方法不會被實例繼承,但會被子類繼承,所以不能通過實例使用靜態方法,而是通過類直接調用。
class User { constructor(name){ this.name = name } static show(){ console.log("123") } } class VipUser extends User{} VipUser.show() // 123 User.show() // 123 var foo = new User("foo") foo.show() // foo.show is not a function(5)靜態屬性
class的靜態屬性指的是 Class 本身的屬性,目前只能通過Class.propName定義靜態屬性
靜態屬性可以被子類繼承,不會被實例繼承
class User{} User.name = "foo" // 為class定義一個靜態屬性 class VipUser extends User{} console.log(VipUser.name) // foo var foo = new User() console.log(foo.name) // undefined(6)私有屬性和私有方法
es6是不支持私有屬性和私有方法,但是日常需求可能會用到私有屬性和私有方法,所以目前有一些提案,不過只是提案,尚未支持。
7. 類的繼承class通過extends關鍵字實現繼承:
class User { constructor(name){ this.name = name } show(){...} } class VipUser extends User{ constructor(vipName){ // 子類的構造器 super(vipName) // 調用父類的constructor。相當于User.prototype.constructor.call(this,vipName) } showVip(){...} } var v = new VipUser("foo") // 創建實例 v instanceof VipUser // v是子類VipUser的實例 v instanceof User // v還是父類User的實例(1)super
super可以當做函數使用,也可以當做對象使用。
當做函數使用
super作為函數調用時,代表父類的構造函數,就是在子類的構造器中執行父類的constructor函數以獲取父類的this對象,因為子類沒有自己的this對象,所以ES6規定子類必須在constructor中執行一次super函數。super()函數只能在子類的constructor中執行,不能在其他地方執行。
雖然super代表父類的構造器,但是super()在執行時內部的this指向子類,所以super()就相當于User.prototype.constructor.call(this)。
當做對象使用
super可以作為對象調用父類的屬性和方法,在子類的普通方法中,指向父類的原型對象(即User.prototype);在子類的靜態方法中,指向父類。
class User { constructor(){ this.x = "hello" } show() { return 2; } } class VipUser extends User { constructor() { super(); console.log(super.show()); // 2 此時super指向User.prototype,相當于User.prototype.show() console.log(super.x) // undefined 無法訪問實例屬性 } } let vip = new VipUser();
由于super對象在普通函數中使用super指向User.prototype,所以super只能訪問父類的原型上的方法,沒法訪問父類的實例屬性和實例方法。
ES6規定如果在子類中使用super對象調用父類的方法時,方法內部的this指向子類
class User { constructor() { this.x = 1 } show() { return this.x; } } class VipUser extends User { constructor() { super(); this.x = 2 console.log(super.show()) // 2 此時show()方法內部的this指向子類,所以輸出2,而不是1 } } let vip = new VipUser();
上述代碼中雖然super.show()調用的是User.prototype.show(),但是由于通過super對象調用父類方法時,方法內部的this指向子類,所以super.show()相當于 super.show().call(this),也就是User.prototype.show().call(this)
在子類的靜態方法中super對象指向父類,而不是父類的原型(User.prototype)。
class User { constructor() { this.x = 1 } static fn() { console.log("父類靜態方法") } } class VipUser extends User { constructor() { super(); this.x = 2 } static childFn() { super.fn() // 相當于User.fn() } } VipUser.childFn()(2)類的prototype和__proto__屬性
在es5中每一個對象都有__proto__屬性,指向對應的構造函數的prototype屬性。Class 作為構造函數的語法糖,同時有prototype屬性和__proto__屬性,因此同時存在兩條繼承鏈。
子類的__proto__屬性,表示構造函數的繼承,總是指向父類。
子類prototype屬性的__proto__屬性,表示方法的繼承,總是指向父類的prototype屬性。
class User { } class VipUser extends User { } VipUser.__proto__ === User // true VipUser.prototype.__proto__ === User.prototype // true(3)實例的__proto__屬性
子類實例的__proto__屬性指向子類的原型(子類的prototype),子類實例的__proto__屬性的__proto__屬性指向父類的原型(父類的prototype)
class User { } class VipUser extends User { } var vip = new VipUser() console.log(vip.__proto__ === VipUser.prototype) // true console.log(vip.__proto__.__proto__ === User.prototype) // true8. 對象 (1) 對象的擴展運算符 ...
對象的擴展運算符(...)可以把對象可枚舉的屬性拆分為鍵值對序列
用于對象拷貝
let obj1 = {a:1,b:2} let obj2 = {...obj1} console.log(obj2) // {a:1,b:2}
用于合并對象
let obj1 = {a:1,b:2} let obj2 = {c:3,d:4} let obj3 = {a:100, ...obj1, ...obj2, e:5, f:6} // {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6} let obj4 = { ...obj1, ...obj2, e:5, f:6, a:100} // {a: 100, b: 2, c: 3, d: 4, e: 5,?f: 6}
如果后面的屬性和前面的屬性key相同,則會覆蓋前面的值
(2) Object.assignObject.assign方法用于對象的合并,將源對象(source)的所有可枚舉屬性,拷貝(淺拷貝)到目標對象。
如果目標對象與源對象有同名屬性,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性。
const target = { a: 1 }; const source1 = {a: 100, b: 2 }; const source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:100, b:2, c:3}
Object.assign拷貝的屬性是有限制的,只拷貝源對象的自身屬性(不拷貝繼承屬性),也不拷貝不可枚舉的屬性。
(3) Object.keys(),Object.values(),Object.entries()ES2017 引入了跟Object.keys、Object.values和Object.entries,作為遍歷一個對象的補充手段,供for...of循環使用。
Object.keys() 返回一個數組,成員是參數對象所有可枚舉的屬性的鍵名
var obj = { foo: "bar", baz: 42 }; Object.keys(obj) // ["foo", "baz"]
Object.values() 返回一個數組,成員是參數對象所有可枚舉屬性的鍵值。
const obj = { 100: "a", 2: "b", 7: "c" }; Object.values(obj) // ["b", "c", "a"]
Object.entries() 返回一個數組,成員是參數對象所有可枚舉屬性的鍵值對數組。
const obj = { foo: "bar", baz: 42 }; Object.entries(obj) // [ ["foo", "bar"], ["baz", 42] ]9. Generator函數
Generator可以理解為生成器,和普通函數沒多大區別,普通函數是只要開始執行,就一直執行到底,而Generator函數是中間可以停,搭配使用next函數繼續執行,用石川blue老師的話說就是踹一腳走一步。
(1)定義一個Generator函數function * fn(){ alert("a") yield alert("b") } var f = fn() f.next() // a f.next() // b
直接調用Generator函數,是什么都不執行的,調用第一個next()才開始執行,一直執行到第一個yield停止,第二次調用next(),從第一個yield執行到第二個yield停止,依次類推
(2)yield和nwxtyield代表暫時暫停執行,next代表繼續執行。
yield和next可以傳參數,也可以有返回值
1 function * fn(arg){ 2 console.log(arg) 3 let x = yield 100 4 console.log(x) 5 return 200 6 } 7 8 var f = fn("hello") 9 let step1 = f.next(1) 10 console.log(step1) 11 let step2 = f.next(2) 12 // 輸出結果為 // hello // {value: 100, done: false} // 2 // {value: 200, done: true}
先看第八行 var f = fn("hello"),可以給Generator第一次執行傳參數"hello",等第一次調用next的時候就開始Generator第一次執行,這時arg的值就是"hello"
然后第九行let step1 = f.next(1),這里需要注意,第一次調用next(1)時傳的參數是作廢的,所以1不會被傳入到Generator函數中去,因為Generator函數第一次執行傳的參數是通過上面傳arg的方式傳的參數。所以調用next()開始Generator第一次執行(console.log(arg)),此時arg的值是‘hello’,所以首先打印‘hello’
然后第十行打印step1的返回值。其實next是有返回值的,返回值是一個對象,其中value屬性的值就是第一個yield返回的值100,done代表Generator時候執行完畢。
然后第十一行let step2 = f.next(2),這里的next(2)的參數可以傳到Generator函數中去,但是為什么把2傳給了x,估計很多人都想不明白,下面借鑒石川blue老師畫的一張圖來解釋一下:
看到黃色方框圈的let step1 = f.next(1),對應執行黃色曲線圈的那部分代碼,紅色方框圈的let step12= f.next(2)對應執行紅色曲線圈的部分代碼,不要管為什么,你只需要這么理解就ok了。所以第二次next(2)傳的參數2就傳給了x
然后再看第十二行console.log(step2),所以第二個next的返回值就是對象{value: 200, done: true},value表示最后Generator函數的返回值return 200,done表示Generator函數執行完畢。
(3) 適用場景這種Generator函數適用多個異步請求之間有邏輯分析的情況,比如有一個需求,先請求用戶數據,根據用戶數據的類型判斷用戶是普通用戶還是VIP用戶,然后再根據判斷結果請求普通商品數據或者VIP商品數據。
// 借助runner腳本,runner腳本規定Generator函數執行完一個next之后自動執行下一個next runner(function() * (){ let userData = yield $.ajax(...) // 請求用戶數據 if(userData.type === "vip") { let goods = yield $.ajax(...) // 請求vip商品數據 } else { let goods = yield $.ajax(...) // 請求普通商品數據 } })
使用Generator函數使得代碼看起來更像同步代碼,其實使用Promise同樣可以實現這種效果,只不過得需要在then()函數中嵌套請求。
9. async awaitasync其實就是對Generator的封裝,只不過async可以自動執行next()。
async function read () { let data1= await new Promise(resolve => { resolve("100") }) let data2 = await 200 return 300 }(1)async 返回值
async默認返回一個Promise,如果return不是一個Promise對象,就會被轉為立即resolve的Promise,可以在then函數中獲取返回值。
async必須等到里面所有的await執行完,async才開始return,返回的Promise狀態才改變。除非遇到return和錯誤。
async function fn () { await 100 await 200 return 300 } fn().then(res => { console.log9(res) // 300 })(3)await
await也是默認返回Promise對象,如果await后面不是一個Promise對象,就會轉為立即resolve的Promise
如果一個await后面的Promise如果為reject,那么整個async都會中斷執行,后面的awiat都不會執行,并且拋出錯誤,可以在async的catch中捕獲錯誤
async function f() { await Promise.reject("error"); await Promise.resolve("hello world"); // 不會執行 } f().then(res =>{ }).catch(err=>{ console.log(err) // error })
如果希望一個await失敗,后面的繼續執行,可以使用try...catch或者在await后面的Promise跟一個catch方法:
// try...catch async function f() { try { await Promise.reject("出錯了"); } catch(e) { } return await Promise.resolve("hello world"); } f() .then(v => console.log(v)) // hello world // catch async function f() { await Promise.reject("出錯了") .catch(e => console.log(e)); // 出錯了 return await Promise.resolve("hello world"); } f() .then(v => console.log(v)) // hello world
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/51939.html
摘要:的翻譯文檔由的維護很多人說,阮老師已經有一本關于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:感覺對我這種沒實習沒工作的新手,雖然一些高級的功能暫時用不上,但是一些基礎的知識還是為平時的開發提供了巨大的便利。學習告一段落,現在結合平時的開發,總結一些常用的知識。日常開發中,塊級作用域中使用的變量,盡量使用或者聲明。使用時,進行合并。 很早之前就學過TypeScript和ES6,后來做項目的時候零零散散用了些。這幾天又系統地把ES6的知識看了一遍。感覺對我這種沒實習沒工作的新手,...
摘要:常用知識總結之前總結了中的一些知識點。在年正式發布了,簡稱,又稱為。作為構造函數的語法糖,同時有屬性和屬性,因此同時存在兩條繼承鏈。子類的屬性,表示構造函數的繼承,總是指向父類。 ES6常用知識總結 之前總結了es5中js的一些知識點。這段時間看了石川blue老師講解的es6課程,結合阮一峰老師的es6教程,隨手做了一些筆記和總結分享給大家。內容還是es6主要的知識點,基本沒有什么創新...
摘要:常用知識總結之前總結了中的一些知識點。在年正式發布了,簡稱,又稱為。作為構造函數的語法糖,同時有屬性和屬性,因此同時存在兩條繼承鏈。子類的屬性,表示構造函數的繼承,總是指向父類。 ES6常用知識總結 之前總結了es5中js的一些知識點。這段時間看了石川blue老師講解的es6課程,結合阮一峰老師的es6教程,隨手做了一些筆記和總結分享給大家。內容還是es6主要的知識點,基本沒有什么創新...
閱讀 1951·2021-09-07 09:59
閱讀 2520·2019-08-29 16:33
閱讀 3688·2019-08-29 16:18
閱讀 2849·2019-08-29 15:30
閱讀 1678·2019-08-29 13:52
閱讀 2035·2019-08-26 18:36
閱讀 530·2019-08-26 12:19
閱讀 694·2019-08-23 15:23