一、数据类型

1. JavaScript 中有哪些数据类型

JavaScript中有八种数据类型:

  • Undefined
  • Null
  • Boolean
  • Number
  • String
  • Object
  • Symbol ES6 , 是独一无二的数据类型,用于解决可能出现全局变量冲突问题
  • BigInt ES6之后,安全存储和操作 大整数 ,即使这个整数超出了 Number 能够表示整数的范围

数据类型又可以分为 原始数据类型 引用数据类型

  • 原始数据类型: String | Numer | Boolean | Undefined | Null | Symbol | BigInt
  • 引用数据类型: Object

栈: 用于存储原始数据类型
堆: 用于存储引用数据类型 函数|数组|对象

两种类型区别在于存储位置不同:

  • 原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
  • 引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

堆和栈的概念存在于数据结构和操作系统内存中,在数据结构中:

  • 在数据结构中,栈中数据的存取方式为先进后出。
  • 堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。 (函数执行上下文)

2. 检测数据类型方式

typeof

  1. console.log(typeof 2); // number
  2. console.log(typeof true); // boolean
  3. console.log(typeof 'str'); // string
  4. console.log(typeof []); // object
  5. console.log(typeof function(){}); // function
  6. console.log(typeof {}); // object
  7. console.log(typeof undefined); // undefined
  8. console.log(typeof null); // object
  9. console.log(typeof class A {}) // function

其中值得注意

  • typeof 检测输出的值 都是 字符串, 例如 "string"
  • typeof 能够检测 funtion
  • typeof 检测 null ,会返回 "object"
  • typeof 检测 [] | {} | null 都会返回 "object"
  • typeof 检测 class 类 , 返回 'function'

    instanceof

    instanceof一般用于检测对象类型。 它的原理 : 判断其在原型链中能否找到该类型的原型 ```javascript

// instanceof 检测数据类型 // 基础类型, 不能够正确判断 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

  1. 可以看到,`instanceof`**只能正确判断引用数据类型**,而不能判断基本数据类型。`instanceof` 运算符可以用来测试一个对象在其原型链中是否存在一个构造函数的 `prototype` 属性
  2. <a name="IWImD"></a>
  3. #### constructor
  4. `constructor` 有两个作用,一是判断数据类型。 二是对象实例通过 `constrcutor` 对象访问它的构造函数。 需要注意,如果如果创建一个对象来改变它的原型,`constructor`就不能用来判断数据类型了
  5. ```javascript
  6. // 3. 使用 constructor 属性检测数据类型
  7. console.log((2).constructor === Number); // true
  8. console.log((true).constructor === Boolean); // true
  9. console.log(('str').constructor === String); // true
  10. console.log(([]).constructor === Array); // true
  11. console.log((function() {}).constructor === Function); // true
  12. console.log(({}).constructor === Object); // true
  13. console.log((2).constructor) // [Function: Number]

Object.prototype.toString.call()

  1. // 4. 使用 Object.prototype.toString.call() 检测数据类型
  2. var typeIndex = Object.prototype.toString;
  3. // 直接看 数据类型 的原型对象
  4. console.log(typeIndex.call(2)) // [object Number]
  5. console.log(typeIndex.call(true)) // [object Boolean]
  6. console.log(typeIndex.call('str')) // [object String]
  7. console.log(typeIndex.call([])) // [object Array]
  8. console.log(typeIndex.call(function(){})) // [object Function]
  9. console.log(typeIndex.call({})) // [object Object]
  10. console.log(typeIndex.call(undefined)) // [object Undefined]
  11. console.log(typeIndex.call(null)) // [object Null]

3. 判断数组的方法

  • 使用 Object.prototype.toString.call() 判断

    1. console.log(typeIndex.call([])) // [object Array]
  • 通过原型链判断

    1. let arr = []
    2. console.log(arr.__proto__ === Array.prototype)
  • 使用ES6的 Array.isArray()

    1. console.log(Array.isArray([])) // true
  • 使用 instanceof 判断

  1. [] instanceof Array

4. undefined 和 null 的区别

首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。

undefined 代表的含义是未定义,null 代表的含义是空对象。一般变量声明了但还没有定义的时候会返回 undefined,null主要用于赋值给一些可能会返回对象的变量,作为初始化。

当对这两种类型使用 typeof 进行判断时,Null 类型化会返回 “object”,这是一个历史遗留的问题。当使用双等号对两种类型的值进行比较时会返回 true,使用三个等号时会返回 false。

5. intanceof 操作符的实现原理及实现

instanceof 运算符用于判断构造函数的 prototype属性是否出现在对象的原型链中的任何位置。

  1. function myInstanceof (left, rigth) {
  2. // 获取对象的原型
  3. let proto = Object.getPrototypeOf(left)
  4. // 获取构造函数的 prototype 对象
  5. let prototype = right.prototype
  6. // 判断构造函数的 prototype 对象是否在对象的原型链上
  7. while(true) {
  8. if (!proto) return false
  9. if (proto === prototype) return true
  10. // 如果没有找到,就继续从其原型上找,Object.getPrototypeOf方法用来获取指定对象的原型
  11. proto = Object.getPrototypeOf(proto)
  12. }
  13. }

