探究js数据类型和底层原理

数据类型

JS 的数据类型分为:基本数据类型和引用类型

  1. 数字(number)
  2. 字符串(string)
  3. 空值(null)
  4. 未定义(undefined)
  5. 布尔值(boolean)
  6. 符号(symbol, ES6 中新增)
  7. 大整数(BigInt, ES2020 引入)
  8. 对象 (object)

其中基本数据类型存放在栈内存中,引用类型存放在堆内存中

  • :原始数据类型(Undefined、Null、Boolean、Number、String、symbol、BigInt)
  • :引用数据类型(对象、数组和函数)

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

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

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

数据结构中:

  • 栈中数据的存取方式为先进后出。
  • 堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。

操作系统中,内存被分为栈区和堆区:

  • 栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
  • 堆区内存一般由开发着分配释放,若开发者不释放,程序结束时可能由垃圾回收机制回收

如何判断数据类型

  1. typeof

    typeof基本数据类型function 判断是准的,但对于 []、object 和 null就会判断成object

    console.log(typeof 2);           //number
    console.log(typeof NaN);         //number
    console.log(typeof "2");         //string
    console.log(typeof true);        //boolean
    console.log(typeof undefined);   //undefined
    console.log(typeof 123n);        //bigint
    console.log(typeof Symbol('张三'));  // symbol
    console.log(typeof function () {}); //function
    
    console.log(typeof []); //object
    console.log(typeof {}); //object
    console.log(typeof null); //object
    
    

    那么,typeof 是根据什么判断的呢?

    在 JavaScript 第一个版本中,所有值都存储在 32 位的单元中,每个单元包含一个小的 类型标签(1-3 bits) 以及当前要存储值的真实数据。类型标签存储在每个单元的低位中,共有五种数据类型:

    码值类型
    000对象
    010浮点数
    100字符串
    110布尔
    1整数

    有两种特殊数据类型:

    • undefined的值是 (-2)30(一个超出整数范围的数字);
    • null 的值是机器码 NULL 指针(null 指针的值全是 0)

    那也就是说 null 的类型标签也是 000,和 Object 的类型标签一样,所以会被判定为 Object。所以 typeof null 的结果是 object。

  2. instanceof

    instanceof对引用类型的判断是准确的,对于基本数据类型都是 false.

    console.log(2 instanceof Number);       // false
    console.log(NaN instanceof Number);     // false
    console.log("2" instanceof String);     // false
    console.log(true instanceof Boolean);   // false
    console.log(Symbol('张三') instanceof Symbol);      // false
    
    console.log(Object instanceof Object);              // true
    console.log(function () {} instanceof Function);    // true
    console.log([] instanceof Array);                   // true
    

    如何实现instanceof ?

    function myInstanceof(left, right) {
      // 获取对象的原型
      let proto = Object.getPrototypeOf(left)
      // 获取构造函数的 prototype 对象
      let prototype = right.prototype;
    
      // 判断构造函数的 prototype 对象是否在对象的原型链上
      while (true) {
        if (!proto) return false;
        if (proto === prototype) return true;
        // 如果没有找到,就继续从其原型上找,Object.getPrototypeOf方法用来获取指定对象的原型
        proto = Object.getPrototypeOf(proto);
      }
    }
    
  3. constructor

    一般的数据类型和引用的都可以判断,但是 undefined 和 null 除外

    	console.log(('').constructor == String)
    	console.log((true).constructor == Boolean)
    	console.log(({}).constructor == Object)
    	console.log(([]).constructor == Array)
    	
    	console.log((undefined).constructor == undefined)   //报错
    	console.log((null).constructor == null)  //报错
    
    

但是注意,constructor 是可以被改变的。

  1. Object.prototype.toString.call()

    可以判断任何数据类型。
    原理:使用 Object 对象的原型方法 toString 来判断数据类型:

    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 方法。

  2. 原型链

    obj.__proto__ === Array.prototype; // true
    
  3. 通过 isPrototypeOf

    Array.prototype.isPrototypeOf(obj)
    

数组判断类型

  1. 通过 ES6 的 Array.isArray()做判断

    Array.isArrray(obj);
    
  2. 通过 instanceof 做判断

    obj instanceof Array
    
  3. 通过 Object.prototype.toString.call()做判断

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

    obj.__proto__ === Array.prototype;
    
  5. 通过 Array.prototype.isPrototypeOf

    Array.prototype.isPrototypeOf(obj)
    
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值