ECMA-262对内置对象的定义是“任何由ECMAScript实现提供、与宿主 环境无关,并在ECMAScript程序开始执行时就存在的对象”。这就意味着, 开发者不用显式地实例化内置对象,因为它们已经实例化好了。前面我们已 经接触了大部分内置对象,包括 Object 、 Array 和 String 。本节介 绍ECMA-262定义的另外两个单例内置对象: Global 和 Math 。
1. Global
Global 对象是ECMAScript中最特别的对象,因为代码不会显式地访 问它。ECMA-262规定 Global 对象为一种兜底对象,它所针对的是不属于 任何对象的属性和方法。事实上,不存在全局变量或全局函数这种东西。在 全局作用域中定义的变量和函数都会变成 Global 对象的属性 。包括 isNaN() 、 isFinite() 、 parseInt() 和 parseFloat() 等,实际上都是 Global 对象的方法。除了这些, Global 对象上还有另外一些方法。
eval() 方法
这个方法就是一个完整的ECMAScript解释器,它接收一个 参数,即一个要执行的ECMAScript(JavaScript)字符串。来看一个例 子:
eval("console.log('hi')");
//等价于
console.log("hi");
当解释器发现 eval() 调用时,会将参数解释为实际的ECMAScript语 句,然后将其插入到该位置。通过 eval() 执行的代码属于该调用所 在上下文,被执行的代码与该上下文拥有相同的作用域链。这意味着定 义在包含上下文中的变量可以在 eval() 调用内部被引用,比如下面 这个例子:
let msg = "hello world";
eval("console.log(msg)"); // "hello world"
这里,变量 msg 是在 eval() 调用的外部上下文中定义的,而 console.log() 显示了文本 “hello world” 。这是因为第二行 代码会被替换成一行真正的函数调用代码。类似地,可以在 eval() 内部定义一个函数或变量,然后在外部代码中引用,如下所示:
eval("function sayHi() { console.log('hi'); }");
sayHi();
这里,函数 sayHi() 是在 eval() 内部定义的。因为该调用会被替 换为真正的函数定义,所以才可能在下一行代码中调用 sayHi() 。对 于变量也是一样的
eval("let msg = 'hello world';");
console.log(msg); // Reference Error: msg is not defined
通过 eval() 定义的任何变量和函数都不会被提升,这是因为在解析 代码的时候,它们是被包含在一个字符串中的。它们只是在 eval() 执行的时候才会被创建。
在严格模式下,在 eval() 内部创建的变量和函数无法被外部访问。 换句话说,最后两个例子会报错。同样,在严格模式下,赋值给 eval 也会导致错误:
"use strict";
eval = "hi"; // 导致错误
Global对象的属性
Global 对象有很多属性,其中一些前面已经提到过了。像 undefined 、 NaN 和 Infinity 等特殊值都是 Global 对象的属性。此外,所有原生引用类型构造函数,比如 Object 和 Function ,也都是 Global 对象的属性。下表列出了所有这些属 性。
window 对象
虽然ECMA-262没有规定直接访问 Global 对象的方式,但浏览器将 window 对象实现为 Global 对象的代理。因此,所有全局作用域中 声明的变量和函数都变成了 window 的属性。
2. Math算数运算
Math
是 JavaScript 的原生对象,提供各种数学功能。该对象不是构造函数,不能生成实例,它是纯静态对象也就是不能被实例化,只能通过静态方法的方式调用(所有的属性和方法都必须在Math对象上调用)。
//javascript算数运算
Math.pow(2,53) // => 9007199254740992: 2 的 53次幂
Math.round(.6) // => 1.0: 四舍五入
Math.ceil(.6) // => 1.0: 向上求整
Math.floor(.6) // => 0.0: 向下求整
Math.abs(-5) // => 5: 求绝对值
Math.max(x,y,z) // 返回最大值
Math.min(x,y,z) // 返回最小值
Math.random() // 生成一个大于等于0小于1.0的伪随机数
Math.PI // π: 圆周率
Math.E // e: 自然对数的底数
Math.sqrt(3) // 3的平方根
Math.pow(3, 1/3) // 3的立方根
Math.sin(0) // 三角函数: 还有Math.cos, Math.atan等
Math.log(10) // 10的自然对数
Math.log(100)/Math.LN10 // 以10为底100的对数
Math.log(512)/Math.LN2 // 以2为底512的对数
Math.exp(3) // e的三次幂
Math.max 返回最大值
Math.max() 是内置的 JavaScript 函数,从给定的一组数字中返回最大值。
其语法如下:
Math.max(value1[, value2, ...])
不带参数的 Math.max()
Math.max(); // => -Infinity
乍看之下上述代码的执行结果似乎有点出人意料,在没有参数的情况下调用 Math.max() 会返回 -Infinity。让我们来看看为什么会发生这种情况,以及为什么要发生这种情况。
如果要确定一个数组的最大数字,可以在数组上使用扩展运算符:
const numbers1 = [1, 2, 3];
Math.max(...numbers1); // => 3
现在,让我们尝试从给定两个数字数组中,确定每个数组的最大数,然后确定这两个最大值中的最大值。
const numbers1 = [1, 2, 3];
const numbers2 = [0, 6];
const max1 = Math.max(...numbers1);
const max2 = Math.max(...numbers2);
max1; // 3
max2; // 6
Math.max(max1, max2); // => 6
如果其中一个数组为空,如何尝试确定最大数组呢?
const numbers1 = [];
const numbers2 = [0, 6];
const max1 = Math.max(...numbers1);
const max2 = Math.max(...numbers2);
max1; // -Infinity
max2; // 6
Math.max(max1, max2); // => 6
从上述代码可以看出,Math.max(…[]) 与不带参数的 Math.max() 结果相同,均为 -Infinity。
然后 Math.max(max1, max2) 即 Math.max(-Infinity, 6),其结果为 6
现在就很清楚为什么在不带参数的情况下 Math.max() 返回 -Infinity 了,无论 numbers2 的最大值是多少,为了让空数组 numbers1(等同于不带参数的 Math.max())的最大值小于 numbers2 的最大值,numbers1 的最大值只能是负无穷,即 -Infinity。
同理,Math.min() return Infinity。
也因此:
Math.min() > Math.max(); // => true
Math.random
返回介于 0(包含) ~ 1(不包含) 之间的一个伪随机数
// 请生成一个16位的随机数字;
console.log(String(Math.random()).split('.')[1].slice(0, 16));
因为 Math.random 不能提供像密码一样安全的随机数字,所以不要使用它来处理有关安全的事情。针对信息安全的场景,你可以使用 Web Crypto API 来代替,并使用更精确的 [window.crypto.getRandomValues()](https://www.yuque.com/southerly/deepweb/rmgxkg)
方法。
Math.sign 获取数字符号 ES6
Math.sign()函数返回一个数字的符号, 指示数字是正数,负数还是零。
此函数共有5种返回值, 分别是 1, -1, 0, -0, NaN. 代表的分别是正数, 负数, 正零, 负零, NaN。
传入该函数的参数会被隐式转换成数字类型。
Math.sign(3); // 1
Math.sign(-3); // -1
Math.sign("-3"); // -1
Math.sign(0); // 0
Math.sign(-0); // -0
Math.sign(-Infinity); // -1
Math.sign(Infinity); // 1
Math.sign(NaN); // NaN
Math.sign("foo"); // NaN
Math.sign(); // NaN
对于不支持的浏览器可以使用下面的Polyfill:
if (!Math.sign) {
Math.sign = function(x) {
// 如果 x 是 NaN, 结果是 NaN.
// 如果 x 是 -0,结果是 -0.
// 如果 x 是 +0,结果是 +0.
// 如果 x 是 负数但不是 -0,结果是 -1.
// 如果 x 是 正数但不是 +0,结果是 +1.
x = +x; // 转换成数值
if (x === 0 || isNaN(x)) {
return Number(x);
}
return x > 0 ? 1 : -1;
};
}
Math.trunc 只保留整数部分 ES6
单词trunc是截断截取的意思,Math.trunc()方法会将数字的小数部分去掉,只保留整数部分。
不像 Math 的其他三个方法: Math.floor()、Math.ceil()、Math.round() ,Math.trunc()的执行逻辑很简单,仅仅是删除掉数字的小数部分和小数点,不管参数是正数还是负数。
传入该方法的参数会被隐式转换成数字类型。
Math.trunc(13.37) // 13
Math.trunc(42.84) // 42
Math.trunc(0.123) // 0
Math.trunc(-0.123) // -0
Math.trunc("-1.123") // -1
Math.trunc(NaN) // NaN
Math.trunc("foo") // NaN
Math.trunc() // NaN
如果想要在IE浏览器中使用,可以试试下面的Polyfill代码:
if (!Math.trunc) {
Math.trunc = function(v) {
v = +v;
return (v - v % 1) || (!isFinite(v) || v === 0 ? v : v < 0 ? -0 : 0);
};
}
Math.cbrt 返回任意数字的立方根 ES6
Math.cbrt()函数返回任意数字的立方根。
Math.cbrt(8); // 2
Math.cbrt(NaN); // NaN
Math.cbrt(-1); // -1
Math.cbrt(-0); // -0
Math.cbrt(-Infinity); // -Infinity
Math.cbrt(0); // 0
Math.cbrt(1); // 1
Math.cbrt(Infinity); // Infinity
Math.cbrt(null); // 0
Math.cbrt(2); // 1.2599210498948734
Polyfill代码如下,可以兼容老旧的浏览器:
if (!Math.cbrt) {
Math.cbrt = function(x) {
var y = Math.pow(Math.abs(x), 1/3);
return x < 0 ? -y : y;
};
}
Math.expm1(x)ES6
Math.expm1() 函数返回 Ex - 1,其中 x 是该函数的参数, E 是自然对数的底数 2.718281828459045。
Math.expm1()中的expm1 是 “exponent minus 1” 的缩写,语义上等同于Math.exp(x)-1,但是实际上两者还是有区别的,当Math.exp()的结果接近于1的时候,Math.expm1()的精度更高,例如:
Math.expm1(1e-10);
// 1.00000000005e-10
Math.exp(1e-10) - 1;
// 1.000000082740371e-10
使用示意:
Math.expm1(-1); // -0.6321205588285577
Math.expm1(0); // 0
Math.expm1(1) // 1.7182818284590453
Math.expm1(-38) // -1
Math.expm1("-38") // -1
Math.expm1("foo") // NaN
Polyfill代码如下,可以兼容Internet Explorer浏览器:
Math.expm1 = Math.expm1 || function(x) {
return Math.exp(x) - 1;
};
推荐阅读
ES6 Math方法和Number新特性简介
https://www.zhangxinxu.com/wordpress/2020/04/es6-math-number/