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)字符串。来看一个例 子:

  1. eval("console.log('hi')");
  2. //等价于
  3. console.log("hi");

当解释器发现 eval() 调用时,会将参数解释为实际的ECMAScript语 句,然后将其插入到该位置。通过 eval() 执行的代码属于该调用所 在上下文,被执行的代码与该上下文拥有相同的作用域链。这意味着定 义在包含上下文中的变量可以在 eval() 调用内部被引用,比如下面 这个例子:

  1. let msg = "hello world";
  2. eval("console.log(msg)"); // "hello world"

这里,变量 msg 是在 eval() 调用的外部上下文中定义的,而 console.log() 显示了文本 “hello world” 。这是因为第二行 代码会被替换成一行真正的函数调用代码。类似地,可以在 eval() 内部定义一个函数或变量,然后在外部代码中引用,如下所示:

  1. eval("function sayHi() { console.log('hi'); }");
  2. sayHi();

这里,函数 sayHi() 是在 eval() 内部定义的。因为该调用会被替 换为真正的函数定义,所以才可能在下一行代码中调用 sayHi() 。对 于变量也是一样的

  1. eval("let msg = 'hello world';");
  2. console.log(msg); // Reference Error: msg is not defined

通过 eval() 定义的任何变量和函数都不会被提升,这是因为在解析 代码的时候,它们是被包含在一个字符串中的。它们只是在 eval() 执行的时候才会被创建。

在严格模式下,在 eval() 内部创建的变量和函数无法被外部访问。 换句话说,最后两个例子会报错。同样,在严格模式下,赋值给 eval 也会导致错误:

  1. "use strict";
  2. eval = "hi"; // 导致错误

Global对象的属性

Global 对象有很多属性,其中一些前面已经提到过了。像 undefined 、 NaN 和 Infinity 等特殊值都是 Global 对象的属性。此外,所有原生引用类型构造函数,比如 Object 和 Function ,也都是 Global 对象的属性。下表列出了所有这些属 性。
image.png

window 对象

虽然ECMA-262没有规定直接访问 Global 对象的方式,但浏览器将 window 对象实现为 Global 对象的代理。因此,所有全局作用域中 声明的变量和函数都变成了 window 的属性。
image.png

2. Math算数运算