6. 为什么 0.1 + 0.2 !== 0.3

问题:

  1. let n1 = 0.1, n2 = 0.2
  2. console.log(n1 + n2) // 0.30000000000000004
  3. // 解决: 使用 toFixed()
  4. (n1 + n2).toFixed(2) // 注意,toFixed为四舍五入


为什么 0.1 + 0.2 !== 0.3

因为计算机是使用 二进制方式存储数据的, 在计算 0.1 + 0.2 时候, 会将它们转为二进制然后再相加。
0.1, 0.2 转为 二进制 是无限循环的数,0.1 是0.0001100110011001100... , 0.2是0.00110011001100...
JavaScript 使用标准的 双精度浮点数。 使用 双精度表示二进制 小数部分,最多只能保留52位, 再加前面的1, 也就是保留53位有效数字, 剩下的需要舍去。

根据这个原则,0.1和0.2的二进制数相加,再转化为十进制数就是:0.30000000000000004

7. isNaN 和 Number.isNaN 区别

首先, 它们都是用来判断一个数值 是不是NaN,

  • isNaN 接收到参数后, 会尝试把这个参数转为Number 类型,不能转为数值的类型 都会返回 true, 因此isNaN 的参数不是number也会返回 true
  • Number.isNaN() 会先去判断传入的参数是否为数字,如果是数字,才会去判断是否为 NaN , 不会对参数类型进行转换。 Number.isNaNisNaN 的判断更准确

8. 其他类型到布尔类型的转换

  1. console.log(Boolean(0)) // false
  2. console.log(Boolean(-0)) // false
  3. console.log(Boolean(NaN)) // false
  4. console.log(Boolean(undefined)) // false
  5. console.log(Boolean(null)) // false
  6. console.log(Boolean(false)) // false
  7. console.log(Boolean('')) // false
  8. // 以上都是假值, 其他都为真

9. Object.is() 比较符 “===” “==”

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

10. JS中的类型转换

  1. // + 操作符
  2. console.log(1 + '23') // '123'
  3. console.log(1 + false) // 1 --- false 会转为0
  4. console.log(1 + null) // 1
  5. console.log(1 + true) // 2
  6. // console.log(1 + Symbol()) // Error --- Symbol() 是唯一的
  7. console.log('1' + false) // 1false
  8. console.log(true + false) // 1
  9. // - * / 操作符 NaN 也是一个数字
  10. console.log(1 * '23') // 23 --> * -> string -> number
  11. console.log(1 * false) // 0
  12. console.log(1 / 'aa') // NaN
  13. console.log(1 / '23') // 0.043478260869565216
  14. // = 和 ==
  15. console.log(3 == true) // 3 转为number为3,true转为number为1
  16. console.log(0 == false) // true '0'转为number为0,false转为number为0
  17. console.log('0' == 0) // true == 会自动类型转换
  18. // < >
  19. console.log('12' < 2) // false '12'转为number为12,2转为number为2
  20. console.log('ca' < 'bd') // false如果两边都是字符串,则比较字母表顺序:
  21. // 其他情况下,转换为数字再比较:
  22. console.log('12' < 13) // true
  23. console.log(false > -1) // true
  24. var a = {}
  25. console.log(a > 2)
  26. /**
  27. * a.valueOf() // {}, 上面提到过,ToPrimitive默认type为number,所以先valueOf,结果还是个对象,下一步
  28. a.toString() // "[object Object]",现在是一个字符串了
  29. Number(a.toString()) // NaN,根据上面 < 和 > 操作符的规则,要转换成数字
  30. NaN > 2 //false,得出比较结果
  31. */

11. + 操作符什么时候用于字符串的拼接?

在进行操作 + 时, 如果其中一个操作数是字符串,或者通过 valueOf() | toString() 转换得到的字符串,则执行字符串拼接, 否则执行数字加法
对于数字来说: 只要其中一方是数字,那么另一方就被转为数字

12. JS的数值表示的范围

(-9007199254740991)Number.MIN_SAFE_INTEGER < Number < Number.MAX_SAFE_INTEGER (9007199254740991)
-2^53+1 < Number < 2^53 -1

13. Object.assign() 和 扩展运算符的区别

两者都是浅拷贝

  • Object.assign()方法接收的第一个参数作为目标对象,后面的所有参数作为源对象。然后把所有的源对象合并到目标对象中。它会修改了一个对象,因此会触发 ES6 setter。
  • 扩展操作符(…)使用它时,数组或对象中的每一个值都会被拷贝到一个新的数组或对象中。它不复制继承的属性或类的属性,但是它会复制ES6的 symbols 属性。

14. 如何判断一个 对象是空对象

  1. 使用JSON自带的.stringify方法来判断:

    1. var Obj = {}
    2. if (JSON.stringify(Obj) === '{}') {
    3. console.log('空对象')
    4. }
  2. 使用ES6新增的方法Object.keys()来判断:

    1. // 使用 ES6 的 Object.keys 判断key
    2. Object.keys(Obj).length === 0 ? console.log('空对象') : console.log('非空对象')