這篇文章僅記錄了本人在系統(tǒng)性學習ES6時辛慰,遇到的產(chǎn)生困惑或留意的點,用于以后開發(fā)中的review干像。
1.字符串擴展
1-1.JSON.stringify()
根據(jù)標準,JSON 數(shù)據(jù)必須是 UTF-8 編碼驰弄。但是麻汰,現(xiàn)在的JSON.stringify()方法有可能返回不符合 UTF-8 標準的字符串。
為了確保返回的是合法的 UTF-8 字符戚篙,ES2019 改變了JSON.stringify()
的行為五鲫。如果遇到0xD800
到0xDFFF
之間的單個碼點,或者不存在的配對形式岔擂,它會返回轉義字符串位喂,留給應用自己決定下一步的處理。
JSON.stringify('\u{D834}') // ""\\uD834""
JSON.stringify('\uDF06\uD834') // ""\\udf06\\ud834""
1-2.模板字符串
如果使用模板字符串表示多行字符串乱灵,所有的空格和縮進都會被保留在輸出之中塑崖。
$('#list').html(`
<ul>
<li>first</li>
<li>second</li>
</ul>
`);
上面代碼中,所有模板字符串的空格和換行痛倚,都是被保留的规婆,比如<ul>標簽前面會有一個換行。如果你不想要這個換行蝉稳,可以使用trim方法消除它抒蚜。
$('#list').html(`
<ul>
<li>first</li>
<li>second</li>
</ul>
`.trim());
模板字符串中嵌入變量,需要將變量名寫在${}之中耘戚。
由于模板字符串的大括號內(nèi)部嗡髓,就是執(zhí)行 JavaScript 代碼,因此如果大括號內(nèi)部是一個字符串收津,將會原樣輸出饿这。
可以進行運算,以及引用對象屬性朋截。還能調用函數(shù)蛹稍。如果大括號中的值不是字符串,將按照一般的規(guī)則轉為字符串部服。比如唆姐,大括號中是一個對象,將默認調用對象的toString方法廓八。
let x = 1;
let y = 2;
`${x} + ${y} = ${x + y}`
// "1 + 2 = 3"
`${x} + ${y * 2} = ${x + y * 2}`
// "1 + 4 = 5"
let obj = {x: 1, y: 2};
`${obj.x + obj.y}`
// "3"
//調用函數(shù)
function fn() {
return "Hello World";
}
`foo ${fn()} bar`
// foo Hello World bar
模板字符串甚至還能嵌套奉芦。
const tmpl = addrs => `
<table>
${addrs.map(addr => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`).join('')}
</table>
`;
const data = [
{ first: '<Jane>', last: 'Bond' },
{ first: 'Lars', last: '<Croft>' },
];
console.log(tmpl(data));
// <table>
//
// <tr><td><Jane></td></tr>
// <tr><td>Bond</td></tr>
//
// <tr><td>Lars</td></tr>
// <tr><td><Croft></td></tr>
//
// </table>
1-3.標簽模板
模板字符串的功能赵抢,不僅僅是上面這些。它可以緊跟在一個函數(shù)名后面声功,該函數(shù)將被調用來處理這個模板字符串烦却。這被稱為“標簽模板”功能(tagged template)。
alert`hello`
// 等同于
alert(['hello'])
標簽模板其實不是模板先巴,而是函數(shù)調用的一種特殊形式其爵。但是,如果模板字符里面有變量伸蚯,就不是簡單的調用了摩渺,而是會將模板字符串先處理成多個參數(shù),再調用函數(shù)剂邮。
let a = 5;
let b = 10;
tag`Hello ${ a + b } world ${ a * b }`;
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);
可以簡單理解為將參數(shù)中的字符串提取出來分割出一個裝有字符串的數(shù)組(字符串分割位置由變量所處位置決定)摇幻,參數(shù)中的變量依次提取并計算結果追加在數(shù)組后
這里有一個值得注意的地方:
let a = 5;
let b = 10;
tag`Hello ${ a + b } ${ a * b } world `;
// 等同于
tag(["Hello ", " ", " world "], 15, 50);
當變量出現(xiàn)在參數(shù)的最后時,參數(shù)數(shù)組中的最后一個元素將會是一個空字符串""
2.字符串的新增方法
2-1.String.raw()
該方法返回一個斜杠都被轉義(即斜杠前面再加一個斜杠)的字符串挥萌,往往用于模板字符串的處理方法绰姻。
String.raw`Hi\n${2+3}!`
// 實際返回 "Hi\\n5!",顯示的是轉義后的結果 "Hi\n5!"
String.raw`Hi\u000A!`;
// 實際返回 "Hi\\u000A!"引瀑,顯示的是轉義后的結果 "Hi\u000A!"
我們看到的打印出的結果是轉義之后的
String.raw`Hi\\n`
// 返回 "Hi\\\\n"
String.raw`Hi\\n` === "Hi\\\\n" // true
如果寫成正常函數(shù)的形式狂芋,它的第一個參數(shù),應該是一個具有raw屬性的對象伤疙,且raw屬性的值應該是一個數(shù)組银酗,對應模板字符串解析后的值。
// `foo${1 + 2}bar`
// 等同于
String.raw({ raw: ['foo', 'bar'] }, 1 + 2) // "foo3bar"
//`s${1 + 2}\n5`
//等同于
String.raw({ raw: ['s', '\\n5'] }, 1 + 2) // "s3\n5"
在大多數(shù)情況下, String.raw()
是用來處理模版字符串的. 不要被上面復雜的參數(shù)要求嚇到徒像,因為像所有的 tag functions一樣黍特,你通常不需要把它看成一個普通函數(shù),你只需要把它放在模板字符串前面就可以了锯蛀,而不是在它后面加個括號和一堆參數(shù)來調用它灭衷,引擎會替你去調用它。
2-2.String.repeat()
repeat方法返回一個新字符串旁涤,表示將原字符串重復n次翔曲。參數(shù)如果是小數(shù),會被向下取整劈愚。
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""
'na'.repeat(2.9) // "nana"
如果repeat的參數(shù)是負數(shù)或者Infinity瞳遍,會報錯。
但是菌羽,如果參數(shù)是 0 到-1 之間的小數(shù)掠械,則等同于 0,這是因為會先進行取整運算。0 到-1 之間的小數(shù)猾蒂,取整以后等于-0均唉,repeat視同為 0。
'na'.repeat(Infinity)
// RangeError
'na'.repeat(-1)
// RangeError
'na'.repeat(-0.9) // ""
參數(shù)NaN等同于 0肚菠。如果repeat的參數(shù)是字符串舔箭,則會先轉換成數(shù)字。
'na'.repeat(NaN) // ""
'na'.repeat('na') // ""
'na'.repeat('3') // "nanana"
2-3.padStart()蚊逢,padEnd()
padStart()和padEnd()一共接受兩個參數(shù)层扶,第一個參數(shù)是字符串補全生效的最大長度,第二個參數(shù)是用來補全的字符串时捌。
如果用來補全的字符串與原字符串怒医,兩者的長度之和超過了最大長度,則會截去超出位數(shù)的補全字符串奢讨。
'abc'.padStart(10, '0123456789')
// '0123456abc'
如果省略第二個參數(shù),默認使用空格補全長度焰薄。
'x'.padStart(4) // ' x'
'x'.padEnd(4) // 'x '
不管第二個參數(shù)是什么類型拿诸,都將先調用參數(shù)的toString方法將其轉為字符串之后進行拼接
'1'.padStart(10, 0) // "0000000001"
'1'.padStart(10, null) // "nullnulln1"
'1'.padStart(10, NaN) // "NaNNaNNaN1"
'1'.padStart(10, {}) // "[object O1"
'1'.padStart(10, []) // "1"
'1'.padStart(10, "") // "1"
'1'.padStart(10, undefined) // " 1"
注意:這里有兩個特殊的地方,當?shù)诙€參數(shù)為空數(shù)組時其返回值和參數(shù)為空字符串時的返回值相同塞茅,參數(shù)為undefined時亩码,返回值以空格進行填充其結果與不傳第二個參數(shù)的結果一致。
2-4.replaceAll()
它的用法與replace()相同野瘦,返回一個新字符串描沟,不會改變原字符串。
String.prototype.replaceAll(searchValue, replacement)
如果searchValue是一個不帶有g修飾符的正則表達式鞭光,replaceAll()會報錯吏廉。這一點跟replace()不同。
// 不報錯
'aabbcc'.replace(/b/, '_')
// 報錯
'aabbcc'.replaceAll(/b/, '_')
replaceAll()的第二個參數(shù)replacement是一個字符串惰许,表示替換的文本席覆,其中可以使用一些特殊字符串。
- $&:匹配的子字符串汹买。
- $`:匹配結果前面的文本佩伤。
- $':匹配結果后面的文本。
- $n:匹配成功的第n組內(nèi)容晦毙,n是從1開始的自然數(shù)生巡。這個參數(shù)生效的前提是,第一個參數(shù)必須是正則表達式见妒。
- $$:指代美元符號$孤荣。
// $& 表示匹配的字符串,即`b`本身
// 所以返回結果與原字符串一致
'abbc'.replaceAll('b', '$&')
// 'abbc'
// $` 表示匹配結果之前的字符串
// 對于第一個`b`,$` 指代`a`
// 對于第二個`b`垃环,$` 指代`ab`
'abbc'.replaceAll('b', '$`')
// 'aaabc'
// $' 表示匹配結果之后的字符串
// 對于第一個`b`邀层,$' 指代`bc`
// 對于第二個`b`,$' 指代`c`
'abbc'.replaceAll('b', `$'`)
// 'abccc'
// $1 表示正則表達式的第一個組匹配遂庄,指代`ab`
// $2 表示正則表達式的第二個組匹配寥院,指代`bc`
'abbc'.replaceAll(/(ab)(bc)/g, '$2$1')
// 'bcab'
// $$ 指代 $
'abc'.replaceAll('b', '$$')
// 'a$c'
2.數(shù)值的擴展
2-1二進制和八進制表示法
ES6 提供了二進制和八進制數(shù)值的新的寫法,分別用前綴0b(或0B)和0o(或0O)表示涛目。
0b111110111 === 503 // true
0o767 === 503 // true
從 ES5 開始秸谢,在嚴格模式之中,八進制就不再允許使用前綴0表示霹肝,ES6 進一步明確估蹄,要使用前綴0o表示。
// 非嚴格模式
(function(){
console.log(0o11 === 011);
})() // true
// 嚴格模式
(function(){
'use strict';
console.log(0o11 === 011);
})() // Uncaught SyntaxError: Octal literals are not allowed in strict mode.
轉成10進制可以用parseInt方法或者Number方法
2-2.Number.isFinite(), Number.isNaN()
ES6 在Number對象上沫换,新提供了Number.isFinite()和Number.isNaN()兩個方法臭蚁。
它們與傳統(tǒng)的全局方法isFinite()和isNaN()的區(qū)別在于,傳統(tǒng)方法先調用Number()將非數(shù)值的值轉為數(shù)值讯赏,再進行判斷垮兑,而這兩個新方法只對數(shù)值有效,Number.isFinite()對于非數(shù)值一律返回false, Number.isNaN()只有對于NaN才返回true漱挎,非NaN一律返回false系枪。
2-3.Number.parseInt(), Number.parseFloat()
ES6 將全局方法parseInt()和parseFloat(),移植到Number對象上面磕谅,行為完全保持不變私爷。
2-4.Number.isInteger()
Number.isInteger()用來判斷一個數(shù)值是否為整數(shù)。
如果對數(shù)據(jù)精度的要求較高膊夹,不建議使用Number.isInteger()判斷一個數(shù)值是否為整數(shù)衬浑。
Number.isInteger(5E-324) // false
Number.isInteger(5E-325) // true
上面代碼中,5E-325由于值太小,會被自動轉為0,因此返回true新锈。\
Number.isInteger(3.0000000000000002) // true
上面代碼中,Number.isInteger的參數(shù)明明不是整數(shù)拓诸,但是會返回true。原因就是這個小數(shù)的精度達到了小數(shù)點后16個十進制位麻昼,轉成二進制位超過了53個二進制位奠支,導致最后的那個2被丟棄了。
Number.isInteger() // false
Number.isInteger(null) // false
Number.isInteger('15') // false
Number.isInteger(true) // false
如果參數(shù)不是數(shù)值抚芦,Number.isInteger返回false倍谜。
2-5.Number.EPSILON
ES6 在Number對象上面迈螟,新增一個極小的常量Number.EPSILON。根據(jù)規(guī)格尔崔,它表示 1 與大于 1 的最小浮點數(shù)之間的差答毫。
對于 64 位浮點數(shù)來說,大于 1 的最小浮點數(shù)相當于二進制的1.00..001季春,小數(shù)點后面有連續(xù) 51 個零洗搂。這個值減去 1 之后,就等于 2 的 -52 次方载弄。
Number.EPSILON === Math.pow(2, -52)
// true
Number.EPSILON
// 2.220446049250313e-16
Number.EPSILON.toFixed(20)
// "0.00000000000000022204"
Number.EPSILON實際上是 JavaScript 能夠表示的最小精度耘拇。誤差如果小于這個值,就可以認為已經(jīng)沒有意義了宇攻,即不存在誤差了惫叛。
引入一個這么小的量的目的,在于為浮點數(shù)計算逞刷,設置一個誤差范圍嘉涌。我們知道浮點數(shù)計算是不精確的。
function withinErrorMargin (left, right) {
return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}
0.1 + 0.2 === 0.3 // false
withinErrorMargin(0.1 + 0.2, 0.3) // true
1.1 + 1.3 === 2.4 // false
withinErrorMargin(1.1 + 1.3, 2.4) // true
2-6.安全整數(shù)和 Number.isSafeInteger()
JavaScript 能夠準確表示的整數(shù)范圍在-253到253之間(不含兩個端點)夸浅,超過這個范圍洛心,無法精確表示這個值。
Math.pow(2, 53) // 9007199254740992
9007199254740992 // 9007199254740992
9007199254740993 // 9007199254740992
Math.pow(2, 53) === Math.pow(2, 53) + 1
// true
實際使用這個函數(shù)時题篷,需要注意。驗證運算結果是否落在安全整數(shù)的范圍內(nèi)厅目,不要只驗證運算結果番枚,而要同時驗證參與運算的每個值。
Number.isSafeInteger(9007199254740993)
// false
Number.isSafeInteger(990)
// true
Number.isSafeInteger(9007199254740993 - 990)
// true
9007199254740993 - 990
// 返回結果 9007199254740002
// 正確答案應該是 9007199254740003
9007199254740993 === 9007199254740992
// true
上面代碼中损敷,9007199254740993不是一個安全整數(shù)葫笼,但是Number.isSafeInteger會返回結果,顯示計算結果是安全的拗馒。這是因為路星,這個數(shù)超出了精度范圍,導致在計算機內(nèi)部诱桂,以9007199254740992的形式儲存洋丐。
所以,如果只驗證運算結果是否為安全整數(shù)挥等,很可能得到錯誤結果友绝。下面的函數(shù)可以同時驗證兩個運算數(shù)和運算結果。
function trusty (left, right, result) {
if (
Number.isSafeInteger(left) &&
Number.isSafeInteger(right) &&
Number.isSafeInteger(result)
) {
return result;
}
throw new RangeError('Operation cannot be trusted!');
}
trusty(9007199254740993, 990, 9007199254740993 - 990)
// RangeError: Operation cannot be trusted!
trusty(1, 2, 3)
// 3
2-7.Math.trunc()
Math.trunc方法用于去除一個數(shù)的小數(shù)部分肝劲,返回整數(shù)部分迁客。
Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc(-4.9) // -4
Math.trunc(-0.1234) // -0
對于非數(shù)值郭宝,Math.trunc內(nèi)部使用Number方法將其先轉為數(shù)值。
Math.trunc('123.456') // 123
Math.trunc(true) //1
Math.trunc(false) // 0
Math.trunc(null) // 0
對于空值和無法截取整數(shù)的值掷漱,返回NaN粘室。
Math.trunc(NaN); // NaN
Math.trunc('foo'); // NaN
Math.trunc(); // NaN
Math.trunc(undefined) // NaN
值得注意的是與Math.floor方法并不相同,當參數(shù)值小于0時,其表現(xiàn)與Math.ceil一致
Math.trunc(-1.6) //-1
Math.floor(-1.6); //-2
Math.ceil(-1.6) // -1
2-8.Math.sign()
Math.sign方法用來判斷一個數(shù)到底是正數(shù)卜范、負數(shù)衔统、還是零。對于非數(shù)值先朦,會先將其轉換為數(shù)值缰冤。
它會返回五種值。
- 參數(shù)為正數(shù)喳魏,返回+1棉浸;
- 參數(shù)為負數(shù),返回-1刺彩;
- 參數(shù)為 0迷郑,返回0;
- 參數(shù)為-0创倔,返回-0;
- 其他值嗡害,返回NaN。
如果參數(shù)是非數(shù)值畦攘,會自動轉為數(shù)值霸妹。對于那些無法轉為數(shù)值的值,會返回NaN知押。
Math.sign('') // 0
Math.sign(true) // +1
Math.sign(false) // 0
Math.sign(null) // 0
Math.sign('9') // +1
Math.sign('foo') // NaN
Math.sign() // NaN
Math.sign(undefined) // NaN
2-9.Math.cbrt()
Math.cbrt()方法用于計算一個數(shù)的立方根叹螟。對于非數(shù)值,Math.cbrt()方法內(nèi)部也是先使用Number()方法將其轉為數(shù)值台盯。
Math.cbrt(-1) // -1
Math.cbrt(0) // 0
Math.cbrt(1) // 1
Math.cbrt(2) // 1.2599210498948732
Math.cbrt('8') // 2
Math.cbrt('hello') // NaN
2-10.Math.clz32()
Math.clz32()方法將參數(shù)轉為 32 位無符號整數(shù)的形式罢绽,然后返回這個 32 位值里面有多少個前導 0。
Math.clz32(0) // 32
Math.clz32(1) // 31
Math.clz32(1000) // 22
Math.clz32(0b01000000000000000000000000000000) // 1
Math.clz32(0b00100000000000000000000000000000) // 2
左移運算符(<<)與Math.clz32方法直接相關静盅。
Math.clz32(0) // 32
Math.clz32(1) // 31
Math.clz32(1 << 1) // 30
Math.clz32(1 << 2) // 29
Math.clz32(1 << 29) // 2
對于小數(shù)良价,Math.clz32方法只考慮整數(shù)部分。對于空值或其他類型的值蒿叠,Math.clz32方法會將它們先轉為數(shù)值明垢,然后再計算。
Math.clz32(3.2) // 30
Math.clz32(3.9) // 30
Math.clz32() // 32
Math.clz32(NaN) // 32
Math.clz32(Infinity) // 32
Math.clz32(null) // 32
Math.clz32('foo') // 32
Math.clz32([]) // 32
Math.clz32({}) // 32
Math.clz32(true) // 31
2-11.Math.imul()
Math.imul方法返回兩個數(shù)以 32 位帶符號整數(shù)形式相乘的結果栈虚,返回的也是一個 32 位的帶符號整數(shù)袖外。
Math.imul(2, 4) // 8
Math.imul(-1, 8) // -8
Math.imul(-2, -2) // 4
如果只考慮最后 32 位,大多數(shù)情況下魂务,Math.imul(a, b)與a * b的結果是相同的曼验,即該方法等同于(a * b)|0的效果(超過 32 位的部分溢出)泌射。之所以需要部署這個方法,是因為 JavaScript 有精度限制鬓照,超過 2 的 53 次方的值無法精確表示熔酷。這就是說,對于那些很大的數(shù)的乘法豺裆,低位數(shù)值往往都是不精確的拒秘,Math.imul方法可以返回正確的低位數(shù)值。
(0x7fffffff * 0x7fffffff)|0 // 0
Math.imul(0x7fffffff, 0x7fffffff) // 1
上面這個乘法算式臭猜,返回結果為 0躺酒。但是由于這兩個二進制數(shù)的最低位都是 1,所以這個結果肯定是不正確的蔑歌,因為根據(jù)二進制乘法羹应,計算結果的二進制最低位應該也是 1。這個錯誤就是因為它們的乘積超過了 2 的 53 次方次屠,JavaScript 無法保存額外的精度园匹,就把低位的值都變成了 0。Math.imul方法可以返回正確的值 1劫灶。
2-12.Math.fround()
Math.fround方法返回一個數(shù)的32位單精度浮點數(shù)形式裸违。
對于32位單精度格式來說,數(shù)值精度是24個二進制位(1 位隱藏位與 23 位有效位)本昏,所以對于 -224 至 224 之間的整數(shù)(不含兩個端點)供汛,返回結果與參數(shù)本身一致。
Math.fround(0) // 0
Math.fround(1) // 1
Math.fround(2 ** 24 - 1) // 16777215
如果參數(shù)的絕對值大于 224涌穆,返回的結果便開始丟失精度紊馏。
Math.fround(2 ** 24) // 16777216
Math.fround(2 ** 24 + 1) // 16777216
Math.fround方法的主要作用,是將64位雙精度浮點數(shù)轉為32位單精度浮點數(shù)蒲犬。如果小數(shù)的精度超過24個二進制位,返回值就會不同于原值岸啡,否則返回值不變(即與64位雙精度值一致)原叮。
// 未丟失有效精度
Math.fround(1.125) // 1.125
Math.fround(7.25) // 7.25
// 丟失精度
Math.fround(0.3) // 0.30000001192092896
Math.fround(0.7) // 0.699999988079071
Math.fround(1.0000000123) // 1
對于 NaN 和 Infinity,此方法返回原值巡蘸。對于其它類型的非數(shù)值奋隶,Math.fround 方法會先將其轉為數(shù)值,再返回單精度浮點數(shù)悦荒。
Math.fround(NaN) // NaN
Math.fround(Infinity) // Infinity
Math.fround('5') // 5
Math.fround(true) // 1
Math.fround(null) // 0
Math.fround([]) // 0
Math.fround({}) // NaN
2-13.Math.hypot()
Math.hypot方法返回所有參數(shù)的平方和的平方根唯欣。
如果參數(shù)不是數(shù)值,Math.hypot方法會將其轉為數(shù)值搬味。只要有一個參數(shù)無法轉為數(shù)值境氢,就會返回 NaN蟀拷。
Math.hypot(3, 4); // 5
Math.hypot(3, 4, 5); // 7.0710678118654755
Math.hypot(); // 0
Math.hypot(NaN); // NaN
Math.hypot(3, 4, 'foo'); // NaN
Math.hypot(3, 4, '5'); // 7.0710678118654755
Math.hypot(-3); // 3
3.對數(shù)方法
3-1.Math.expm1()
Math.expm1(x)返回 ex - 1,即Math.exp(x) - 1萍聊。
Math.expm1(-1) // -0.6321205588285577
Math.expm1(0) // 0
Math.expm1(1) // 1.718281828459045
3-2.Math.log1p()
Math.log1p(x)方法返回1 + x的自然對數(shù)问芬,即Math.log(1 + x)。如果x小于-1寿桨,返回NaN此衅。
Math.log1p(1) // 0.6931471805599453
Math.log1p(0) // 0
Math.log1p(-1) // -Infinity
Math.log1p(-2) // NaN
3-3.Math.log10()
Math.log10(x)返回以 10 為底的x的對數(shù)。如果x小于 0亭螟,則返回 NaN挡鞍。
Math.log10(2) // 0.3010299956639812
Math.log10(1) // 0
Math.log10(0) // -Infinity
Math.log10(-2) // NaN
Math.log10(100000) // 5
3-4.Math.log2()
Math.log2(x)返回以 2 為底的x的對數(shù)。如果x小于 0预烙,則返回 NaN墨微。
Math.log2(3) // 1.584962500721156
Math.log2(2) // 1
Math.log2(1) // 0
Math.log2(0) // -Infinity
Math.log2(-2) // NaN
Math.log2(1024) // 10
Math.log2(1 << 29) // 29
雙曲函數(shù)方法
ES6 新增了 6 個雙曲函數(shù)方法。
- Math.sinh(x) 返回x的雙曲正弦(hyperbolic sine)
- Math.cosh(x) 返回x的雙曲余弦(hyperbolic cosine)
- Math.tanh(x) 返回x的雙曲正切(hyperbolic tangent)
- Math.asinh(x) 返回x的反雙曲正弦(inverse hyperbolic sine)
- Math.acosh(x) 返回x的反雙曲余弦(inverse hyperbolic cosine)
- Math.atanh(x) 返回x的反雙曲正切(inverse hyperbolic tangent)
4.指數(shù)運算符
ES2016 新增了一個指數(shù)運算符(**)默伍。
2 ** 2 // 4
2 ** 3 // 8
這個運算符的一個特點是右結合欢嘿,而不是常見的左結合。多個指數(shù)運算符連用時也糊,是從最右邊開始計算的炼蹦。
// 相當于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
指數(shù)運算符可以與等號結合,形成一個新的賦值運算符(**=)狸剃。
let a = 1.5;
a **= 2;
// 等同于 a = a * a;
let b = 4;
b **= 3;
// 等同于 b = b * b * b;
5.BigInt 數(shù)據(jù)類型
JavaScript 所有數(shù)字都保存成 64 位浮點數(shù)掐隐,這給數(shù)值的表示帶來了兩大限制。一是數(shù)值的精度只能到 53 個二進制位(相當于 16 個十進制位)钞馁,大于這個范圍的整數(shù)虑省,JavaScript 是無法精確表示的,這使得 JavaScript 不適合進行科學和金融方面的精確計算僧凰。二是大于或等于2的1024次方的數(shù)值探颈,JavaScript 無法表示,會返回Infinity训措。
// 超過 53 個二進制位的數(shù)值伪节,無法保持精度
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
// 超過 2 的 1024 次方的數(shù)值,無法表示
Math.pow(2, 1024) // Infinity
ES2020 引入了一種新的數(shù)據(jù)類型 BigInt(大整數(shù))绩鸣,來解決這個問題怀大,這是 ECMAScript 的第八種數(shù)據(jù)類型。BigInt 只用來表示整數(shù)呀闻,沒有位數(shù)的限制化借,任何位數(shù)的整數(shù)都可以精確表示。
const a = 2172141653n;
const b = 15346349309n;
// BigInt 可以保持精度
a * b // 33334444555566667777n
// 普通整數(shù)無法保持精度
Number(a) * Number(b) // 33334444555566670000
為了與 Number 類型區(qū)別捡多,BigInt 類型的數(shù)據(jù)必須添加后綴n蓖康。
BigInt 同樣可以使用各種進制表示铐炫,都要加上后綴n。
1234 // 普通整數(shù)
1234n // BigInt
typeof 123n // 'bigint'
// BigInt 的運算
1n + 2n // 3n
0b1101n // 二進制
0o777n // 八進制
0xFFn // 十六進制
BigInt 與普通整數(shù)是兩種值钓瞭,它們之間并不相等驳遵。也無法用BigInt與普通整數(shù)進行運算
42n === 42 // false
123n-1 //Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
BigInt 可以使用負號(-),但是不能使用正號(+)山涡,因為會與 asm.js 沖突堤结。
-42n // 正確
+42n // 報錯
5-1.BigInt 對象
JavaScript 原生提供BigInt對象,可以用作構造函數(shù)生成 BigInt 類型的數(shù)值鸭丛。轉換規(guī)則基本與Number()一致竞穷,將其他類型的值轉為 BigInt。
BigInt()構造函數(shù)必須有參數(shù)鳞溉,而且參數(shù)必須可以正常轉為數(shù)值瘾带,下面的用法都會報錯。參數(shù)如果是小數(shù)熟菲,也會報錯看政。
BigInt(123) // 123n
BigInt('123') // 123n
BigInt(false) // 0n
BigInt(true) // 1n
new BigInt() // TypeError
BigInt(undefined) //TypeError
BigInt(null) // TypeError
BigInt('123n') // SyntaxError
BigInt('abc') // SyntaxError
BigInt(1.5) // RangeError
BigInt('1.5') // SyntaxError
上面代碼中,尤其值得注意字符串123n無法解析成 Number 類型抄罕,所以會報錯允蚣。
BigInt 對象繼承了 Object 對象的兩個實例方法。
- BigInt.prototype.toString()
- BigInt.prototype.valueOf()
它還繼承了 Number 對象的一個實例方法呆贿。
- BigInt.prototype.toLocaleString()
此外嚷兔,還提供了三個靜態(tài)方法。
- BigInt.asUintN(width, BigInt): 給定的 BigInt 轉為 0 到 2width - 1 之間對應的值做入。
- BigInt.asIntN(width, BigInt):給定的 BigInt 轉為 -2width - 1 到 2width - 1 - 1 之間對應的值冒晰。
- BigInt.parseInt(string[, radix]):近似于Number.parseInt(),將一個字符串轉換成指定進制的 BigInt竟块。
const max = 2n ** (64n - 1n) - 1n;
BigInt.asIntN(64, max)
// 9223372036854775807n
BigInt.asIntN(64, max + 1n)
// -9223372036854775808n
BigInt.asUintN(64, max + 1n)
// 9223372036854775808n
上面代碼中壶运,max是64位帶符號的 BigInt 所能表示的最大值。如果對這個值加1n浪秘,BigInt.asIntN()將會返回一個負值前弯,因為這時新增的一位將被解釋為符號位。而BigInt.asUintN()方法由于不存在符號位秫逝,所以可以正確返回結果。
如果BigInt.asIntN()和BigInt.asUintN()指定的位數(shù)询枚,小于數(shù)值本身的位數(shù)违帆,那么頭部的位將被舍棄。
const max = 2n ** (64n - 1n) - 1n;
BigInt.asIntN(32, max) // -1n
BigInt.asUintN(32, max) // 4294967295n
上面代碼中金蜀,max是一個64位的 BigInt刷后,如果轉為32位的畴,前面的32位都會被舍棄。
下面是BigInt.parseInt()的例子尝胆。
// Number.parseInt() 與 BigInt.parseInt() 的對比
Number.parseInt('9007199254740993', 10)
// 9007199254740992
BigInt.parseInt('9007199254740993', 10)
// 9007199254740993n
上面代碼中丧裁,由于有效數(shù)字超出了最大限度,Number.parseInt方法返回的結果是不精確的含衔,而BigInt.parseInt方法正確返回了對應的 BigInt煎娇。
對于二進制數(shù)組,BigInt 新增了兩個類型BigUint64Array和BigInt64Array贪染,這兩種數(shù)據(jù)類型返回的都是64位 BigInt缓呛。DataView對象的實例方法DataView.prototype.getBigInt64()和DataView.prototype.getBigUint64(),返回的也是 BigInt杭隙。
5-2.轉換規(guī)則
可以使用Boolean()哟绊、Number()和String()這三個方法,將 BigInt 可以轉為布爾值痰憎、數(shù)值和字符串類型票髓。
另外,取反運算符(!)也可以將 BigInt 轉為布爾值铣耘。
Boolean(0n) // false
Boolean(1n) // true
Number(1n) // 1
String(1n) // "1"
!0n // true
!1n // false
上面代碼中洽沟,注意最后一個例子,轉為字符串時后綴n會消失涡拘。
5-3.數(shù)學運算
數(shù)學運算方面玲躯,BigInt 類型的+、-鳄乏、和*這四個二元運算符跷车,與 Number 類型的行為一致。除法運算/會舍去小數(shù)部分橱野,返回一個整數(shù)朽缴。
9n / 5n
// 1n
幾乎所有的數(shù)值運算符都可以用在 BigInt,但是有兩個例外水援。
- 不帶符號的右移位運算符>>>
- 一元的求正運算符+
上面兩個運算符用在 BigInt 會報錯密强。前者是因為>>>運算符是不帶符號的,但是 BigInt 總是帶有符號的蜗元,導致該運算無意義或渤,完全等同于右移運算符>>。后者是因為一元運算符+在 asm.js 里面總是返回 Number 類型奕扣,為了不破壞 asm.js 就規(guī)定+1n會報錯薪鹦。
BigInt 不能與普通數(shù)值進行混合運算。
1n + 1.3 // 報錯
上面代碼報錯是因為無論返回的是 BigInt 或 Number,都會導致丟失精度信息池磁。比如(2n**53n + 1n) + 0.5這個表達式奔害,如果返回 BigInt 類型,0.5這個小數(shù)部分會丟失地熄;如果返回 Number 類型华临,有效精度只能保持 53 位,導致精度下降端考。
同樣的原因雅潭,如果一個標準庫函數(shù)的參數(shù)預期是 Number 類型,但是得到的是一個 BigInt跛梗,就會報錯寻馏。
// 錯誤的寫法
Math.sqrt(4n) // 報錯
// 正確的寫法
Math.sqrt(Number(4n)) // 2
上面代碼中,Math.sqrt的參數(shù)預期是 Number 類型核偿,如果是 BigInt 就會報錯诚欠,必須先用Number方法轉一下類型,才能進行計算漾岳。
asm.js 里面轰绵,|0跟在一個數(shù)值的后面會返回一個32位整數(shù)。根據(jù)不能與 Number 類型混合運算的規(guī)則尼荆,BigInt 如果與|0進行運算會報錯左腔。
1n | 0 // 報錯
5-4.其他運算
BigInt 對應的布爾值,與 Number 類型一致捅儒,即0n會轉為false液样,其他值轉為true。
if (0n) {
console.log('if');
} else {
console.log('else');
}
// else
上面代碼中巧还,0n對應false鞭莽,所以會進入else子句。
比較運算符(比如>)和相等運算符(==)允許 BigInt 與其他類型的值混合計算麸祷,因為這樣做不會損失精度澎怒。
0n < 1 // true
0n < true // true
0n == 0 // true
0n == false // true
0n === 0 // false
BigInt 與字符串混合運算時,會先轉為字符串阶牍,再進行運算喷面。
'' + 123n // "123"