JS


JS 有哪些数据类型,它们的区别?

  • JS 共有八种数据类型,分别是 Undefined、String、Symbol、Object、Null、Number、Boolean、BigInt
  • Symbol 和 BigInt 是 es6 新增的数据类型:
    • Symbol 代表创建后独一无二且不可变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题。
    • BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数字已经超出了 Number 能够表示的安全整数范围。
  • 这些数据可以分为原始数据类型和引用数据类型:
    • 栈:原始数据类型(Undefined、Null、Boolean、Number、String)
    • 堆:引用数据类型(对象、数组、函数)
  • 两种类型的区别在于存储的位置不同:
    • 原始数据类型存储在栈(stack)中的简单数据段,占据空间小,大小固定,属于被频繁使用数据,所以放在栈中存储。
    • 引用数据类型存储在堆(heap)中的对象,占据空间大,大小不固定。如果存储在栈中,将会影响程序的性能,引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
  • 堆和栈的概念存在于数据结构和操作系统内存中,在数据结构中:
    • 栈中的数据的存取方式为先进后出
    • 堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定
  • 在操作系统中,内存被分为栈区和堆区:
    • 栈区内存有编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
    • 堆区内存一般由开发者分配释放,若开发者不释放,程序结束时可能由垃圾回收机制回收。

判断数组的方式有哪些?

  1. 通过 Object.prototype.toString.call()

    1
    Object.prototype.toString.call(obj).slice(8, -1) === "Array";
  2. 通过原型链做判断

    1
    obj.__proto__ = Array.prototype;
  3. 通过 ES6 的 Array.isArray()做判断

    1
    Array.isArray(obj);
  4. 通过 instanceof 做判断

    1
    obj instanceof Array
  5. 通过 Array.prototype.isPrototypeOf

    1
    Array.prototype.isPrototypeOf(obj);

null 和 undefined 的区别

  • 首先 Undefined 和 null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null
  • null 表示一个被明确赋值为空值的值。它通常被用来表示变量或者属性的空值。
  • undefined 表示一个变量或者属性尚未定义或没有赋值,当声明一个变量但是没有给它赋值时,该变量被默认为 undefined,或者,如果一个对象中不存在某个属性,访问该属性时也会得到 undefined。
  • null 的使用场景:
    • 表示变量或属性的空值。
    • 明确指示某个值为无效或不存在
    • 通常由程序员赋值
    • 可以用来重置变量,将其显式地设置为空。
  • undefined 的使用场景:
    • 变量被声明但未被赋值。
    • 对象访问属性时,该属性不存在。
    • 函数没有明确返回值时,默认返回 undefined。

数据类型检测的方式有哪些?

  1. typeof

    1
    2
    3
    4
    5
    6
    7
    8
    console.log(typeof 2); //number
    console.log(typeof true); //boolean
    console.log(typeof "str"); //string
    console.log(typeof []); //object
    console.log(typeof function () {}); //function
    console.log(typeof {}); //object
    console.log(typeof undefined); //undefined
    console.log(typeof null); //object
    • 其中数组、对象、null 都会被判断为 object,其他判断都正确。
  2. instanceof

    instanceof 可以正确判断对象的类型,其内部运行机制是判断在其原型链中能否找到该类型的原型。

    1
    2
    3
    4
    5
    6
    7
    console.log(2 instanceof Number); //false
    console.log(true instanceof Boolean); //false
    console.log("str" instanceof String); //false

    console.log([] instanceof Array); //true
    console.log(function () {} instanceof Function); //true
    console.log({} instanceof Object); //true
    • 可以看到,instanceof 只能正确判断引用数据类型,而不能判断基本数据类型(没有原型链),instanceof 运算符可以测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
  3. constructor

    1
    2
    3
    4
    5
    6
    console.log((2).constructor === Number); //true
    console.log(true.constructor === Boolean); //true
    console.log("str".constructor === String); //true
    console.log([].constructor === Array); //true
    console.log(function () {}.constructor === Function); //true
    console.log({}.constructor === Object); //true
    • constructor 有两个作用,一是判断数据类型,二是对象实例通过 constructor 对象访问它的构造函数。注意,如果创建一个对象来改变它的原型,constructor 就不能用来判断数据类型了。

      1
      2
      3
      4
      5
      function Fn() {}
      Fn.prototype = new Array();
      var f = new Fn();
      console.log(f.constructor === Fn); //false
      console.log(f.constructor === Array); //true
  4. Object.prototype.toString.call()

    Object.prototype.toString.call()使用 Object 对象的原型方法 toString 来判断数据类型。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var a = Object.prototype.toString;

    console.log(a.call(2)); //[object Number]
    console.log(a.call(true)); //[object Boolean]
    console.log(a.call("str")); //[object String]
    console.log(a.call([])); //[object Array]
    console.log(a.call(function () {})); //[object Function]
    console.log(a.call({})); //[object Object]
    console.log(a.call(undefined)); //[object Undefined]
    console.log(a.call(null)); //[object Null]
    • 同样是检测对象 obj 调用 toString 方法,obj.toString()的结果和 Object.prototype.toString.call(obj)的结果不一样,这是为什么?

      这是因为 toString 是 Object 的原型方法,而 Array、function 等类型作为 Object 的实例,都重写了 toString 方法。不同的对象类型调用 toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法(function 类型返回内容为函数体的字符串,Array 类型返回元素组成的字符串…),而不会去调用 Object 上原型 toString 方法(返回对象的具体类型),所以采用 obj.toString()不能得到其对象类型,只能将 obj 转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用 Object 原型上的 toString 方法。

