整数的工作机制
在之前类型化数组里有讨论到关于js存储数字类型的不同,IEEE 754
引起了很多的混乱。js
需要付出巨大努力才能确保开发人员不必担心数字编码的细节,但问题仍然不时泄漏。ES 6
旨在通过使整数更容易识别和使用来解决这个问题。
识别整数
ES6
添加了Number.isInteger
方法来表示是否整数。虽然js
使用IEEE 754
编码系统来表示整数和浮点型,但是他们的存储有些不同,isInteger
就是利用这一点,去检查值是不是一个整数。这就意味着看起来是浮点型的数字,可能存储的是整数,导致isInteger
返回的是true
。
console.log(Number.isInteger(25)); // true
console.log(Number.isInteger(25.0)); // true
console.log(Number.isInteger(25.1)); // false
Safe Integers
IEEE 754
只能准确地表示-2的53次方
和2的53次方
之间的整数,并且在这个“安全”范围之外,二进制最终会复用于多个数值。
这就意味着js
只能表示这个安全范围内的数字:
console.log(Math.pow(2, 53)); // 9007199254740992
console.log(Math.pow(2, 53) + 1); // 9007199254740992
可以发现,超出这个范围就不行了。
ES6
通过Number.isSafeInteger
方法去识别一个数字是不是安全的。Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
表示最大和最小的值。
看下面这个例子:
var inside = Number.MAX_SAFE_INTEGER,
outside = inside + 1;
console.log(Number.isInteger(inside)); // true
console.log(Number.isSafeInteger(inside)); // true
console.log(Number.isInteger(outside)); // true
console.log(Number.isSafeInteger(outside)); // false
你可以试下,MAX_SAFE_INTEGER
就是2的53次方减1
。
new math methods
ECMAScript 6
为Math
对象添加了几种方法,以提高常见数学计算的速度。提高通用计算的速度还可以提高执行许多计算的应用程序的整体速度,例如图形程序。
Math.acosh(x)返回x的反双曲余弦值。
Math.asinh(x)返回x的反双曲正弦值。
Math.atanh(x)返回x的反双曲正切
Math.cbrt(x)返回x的立方根。
Math.clz32(x)返回x的32位整数表示中的前导零位数。
Math.cosh(x)返回x的双曲余弦值。
Math.expm1(x)返回从x的指数函数中减去1的结果。
Math.fround(x)返回x的最近的单精度浮点数。
Math.hypot(... values)返回每个参数的平方和的平方根。
Math.imul(x,y)返回执行两个参数的真正32位乘法的结果。
Math.log1p(x)返回1 + x的自然对数。
Math.log10(x)返回x的基数10对数。
Math.log2(x)返回x的基数2对数。
Math.sign(x)如果x为负,则返回-1;如果x为+0或-0,则返回0;如果x为正,则返回1。
Math.sinh(x)返回x的双曲正弦值。
Math.tanh(x)返回x的双曲正切值。
Math.trunc(x)从float中移除小数位并返回一个整数。
解释每种新方法及其详细功能超出了本章的范围。但是,如果对应用程序需要进行常见的计算,请务必在自己实现之前检查新的Math方法。
Unicode Identifiers
ES6
相对之前的版本提供了更好的支持,它还可以更改可用作标识符的字符。在ECMAScript 5
中,已经可以将Unicode
转义序列用于标识符。例如:
// Valid in ECMAScript 5 and 6
var \u0061 = "abc";
console.log(\u0061); // "abc"
// equivalent to:
console.log(a); // "abc"
在此示例中的var
语句之后,可以使用\ u0061
或a
来访问该变量。在ECMAScript 6
中,还可以使用Unicode
代码点转义序列作为标识符,如下所示:
// Valid in ECMAScript 5 and 6
var \u{61} = "abc";
console.log(\u{61}); // "abc"
// equivalent to:
console.log(a); // "abc"
此外,ECMAScript 6
根据Unicode
标准附件#31:Unicode标识符和模式语法正式指定了有效标识符,它提供了以下规则:
- 第一个字符必须是
$
,_
或任何带有ID_Start
的派生核心属性的Unicode
符号。 - 每个后续字符必须是
$
,_
,\ u200c
(零宽度非连接符),\ u200d
(零宽度连接符),或具有ID_Continue
的派生核心属性的任何Unicode
符号。
ID_Start
和ID_Continue
派生的核心属性在Unicode
标识符和模式语法中定义,作为识别适用于变量和域名等标识符的符号的方法。该规范不是特定于JavaScript。
Formalizing the proto Property
甚至在ECMAScript 5``完成之前,几个JavaScript
引擎已经实现了一个名为__proto__
的自定义属性,可用于获取和设置[[Prototype]]
属性。实际上,__ proto__
是Object.getPrototypeOf
和Object.setPrototypeOf
方法的早期前身。期望所有JavaScript
引擎删除此属性是不现实的(有流行的JavaScript
库使用__proto__
),因此ECMAScript
6也正式化了__proto__
行为。但正式化出现在ECMA-262的附录B
中以及此警告:
这些功能不被视为核心
ECMAScript
语言的一部分。编写新的ECMAScript
代码时,程序员不应使用或假设存在这些特性和行为。不鼓励ECMAScript
实现这些功能,除非实现是Web
浏览器的一部分或者需要运行Web
浏览器遇到的相同的旧ECMAScript
代码。
ECMAScript
规范建议使用Object.getPrototypeOf
和Object.setPrototypeOf
,因为__proto__
具有以下特征:
- 只能在对象文字中指定
__proto__
一次。如果指定两个__proto__
属性,则会引发错误。这是具有该限制的唯一对象文字属性。 - 计算形式
[“__ proto__”]
的作用类似于常规属性,不会设置或返回当前对象的原型。与对象文字属性相关的所有规则都适用于此形式,而非计算形式则具有例外。
虽然应该避免使用__proto__
属性,但规范定义它的方式很有趣。在ECMAScript 6
引擎中,Object.prototype .__ proto__
被定义为一个访问器属性,其get
方法调用Object.getPrototypeOf
并且其set
方法调用Object.setPrototypeOf
方法。这与使用__proto__
和Object.getPrototypeOf/ Object.setPrototypeOf
之间没有什么区别,只是__proto__
允许直接设置对象文字的原型。看下面例子:
let person = {
getGreeting() {
return "Hello";
}
};
let dog = {
getGreeting() {
return "Woof";
}
};
// prototype is person
let friend = {
__proto__: person
};
console.log(friend.getGreeting()); // "Hello"
console.log(Object.getPrototypeOf(friend) === person); // true
console.log(friend.__proto__ === person); // true
// set prototype to dog
friend.__proto__ = dog;
console.log(friend.getGreeting()); // "Woof"
console.log(friend.__proto__ === dog); // true
console.log(Object.getPrototypeOf(friend) === dog); // true
此示例不是调用Object.create
来创建friend
对象,而是创建一个标准对象文字,为__proto__
属性赋值。使用Object.create
方法创建对象时,必须为任何其他对象属性指定完整属性描述符。不够直接。