let和const命令
let命令
- 循環體的let變量只對花括號作用域可見,花括號外不可見
- 循環體的語句部分是一個父作用域,而循環體內部是一個單獨的子作用域
- let聲明的變量不存在變量提升,未聲明的使用會報錯
- 只要塊級作用域內存在let聲明,它所聲明的變量就綁定了這個區域,不再受外部的影響
- let不允許在相同的作用域重復聲明同一個變量,子父級作用域可以同名變量聲明
const命令
- const常量的值一旦聲明就不得改變
- const一旦聲明變量,就必須立即初始化,不能留到以后賦值
- const的作用域與let命令相同,只在聲明所在的塊級作用域內有效
- const命令同樣存在暫時性死區,只能在聲明的位置后面使用
- const聲明的常量,也與let一樣不可重復聲明
- 對於復合類型的變量,變量名不指向數據,而是指向數據所在的地址,所以const命令只是保證變量名指向的地址不變,並不保證該地址的數據不變
——let和const命令的聲明不再自動納入global對象(window)
塊級作用域與函數聲明
- ES6的瀏覽器下,塊級作用域內聲明的函數會被提升至全局作用域或函數作用域頂部,作為
var fn = undefined - 其他的游覽器下,還是將塊級作用域的函數聲明當作let處理
- 應該避免在塊級作用域內聲明函數,如果確實需要也應該使用函數表達式而不是函數聲明
- ES6的塊級作用域允許聲明函數,但只在使用大括號的情況下成立,如果沒有使用大括號就會報錯
變量的解構賦值
數組的解構賦值
- 只要某種數據結構具有Iterator接口,都可以采用數組形式的解構賦值
- 解構賦值允許指定默認值,但是如果一個數組成員不嚴格等於undefined,默認值不會生效
- 如果默認值是一個表達式,那么這個表達式是惰性求值的,即只有在用到的時候才會求值
- 默認值可以引用解構賦值的其他變量,但該變量必須已經聲明
- 可以使用嵌套進行解構賦值
let [foo, [[bar], baz]] = [1, [[2], 3]];
對象的解構賦值
- 變量必須與屬性同名,才能取到正確的值
let { foo, bar } = { foo: "aaa", bar: "bbb" };
- 如果變量名與屬性名不一致,必須寫成
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
- 聲明后再進行的賦值必須加圓括號
let foo;——({foo} = {foo: 1});
- 對象的解構也可以指定默認值,同數組一樣成員對應必須嚴格等於undefined,默認值才會生效
字符串的解構賦值
- 字符串進行解構賦值時,會被轉換成一個類似數組的對象
const ### a, b, c, d, e = 'hello'
- 類似數組的對象都有一個length屬性,因此還可以對這個屬性解構賦值
let { length : len } = 'hello'
數值和布爾值的解構賦值
- 解構賦值的規則是,只要等號右邊的值不是對象或數組,就先將其轉為對象
let {toString: s} = 123; s === Number.prototype.toString // true
- 由於undefined和null無法轉為對象,所以對它們進行解構賦值,都會報錯
let { prop: x } = undefined; // TypeError
函數參數的解構賦值
- 函數參數的解構賦值遵循基本解構類型的特點
圓括號問題
- 不能使用圓括號的情況:
- a.變量聲明語句中,不能帶有圓括號
- b.函數參數中,模式不能帶有圓括號
- c.賦值語句中,不能將整個模式,或嵌套模式中的一層,放在圓括號之中
- 能使用圓括號的情況:
- a.賦值語句的非模式部分,可以使用圓括號
解構賦值的用途
- 交換變量的值
- 從函數返回多個值
- 函數參數的定義
- 提取JSON數據
- 函數參數的默認值
- 遍歷Map結構
- 輸入模塊的指定方法
字符串的擴展
- codePointAt()——處理4個字節儲存的字符,返回一個字符的碼點(默認十進制,十六進制可以使用toString方法轉換)
- String.fromCodePoint()——可以識別大於0xFFFF的字符,彌補了String.fromCharCode方法的不足
- ES6為字符串添加了遍歷器接口for...of,除了遍歷字符串,這個遍歷器最大的優點是可以識別大於0xFFFF的碼點
- at()提案——識別Unicode編號大於0xFFFF的字符,返回正確的字符
- normalize()——將字符的不同表示方法統一為同樣的形式,這稱為Unicode正規化
- 索引字符是否存在——接受二個參數,第一個表示索引字符,第二個表示起始位置
- includes()——返回布爾值,表示是否找到了參數字符串
- startsWith()——返回布爾值,表示參數字符串是否在源字符串的頭部
- endsWith()——返回布爾值,表示參數字符串是否在源字符串的尾部
- repeat()——將原字符串重復n次,返回一個新字符串
- a.參數如果是小數,會被取整
- b.參數是負數或Infinity,會報錯
- c.參數是0到-1之間的小數,則等同於0(這是因為會先進行取整運算)
- d.參數是字符串,則會先轉換成數字
- 字符串補全長度——接受兩個參數(第一個表示字符串補全的最小長度,第二個表示要參與補全的字符串)
- padStart()——用於頭部補全
- padEnd()——用於尾部補全
- a.如果原字符串的長度,等於或大於指定的最小長度,則返回原字符串
- b.如果用來補全的字符串與原字符串,兩者的長度之和超過了指定的最小長度,則會截去超出位數的補全字符串
- c.如果省略第二個參數,默認使用空格補全長度
- 用途:
- a.為數值補全指定位數
'1'.padStart(10, '0') - b.日期字符串格式化
'12'.padStart(10, 'YYYY-MM-DD')
- a.為數值補全指定位數
- 模板字符串
- a.模板字符串是增強版的字符串,用反引號()標識
- b.可以當作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量
- c.空格,縮進和換行的狀態都會被保持,除非使用.trim()去除
- d.模板字符串中嵌入變量,需要將變量名寫在${}之中,{}相當於js執行域,可以放置變量,表達式和函數(可調用)等
- e.如果大括號中的值不是字符串,將按照一般的規則轉為字符串;如果內部是一個字符串,將原樣輸出
- 標簽模版
- a.模版字符串前跟函數名,該函數將會被調用來處理該模版字符串,這被稱為"標簽模版"功能
- b.函數處理模版字符串的時候,會將沒有變量${}的部分拼合成數組參數,變量${}部分的結果作為后續參數
- c.函數處理模版字符串時,參數形式被轉換為(數組,參數1,參數2..),其中的數組項有一個raw屬性,與參數數組的項幾乎相同,唯一的區別是字符串里面的斜杠都被轉義了
- String.raw模版字符串
- a.用於處理模版字符串,返回一個斜杠都被轉義的字符串(如果原字符串斜杠已經轉義,String.raw不會做任何處理)
- b.也可以作為正常的函數使用,第一個參數應該是一個具有raw屬性的對象,且raw屬性的值應該是一個數組
- 模版字符串的限制
- a.默認會將字符串轉義,因此導致了無法嵌入其他語言
- b.解決提案——遇到不合法的字符串轉義,就返回undefined,而不是報錯,並且從raw屬性上面可以得到原始字符串
正則的擴展
數值的擴展
二進制和八進制表示法
- 二進制——0b(或0B)
- 八進制——0o(或0O)
新增api
- Number.isInteger()——是否為整數
- Number.EPSILON——極小常量,可以接受的誤差范圍
- Number.MAX_SAFE_INTEGER
- Number.MIN_SAFE_INTEGER——安全整數
- Number.isSafeInteger()——整數是否落在安全范圍(超出精度的結果會被自動設為界點值,所以驗證運算結果是否落在安全整數的范圍內,不要只驗證運算結果,而要同時驗證參與運算的每個值)
function trusty (left, right, result) { if (Number.isSafeInteger(left) && Number.isSafeInteger(right) && Number.isSafeInteger(result)) { return result; } throw new RangeError('Operation cannot be trusted!'); }
Math對象的拓展
- Math.trunc()——去除一個數的小數部分
- Math.sign()——判斷一個數是正數,負數,還是零
- Math.cbrt()——計算一個數的立方根
- Math.clz32()——一個數的32位無符號整數形式有多少個前導0
- Math.imul()——返回兩個數以32位帶符號整數形式相乘的結果(解決相乘數超過js精度的問題)
- Math.fround()——返回一個數的單精度浮點數形式(主要用於那些無法用64個二進制位精確表示的小數)
- Math.hypot()——返回所有參數的平方和的平方根
指數運算符
- 數值
**數值=結果——2**3=8
函數的擴展
函數參數的默認值
- 參數變量是默認聲明的,不能用let或const再次聲明(可用var,兩者獨立)
- 使用參數默認值時,不能有同名參數
- 參數默認值是惰性求值的——如果參數默認值是變量,那么參數實際值只有運行時才能確定
- 參數默認值可為解構賦值形式
function({a}={}){}
函數的length屬性
- 函數的length屬性,不包括rest參數和參數默認值
函數默認值作用域
- 一旦設置了參數默認值,函數進行聲明初始化時,參數會形成一個單獨的作用域
- 參數默認值形成的作用域與函數體內部的作用域不屬於同一個作用域,后者優先級大於前者
rest參數
- 獲取函數的多余參數,在參數中展現為數組
- rest只能作為最后一個,它之后不能再有其他參數
擴展運算符
- 將一個數組轉為用逗號分隔的參數序列——可用於偽數組(arguments,nodelist)
- 如果將擴展運算符用於數組的解構賦值,只能放在參數的最后一位,否則會報錯
- 任何實現了Iterator接口的對象,都可以用擴展運算符轉為真正的數組——### ...偽數組
- 擴展運算符在處理字符串(Iterator接口)時,除能將其轉換為數組還能識別32位的Unicode字符
嚴格模式
- 函數參數使用了默認值、解構賦值、或者擴展運算符,那么函數內部就不能顯式設定為嚴格模式,否則會報錯
- 解決辦法——在函數體外設置嚴格模式
name屬性
- 函數表達式下,es5的name取值為空字符串,es6為變量名
- Function構造函數返回的函數實例,name屬性的值為anonymous
- bind返回的函數,name屬性值會加上bound前綴+函數名
- 用一個Symbol值的變量去定義方法,name屬性返回Symbol值的描述
箭頭函數
- 函數體內的this對象,就是定義生效時所在的對象,而不是使用時所在的對象
- 不可以當作構造函數的new命令,因為箭頭函數內部沒有this,只能獲取到外層this
- 不可以使用arguments對象,該對象在函數體內不存在,可用rest參數代替
- 不可以使用yield命令,因此箭頭函數不能用作Generator函數
- super、new.target在箭頭函數中同樣不存在,因為不存在內部this,所以用call()、apply()、bind()去改變函數this指向的操作無效
綁定this
- call、apply、bind的替代品——對象::函數,返回原對象
- 該運算符會自動將左邊的對象,作為上下文環境(即this對象),綁定到右邊的函數上面
尾調用優化
- 當尾調用函數內部不依賴於外層變量作用域的時候,函數執行時調用幀就只有一項,這將大大節省內存
- ES6的尾調用優化只在嚴格模式下開啟,正常模式是無效的
尾遞歸優化
- 對於尾遞歸來說,由於只存在一個調用幀,所以永遠不會發生“棧溢出”錯誤
- 尾遞歸的單次調用幀降低了算法復雜度,減輕了運算壓力
- 尾遞歸應該是單項的,對於多項尾遞歸同時進行同樣會增加調用幀,造成“棧溢出”
- 解決尾遞歸調用棧太多的辦法是采用"循環"換掉"遞歸",在循環中每一步返回另一個函數並執行
函數參數的尾逗號
- 函數參數定義和調用時的參數書寫都可以在參數尾部添加多於的逗號
數組的擴展
靜態方法
- Array.from——用於將類似數組的對象和可遍歷的對象(帶iterable句柄)轉換為真正的數組
- a.接受3個參數——Array.from(對象,過濾函數,上下文)
- b.任何有length屬性的對象,都可以通過Array.from方法轉為數組,擴展運算符無法轉做到
- c.能正確處理各種Unicode字符,可以避免JavaScript將大於\uFFFF的Unicode字符,算作兩個字符的bug
- Array.of——用於將一組值,轉換為數組
- a.彌補用數組構造函數生成數組的差異性
- b.Array(3) ### '','',''與Array(3, 11, 8) ### 3, 11, 8
實例方法
- copyWithin()
在當前數組內部,將指定位置的成員復制到其他位置(覆蓋該位置),返回當前數組(開始替換的位置,開始復制的位置,結束復制的位置) - fill()
填充和替換(值,起始位置,結束為止) - includes(值,位置)
是否包含給定值(不像indexOf方法一樣采用全等於進行比較) - find(條件函數)
查找符合條件的值並返回 - findIndex(條件函數)
查找符合條件的值並返回位置 - entries()
鍵值對遍歷器(iterator) - keys()
鍵名遍歷器(iterator) - values()
鍵值遍歷器(iterator)
數組的空位
- Array(2)——['','']
- es6的數組遍歷方法都會跳過空位,map也會跳過,但是會保留空位
- join()和toString()會將空位視為undefined,而undefined和null會被處理成空字符串
- es6新增方法會默認將空位轉換為undefined,for...of循環則會遍歷空位
對象的拓展
屬性的簡潔表示法
- 允許在對象中直接寫入變量,變量名作為鍵,變量值作為值
- 允許對象定義中的方法簡寫(省卻:和function)
屬性名表達式
- 允許在對象字面量定義中,[表達式]作為鍵的寫法
- 如果屬性名表達式的鍵是一個對象變量,那它會自動轉換為"[object Object]"名
新增api
- Object.is(值1,值2)
是否嚴格相等 - Object.assign(target,source1,source2..)
對象淺拷貝- a.如果target傳遞undefind和null會報錯
- b.除引用類型外,source只接受字符串,其余忽略
- c.屬性名為Symbol值的屬性也會被Object.assign拷貝
- d.對於嵌套形式的對象形式,source會覆蓋整個鍵名對應的對象
- f.處理數組時會把數組視為對象,通過對應數組下標進行屬性拷貝和覆蓋
- e.無法正確拷貝get屬性和set屬性(undefined)
- Object.setPrototypeOf(obj,prototype)
為對象設置原型 - Object.getPrototypeOf(obj)
返回對象的原型 - Object.getOwnPropertySymbols()
返回Symbol屬性鍵數組 - Object.keys()
返回鍵數組(可枚舉) - Object.values()
返回值數組(可枚舉) - Object.entries()
返回鍵值對數組 - Object.getOwnPropertyDescriptors()
返回指定對象所有自身屬性的描述對象
對象的擴展運算符
- 在解構賦值下,"...obj"等於剩余對象集
- 在解構賦值下,"...obj"必須處於最后一位
- 在對象使用中,"...obj"將對象拆散成單個鍵值對放入{}(可用於淺拷貝和對象合並)
- 在對象使用中,如果出現同名鍵值對,后面會覆蓋前面的(適用於對象的擴展運算符)
- 在對象使用中,如果擴展運算符代表的對象鍵值對中有get取值函數,這個函數會執行
Null傳導運算符·提案
- 通過符號"?."簡化邏輯與,簡化對象判斷
const firstName = (message&&message.body&&message.body.user&&message.body.user.firstName) || 'default'const firstName = message?.body?.user?.firstName || 'default'
Symbol
使用注意
- 一種新的原始數據類型,表示獨一無二的值
- 可以接受一個字符串參數,用於Symbol值的描述區分
Symbol("xjh") - 不能用new操作符,也不能參與運算,類似於字符串
- 對象操作必須用中括號表示,用點運算符賦值無效——用Symbol值直接定義或賦值會轉換為字符串鍵
- 如果Symbol()的參數是一個對象,就會調用該對象的toString方法將其轉為字符串,然后才生成一個Symbol值
- Symbol值可以強制類型轉換為字符串,布爾值,數組和對象,但是不能轉換為數
相關api
- Symbol.for("name")
- a.如果存在登記為name的symbol值就取到,否則就創建
- b.如果存在登記為name的symbol值,重復的調用只會得到同一個symbol值
- c.Symbol.for創建的symbol值是全局的,iframe生成的可以在主頁獲取到
- Symbol.keyFor("name")
- a.返回已登記的Symbol類型值的key
- b.Symbol("name")創建的不屬於登記返回,無法返回key
內置Symbol
- Symbol.hasInstance
- a.等同於instancsof,判斷是否為該對象的實例時,會調用這個方法
- b.foo instanceof Foo在語言內部,實際調用的是Foo[Symbol.hasInstance(foo)]
- Symbol.isConcatSpreadable
- a.等於一個布爾值,表示該對象使用Array.prototype.concat()時,是否可以展開
- b.數組的默認行為是可以展開的,Symbol.isConcatSpreadable屬性等於true或undefined,都有這個效果
- c.類似數組的對象也可以展開,但它的Symbol.isConcatSpreadable屬性默認為false,必須手動打開
- Symbol.species
- a.指向當前對象的構造函數,創造實例時,默認會調用這個方法
- b.定義Symbol.species屬性要采用get讀取器,默認讀取this
- Symbol.match
- a.返回一個執行正則的match函數
- b.當執行str.match(obj)時,如果obj中存在該屬性,則在會調用它
- Symbol.replace
- a.返回一個執行替換的replace函數
- b.當執行str.replace(obj,"World")時,如果obj中存在該屬性,則在會調用它
- Symbol.search
- a.返回一個執行查找的search函數
- b.當執行str.search(obj)時,如果obj中存在該屬性,則在會調用它
- Symbol.split
- a.返回一個執行查找的search函數
- b.當執行str.split(obj)時,如果obj中存在該屬性,則在會調用它
- Symbol.iterator
- a.指向當前對象默認的遍歷器方法
- b.對象進行for...of循環時,會調用Symbol.iterator方法
- Symbol.toPrimitive
- a.返回將對象轉為原始類型值的方法
- b.Symbol.toPrimitive被調用時,會接受一個字符串參數,表示當前運算的模式
- Number:該場合需要轉成數值
- tring:該場合需要轉成字符串
- Default:該場合可以轉成數值,也可以轉成字符串
- Symbol.toStringTag
- a.返回一個類型字符串表示的函數
- b.當執行Object.prototype.toString時,如果obj中存在該屬性,則在會調用它
- Symbol.unscopables
- a.指向一個對象,指定了使用with關鍵字時,哪些屬性會被with環境排除
- b.被它指定的屬性和方法將在with作用域中被忽略
Set和Map數據結構
Set
- 一種類似於數組的新數據結構,成員的值都是唯一的,不存在重復
- Set的構造函數接受數組(或具有iterable接口的其他數據結構)作為參數
- Set的值是跟內存地址綁定的,只要內存地址不一樣,就視為兩個值
- Set的實例默認可遍歷,它的默認遍歷器生成函數就是values方法
- Set的遍歷順序就是插入順序,keys方法和values方法的行為完全一致
- 實例屬性和方法
- size 返回成員個數
- add() 添加某個值(返回Set實例)
- delete() 刪除某個值(返回布爾值)
- has() 返回布爾值
- clear() 清除所有成員(沒有返回值)
- keys() 返回鍵名遍歷器
- values() 返回鍵值遍歷器
- entries() 返回鍵值對遍歷器
- forEach() 帶回調函數的遍歷方法
WeakSet
- WeakSet結構與Set類似,也是不重復的值的集合,沒有size和length屬性
- 構造函數的參數也只能接受數組或類似數組,但其成員必須為對象
- WeakSet中的對象都是弱引用,其指向的對象不計入垃圾回收機制
- WeakSet用於存儲DOM節點時,如果節點從文檔移除,會自動進行垃圾回收
- 實例屬性和方法
- add() 添加對象(返回實例)
- delete() 刪除某個值(返回布爾值)
- has() 返回布爾值
Map
- 一種類似於對象的新數據結構,但是鍵的范圍不限於字符串,各種類型的值都可以當作鍵
- 構造函數接受數組(或具有iterable接口的其他數據結構)作為參數,數組項為代表鍵值項的數組(### "a",1)
- Map的鍵上是跟內存地址綁定的,只要內存地址不一樣,就視為兩個鍵
- 實例屬性和方法
- size 返回成員個數
- set 添加鍵值對(返回Set實例)
- get 返回值(無則undefined)
- has() 返回布爾值
- delete() 刪除某個值(返回布爾值)
- clear() 清除所有成員(沒有返回值)
- keys() 返回鍵名的遍歷器
- values() 返回鍵值的遍歷器
- entries() 返回所有成員的遍歷器
- forEach() 遍歷Map的所有成員
WeakMap
- WeakMap結構與Map結構類似,也是用於生成鍵值對的集合,沒有size和length屬性
- WeakMap只接受對象作為鍵名(null除外),不接受其他類型的值作為鍵名
- WeakMap的鍵名都是弱引用,鍵名所指向的對象不計入垃圾回收機制
- WeakMap用於儲存dom節點的臨時數據時,如果節點從文檔移除,會自動進行垃圾回收
- 實例屬性和方法
- get() 得到對象
- set() 添加對象(返回實例)
- delete() 刪除某個值(返回布爾值)
- has() 返回布爾值
Proxy
概述
- Proxy用於修改某些操作的默認行為,等同於在語言層面做出修改,屬於一種“元編程”
- new Proxy(target, handler)接收2個參數,target代表目標對象,handler代表參數對象,用於定制行為
- 如果handler沒有設置任何攔截,那就等同於直接指向原對象
- 如果一個屬性不可配置和不可寫,則該屬性不能被代理,通過Proxy對象操作該屬性會報錯
實例方法
- get()
- set()
- apply()
- has()——對象是否具有某個屬性
- a.對for...in無效
- construct()——針對new命令
- a.方法返回的必須是一個對象,否則會報錯
- deleteProperty()——delete操作
- a.方法返回false,屬性就無法被delete刪除
- defineProperty()
- getOwnPropertyDescriptor()
- getPrototypeOf()
- a.getPrototypeOf方法的返回值必須是對象或者null,否則會報錯
- isExtensible()——是否鎖定[不可拓展屬性]
- a.該方法只能返回布爾值,否則返回值會被自動轉為布爾值
- b.proxy(攔截函數返回值)與target的Object.isExtensible()結果必須一致,否則報錯
- ownKeys()——對象自身屬性的讀取操作
- a.主要攔截Object.keys(),Object.getOwnPropertyNames()和Object.getOwnPropertySymbols()函數
- b.ownKeys方法返回的數組成員,只能是字符串,否則會報錯
- c.攔截Object.keys時,有三類屬性會被ownKeys方法自動過濾——不存在的屬性,不可遍歷的屬性和Symbol值屬性
- d.如果目標對象是不可擴展的,ownKeys返回的數組之中必須包含原對象的所有屬性,且不能包含多余的屬性,否則報錯
- preventExtensions()——鎖定操作
- a.該方法只能返回布爾值,否則返回值會被自動轉為布爾值
- b.只有目標對象不可擴展時,返回值才能為true,否則會報錯
- setPrototypeOf()——設置原型屬性
- a.該方法只能返回布爾值,否則返回值會被自動轉為布爾值
- b.如果目標對象不可擴展,則setPrototypeOf方法不得改變目標對象的原型
靜態方法
- Proxy.revocable()
- a.返回一個可取消的實例
-
b.執行實例的revoke方法后,proxy實例不可再訪問,否則會報錯
this問題
- 在Proxy代理的情況下,目標對象內部的this關鍵字會指向proxy實例
- 有些原生對象的內部屬性,只有通過正確的this才能拿到,所以Proxy也無法代理這些原生對象的屬性
Reflect
概述
- 將Object對象的一些明顯屬於語言內部的方法放到Reflect對象上
- 現階段,某些方法同時在Object和Reflect對象上部署,未來的新方法將只部署在Reflect對象上
- 修改某些Object方法的返回結果,讓其變得更合理
- Object.defineProperty在無法定義屬性時,會拋出一個錯誤,而Reflect.defineProperty則會返回false
- 讓之前是命令式的Object操作行為變成函數行為
"a" in obj,delet obj['a']變成Reflect.has(obj, name),Reflect.deleteProperty(obj, name)
-
Reflect對象的方法與Proxy對象的方法一一對應,只要是Proxy對象的方法,就能在Reflect對象上找到對應的方法
靜態方法
- get()
- set()
- a.如果Proxy對象和Reflect對象聯合使用,通過proxy對Reflect傳入context會觸發proxy的defineProperty攔截
- has()
- deleteProperty()
- a.如果刪除成功或者被刪除的屬性不存在,返回true;刪除失敗,返回false
- construct()
- a.等同於new target(...args),提供了一種不使用new,來調用構造函數的方法
- getPrototypeOf()
- a.如果參數不是對象,Object.getPrototypeOf會將這個參數轉為對象,然后再運行,而Reflect.getPrototypeOf會報錯
- setPrototypeOf()
- a.用於設置對象的__proto__屬性,返回第一個參數對象
- b.如果第一個參數不是對象,Object.setPrototypeOf會返回這個參數,而Reflect.setPrototypeOf會報錯
- **apply()
- defineProperty()
- getOwnPropertyDescriptor()
- a.如果第一個參數不是對象,Object.getOwnPropertyDescriptor會返回undefined,而Reflect.getOwnPropertyDescriptor會拋出錯誤
- isExtensible()
- a.對象是否可以拓展
- b.如果參數不是對象,Object.isExtensible會返回false,因為非對象本來就是不可擴展的,而Reflect.isExtensible會報錯
- preventExtensions()
- a.設置對象為不可拓展
- b.如果參數不是對象,Object.preventExtensions在es5環境報錯,在es6環境返回傳入的參數,而Reflect.preventExtensions會報錯
- ownKeys()
- a.返回對象的所有屬性(可枚舉和不可枚舉,可讀和不可讀)
Promise對象
Promise含義
- Promise是一個可以獲取異步操作消息的對象,它提供了統一的API,使得各種異步操作都可以用同樣的方法進行處理
- Promise有三種狀態Pending,Resolved和Rejected,只有異步操作的結果,可以決定當前是哪一種狀態
- Promise對象的狀態不受外界影響,一旦狀態改變,就不會再變,任何時候得到的都是這個結果
- Promise實例之間進行傳遞的時候,被傳遞實例會等待傳遞實例的狀態改變后才進行回調狀態操作
- 優點:
- a.可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數
- b.統一的接口使得控制異步操作更加容易
- 缺點:
- a.無法取消Promise,一旦新建它就會立即執行,無法中途取消
- b.如果不設置回調函數,Promise內部拋出的錯誤不會反應到外部
- c.當處於Pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)
基本用法
var promise = new Promise(function(resolve, reject) { if (true){ resolve(value); } else { reject(error); } }); promise.then(function(value) { // success }, function(error) { // failure });
Promise.prototype.then
- then方法會默認返回一個新Promise實例,因此可以進行鏈式操作
- then方法主動return的值會作為下一個then方法的參數
- then方法主動return的new Promise實例會被加入異步堆棧,只有其狀態改變才會執行其鏈式的then回調
Promise.prototype.catch
- Promise.prototype.catch方法是.then(null,Rejected)的別名,用於指定發生錯誤時的回調函數
- 如果異步操作拋出錯誤,狀態就會變為Rejected,就會調用catch方法指定的回調函數
- then方法指定的回調函數,如果運行中拋出錯誤,也會被catch方法捕獲
- 在Promise構造函數回調中直接調用Rejected方法會觸發catch方法
- catch方法返回的還是一個Promise對象,因此后面還可以接着調用then方法
- catch方法之中,還能再拋出錯誤,當還存在下一個catch的時候就會捕獲並執行
Promise.all
- 用於將多個Promise實例,包裝成一個新的Promise實例
- Promise.all方法接受一個數組作為參數
- a.如果數組由Promise實例組成,則會等待其中的Promise都完成時才會觸發Promise.all實例的狀態變化
- b.如果數組不由Promise實例組成,就會直接調用Promise.resolve方法,將參數轉為Promise實例,再進一步處理
- 只有p1、p2、p3的狀態都完成,組合p的狀態才會完成
- 只要p1、p2、p3之中有一個被rejected,組合p的狀態就變成rejected(此時第一個被reject的實例返回值會傳遞給p的回調函數)
Promise.race
- 同上
- 只要有一個Promise參數實例完成,就會調用Promise.race實例的狀態變化,將率先完成的子Promise參數傳遞給Promise.race回調
Promise.resolve
- 將現有對象轉為Promise對象
- a.當參數為Promise對象時,會原封不動返回該對象
- b.當參數為帶"then"鍵名方法的對象時,會將這個對象轉為Promise對象,然后就立即執行該對象的then方法
- c.當參數為非帶"then"鍵名方法的對象時,Promise.resolve方法返回一個新的Promise對象,狀態為Resolved
- d.不帶參數時,Promise.resolve方法直接返回一個新的Promise對象,狀態為Resolved
Promise.reject
- 返回一個新的Promise實例,狀態為rejected,參數為錯誤信息
- Promise.reject()方法的參數,會原封不動地作為reject或catch的回調參數
Promise.try提案
- 對於那種可能是同步可能是異步的返回操作提供統一的處理方式,動態執行對應的同步/異步狀態
- database.users.get({id: userId})有可能報同步錯誤,有可能報異步錯誤
Promise.try(database.users.get({id: userId})).then(...).catch(...)
Iterator和for...of循環
Iterator的作用
- 為各種數據結構,提供一個統一的、簡便的訪問接口(for...of)
- 使得數據結構的成員能夠按某種次序排列
- 當使用for...of循環遍歷某種數據結構時,該循環會自動去尋找Iterator接口
默認Iterator接口
- 部署了Symbol.iterator屬性的數據結構,就稱為部署了遍歷器接口
- 原生具備Iterator接口的數據結構有:Array,Map,Set,String,TypedArray和函數的arguments對象
調用場合
- 解構賦值,擴展運算符,yield*,for..of
遍歷器對象的return和throw方法
- return方法
- 調用場景——如果for...of循環提前退出(通常是因為出錯,或者有break語句或continue語句)
- 部署場景——如果一個對象在完成遍歷前,需要清理或釋放資源,就可以部署return方法
- throw方法
- 主要是配合Generator函數使用,一般的遍歷器對象用不到這個方法
for...of循環
- 一個數據結構只要部署了Symbol.iterator屬性,就被視為具有iterator接口,就可以用for...of循環遍歷它的成員
- for...of循環內部調用的就是數據結構的Symbol.iterator方法
- 擁有iterator接口的數據結構——字符串,數組,類數組(arguments和DOM NodeList),Generator對象
- for...of更常用於數組循環,for...in更常用於對象循環
Generator函數的語法
基本概念
- 語法上,function關鍵字與函數名之間有一個星號*,函數體內部使用yield表達式
- Generator屬於普通函數,調用不會立即執行,而是返回一個遍歷器對象,需要調用next()才能執行yield狀態
- Generator函數就是遍歷器生成函數,因此可以把Generator賦值給對象的Symbol.iterator屬性,從而使得該對象具有Iterator接口,可以被for...of循環和擴展運算符轉換
yield表達式
- yield表達式如果用在一個表達式中,必須放在圓括號里面;如果用作函數參數或放在賦值表達式右邊,可以不加括號
- yield表達式本身沒有返回值,總是返回undefined;
- next方法可以帶一個參數,該參數就會被當作上一個yield表達式的返回值
- 由於next方法的參數表示上一個yield表達式的返回值,所以在第一次使用next方法時,傳遞參數是無效的
Generator.prototype.throw()
- Generator函數返回的遍歷器對象,都有一個throw方法,可以在函數體外拋出錯誤,然后在Generator函數體內捕獲
- throw方法可以接受一個參數,該參數會被catch語句接收,建議拋出Error對象的實例
- 一旦Generator執行過程中拋出錯誤,且沒有被內部捕獲,就不會再執行下去了,Generator函數默認結束
Generator.prototype.return()
- 調用return方法后會終結Generator函數,返回值的value屬性就是return方法的參數,沒有即為undefined
- 如果Generator函數內部有try...finally代碼塊,那么return方法會推遲到finally代碼塊執行完再執行
yield* 表達式
- yield* obj,如果obj是遍歷器對象,將會遍歷該對象的yield,增加步長
- 任何數據結構只要有Iterator接口,就可以被yield*遍歷
Generator函數的異步應用
- Generator函數將異步操作表示得很簡潔,但是流程管理卻不方便
- 解決方案
- Thunk函數
- a.js版本的Thunk函數方案是將多參數函數轉換為單參數函數版本
- b.可引入node模塊,也可以自己書寫,用於管理Generator函數流程
- co模塊
- a.js版本的co函數方案是對promise的包裝
- Thunk函數
async函數
- Generator函數的語法糖
- 改進特點
- a.內置執行器 ——自動執行完,無需寫thunk和co自執行方案
- b.更好的語義
- c.更廣的適用性 ——異步等待執行,同步直接執行
- d.返回promise ——可用then方法指定下一步操作
- 基本語法
- a.async函數返回一個Promise對象,可以使用then方法添加回調函數
- b.async函數的return值會成為返回的Promise對象的值,then方法的參數
- c.遇到await就會等待異步/同步操作完成,然后接着執行函數體
- d.await命令后是一個Promise對象,如果不是則會被轉成一個立即resolve的Promise對象
- 錯誤處理
- a.async函數內拋出錯誤,會導致返回的promise對象變為reject狀態
- b.只要一個await語句后面的Promise變為reject,那么整個async函數都會中斷執行,錯誤信息會傳入catch方法
- c.如果異步操作失敗,卻不希望中斷后續異步操作,方法有:
- 1).使用try...catch語句,將await放入try,catch捕捉后會繼續執行后續代碼
- 2).對await后的promise對象增添catch方法進行錯誤捕捉,然后程序會繼續執行后續代碼
- 異步遍歷器(提案)
- a.異步遍歷器的最大的語法特點就是,用遍歷器的next方法,能返回一個Promise對象
- b.對象的異步遍歷器接口,部署在Symbol.asyncIterator屬性上面
- c.next方法可以連續調用,不必等到上一步Promise對象resolve以后再調用;next方法會累積起來,自動按照每一步的順序運行下去
- for await...of(提案)
- a.for await...of循環的一個用途,是部署了asyncIterable操作的異步接口,可以直接放入這個循環
- b.for...of自動調用遍歷器的next方法,得到一個Promise對象;await用來處理這個Promise對象,一旦resolve,就把得到的值傳入循環體
- c.for await...of循環也可以用於同步遍歷器
- 異步Generator函數(提案)
- a.async函數與Generator函數的結合,await后面的操作會返回Promise對象
- b.普通的async函數返回的是一個Promise對象,而異步Generator函數返回的是一個異步Iterator對象,通過調用next方法來返回可操作的Promise對象
- c.yield *后面同樣可以繼續跟異步Generator函數
Class
Class基本語法
- 類的數據類型就是函數,類本身指向構造函數
- 類內部所有定義的方法都是不可枚舉的
- 類本身和內部的屬性方法可以采用變量來聲明和表示
- 不使用new的類調用會報錯
- 當constructor未被顯示添加,空的constructor會被默認添加
- class聲明不存在變量提升
- 采用class表達式,可以寫出立即執行的class
- 類和模塊的內部,默認就是嚴格模式
- class的get和set函數也定義在原型上
Class的靜態方法
- 父類的靜態方法,可以被子類繼承——子類調用父類靜態方法
- 子類也可以通過super,在靜態方法中調用父類的靜態方法
Class的靜態屬性和實例屬性
[es6用法]
// 實例屬性 class MyClass { constructor() { this.myProp=42; } } // 靜態屬性 MyClass.屬性=值; [es7提案] // 實例屬性——實例可以取到 class MyClass { myProp=42; constructor() { console.log(this.myProp); // 42 } } // 靜態屬性 class MyClass { static myProp=42; constructor() { console.log(MyClass.myProp); // 42 } }
class的私有屬性
- es7提案
- a.私有屬性用#表示,也用於表示私有方法,在類的外包無法訪問
- b.私有屬性可以指定初始值,在構造函數執行時進行初始化
class Foo { #a; #b; #c=0; #sum() { return #a + #b; } printSum() { console.log(#sum()); } constructor(a, b) { #a = a; #b = b; } }
new.target屬性
- 返回new命令作用的那個構造函數,如果是class內部調用則返回當前class
- new.target只適用於構造函數或class內部的constructor方法
- 如果構造函數不是通過new命令調用的,則new.target會返回undefined
- 可以用來確定構造函數是怎么調用的,也可以用其做不可實例化的抽象類
Class的繼承
Class繼承
- 基本用法
- 1.存在繼承關系后,constructor內必須執行super()操作,否則會報無this的錯誤
- 2.子類實例的構建是基於對父類實例加工,super()相當於對子類進行父類.call(this)
- 3.super返回父類實例后,植入子類原型屬性和constructor,然后再接入到子類原型上
- super關鍵字
- 1.super用作函數時,必須用在constructor之內,否則會報錯
- 2.super用作對象時,在普通方法中指向父類原型對象,在靜態方法中指向父類
- 3.通過super調用父類的方法時,相當於父級原型調用該方法,但是super會綁定子類的this
- 4.通過super對某個屬性賦值時,因為super綁定了子類的this,因而會賦值到子類屬性上,但是調用時依然會在父級原型查找
- 5.super並不是動態綁定的,而是在聲明時“靜態”綁定的
- 原生構造函數的繼承
- 1.es5之前原生構造函數無法用this去綁定,導致拿不到其內部實例屬性,無法實現真正繼承
- 2.es6通過extends繼承可以自定義原生數據結構,實現子類的真正繼承和拓展能力
3.super傳參對Object原生類型無效,es6規定Object構造函數會忽略參數
Decorator
基本語法
- 書寫上,置於要修改的類和方法之上
- 只能用於類和類的方法,不能用於函數,因為存在函數提升
- 不管是修飾類還是修飾方法,都支持多個修飾器
- 修飾器對行為的改變,發生在編譯器,而不是運行時,其本質是編譯時執行函數
類的修飾
- 當用於修飾類的時候,它的第一個參數代表所要修飾的目標類
方法的修飾
- 修飾器不僅可以修飾類,還可以修飾類的方法
- 修飾方法的時候,接受三個參數(target, name, descriptor)
- 當多個修飾器一起用時,遵循先從外到內進入,然后由內向外執行
Module的語法
export命令
- export命令規定的是對外的接口,因此必須與模塊內部的變量建立一一對應關系
export { 變量名 } - export語句輸出的接口,與其對應的值是動態綁定關系,即通過該接口,可以取到模塊內部實時的值一一定時器動態改變值的情況
- export命令可以出現在模塊的任何位置,只要處於模塊頂層就可以,如果處於塊級作用域內就會報錯一一import命令同樣如此
import命令
- import命令具有提升效果,會提升到整個模塊的頭部首先執行,因為import命令是屬於編譯階段執行
- 由於import是靜態執行,所以不能使用表達式和變量這些只有在運行時才能得到結果的語法結構
- import語句會執行所加載的模塊,因此可以有如下的寫法
import 'a'; import '1';
- 多次重復執行同一句import語句,那么只會執行一次,而不會執行多次
import { foo } from 'my_module'; import { bar } from 'my_module';
模塊的整體加載
import * as 模塊名 from './文件名';
export default命令
- 為模塊指定默認輸出時,import命令可以為模塊指定任意名字,且不需要用{}括號包起來
- 模塊內部的聲明函數在外部是無效的,加載的時候視同為匿名函數進行加載
- 一個模塊只能有一個默認輸出
- export default本質上就是一個叫做default的變量或方法,因此可以用as語句進行改名
var a = 1; export default a;——將變量a的值賦給變量default,因此export default 1也是可以的- 同時輸入默認方法和其他變量
import abc,{ each } from 'lodash'
export與import的復合寫法
如果在一個模塊之中,先輸入后輸出同一個模塊,import語句可以與export語句寫在一起
- 寫法1·默認用法
export { foo, bar } from 'my_module'; //等同於 import { foo, bar } from 'my_module'; export { foo, bar };
- 寫法2·整體輸出
export * from 'my_module'; ——會忽略my_module模塊的default
- 寫法3·默認接口
export { default } from 'foo';
- 寫法4·接口改名
export { foo as myFoo } from 'my_module';
- 寫法5·具名接口改為默認接口
export { es6 as default } from './someModule';
- 寫法6·默認接口改為具名接口
export { default as es6 } from './someModule';
import()提案
- 屬於運行時執行的動態加載,區別於import的靜態加載
- import()函數可以用在任何地方,不僅僅是模塊,非模塊的腳本也可以使用
- import()函數與所加載的模塊沒有靜態連接關系,這點也是與import語句不相同
- import()類似於Node的require方法,區別主要是前者是異步加載,后者是同步加載
- import()返回一個Promise對象,並允許模塊路徑動態生成——import(f()).then(...)
Module的加載實現
游覽器加載
- script標簽中defer和async的區別——defer是渲染完再執行,async是下載完就執行——即不能保證執行順序
- 瀏覽器加載ES6模塊也使用
<script>標簽,但要加入type="module"屬性,效果等同於defer- a.代碼運行在模塊作用域,頂層變量對外不可見
- b.默認采用嚴格模式,不管有無"use strict"
- c.模塊之中,import和export指令對應模塊時,.js后綴不能省略
- d.模塊頂層this為undefined
es6模塊與commonjs模塊的差異
- commonjs模塊輸出的是一個值的拷貝,es6模塊輸出的是值的引用
- commonjs模塊是運行時加載,es6模塊是編譯時輸出接口
- commonjs頂層this指向當前模塊,es6頂層this指向undefined
- es6模塊是動態引用,不會緩存值,模塊里面的變量綁定其所在的模塊,意味着可以獲取模塊的動態變化
- es6輸入的模塊變量,只是一個“符號連接”,屬於只讀的,對它進行重新賦值會報錯
- export通過接口輸出的是同一個值,因此不同的腳本加載這個接口,得到的都是同樣的實例
node加載
- node中采用兩套方案進行加載,es6模塊和commonjs采用各自的加載方案
- 如果不輸出任何接口,但希望被node認為是es6模塊,可以在腳本中寫"export {}"
import加載commonjs模塊
- import加載commonjs模塊,node會自動將module.exports屬性當作模塊的默認輸出,即等同於export default
- import加載commonjs模塊時,commonjs模塊的輸出緩存機制依然有效,被引入模塊內部的變化不會更新到引入模塊
- import {readfile} from 'fs'報錯
原因——fs是commonjs格式,只有在運行時才能確定readfile接口,而import命令要求編譯時就確定這個接口
解決辦法——改為整體輸入
require加載es6模塊
- 采用require命令加載es6模塊時,es6模塊的所有輸出接口會成為輸入對象的屬性
- require加載es6模塊依然存在緩存機制,被引入模塊內部的變化不會更新到引入模塊
循環加載·commonjs
- commonjs的重要特性就是加載時執行,即腳本代碼在require的時候就會執行,然后在內存生成一個對象
- commonjs模塊無論加載多少次,都只會在第一次加載時運行一次,以后再執行加載,都只會到緩存中取值,返回第一次運行結果
循環加載·es6
- es6模塊是動態引用,如果使用import從一個模塊加載變量(即import foo from 'foo'),那些變量不會被緩存,而是成為一個
指向被加載模塊的引用,意味着可以取到值得變化
