es6新增的api
從值的轉換到數學計算,ES6給各種內建原生類型和對象增加了許多靜態屬性和方法來輔助這些常見任務。另外,一些原生類型的實例通過各種新的原型方法獲得了新的能力。
註意: 大多數這些特性都可以被忠實地填補。我們不會在這裏深入這樣的細節,但是關於兼容標準的shim/填補,你可以看一下“ES6 Shim”(https://github.com/paulmillr/es6-shim/)。
Array
在JS中被各種用戶庫擴展得最多的特性之一就是數組類型。ES6在數組上增加許多靜態的和原型(實例)的幫助功能應當並不令人驚訝。
Array.of(..)
靜態函數
Array(..)
的構造器有一個盡人皆知的坑:如果僅有一個參數值被傳遞,而且這個參數值是一個數字的話,它並不會制造一個含有一個帶有該數值元素的數組,而是構建一個長度等於這個數字的空數組。這種操作造成了不幸的和怪異的“空值槽”行為,而這正是JS數組為人詬病的地方。
Array.of(..)
作為數組首選的函數型構造器取代了Array(..)
,因為Array.of(..)
沒有那種單數字參數值的情況。考慮如下代碼:
var a = Array( 3 );
a.length; // 3
a[0]; // undefined
var b = Array.of( 3 );
b.length; // 1
b[0]; // 3
var c = Array.of( 1, 2, 3 );
c.length; // 3
c; // [1,2,3]
在什麽樣的環境下,你才會想要是使用Array.of(..)
來創建一個數組,而不是使用像c = [1,2,3]
這樣的字面語法呢?有兩種可能的情況。
如果你有一個回調,傳遞給它的參數值本應當被包裝在一個數組中時,Array.of(..)
就完美地符合條件。這可能不是那麽常見,但是它可以為你的癢處撓上一把。
另一種場景是如果你擴展Array
構成它的子類,而且希望能夠在一個你的子類的實例中創建和初始化元素,比如:
class MyCoolArray extends Array {
sum() {
return this.reduce( function reducer(acc,curr){
return acc + curr;
}, 0 );
}
}
var x = new MyCoolArray( 3 );
x.length; // 3 -- 噢!
x.sum(); // 0 -- 噢!
var y = [3]; // Array,不是 MyCoolArray
y.length; // 1
y.sum(); // `sum` is not a function
var z = MyCoolArray.of( 3 );
z.length; // 1
z.sum(); // 3
你不能(簡單地)只創建一個MyCoolArray
的構造器,讓它覆蓋Array
父構造器的行為,因為這個父構造器對於實際創建一個規範的數組值(初始化this
)是必要的。在MyCoolArray
子類上“被繼承”的靜態of(..)
方法提供了一個不錯的解決方案。
Array.from(..)
靜態函數
在JavaScript中一個“類數組對象”是一個擁有length
屬性的對象,這個屬性明確地帶有0或更高的整數值。
在JS中處理這些值出了名地讓人沮喪;將它們變形為真正的數組曾經是十分常見的做法,這樣各種Array.property
方法(map(..)
,indexOf(..)
等等)才能與它一起使用。這種處理通常看起來像:
// 類數組對象
var arrLike = {
length: 3,
0: "foo",
1: "bar"
};
var arr = Array.prototype.slice.call( arrLike );
另一種slice(..)
經常被使用的常見任務是,復制一個真正的數組:
var arr2 = arr.slice();
在這兩種情況下,新的ES6Array.from(..)
方法是一種更易懂而且更優雅的方式 —— 也不那麽冗長:
var arr = Array.from( arrLike );
var arrCopy = Array.from( arr );
Array.from(..)
會查看第一個參數值是否是一個可叠代對象(參見第三章的“叠代器”),如果是,它就使用叠代器來產生值,並將這些值“拷貝”到將要被返回的數組中。因為真正的數組擁有一個可以產生這些值的叠代器,所以這個叠代器會被自動地使用。
但是如果你傳遞一個類數組對象作為Array.from(..)
的第一個參數值,它的行為基本上是和slice()
(不帶參數值的!)或apply()
相同的,它簡單地循環所有的值,訪問從0
開始到length
值的由數字命名的屬性。
考慮如下代碼:
var arrLike = {
length: 4,
2: "foo"
};
Array.from( arrLike );
// [ undefined, undefined, "foo", undefined ]
因為在arrLike
上不存在位置0
,1
,和3
,所以對這些值槽中的每一個,結果都是undefined
值。
你也可以這樣產生類似的結果:
var emptySlotsArr = [];
emptySlotsArr.length = 4;
emptySlotsArr[2] = "foo";
Array.from( emptySlotsArr );
// [ undefined, undefined, "foo", undefined ]
避免空值槽
前面的代碼段中,在emptySlotsArr
和Array.from(..)
調用的結果有一個微妙但重要的不同。Array.from(..)
從不產生空值槽。
在ES6之前,如果你想要制造一個被初始化為在每個值槽中使用實際undefined
值(不是空值槽!)的特定長數組,你不得不做一些額外的工作:
var a = Array( 4 ); // 四個空值槽!
var b = Array.apply( null, { length: 4 } ); // 四個 `undefined` 值
但現在Array.from(..)
使這件事簡單了些:
var c = Array.from( { length: 4 } ); // 四個 `undefined` 值
警告: 使用一個像前面代碼段中的a
那樣的空值槽數組可以與一些數組函數工作,但是另一些函數會忽略空值槽(比如map(..)
等)。你永遠不應該刻意地使用空值槽,因為它幾乎肯定會在你的程序中導致奇怪/不可預料的行為。
映射
Array.from(..)
工具還有另外一個絕技。第二個參數值,如果被提供的話,是一個映射函數(和普通的Array#map(..)
幾乎相同),它在將每個源值映射/變形為返回的目標值時調用。考慮如下代碼:
var arrLike = {
length: 4,
2: "foo"
};
Array.from( arrLike, function mapper(val,idx){
if (typeof val == "string") {
return val.toUpperCase();
}
else {
return idx;
}
} );
// [ 0, 1, "FOO", 3 ]
註意: 就像其他接收回調的數組方法一樣,Array.from(..)
接收可選的第三個參數值,它將被指定為作為第二個參數傳遞的回調的this
綁定。否則,this
將是undefined
。
一個使用Array.from(..)
將一個8位值數組翻譯為16位值數組的例子,參見第五章的“類型化數組”。
創建 Arrays 和子類型
在前面幾節中,我們討論了Array.of(..)
和Array.from(..)
,它們都用與構造器相似的方法創建一個新數組。但是在子類中它們會怎麽做?它們是創建基本Array
的實例,還是創建衍生的子類的實例?
class MyCoolArray extends Array {
..
}
MyCoolArray.from( [1, 2] ) instanceof MyCoolArray; // true
Array.from(
MyCoolArray.from( [1, 2] )
) instanceof MyCoolArray; // false
of(..)
和from(..)
都使用它們被訪問時的構造器來構建數組。所以如果你使用基本的Array.of(..)
你將得到Array
實例,但如果你使用MyCoolArray.of(..)
,你將得到一個MyCoolArray
實例。
在第三章的“類”中,我們講解了在所有內建類(比如Array
)中定義好的@@species
設定,它被用於任何創建新實例的原型方法。slice(..)
是一個很棒的例子:
var x = new MyCoolArray( 1, 2, 3 );
x.slice( 1 ) instanceof MyCoolArray; // true
一般來說,這種默認行為將可能是你想要的,但是正如我們在第三章中討論過的,如果你想的話你 可以 覆蓋它:
class MyCoolArray extends Array {
// 強制 `species` 為父類構造器
static get [Symbol.species]() { return Array; }
}
var x = new MyCoolArray( 1, 2, 3 );
x.slice( 1 ) instanceof MyCoolArray; // false
x.slice( 1 ) instanceof Array; // true
要註意的是,@@species
設定僅適用於原型方法,比如slice(..)
。of(..)
和from(..)
不使用它;它們倆都只使用this
綁定(哪個構造器被用於發起引用)。考慮如下代碼:
class MyCoolArray extends Array {
// 強制 `species` 為父類構造器
static get [Symbol.species]() { return Array; }
}
var x = new MyCoolArray( 1, 2, 3 );
MyCoolArray.from( x ) instanceof MyCoolArray; // true
MyCoolArray.of( [2, 3] ) instanceof MyCoolArray; // true
copyWithin(..)
原型方法
Array#copyWithin(..)
是一個對所有數組可用的新修改器方法(包括類型化數組;參加第五章)。copyWithin(..)
將數組的一部分拷貝到同一個數組的其他位置,覆蓋之前存在在那裏的任何東西。
它的參數值是 目標(要被拷貝到的索引位置),開始(拷貝開始的索引位置(含)),和可選的 結束(拷貝結束的索引位置(不含))。如果這些參數值中存在任何負數,那麽它們就被認為是相對於數組的末尾。
考慮如下代碼:
[1,2,3,4,5].copyWithin( 3, 0 ); // [1,2,3,1,2]
[1,2,3,4,5].copyWithin( 3, 0, 1 ); // [1,2,3,1,5]
[1,2,3,4,5].copyWithin( 0, -2 ); // [4,5,3,4,5]
[1,2,3,4,5].copyWithin( 0, -2, -1 ); // [4,2,3,4,5]
copyWithin(..)
方法不會擴張數組的長度,就像前面代碼段中的第一個例子展示的。當到達數組的末尾時拷貝就會停止。
與你可能想象的不同,拷貝的順序並不總是從左到右的。如果起始位置與目標為重疊的話,它有可能造成已經被拷貝過的值被重復拷貝,這大概不是你期望的行為。
所以在這種情況下,算法內部通過相反的拷貝順序來避免這個坑。考慮如下代碼:
[1,2,3,4,5].copyWithin( 2, 1 ); // ???
如果算法是嚴格的從左到右,那麽2
應當被拷貝來覆蓋3
,然後這個被拷貝的2
應當被拷貝來覆蓋4
,然後這個被拷貝的2
應當被拷貝來覆蓋5
,而你最終會得到[1,2,2,2,2]
。
與此不同的是,拷貝算法把方向反轉過來,拷貝4
來覆蓋5
,然後拷貝3
來覆蓋4
,然後拷貝2
來覆蓋3
,而最後的結果是[1,2,2,3,4]
。就期待的結果而言這可能更“正確”,但是如果你僅以單純的從左到右的方式考慮拷貝算法的話,它就可能讓人糊塗。
fill(..)
原型方法
ES6中的Array#fill(..)
方法原生地支持使用一個指定的值來完全地(或部分地)填充一個既存的數組:
var a = Array( 4 ).fill( undefined );
a;
// [undefined,undefined,undefined,undefined]
fill(..)
可選地接收 開始 與 結束 參數,它們指示要被填充的數組的一部分,比如:
var a = [ null, null, null, null ].fill( 42, 1, 3 );
a; // [null,42,42,null]
find(..)
原型方法
一般來說,在一個數組中搜索一個值的最常見方法曾經是indexOf(..)
方法,如果值被找到的話它返回值的位置索引,沒有找到的話返回-1
:
var a = [1,2,3,4,5];
(a.indexOf( 3 ) != -1); // true
(a.indexOf( 7 ) != -1); // false
(a.indexOf( "2" ) != -1); // false
indexOf(..)
比較要求一個嚴格===
匹配,所以搜索"2"
找不到值2
,反之亦然。沒有辦法覆蓋indexOf(..)
的匹配算法。不得不手動與值-1
進行比較也很不幸/不優雅。
提示: 一個使用~
操作符來繞過難看的-1
的有趣(而且爭議性地令人糊塗)技術,參見本系列的 類型與文法。
從ES5開始,控制匹配邏輯的最常見的迂回方法是some(..)
。它的工作方式是為每一個元素調用一個回調函數,直到這些調用中的一個返回true
/truthy值,然後它就會停止。因為是由你來定義這個回調函數,所以你就擁有了如何做出匹配的完全控制權:
var a = [1,2,3,4,5];
a.some( function matcher(v){
return v == "2";
} ); // true
a.some( function matcher(v){
return v == 7;
} ); // false
但這種方式的缺陷是你只能使用true
/false
來指示是否找到了合適的匹配值,而不是實際被匹配的值。
ES6的find(..)
解決了這個問題。它的工作方式基本上與some(..)
相同,除了一旦回調返回一個true
/truthy值,實際的數組值就會被返回:
var a = [1,2,3,4,5];
a.find( function matcher(v){
return v == "2";
} ); // 2
a.find( function matcher(v){
return v == 7; // undefined
});
使用一個自定義的matcher(..)
函數還允許你與對象這樣的復雜值進行匹配:
var points = [
{ x: 10, y: 20 },
{ x: 20, y: 30 },
{ x: 30, y: 40 },
{ x: 40, y: 50 },
{ x: 50, y: 60 }
];
points.find( function matcher(point) {
return (
point.x % 3 == 0 &&
point.y % 4 == 0
);
} ); // { x: 30, y: 40 }
註意: 和其他接收回調的數組方法一樣,find(..)
接收一個可選的第二參數。如果它被設置了的話,就將被指定為作為第一個參數傳遞的回調的this
綁定。否則,this
將是undefined
。
findIndex(..)
原型方法
雖然前一節展示了some(..)
如何在一個數組檢索給出一個Boolean結果,和find(..)
如何從數組檢索中給出匹配的值,但是還有一種需求是尋找匹配的值的位置索引。
indexOf(..)
可以完成這個任務,但是沒有辦法控制它的匹配邏輯;它總是使用===
嚴格等價。所以ES6的findIndex(..)
才是答案:
var points = [
{ x: 10, y: 20 },
{ x: 20, y: 30 },
{ x: 30, y: 40 },
{ x: 40, y: 50 },
{ x: 50, y: 60 }
];
points.findIndex( function matcher(point) {
return (
point.x % 3 == 0 &&
point.y % 4 == 0
);
} ); // 2
points.findIndex( function matcher(point) {
return (
point.x % 6 == 0 &&
point.y % 7 == 0
);
} ); // -1
不要使用findIndex(..) != -1
(在indexOf(..)
中經常這麽幹)來從檢索中取得一個boolean,因為some(..)
已經給出了你想要的true
/false
了。而且也不要用a[ a.findIndex(..) ]
來取得一個匹配的值,因為這是find(..)
完成的任務。最後,如果你需要嚴格匹配的索引,就使用indexOf(..)
,如果你需要一個更加定制化的匹配,就使用findIndex(..)
。
註意: 和其他接收回調的數組方法一樣,find(..)
接收一個可選的第二參數。如果它被設置了的話,就將被指定為作為第一個參數傳遞的回調的this
綁定。否則,this
將是undefined
。
entries()
, values()
, keys()
原型方法
在第三章中,我們展示了數據結構如何通過一個叠代器來提供一種模擬逐個值的叠代。然後我們在第五章探索新的ES6集合(Map,Set,等)如何為了產生不同種類的叠代器而提供幾種方法時闡述了這種方式。
因為Array
並不是ES6的新東西,所以它可能不被認為是一個傳統意義上的“集合”,但是在它提供了相同的叠代器方法:entries()
,values()
,和keys()
的意義上,它是的。考慮如下代碼:
var a = [1,2,3];
[...a.values()]; // [1,2,3]
[...a.keys()]; // [0,1,2]
[...a.entries()]; // [ [0,1], [1,2], [2,3] ]
[...a[Symbol.iterator]()]; // [1,2,3]
就像Set
一樣,默認的Array
叠代器與values()
放回的東西相同。
在本章早先的“避免空值槽”一節中,我們展示了Array.from(..)
如何將一個數組中的空值槽看作帶有undefined
的存在值槽。其實際的原因是,在底層數組叠代器就是以這種方式動作的:
var a = [];
a.length = 3;
a[1] = 2;
[...a.values()]; // [undefined,2,undefined]
[...a.keys()]; // [0,1,2]
[...a.entries()]; // [ [0,undefined], [1,2], [2,undefined] ]
Object
幾個額外的靜態幫助方法已經被加入Object
。從傳統意義上講,這種種類的函數是關註於對象值的行為/能力的。
但是,從ES6開始,Object
靜態函數還用於任意種類的通用全局API —— 那些還沒有更自然地存在於其他的某些位置的API(例如,Array.from(..)
)。
Object.is(..)
靜態函數
Object.is(..)
靜態函數進行值的比較,它的風格甚至要比===
比較還要嚴格。
Object(..)
調用底層的SameValue
算法(ES6語言規範,第7.2.9節)。SameValue
算法基本上與===
嚴格等價比較算法相同(ES6語言規範,第7.2.13節),但是帶有兩個重要的例外。
考慮如下代碼:
var x = NaN, y = 0, z = -0;
x === x; // false
y === z; // true
Object.is( x, x ); // true
Object.is( y, z ); // false
你應當為嚴格等價性比較繼續使用===
;Object.is(..)
不應當被認為是這個操作符的替代品。但是,在你想要嚴格地識別NaN
或-0
值的情況下,Object.is(..)
是現在的首選方式。
註意: ES6還增加了一個Number.isNaN(..)
工具(在本章稍後討論),它可能是一個稍稍方便一些的測試;比起Object.is(x, NaN)
你可能更偏好Number.isNaN(x)
。你 可以 使用笨拙的x == 0 && 1 / x === -Infinity
來準確地測試-0
,但在這種情況下Object.is(x,-0)
要好得多。
Object.getOwnPropertySymbols(..)
靜態函數
第二章中的“Symbol”一節討論了ES6中的新Symbol基本值類型。
Symbol可能將是在對象上最經常被使用的特殊(元)屬性。所以引入了Object.getOwnPropertySymbols(..)
,它僅取回直接存在於對象上的symbol屬性:
var o = {
foo: 42,
[ Symbol( "bar" ) ]: "hello world",
baz: true
};
Object.getOwnPropertySymbols( o ); // [ Symbol(bar) ]
Object.setPrototypeOf(..)
靜態函數
還是在第二章中,我們提到了Object.setPrototypeOf(..)
工具,它為了 行為委托 的目的(意料之中地)設置一個對象的[[Prototype]]
(參見本系列的 this與對象原型)。考慮如下代碼:
var o1 = {
foo() { console.log( "foo" ); }
};
var o2 = {
// .. o2 的定義 ..
};
Object.setPrototypeOf( o2, o1 );
// 委托至 `o1.foo()`
o2.foo(); // foo
另一種方式:
var o1 = {
foo() { console.log( "foo" ); }
};
var o2 = Object.setPrototypeOf( {
// .. o2 的定義 ..
}, o1 );
// 委托至 `o1.foo()`
o2.foo(); // foo
在前面兩個代碼段中,o2
和o1
之間的關系都出現在o2
定義的末尾。更常見的是,o2
和o1
之間的關系在o2
定義的上面被指定,就像在類中,而且在對象字面量的__proto__
中也是這樣(參見第二章的“設置[[Prototype]]
”)。
警告: 正如展示的那樣,在對象創建之後立即設置[[Prototype]]
是合理的。但是在很久之後才改變它一般不是一個好主意,而且經常會導致困惑而非清晰。
Object.assign(..)
靜態函數
許多JavaScript庫/框架都提供將一個對象的屬性拷貝/混合到另一個對象中的工具(例如,jQuery的extend(..)
)。在這些不同的工具中存在著各種微妙的區別,比如一個擁有undefined
值的屬性是否被忽略。
ES6增加了Object.assign(..)
,它是這些算法的一個簡化版本。第一個參數是 目標對象 而所有其他的參數是 源對象,它們會按照羅列的順序被處理。對每一個源對象,它自己的(也就是,不是“繼承的”)可枚舉鍵,包括symbol,將會好像通過普通=
賦值那樣拷貝。Object.assign(..)
返回目標對象。
考慮這種對象構成:
var target = {},
o1 = { a: 1 }, o2 = { b: 2 },
o3 = { c: 3 }, o4 = { d: 4 };
// 設置只讀屬性
Object.defineProperty( o3, "e", {
value: 5,
enumerable: true,
writable: false,
configurable: false
} );
// 設置不可枚舉屬性
Object.defineProperty( o3, "f", {
value: 6,
enumerable: false
} );
o3[ Symbol( "g" ) ] = 7;
// 設置不可枚舉 symbol
Object.defineProperty( o3, Symbol( "h" ), {
value: 8,
enumerable: false
} );
Object.setPrototypeOf( o3, o4 );
僅有屬性a
,b
,c
,e
,和Symbol("g")
將被拷貝到target
:
Object.assign( target, o1, o2, o3 );
target.a; // 1
target.b; // 2
target.c; // 3
Object.getOwnPropertyDescriptor( target, "e" );
// { value: 5, writable: true, enumerable: true,
// configurable: true }
Object.getOwnPropertySymbols( target );
// [Symbol("g")]
屬性d
,f
,和Symbol("h")
在拷貝中被忽略了;非枚舉屬性和非自身屬性將會被排除在賦值之外。另外,e
作為一個普通屬性賦值被拷貝,而不是作為一個只讀屬性被復制。
在早先一節中,我們展示了使用setPrototypeOf(..)
來在對象o2
和o1
之間建立一個[[Prototype]]
關系。這是利用Object.assign(..)
的另外一種形式:
var o1 = {
foo() { console.log( "foo" ); }
};
var o2 = Object.assign(
Object.create( o1 ),
{
// .. o2 的定義 ..
}
);
// 委托至 `o1.foo()`
o2.foo(); // foo
註意: Object.create(..)
是一個ES5標準工具,它創建一個[[Prototype]]
鏈接好的空對象。更多信息參見本系列的 this與對象原型。
Math
ES6增加了幾種新的數學工具,它們協助或填補了常見操作的空白。所有這些操作都可以被手動計算,但是它們中的大多數現在都被原生地定義,這樣JS引擎就可以優化計算的性能,或者進行與手動計算比起來小數精度更高的計算。
與直接的開發者相比,asm.js/轉譯的JS代碼(參見本系列的 異步與性能)更可能是這些工具的使用者。
三角函數:
cosh(..)
- 雙曲余弦acosh(..)
- 雙曲反余弦sinh(..)
- 雙曲正弦asinh(..)
- 雙曲反正弦tanh(..)
- 雙曲正切atanh(..)
- 雙曲反正切hypot(..)
- 平方和的平方根(也就是,廣義勾股定理)
算數函數:
cbrt(..)
- 立方根clz32(..)
- 計數32位二進制表達中前綴的零expm1(..)
- 與exp(x) - 1
相同log2(..)
- 二進制對數(以2為底的對數)log10(..)
- 以10為底的對數log1p(..)
- 與log(x + 1)
相同imul(..)
- 兩個數字的32為整數乘法
元函數:
sign(..)
- 返回數字的符號trunc(..)
- 僅返回一個數字的整數部分fround(..)
- 舍入到最接近的32位(單精度)浮點數值
Number
重要的是,為了你的程序能夠正常工作,它必須準確地處理數字。ES6增加了一些額外的屬性和函數來輔助常見的數字操作。
兩個在Number
上新增的功能只是既存全局函數的引用:Number.parseInt(..)
和Number.parseFloat(..)
。
靜態屬性
ES6以靜態屬性的形式增加了一些有用的數字常數:
Number.EPSILON
- 在任意兩個數字之間的最小值:2^-52
(關於為了應對浮點算數運算不精確的問題而將這個值用做容差的講解,參見本系列的 類型與文法 的第二章)Number.MAX_SAFE_INTEGER
- 可以用一個JS數字值明確且“安全地”表示的最大整數:2^53 - 1
Number.MIN_SAFE_INTEGER
- 可以用一個JS數字值明確且“安全地”表示的最小整數:-(2^53 - 1)
或(-2)^53 + 1
.
註意: 關於“安全”整數的更多信息,參見本系列的 類型與文法 的第二章。
Number.isNaN(..)
靜態函數
標準的全局isNaN(..)
工具從一開始就壞掉了,因為不僅對實際的NaN
值返回true
,而且對不是數字的東西也返回true
。其原因是它會將參數值強制轉換為數字類型(這可能失敗而導致一個NaN)。ES6增加了一個修復過的工具Number.isNaN(..)
,它可以正確工作:
var a = NaN, b = "NaN", c = 42;
isNaN( a ); // true
isNaN( b ); // true —— 噢!
isNaN( c ); // false
Number.isNaN( a ); // true
Number.isNaN( b ); // false —— 修好了!
Number.isNaN( c ); // false
Number.isFinite(..)
靜態函數
看到像isFinite(..)
這樣的函數名會誘使人們認為它單純地意味著“不是無限”。但這不十分正確。這個新的ES6工具有更多的微妙之處。考慮如下代碼:
var a = NaN, b = Infinity, c = 42;
Number.isFinite( a ); // false
Number.isFinite( b ); // false
Number.isFinite( c ); // true
標準的全局isFinite(..)
會強制轉換它收到的參數值,但是Number.isFinite(..)
會省略強制轉換的行為:
var a = "42";
isFinite( a ); // true
Number.isFinite( a ); // false
你可能依然偏好強制轉換,這時使用全局isFinite(..)
是一個合法的選擇。或者,並且可能是更明智的選擇,你可以使用Number.isFinite(+x)
,它在將x
傳遞前明確地將它強制轉換為數字(參見本系列的 類型與文法 的第四章)。
整數相關的靜態函數
JavaScript數字值總是浮點數(IEEE-754)。所以判定一個數字是否是“整數”的概念與檢查它的類型無關,因為JS沒有這樣的區分。
取而代之的是,你需要檢查這個值是否擁有非零的小數部分。這樣做的最簡單的方法通常是:
x === Math.floor( x );
ES6增加了一個Number.isInteger(..)
幫助工具,它可以潛在地判定這種性質,而且效率稍微高一些:
Number.isInteger( 4 ); // true
Number.isInteger( 4.2 ); // false
註意: 在JavaScript中,4
,4.
,4.0
,或4.0000
之間沒有區別。它們都將被認為是一個“整數”,因此都會從Number.isInteger(..)
中給出true
。
另外,Number.isInteger(..)
過濾了一些明顯的非整數值,它們在x === Math.floor(x)
中可能會被混淆:
Number.isInteger( NaN ); // false
Number.isInteger( Infinity ); // false
有時候處理“整數”是信息的重點,它可以簡化特定的算法。由於為了僅留下整數而進行過濾,JS代碼本身不會運行得更快,但是當僅有整數被使用時引擎可以采取幾種優化技術(例如,asm.js)。
因為Number.isInteger(..)
對Nan
和Infinity
值的處理,定義一個isFloat(..)
工具並不像!Number.isInteger(..)
一樣簡單。你需要這麽做:
function isFloat(x) {
return Number.isFinite( x ) && !Number.isInteger( x );
}
isFloat( 4.2 ); // true
isFloat( 4 ); // false
isFloat( NaN ); // false
isFloat( Infinity ); // false
註意: 這看起來可能很奇怪,但是無窮即不應當被認為是整數也不應當被認為是浮點數。
ES6還定義了一個Number.isSafeInteger(..)
工具,它檢查一個值以確保它是一個整數並且在Number.MIN_SAFE_INTEGER
-Number.MAX_SAFE_INTEGER
的範圍內(包含兩端)。
var x = Math.pow( 2, 53 ),
y = Math.pow( -2, 53 );
Number.isSafeInteger( x - 1 ); // true
Number.isSafeInteger( y + 1 ); // true
Number.isSafeInteger( x ); // false
Number.isSafeInteger( y ); // false
String
在ES6之前字符串就已經擁有好幾種幫助函數了,但是有更多的內容被加入了進來。
Unicode 函數
在第二章的“Unicode敏感的字符串操作”中詳細討論了String.fromCodePoint(..)
,String#codePointAt(..)
,String#normalize(..)
。它們被用來改進JS字符串值對Unicode的支持。
String.fromCodePoint( 0x1d49e ); // "??"
"ab??d".codePointAt( 2 ).toString( 16 ); // "1d49e"
normalize(..)
字符串原型方法用來進行Unicode規範化,它將字符與相鄰的“組合標誌”進行組合,或者將組合好的字符拆開。
一般來說,規範化不會對字符串的內容產生視覺上的影響,但是會改變字符串的內容,這可能會影響length
屬性報告的結果,以及用位置訪問字符的行為:
var s1 = "e\u0301";
s1.length; // 2
var s2 = s1.normalize();
s2.length; // 1
s2 === "\xE9"; // true
normalize(..)
接受一個可選參數值,它用於指定使用的規範化形式。這個參數值必須是下面四個值中的一個:"NFC"
(默認),"NFD"
,"NFKC"
,或者"NFKD"
。
註意: 規範化形式和它們在字符串上的效果超出了我們要在這裏討論的範圍。更多細節參見“Unicode規範化形式”(http://www.unicode.org/reports/tr15/)。
String.raw(..)
靜態函數
String.raw(..)
工具被作為一個內建的標簽函數來與字符串字面模板(參見第二章)一起使用,取得不帶有任何轉譯序列處理的未加工的字符串值。
這個函數幾乎永遠不會被手動調用,但是將與被標記的模板字面量一起使用:
var str = "bc";
String.raw`\ta${str}d\xE9`;
// "\tabcd\xE9", not " abcdé"
在結果字符串中,`和
t是分離的未被加工過的字符,而不是一個轉譯字符序列
\t`。這對Unicode轉譯序列也是一樣。
repeat(..)
原型函數
在Python和Ruby那樣的語言中,你可以這樣重復一個字符串:
"foo" * 3; // "foofoofoo"
在JS中這不能工作,因為*
乘法是僅對數字定義的,因此"foo"
會被強制轉換為NaN
數字。
但是,ES6定義了一個字符串原型方法repeat(..)
來完成這個任務:
"foo".repeat( 3 ); // "foofoofoo"
字符串檢驗函數
作為對ES6以前的String#indexOf(..)
和String#lastIndexOf(..)
的補充,增加了三個新的搜索/檢驗函數:startsWith(..)
,endsWidth(..)
,和includes(..)
。
var palindrome = "step on no pets";
palindrome.startsWith( "step on" ); // true
palindrome.startsWith( "on", 5 ); // true
palindrome.endsWith( "no pets" ); // true
palindrome.endsWith( "no", 10 ); // true
palindrome.includes( "on" ); // true
palindrome.includes( "on", 6 ); // false
對於所有這些字符串搜索/檢驗方法,如果你查詢一個空字符串""
,那麽它將要麽在字符串的開頭被找到,要麽就在字符串的末尾被找到。
警告: 這些方法默認不接受正則表達式作為檢索字符串。關於關閉實施在第一個參數值上的isRegExp
檢查的信息,參見第七章的“正則表達式Symbol”。
復習
ES6在各種內建原生對象上增加了許多額外的API幫助函數:
Array
增加了of(..)
和from(..)
之類的靜態函數,以及copyWithin(..)
和fill(..)
之類的原型函數。Object
增加了is(..)
和assign(..)
之類的靜態函數。Math
增加了acosh(..)
和clz32(..)
之類的靜態函數。Number
增加了Number.EPSILON
之類的靜態屬性,以及Number.isFinite(..)
之類的靜態函數。String
增加了String.fromCodePoint(..)
和String.raw(..)
之類的靜態函數,以及repeat(..)
和includes(..)
之類的原型函數。
這些新增函數中的絕大多數都可以被填補(參見ES6 Shim),它們都是受常見的JS庫/框架中的工具啟發的。
es6新增的api