Math是 JavaScript 的原生对象,提供各种数学功能。该对象不是构造函数,不能生成实例,它是纯静态对象也就是不能被实例化,只能通过静态方法的方式调用(所有的属性和方法都必须在Math对象上调用)。

  1. //javascript算数运算
  2. Math.pow2,53 // => 9007199254740992: 2 的 53次幂
  3. Math.round(.6 // => 1.0: 四舍五入
  4. Math.ceil(.6 // => 1.0: 向上求整
  5. Math.floor(.6 // => 0.0: 向下求整
  6. Math.abs(-5 // => 5: 求绝对值
  7. Math.maxx,y,z // 返回最大值
  8. Math.minx,y,z // 返回最小值
  9. Math.random() // 生成一个大于等于0小于1.0的伪随机数
  10. Math.PI // π: 圆周率
  11. Math.E // e: 自然对数的底数
  12. Math.sqrt3 // 3的平方根
  13. Math.pow3, 1/3 // 3的立方根
  14. Math.sin0 // 三角函数: 还有Math.cos, Math.atan等
  15. Math.log10 // 10的自然对数
  16. Math.log100)/Math.LN10 // 以10为底100的对数
  17. Math.log512)/Math.LN2 // 以2为底512的对数
  18. Math.exp3 // e的三次幂

Math.max 返回最大值

Math.max() 是内置的 JavaScript 函数,从给定的一组数字中返回最大值。

其语法如下:

  1. Math.max(value1[, value2, ...])

不带参数的 Math.max()

  1. Math.max(); // => -Infinity

乍看之下上述代码的执行结果似乎有点出人意料,在没有参数的情况下调用 Math.max() 会返回 -Infinity。让我们来看看为什么会发生这种情况,以及为什么要发生这种情况。

如果要确定一个数组的最大数字,可以在数组上使用扩展运算符:

  1. const numbers1 = [1, 2, 3];
  2. Math.max(...numbers1); // => 3

现在,让我们尝试从给定两个数字数组中,确定每个数组的最大数,然后确定这两个最大值中的最大值。

  1. const numbers1 = [1, 2, 3];
  2. const numbers2 = [0, 6];
  3. const max1 = Math.max(...numbers1);
  4. const max2 = Math.max(...numbers2);
  5. max1; // 3
  6. max2; // 6
  7. Math.max(max1, max2); // => 6

如果其中一个数组为空,如何尝试确定最大数组呢?

  1. const numbers1 = [];
  2. const numbers2 = [0, 6];
  3. const max1 = Math.max(...numbers1);
  4. const max2 = Math.max(...numbers2);
  5. max1; // -Infinity
  6. max2; // 6
  7. 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。

也因此:

  1. Math.min() > Math.max(); // => true

Math.random

返回介于 0(包含) ~ 1(不包含) 之间的一个伪随机数

image.png

  1. // 请生成一个16位的随机数字;
  2. 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在ES6中新增了几个方法。

Math.sign 获取数字符号 ES6

Math.sign()函数返回一个数字的符号, 指示数字是正数,负数还是零。

此函数共有5种返回值, 分别是 1, -1, 0, -0, NaN. 代表的分别是正数, 负数, 正零, 负零, NaN。

传入该函数的参数会被隐式转换成数字类型。

  1. Math.sign(3); // 1
  2. Math.sign(-3); // -1
  3. Math.sign("-3"); // -1
  4. Math.sign(0); // 0
  5. Math.sign(-0); // -0
  6. Math.sign(-Infinity); // -1
  7. Math.sign(Infinity); // 1
  8. Math.sign(NaN); // NaN
  9. Math.sign("foo"); // NaN
  10. Math.sign(); // NaN

对于不支持的浏览器可以使用下面的Polyfill:

  1. if (!Math.sign) {
  2. Math.sign = function(x) {
  3. // 如果 x 是 NaN, 结果是 NaN.
  4. // 如果 x 是 -0,结果是 -0.
  5. // 如果 x 是 +0,结果是 +0.
  6. // 如果 x 是 负数但不是 -0,结果是 -1.
  7. // 如果 x 是 正数但不是 +0,结果是 +1.
  8. x = +x; // 转换成数值
  9. if (x === 0 || isNaN(x)) {
  10. return Number(x);
  11. }
  12. return x > 0 ? 1 : -1;
  13. };
  14. }

Math.trunc 只保留整数部分 ES6

单词trunc是截断截取的意思,Math.trunc()方法会将数字的小数部分去掉,只保留整数部分。

不像 Math 的其他三个方法: Math.floor()、Math.ceil()、Math.round() ,Math.trunc()的执行逻辑很简单,仅仅是删除掉数字的小数部分和小数点,不管参数是正数还是负数。

传入该方法的参数会被隐式转换成数字类型。

  1. Math.trunc(13.37) // 13
  2. Math.trunc(42.84) // 42
  3. Math.trunc(0.123) // 0
  4. Math.trunc(-0.123) // -0
  5. Math.trunc("-1.123") // -1
  6. Math.trunc(NaN) // NaN
  7. Math.trunc("foo") // NaN
  8. Math.trunc() // NaN

如果想要在IE浏览器中使用,可以试试下面的Polyfill代码:

  1. if (!Math.trunc) {
  2. Math.trunc = function(v) {
  3. v = +v;
  4. return (v - v % 1) || (!isFinite(v) || v === 0 ? v : v < 0 ? -0 : 0);
  5. };
  6. }

Math.cbrt 返回任意数字的立方根 ES6

Math.cbrt()函数返回任意数字的立方根。

  1. Math.cbrt(8); // 2
  2. Math.cbrt(NaN); // NaN
  3. Math.cbrt(-1); // -1
  4. Math.cbrt(-0); // -0
  5. Math.cbrt(-Infinity); // -Infinity
  6. Math.cbrt(0); // 0
  7. Math.cbrt(1); // 1
  8. Math.cbrt(Infinity); // Infinity
  9. Math.cbrt(null); // 0
  10. Math.cbrt(2); // 1.2599210498948734

Polyfill代码如下,可以兼容老旧的浏览器:

  1. if (!Math.cbrt) {
  2. Math.cbrt = function(x) {
  3. var y = Math.pow(Math.abs(x), 1/3);
  4. return x < 0 ? -y : y;
  5. };
  6. }

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()的精度更高,例如:

  1. Math.expm1(1e-10);
  2. // 1.00000000005e-10
  3. Math.exp(1e-10) - 1;
  4. // 1.000000082740371e-10

使用示意:

  1. Math.expm1(-1); // -0.6321205588285577
  2. Math.expm1(0); // 0
  3. Math.expm1(1) // 1.7182818284590453
  4. Math.expm1(-38) // -1
  5. Math.expm1("-38") // -1
  6. Math.expm1("foo") // NaN

Polyfill代码如下,可以兼容Internet Explorer浏览器:

  1. Math.expm1 = Math.expm1 || function(x) {
  2. return Math.exp(x) - 1;
  3. };

推荐阅读

ES6 Math方法和Number新特性简介

https://www.zhangxinxu.com/wordpress/2020/04/es6-math-number/
image.png