JS系列之数据类型,判断方式以及存储位置

吐槽君 分类:javascript

JS的数据类型

JS的数据类型分为两大类,他分为基本数据类型,与引用数据类型

  1. 基本数据类型有: String, Boolean, Number, null, undefined。
  2. 引用数据类型: Object, Array, Function, Date, RegExp等。

JS数据类型存储位置

基本数据类型以及引用数据类型的地址都存在栈上,引用数据类型都存在堆上。这些估计大部分人都能知道,但是有没有人和作者一样发出疑问,为啥基本数据结构要存在栈上,引用数据类型都存在堆上,堆和栈有啥区别?

带着疑问,我顺手打开了百度,进行了一系列的`面向百度编程,最后得到的加以总结,如下:

  1. 栈是程序在编译运行的时候,系统就已经分配好了一定的空间给程序执行,而堆不同,堆的大小是动态的,在通过构造函数生成一个对象的时候,这个时候就需要向系统申请一块地方,在执行完毕之后根据语言的不同,C语言需要程序员自己释放内存。Java,JavaScript等语言会自动进行垃圾回收,从而进行空间的释放。程序在申请空间和释放空间时候的性能比较底下,所以堆相比于编译运行时已经确定好的空间相比,性能会有所下降。
  2. 栈上的存取速度非常快,仅次于寄存器,既然栈上的存取速度很快,为啥引用类型还要存在堆上?首先当你生成一个对象的时候,这个对象可能是无限大的,那么当这个对象的大小超过栈的话(第二点说明了,栈内存是编译运行的时候已经确定了),就会出现栈溢出导致程序崩溃。那基础类型不会吗?基础类型的大小都是确定的,都会有一个最大值。例如字JS中的数字,Number 类型都是浮点类型并且存储空间为 8 数节(byte)(8*8 bit位)。其中Number 类型值的整数最多15位,小数最多17位。
  3. 正是因为了引用类型的大小不可测,需要将其存储在空间可以自由分配的堆上。

存储的不同

栈上存的是基本类型,以及引用类型的地址,引用类型的值是存在堆上的,可以通过栈上的地址去访问。

如何进行类型判断

  1. typeof

    ​ 用法: typeof 变量名 typeof(表达式)

    ​ 返回值:

    'undefined': 对应的是基本数据类型中的 undefined
    'string': 对应的基本数据类型中的 string
    'boolean':对应的是基本数据类型中的  Boolean 
    'number': 对应的是基本数据类型中的 Number
    'Object': 对应的是 基本基本数据结构中的 null 以及引用类型中的 Object Array Date RegExp等。
    'function':对应的是引用类型的 Fuction
     

    从上面的返回值我们可以看出来,typeof的类型检测,相对来说并不是很准确。因此使用typeof的话,一定要慎重

  2. instanceof

    用法:被检测对象 instanceof 构造函数

    原理: 用来检测构造函数的peototype属性是否在这个被检测的对象的原型链上

    实例:

    const str = '';
    str instanceof String // false 
    const boolean = false;
    boolean instanceof Boolean // false
    const arr = [];
    arr instanceof Array // true
    ...
    // 定义构造函数
    function C(){}
    function D(){}
    
    var o = new C();
    
    
    o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype
    
    
    o instanceof D; // false,因为 D.prototype 不在 o 的原型链上
    
    o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 true
    C.prototype instanceof Object // true,同上
    
    C.prototype = {};
    var o2 = new C();
    
    o2 instanceof C; // true
    
    o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上.
    
    D.prototype = new C(); // 继承
    var o3 = new D();
    o3 instanceof D; // true
    o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上
     

从上面的的例子中我们可以看出,相比于typeof,instanceof能够精确地检测引用类型,但是对于基本类型的检测,可以说是一塌糊涂。

  1. Object.prototype.toString.call();

    用法:Object.prototype.toString.call(被检测对象)

    原理:(ES5以前与ES6有少许不同)

    在调用Object.prototype.toString方法的时候,会获取当前的this对象的[[Class]]属性的值,然后将计算好的值放到"[object ]"中,组成 "[object Class代表的东西]"。

    那么[[Class]]代表的是什么呢?

    在规范中是这么定义的:一个字符串值,表明了该对象的类型.

    但是问题又来了,那我们可以直接用Object.prototype.toString方法就行了,为啥还要调用一下call这个方法。相信大家都知道,在JS中的,谁调用我,那么我的this就指向他,也就是说,如果不改变this指向的话,那么永远得到的值都是[object, object]。因此需要改变一下指向。

    ES6之后呢,获取的不在是[[Class]]而是[[NativeBrand]]不过返回的也是正确的数据,大家如果想要懂得更多,可以去www.jb51.net/article/799…

    var toString = Object.prototype.toString;
    
    toString.call(new Date); // [object Date]
    toString.call(new String); // [object String]
    toString.call(Math); // [object Math]
    
    //Since JavaScript 1.8.5
    toString.call(undefined); // [object Undefined]
    toString.call(null); // [object Null]
     

综上所述,Object.prototype.toString.call()检测是相较于typeof instanceof 最准确的。在开发中,更加建议使用Object.prototype.toString.call()去检测

function checkType(checkInstance, type) {
  return Object.prototype.toString.call(checkInstance) === `[object ${type}]`;
},
 

回复

我来回复
  • 暂无回复内容