前言
本文参考了 《你不知道的JavaScript(下)》
从值的转换到数学计算,ES6 为各种内置原生类型和对象新增了很多静态属性和方法,用 来辅助完成一些常见的任务。另外,某些原生类型的实例通过新的原型方法有了新的功能。
本文会介绍 Array
,Object
,Number
,String
四个内置对象新增的一些api
,在不同的阶段读相同的书会有不一样的收获,也许这一次的认真思考后我们会多一种实现方式。
Array
静态函数 Array.of(..)
Array(..)
构造器有一个众所周知的陷阱,就是如果只传入一个参数,并且这个参数是数 字的话,那么不会构造一个值为这个数字的单个元素的数组,而是构造一个空数组,其 length
属性为这个数字。这个动作会产生不幸又诡异的“空槽”行为,这是 JavaScript
数组广为人所诟病的一点。
Array.of(..)
取代了 Array(..)
成为数组的推荐函数形式构造器,因为 Array.of(..)
并没 有这个特殊的单个数字参数的问题。考虑:
// Array(..)
var a = Array(3); // [empty × 3]
a.length; // 3
a[0]; // undefined
// Array.of(..)
var b = Array.of(3); // [3]
b.length; // 1
b[0]; // 3
静态函数 Array.from(..)
JavaScript 中的“类(似)数组对象”是指一个有 length 属性。但不能使用数组的其他api
,比如函数的arguments
, 还有NodeList
。
普遍的需求就是把它们转 换为真正的数组,这样就可以应用各种 Array.prototype
方法。Array.from(..)
便是优雅简洁的实现方法
function test(){
console.log(Array.from(arguments).map(item => item))
}
test(1,2,3,4) // [1,2,3,4]
原型方法 copyWithin(..)
Array#copyWithin(..)
是一个新的修改器方法,(包括带类型的数组在内的) 所有数组都支持。copyWithin(..)
从一个数组中复制一部分到同一个数组的另一个位置, 覆盖这个位置所有原来的值。
参数是 target
(要复制到的索引)、start
(开始复制的源索引,包括在内)以及可选的 end
(复制结束的不包含索引)。如果任何一个参数是负数,就被当作是相对于数组结束的相 对值。 考虑:
[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]
原型方法 fill(...)
可以通过 ES6
原生支持的方法 Array#fill(..)
用指定值完全(或部分)填充已存在的数 组:
考虑如下代码
// Array(..)
var a = Array(3); // [empty × 3]
a.length; // 3
a[0]; // undefined
现在我们希望数组每一项默认用{}
来填充
var a = Array(3).fill({}) // [{},{},{}]
// fill(..) 可选地接收参数 start 和 end,它们指定了数组要填充的子集位置,比如:
var b = Array(3).fill({} , 1,2) // [empty, {}, empty]
原型方法 entries()
、values()
、keys()
以从传统角度来说,它可能不会被看作是 “集合”,但是它提供了同样的迭代器方法 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] ]
Object
静态函数 Object.is(..)
静态函数 Object.is(..)
执行比 ===
比较更严格的值比较。考虑:
var x = NaN, y = 0, z = -0;
x === x; // false
y === z; // true
Object.is( x, x ); // true
Object.is( y, z ); // false
如果需要严格识别 NaN
或者 -0
值,那么应该选择 Object.is(..)
。
静态函数 Object.getOwnPropertySymbols(..)
Symbol
很 可 能 会 成 为 对 象 最 常 用 的 特 殊( 元 ) 属 性。 所 以 引 入 了 工 具 Object. getOwnPropertySymbols(..)
,它直接从对象上取得所有的符号属性:
var o = {
foo: 42,
[ Symbol( "bar" ) ]: "hello world",
baz: true
};
Object.getOwnPropertySymbols( o ); // [ Symbol(bar) ]
静态函数Object.setPrototypeOf(..)
设置对象的[[Prototype]]
用于行为委托 , 考虑:
var o1 = {
foo() { console.log( "foo" );
}
};
var o2 = {
// .. o2的定义 ..
};
Object.setPrototypeOf( o2, o1 ); // 委托给o1.foo()
o2.foo(); // foo
静态函数 Object.assign(..)
很多 JavaScript
库 / 框架提供了用于把一个对象的属性复制 / 混合到另一个对象中的工具 (比如,JQuery
的 extent(..)
)。这些不同的工具之间有各种细微的区别,比如是否忽略值 为 undefined
的属性。
ES6
新增了 Object.assign(..)
,这是这些算法的简化版本。第一个参数是 target
,其他 传入的参数都是源,它们将按照列出的顺序依次被处理。对于每个源来说,它的可枚举 和自己拥有的(也就是不是“继承来的”)键值,包括符号都会通过简单 = 赋值被复制。 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;
// 设定不可枚举符号
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")]
Number
更重要的是,要让程序正常工作,必须精确处理数字。
ES6
新增了额外的属性和函数来提 供常用数字运算。
对 Number
的两个新增内容就是指向已有的全局函数的引用:Number.parseInt(..)
和 Number.parseFloat(..)
。
Number
的静态属性
Number.EPSILON
任意两个值之间的最小差:2^-52
Number.MAX_SAFE_INTEGER
JavaScript
可以用数字值无歧义“安全”表达的最大整数:2^53 - 1
Number.MIN_SAFE_INTEGER
JavaScript
可以用数字值无歧义“安全”表达的最小整数:-(2^53 - 1) 或 (-2)^53 + 1
静态函数 Number.isNaN(..)
标准全局工具 isNaN(..)
自出现以来就是有缺陷的,它对非数字的东西都会返回 true
,而 不是只对真实的 NaN
值返回 true
,因为它把参数强制转换为数字类型(可能会错误地导致NaN
)。
ES6
增加了一个修正工具 Number.isNaN(..)
,可以按照期望工作:
var a = NaN, b = "NaN", c = 42;
isNaN( a ); // true
isNaN( b ); // true--oops!
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
String
静态函数String.raw(..)
String.raw(..)
工具作为内置标签函数提供,与模板字符串字面值一起使 用,用于获得不应用任何转义序列的原始字符串。
这个函数基本上不会被手动调用,而是与标签模板字面值一起使用:
var str = "bc";
String.raw`\ta${str}d\xE9`; // "\tabcd\xE9", 而不是" abcdé"
原型函数 repeat(..)
像 Python
和 Ruby
这样的语言中,可以这样重复字符串: "foo" * 3; // "foofoofoo"
JavaScript
不支持这种形式,因为乘法 * 只对数字有定义,因此 "foo"
被强制转换成了数 字 NaN。
然而,ES6
定义了一个字符串原型方法 repeat(..)
来完成这个任务:
"foo".repeat( 3 ); // "foofoofoo"
字符串检查函数
除了 ES6
之前的 String#indexOf(..)
和 String#lastIndexOf(..)
,又新增了 3 个用于搜索 / 检查的新方法: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