JavaScript类型问题

JavaScript类型问题

​ JavaScript 类型的问题其实是个挺折磨人的话题, 不然也不会有 TypeScript 出现了。此篇博文主要记录与此相关的一些问题。有兴趣研究的同学可以去阅读 lodash 的源代码,学习在这种成熟的JS库中是如何处理类型问题以及如何进行类型判断的。

JavaScript内置类型

* null

* undefined

* boolean

* number

* string

* object

* symbol (ES6中新增类型)

除了object之外,其他统称为基本数据类型。object类型则包括Object 、Array 、Function 、Date等,我们一般也把它们称为引用数据类型。

基本数据类型的值存放在栈(Stack)中,而引用数据类型的值存放在堆(Heap)中,栈中只存放对它们的“引用”

JavaScript常见的一些类型问题

1、== 和 ===问题

  • 相等(==)

    比较操作符会为两个不同类型的操作数转换类型,然后进行严格比较。当两个操作数都是对象时,JavaScript会比较其内部引用,当且仅当他们的引用指向内存中的相同对象(区域)时才相等,即他们在栈内存中的引用地址相同。

    1
    2
    3
    4
     1   ==  1     // true
    "1" == 1 // true
    1 == '1' // true
    0 == false // true
  • 严格相等(===)

    一致运算符不会进行类型转换,仅当操作数严格相等时返回true

    1
    2
    3
    4
    5
    3 === 3   // true
    3 === '3' // false
    var object1 = {"value":"key"}, object2={"value":"key"};
    object1 === object2 //false
    undefined == null // true
    可以很明显的看出,== 和===的区别就在于在判断的时候是否进行了自动的**类型转换**。需要注意的是ECMAScript认为undefined是从null派生出来的,所以把它们定义为相等的 。
    

2、如何判断JavaScript中变量的类型

  • typeof

    主要用于判断数据是不是基本数据类型String、Number、Object、Null、Undefined,但是无法判断出function(有些浏览器会出错,V8中能正常判断 )、array、regExp

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

    instanceof 左操作数是一个类,右操作数是标识对象的类。如果左侧的对象是右侧类的实例,则返回true.而js中对象的类是通过初始化它们的构造函数来定义的。即instanceof的右操作数应当是一个函数。所有的对象都是object的实例。如果左操作数不是对象,则返回false,如果右操作数不是函数,则抛出typeError。

    1
    2
    3
    4
    5
    new String('foo') instanceof String; // true
    new String('foo') instanceof Object; // true

    'foo' instanceof String; // false
    'foo' instanceof Object; // false
  • Object.prototype.toString.call()

    这是对象的一个原生原型扩展函数,用来精确的区分数据类型

    1
    2
    3
    4
    5
    6
    7
    const toString = Object.prototype.toString;

    toString.call(new Date); // [object Date]
    toString.call(new String); // [object String]
    toString.call(Math); // [object Math]
    toString.call(undefined); // [object Undefined]
    toString.call(null); // [object Null]
    上述的3种类型判断方式都有各有利弊,typeof验证**基本数据类型**(除了null哦) 一般不会有什么问题,但是验证引用数据类型,则会出现各种谬误。instanceof用来验证一个对象是否是一个类的实例,但是由于JavaScript中所有引用数据类型都是继承自Object,所以没法判断对象的准确类型。Object.prototype.toString.call()虽然可以精确判断数据类型,但是在面对自定义的类型时也存在风险。(想一下,如果我重写了Array的toString方法)
    

3、基本数据类型以及引用数据类型的问题(值传递与引用传递)

> js 中什么类型是引用传递, 什么类型是值传递? 如何将值类型的变量以引用的方式传递? 

​ 简单点说, 对象是引用传递, 基础类型是值传递, 通过将基础类型包装 (boxing) 可以以引用的方式传递 。例如 a = new Number(1) 这时候a 就可以使用引用的方式传递了

​ 这地方有一个有意思的小问题。用更严谨的技术说法,在JavaScript中没有所谓的引用传递而是传递引用,也叫作call-by sharing 。想要了解的同学可以看看Stack Overflow上的这个讨论Is JavaScript a pass-by-reference or pass-by-value language?

调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递的隐式引用)。 它和按引用传递的不同在于:在共享传递中对函数形参的赋值,不会影响实参的值。 然而,虽然引用是副本,引用的对象是相同的。它们共享相同的对象,所以修改形参对象的属性值,也会影响到实参的属性值。

换句话来说

值传递:传内存拷贝

​ 引用传递:传内存指针

​ 共享传递(call-by sharing):传内存指针的拷贝

4、JavaScript中值为false的值

  • null
  • undefined
  • “” (空字符串)
  • NaN
  • 0
  • false