typeof null 的结果是什么?为什么?

  • typeof null 的结果是 Object

  • 在 js 最初的实现中,它使用 32 位的数据来存储变量的值,其中 3 位用于表示变量的类型信息。在这个类型信息中,对象类型被表示为二进制的 000,而 null 被认为是一个空指针对象,它的二进制表示正好是全部为 0。

  • 000: object   - 当前存储的数据指向一个对象。
      1: int      - 当前存储的数据是一个 31 位的有符号整数。
    010: double   - 当前存储的数据指向一个双精度的浮点数。
    100: string   - 当前存储的数据指向一个字符串。
    110: boolean  - 当前存储的数据是布尔值。
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    - 当 js 在判断一个值类型时,会查看该值的类型信息,由于 null 的类型信息全部为 0,与对象类型一致,因此 typeof null 返回的是 Object

    ### 为什么 0.1+0.2!==0.3,如何让其相等

    ```js
    let n1 = 0.1,
    n2 = 0.2;
    console.log(n1 + n2); // 0.30000000000000004
  • 要想得到 0.3,可以进行 bao’liu’lang’wei’xiao’shu

1
(n1 + n2).toFixed(2);
  • 计算机是通过二进制的方式存储数据的,所以计算机计算 0.1+0.2 的时候,实际上是计算两个数的二进制的和。0.1 的二进制是 0.00011001100110011001100…(1100 循环),0.2 的二进制是 0.00110011001100…(1100 循环),这两个数的都是无限循环的数。
  • 一般我们认为数字包括小数和整数,但是 js 中只有一种数字类型:Number,它的实现遵循 IEEE 745 标准,使用 64 位固定长度来表示,也就是标准的 double 双精度浮点数。在二进制科学表示法中,双精度浮点数的小数部分最多只能保留 52 位,再加上前面的 1,其实就是保留 53 位有效数字,剩余的需要舍去,遵从“0 舍 1 入”的原则。

typeof NaN 的结果是什么?

  • typeof NaN 的结果是 number

  • NaN 指“不是一个数字”,NaN 是一个“警戒线”,用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”

  • NaN 是一个特殊值,它和本身不相等,是唯一一个非自反的值,而 NaN!==NaN 为 true

  • 在 JavaScript 中,NaN 代表非数字(Not-a-Number)。当涉及到 NaN 时,有一个特殊的规则:NaN 与任何值(包括 NaN 本身)进行比较时,结果都是 false。

    这包括相等运算符(==和===),因此 NaN 与 NaN 进行比较会得到 false(NaN !== NaN)。这是因为 NaN 表示一个特殊的数值状态,它被认为是不可比较的、不等于任何其他值的。

    这种设计是基于 JavaScript 中的 IEEE 754 浮点数标准,其中 NaN 是一种特殊的数值。它用于表示一些不确定或无效的运算结果,例如 0 除以 0 或负数的平方根。

Number.isNaN 和 isNaN 的区别?

  • 函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的值都会返回 true,因此非数字值传入也会返回 true,会影响 NaN 的判断。
  • 函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字就继续判断是否为 NaN,不会进行数据类型的转换,这种方法更准确。

其他值到字符串的转换规则

  • Null 和 Undefined 类型,null 转换为’null’,undefined 转换为"undefined"
  • Boolean 类型,true 转换为“true”,false 转换为"false"
  • Number 类型的值直接转换,不过那些极小和极大数会使用指数形式。
  • Symbol 类型的值直接转换,但是只允许显示强制类型转换,使用隐式强制类型转换会产生错误。
  • 对于普通对象来说,除非自定义 toString()方法,否则会调用 toString()(Object.prototype.toString())来返回内部属性值,如"[Object Object]",如果对象有自己的 toString()方法,字符串化时会调用该方法并使用其返回值。

Object.is()与操作符"==“、”==="的区别?

  • 使用双等号进行相等判断时,如果两边类型不一致,则会进行强制类型转化后再进行比较。
  • 使用三等号进行相等判断时,如果两边的类型不一致,不会做强制类型转换,直接返回 false
  • 使用 Object.is()来进行相等判断时,一般情况下和三等号的判断相同,他会处理一些特殊情况,比如:-0 和 0 不相等,两个 NaN 是相等的。