Es6學習筆記
一、let和const
1. let,const不存在變量提升,只在聲明所在的塊級作用域中有效。
var tmp = 123
if(true)
{
let tmp = 'abc'
console.log(tmp)
}
console.log(tmp)
2. Es5只有全局作用域和函數作用域,Es6添加了塊級作用域。
3. 考慮到環(huán)境導致的行為差異太大,應該避免在塊級作用域內聲明函數。如果確實需要,也應該寫成函數表達式,而不是函數聲明語句。
4. 對于復合類型的變量,變量指向數據所在的地址,不保證該地址的數據不變,所以在將對象聲明為常量時必須特別小心。
const foo = {name:'zhangsan'}
foo.age = 18
console.log(foo) // Object { name: "zhangsan", age: 18 }
5.let, const, class定義的全局變量與頂層對象脫鉤
二、變量的解析賦值
1. 對象的解析賦值的內部機制,是先找到同名屬性,然后再賦給對應的變量。真正被賦值的是后者,而不是前者。如:
var {foo:bar, bar:foo} = {foo:'hello', bar:'world'}
console.log(foo, bar) // 輸出world hello
解析過程:先找到變量foo,其值為bar,將后面bar對應的值'world'賦給了foo。
2. 解析也可用于嵌套結構的對象,如
var obj = {p: ['hello', {y: 'world'}]}
var {p:[x, {y}]} = obj
console.log(x, y) // hello world
注意這里的p是模式,不是變量,因此不會被賦值
let node = {loc: {start: {line: 1, column: 5}}}
let {loc: { start: {line, column}}} = node
console.log(line, column) // 1, 5
這里loc和start都是模式,不會被賦值
3. 對象的解析賦值,可以很方便的將現(xiàn)有對象的方法,賦值到某個變量,如
let {log, sin, cos} = Math
上面代碼將Math的對數,正弦,余弦三個方法賦值到了對應的變量上,使用起來就會方便很多。
4. 圓括號問題,解析賦值解析圓括號時很麻煩,只要由可能,就不要在模式中放置圓括號。另外:
* 變量聲明語句中,不能帶有圓括號
* 函數參數中,模式不能帶有圓括號
* 賦值語句中,不能將整個模式,或嵌套模式中的一層,放在圓括號中
三、字符串的擴展
1. JS內部,字符以UTF-16的格式存儲,每個字符固定為2個字節(jié)。對于那些需要4個字節(jié)存儲的字符(Unicode碼大于0xFFFF的字符),JS會認為它們是兩個字符。
2. Es6提供的codePointAt方法,直接使用時參數也不能正確識別,如下面的字符串中,a的位置應該是1,但必須傳入2才能取到字符,解決的辦法是使用for...of循環(huán),它能正確識別32位的UTF-16字符。
var s = "??a"
console.log(s.codePointAt(0)) // 134071
console.log(s.codePointAt(1)) // 57271
console.log(s.codePointAt(2)) // 97
console.log(s.codePointAt(0).toString(16)) // 20bb7
console.log(s.codePointAt(2).toString(16)) // 61
for(let ch of s)
{
console.log(ch.codePointAt(0).toString(16))// 20bb7 61
}
3. Es6中提供了另外操作32位的UTF-16的函數:String.fromCodePoint(), at(), includes(), startsWith(), endsWith()如
console.log(String.fromCodePoint(0x20BB7))
let s = 'Hello World!'
console.log(s.includes('W')) // true
console.log(s.startsWith('H')) // true
console.log(s.endsWith('d')) // false
當前的FireFox50.1和最新的360極速瀏覽器(內核為Chromium50.0.2661.102)均不支持at()函數
4.模板字符串
var s = `<span>這是一個
模板字符串!
</span>
`
console.log(s)
四、數值
1. 二進制和八進制的表示法分別使用前綴0b/0B和0o/0O。
2. 新的函數Number.isFinite(), Number.isNaN(), Number.parstInt(), Number.parseFloat(), Number.isInteger(),
console.log(Number.isInteger(3)) // true
console.log(Number.isInteger(3.0)) // true
在JS內部,整數和浮點數是同樣的存儲方法,所以3和3.0被視為同一個值。
3. 安全整數檢測Number.isSafeInteger(), Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER。
console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
console.log(Number.MIN_SAFE_INTEGER) // -9007199254740991
在使用Number.isSafeInteger()時,不能只驗證運算結果,而要同時驗證參與運算的每個值。
4. ES6在Math對象上新增了17個與數學相關的方法。
五、數組
1. Array.from()用于將類似數組的對象(含有l(wèi)ength屬性)和可遍歷的對象(包括ES6新增的數據結構Set和Map)轉換為數組
let a = {'0':1, '1':2, '2':3, 'length':3}
console.log(Array.from(a)) // [1, 2, 3]
console.log([].slice.call(a)) // [1, 2, 3]
let s = new Set(['a', 'b', 'c'])
console.log(Array.from(s)) // ["a", "b", "c"]
2. Array.of()用來將一組值轉換為數組,主要目的是彌補Array()和new Array()由于參數的不同導致的行為的差異,如
console.log(Array.of()) // []
console.log(Array.of(3)) // [3]
console.log(Array.of(3, 4)) // [3, 4]
console.log(Array()) // []
console.log(Array(3)) // [ , , ,]三個空的存儲位置
console.log(Array(3, 4)) // [3, 4]
console.log(new Array()) //[]
console.log(new Array(3)) // [ , , , ]三個空的存儲位置
console.log(new Array(3, 4)) // [3, 4]
3. 數組實例的copyWithin(),在當前數組內部,將指定位置的成員復制到其他位置(會覆蓋原有成員)。
console.log([1,2,3,4,5].copyWithin(0, 3,4)) // 4,2,3,4,5
4. 數組實例的find()和findIndex()函數
console.log([1, 4, -2, 5].find((n) => n < 0))
5. 數組實例的includes()函數
console.log([1, 2, 3].includes(2))
同Set結構的has()方法一樣,數組的includes()函數是用來查找值的;而Map結構的has()方法是用來查找鍵名的。
6. 由于空位的處理規(guī)則非常不統(tǒng)一,所以要避免出現(xiàn)空位的情況。
六、函數
1. 函數默認值
function log(x=0, y=0)
{
console.log(x, y)
}
log()
2. 函數的length屬性,表示該函數預期傳入的參數個數。如果參數有默認值,則length不包含這個參數。同理,rest參數也不會計入length屬性
3. 如果將參數默認值設為undefined,表明這個參數是可以省略的。
function foo(bar = undefined){......}
4. rest參數,形式為“...變量名“,用于獲取函數的多余參數,這樣就不需要使用arguments對象了。
function add(...args)
{
let sum = 0;
for(let v of args)
{
sum += v;
}
console.log(sum)
}
add(2,3,4)
5. 擴展運算符“...” ,它的作用是將一個數組轉為用逗號分隔的參數序列。
console.log(...[1, 2, 3])
擴展運算符內部調用的是數據結構的Iterator借口,因此只要具有Iterator接口的對象,都可以使用擴展運算符,比如Map結構。
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
])
console.log(...map.values()) // one two three
6. 箭頭函數
var f = v => console.log(v);
f('hello world')
等價于
var f = function(v){
console.log(v);
}
let f = n => n % 2 == 0;
等價于
let f = function(n)
{
return n % 2 == 0
}
如果沒有參數,或多個參數,就使用一個圓括號代表參數部分
var f = (name) => console.log('hello' + name)
f('world!')
var f = (string, name) => console.log(string + " " + name)
f('hello', 'world!')
感覺添加一個小括號比較好,這樣可以統(tǒng)一起來
7.箭頭函數中的this,指向固定化,并不是因為箭頭函數內部有綁定this的機制,實際原因是箭頭函數根本沒有自己的this,導致內部的this就是外層代碼塊的this。
function foo()
{
return () => {
return () => {
return () => {
console.log('id:', this.id)
}
}
}
}
var f = foo.call({id:1})
var t1 = f.call({id:2})()()
var t2 = f().call({id:3})()
var t3 = f()().call({id:4})
上面的代碼中,只有一個this,就是函數foo的this,所以t1,t2,t3輸出同樣的結果,因為內部所以的箭頭函數都沒有自己的this,他們的this實際都是最外層foo函數的this。
長期以來,Js的this對象一直都是一個令人頭疼的問題,在對象方法中使用this,必須非常小心,箭頭函數“綁定”this,很大程度上解決了這個困擾。
7.尾調用優(yōu)化和尾遞歸
七、對象,允許使用變量和函數,作為對象的屬性和方法
1.簡潔表示法
let birthday = '2010/10/10'
let Person = {
name: '張三',
birthday,
happy() {console.log('Happy Birthday ' + this.name)}
}
Person.happy()
2.判斷兩個值是否相等使用Object.is()
3.Object.assign()方法用于對象的合并,將源對象source的所有可枚舉屬性,復制到目標對象target。
var target = {a: 1}
var source1 = {b: 2}
var source2 = {c: 3}
console.log(Object.assign(target, source1, source2))
var target = {a:{b:'222', d:'333'}}
var source = {a:{b:'hello'}}
console.log(Object.assign(target, source)) // {a: {b: 'hello'}}
如果目標對象與源對象有同名屬性,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性
Object.assign方法是淺拷貝,如果源對象某個屬性的值是對象,則目標對象拷貝得到的是這個對象的引用。
Object.assign()的用途
- 為對象添加屬性
- 為對象添加方法
- 克隆對象
- 合并多個對象
- 為屬性指定默認值
4.遍歷對象 Object.keys, Object.values, Object.entries
5.擴展運算符
let clone = {...a}
等價于
let clone = Object.assign({}, a)
八、Set和Map
1. Set類似于數組,但它的成員值都是唯一的。
* keys():返回鍵名的遍歷器
* values():返回鍵值的遍歷器
* entries():返回鍵值對的遍歷器
* forEach():使用回調函數遍歷每個成員
> 擴展運算符...內部使用for...of循環(huán),所以也可以用于Set結構
九、一些不常用的功能
1. SIMD(Single Instruction/Multiple Data),即單指令,多數據
它是JS操作CPU對應指令的接口,可以使用一個指令,完成多個數據的運算,所以被廣泛用于3D圖形運算、物理模擬等運算量超大的項目之中。
SIMD是數據并行處理(parallelism)的一種手段,可以加速一些運算密集型操作的速度。
十、編程風格
1. let和const
- let取代var
- 所有的函數都應該設置為常量
- 長遠來看,JS可能會有多線程的實現(xiàn),let表示的變量,這時let表示的變量,只應出現(xiàn)在單線程運行的代碼中,不能是多線程共享的,這樣有利于保證線程安全。
2. 字符串
- 靜態(tài)字符串一律使用單引號或反引號,不使用雙引號
- 動態(tài)字符串使用反引號
3.解構賦值
- 使用數組成員對變量賦值時,優(yōu)先使用解構賦值
- 函數的參數如果是對象的成員,優(yōu)先使用解構賦值
function getFullName({firstName, lastName}){ }
- 如果函數返回多個值,優(yōu)先使用對象的解構賦值,而不是數組的解構賦值
function getInput(input) {
return { left, right, top, bottom};
}
4. 對象
- 單行定義的對象,最后一個成員不以逗號結尾。多行定義的對象,最后一個成員以逗號結尾
- 對象盡量靜態(tài)化,一旦定義,就不得隨意添加新的屬性。如果添加屬性不可避免,要使用Object.assign方法
- 如果對象的屬性名是動態(tài)的,可以在創(chuàng)造對象的時候,使用屬性表達式定義。
const obj = {
id: 5,
name: 'Tom',
[getKey('enabled')]: true,
};
5.數組
- 使用擴展運算符(...)拷貝數組
- 使用Array.from方法,將類似數組的對象轉為數組
6.函數
- 立即執(zhí)行函數可以寫成箭頭函數的形式
(() => {
console.log(' Hello World');
})();
- 簡單的、單行的、不會復用的函數,建議采用箭頭函數。如果函數體較為復雜,行數較多,還是應該采用傳統(tǒng)的函數寫法
7. Map和Object
- 只有模擬現(xiàn)實世界的實體對象時,才使用Object。如果只是需要key : value的數據結構,使用Map結構。因為Map有內建的遍歷機制