1 数据类型基础

1.1 JS内置类型

  • JS 中分为七种内置类型,七种内置类型又分为两大类型:基本类型和对象(Object)。
  • 基本类型有七种: nullundefinedbooleannumberstringsymbol, bigint
    • BigIntES10 新增的数据类型
    • Symbol 代表独一无二的值,最大的用法是用来定义对象的唯一属性名。
    • BigInt 可以表示任意大小的整数。
  • 其中 JS 的数字类型是浮点类型的,没有整型。并且浮点类型基于 IEEE 754标准实现,在使用中会遇到某些 Bug。NaN 也属于 number 类型,并且 NaN 不等于自身。
  • 对于基本类型来说,如果使用字面量的方式,那么这个变量只是个字面量,只有在必要的时候才会转换为对应的类型。

引用数据类型:

  • 对象Object(包含普通对象-Object,数组对象-Array,正则对象-RegExp,日期对象-Date,数学函数-Math,函数对象-Function
    let a = 111 // 这只是字面量,不是 number 类型
    a.toString() // 使用时候才会转换为对象类型

对象(Object)是引用类型,在使用过程中会遇到浅拷贝和深拷贝的问题。

    let a = { name: 'FE' }
    let b = a
    b.name = 'EF'
    console.log(a.name) // EF

说出下面运行的结果,解释原因。

    function test(person) {
      person.age = 26
      person = {
        name: 'hzj',
        age: 18
      }
      return person
    }
    const p1 = {
      name: 'fyq',
      age: 19
    }
    const p2 = test(p1)
    console.log(p1) // -> ?
    console.log(p2) // -> ?
    // 结果:
    p1:{name: “fyq”, age: 26}
    p2:{name: “hzj”, age: 18}

原因: 在函数传参的时候传递的是对象在堆中的内存地址值,test函数中的实参person是p1对象的内存地址,通过调用person.age = 26确实改变了p1的值,但随后person变成了另一块内存空间的地址,并且在最后将这另外一份内存空间的地址返回,赋给了p2。

1.2 null和undefined区别

Undefined类型只有一个值,即undefined。当声明的变量还未被初始化时,变量的默认值为undefined。用法

  • 变量被声明了,但没有赋值时,就等于undefined
  • 调用函数时,应该提供的参数没有提供,该参数等于undefined
  • 对象没有赋值的属性,该属性的值为undefined
  • 函数没有返回值时,默认返回undefined

Null类型也只有一个值,即nullnull用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。用法

  • 作为函数的参数,表示该函数的参数不是对象。
  • 作为对象原型链的终点

1.3 null是对象吗?为什么?

结论: null不是对象。

解释: 虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象然而 null 表示为全零,所以将它错误的判断为 object 。

1.4 '1'.toString()为什么可以调用?

其实在这个语句运行的过程中做了这样几件事情:

    var s = new Object('1');
    s.toString();
    s = null;
  • 第一步: 创建Object类实例。注意为什么不是String ? 由于Symbol和BigInt的出现,对它们调用new都会报错,目前ES6规范也不建议用new来创建基本类型的包装类。
  • 第二步: 调用实例方法。
  • 第三步: 执行完方法立即销毁这个实例。

整个过程体现了基本包装类型的性质,而基本包装类型恰恰属于基本数据类型,包括Boolean, Number和String。

1.5 0.1+0.2为什么不等于0.3?如何让其相等

0.10.2在转换成二进制后会无限循环,由于标准位数的限制后面多余的位数会被截掉,此时就已经出现了精度的损失,相加后因浮点数小数位的限制而截断的二进制数字在转换为十进制就会变成0.30000000000000004

我们都知道计算机时是通过二进制来进行计算的,即 01

  • 就拿 0.1 + 0.2 来说,0.1表示为0.0001100110011001...,而0.2表示为0.0011001100110011...
  • 而在二进制中 1 + 1 = 10,所以 0.1 + 0.2 = 0.0100110011001100...
  • 转成10进制就近似表示为 0.30000000000000004

简单来说就是,浮点数转成二进制时丢失了精度,因此在二进制计算完再转回十进制时可能会和理论结果不同

1. ES6提供的Number.EPSILON方法

    function isEqual(a, b) {
      return Math.abs(a - b) < Number.EPSILON;
    }
    
    console.log(isEqual(0.1 + 0.2, 0.3)); // true

Number.EPSILON 的实质是一个可以接受的最小误差范围,一般来说为 Math.pow(2, -52)

2. 乘以一个10的幂次方

把需要计算的数字乘以10n次方,让数值都变为整数,计算完后再除以10n次方,这样就不会出现浮点数精度丢失问题

    (0.1*10 + 0.2*10) / 10 == 0.3 //true

1.6 如何理解BigInt

什么是BigInt?

BigInt是一种新的数据类型,用于当整数值大于Number数据类型支持的范围时。这种数据类型允许我们安全地对大整数执行算术操作,表示高分辨率的时间戳,使用大整数id,等等,而不需要使用库。

为什么需要BigInt?

在JS中,所有的数字都以双精度64位浮点格式表示,那这会带来什么问题呢?

这导致JS中的Number无法精确表示非常大的整数,它会将非常大的整数四舍五入,确切地说,JS中的Number类型只能安全地表示-9007199254740991(-(2^53-1))和9007199254740991((2^53-1)),任何超出此范围的整数值都可能失去精度。

    console.log(999999999999999);  //=>10000000000000000

同时也会有一定的安全性问题:

    9007199254740992 === 9007199254740993;    // → true 居然是true!

如何创建并使用BigInt?

要创建BigInt,只需要在数字末尾追加n即可

    console.log( 9007199254740995n );    // → 9007199254740995n	
    console.log( 9007199254740995 );     // → 9007199254740996

另一种创建BigInt的方法是用BigInt()构造函数

    BigInt("9007199254740995");    // → 9007199254740995n

简单使用如下:

    10n + 20n;    // → 30n	
    10n - 20n;    // → -10n	
    +10n;         // → TypeError: Cannot convert a BigInt value to a number	
    -10n;         // → -10n	
    10n * 20n;    // → 200n	
    20n / 10n;    // → 2n	
    23n % 10n;    // → 3n	
    10n ** 3n;    // → 1000n	
    
    const x = 10n;	
    ++x;          // → 11n	
    --x;          // → 9n
    console.log(typeof x);   //"bigint"

值得警惕的点

BigInt不支持一元加号运算符, 这可能是某些程序可能依赖于 + 始终生成 Number 的不变量,或者抛出异常。另外,更改 + 的行为也会破坏 asm.js 代码。

因为隐式类型转换可能丢失信息,所以不允许在bigintNumber 之间进行混合操作。当混合使用大整数和浮点数时,结果值可能无法由BigIntNumber精确表示。

    10 + 10n;    // → TypeError

不能将BigInt传递给Web api和内置的 JS 函数,这些函数需要一个 Number 类型的数字。尝试这样做会报TypeError错误。

    Math.max(2n, 4n, 6n);    // → TypeError

Boolean 类型与 BigInt 类型相遇时,BigInt 的处理方式与Number类似,换句话说,只要不是0nBigInt就被视为truthy的值。

    if(0n){//条件判断为false
    
    }
    if(3n){//条件为true
    
    }
  • 元素都为BigInt的数组可以进行sort。
  • BigInt可以正常地进行位运算,如|&<<>>^

浏览器兼容性

caniuse的结果:

其实现在的兼容性并不怎么好,只有chrome67、firefox、Opera这些主流实现,要正式成为规范,其实还有很长的路要走

补充(现代做法):BigInt 早已是 ES2020 正式标准,截至 2020 年起所有现代浏览器(Chrome / Firefox / Safari / Edge)与 Node 10.4+ 均已支持,上文「兼容性不好」的说法已过时。判空可直接用 typeof x === 'bigint';与 Number 仍不能混合运算,需先显式转换。

1.7 JS 整数是怎么表示的

通过 Number 类型来表示,遵循 IEEE754 标准,通过 64 位来表示一个数字,(1 + 11 + 52),最大安全数字是 Math.pow(2, 53) - 1,对于 16 位十进制。(符号位 + 指数位 + 小数部分有效位)

1.8 Number() 的存储空间是多大?如果后台发送了一个超过最大自己的数字怎么办

Math.pow(2, 53) ,53 为有效数字,会发生截断,等于 JS 能支持的最大数字。

Last Updated:
Contributors: leeguooooo