Javascript 的数据类型详解

808 阅读5分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

JS 数据类型是前端基本知识之一,也是面试过程中最常见的面试题目之一,当被问到相关的题目时,你是否只是笼统地介绍一遍各种类型呢,本篇文章带大家来详解 JS 的数据类型。

阅读本文您将收获

  • 两种数据类型
  • 数据类型的判断方法
  • 数据类型的隐式转换

原始数据类型(基本数据类型)

种类

  • number 、boolean、 string、null、 undefined、symbol(ES6新增)、bigInt(新标准,兼容性很差)

特性

  • 保存在 栈内存 中的简单数据段
  • 数据是不可变的,即不能强行修改 Array.prototype.sort.call('abc');(会报错)
  • 没有原型链中的 _proto_ 属性

检测

  • 使用 typeof 可以检测基本数据类型
  • 但是 typeof(null) == 'object'
    • null 得到检测错误是历史遗留问题,在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象然而 null 表示为全零,所以将它错误的判断为 object

引用数据类型

种类

  • Object (包含普通对象-Object,数组对象-Array,正则对象-RegExp,日期对象-Date,数学函数-Math,函数对象-Function)

特性

  • 引用类型正在创建时会分配两个空间
    • 一块在 上,储存引用类型本身的数据(当然数据量会比较大)
    • 一块在 上,储存对堆上数据的引用(存储堆上的内存地址,也就是指针)
  • 引用类型是可变的:即 let a={}; a.x=1;
  • function 参数是值传递,不能修改引用

检测

  • 通过 Object.prototype.toString.call 检测最为精准
// Vue 源码的检测方法
let _toString = Object.prototype.toString;

function toRawType (value) {
    // 获取 从第九个到倒数第二个字符
    // 比如 [object string]  获取 string
    return _toString.call(value).slice(8, -1)
}

// 严格的对象类型检查,只返回true
// 这里主要用于普通的javascript对象

function isPlainObject (obj) {
	return _toString.call(obj) === '[object Object]'
}

数据类型判断的各种方法

typeof

  • typeof 返回值对应表
类型结果
String"string"
Number"number"
Boolean"boolean"
Undefined"undefined"
Object、Array、RegExp、null、Date、Error"object"
Function"function"
Symbol(ES6新增)"symbol"

instanceof

  • instanceof运算符需要指定一个构造函数,或者说指定一个特定的类型,它用来判断这个构造函数的原型是否在给定对象的原型链上
123 instanceof Number, //false
'dsfsf' instanceof String, //false
false instanceof Boolean, //false
[1,2,3] instanceof Array, //true
{a:1,b:2,c:3} instanceof Object, //true
function(){console.log('aaa');} instanceof Function, //true
undefined instanceof Object, //false
null instanceof Object, //false
new Date() instanceof Date, //true
/^[a-zA-Z]{5,20}$/ instanceof RegExp, //true
new Error() instanceof Error //true
  • Number,String,Boolean没有检测出他们的类型,但是如果使用下面的写法则可以检测出来:
var num = new Number(123);
var str = new String('dsfsf');
var boolean = new Boolean(false);
  • 还需要注意nullundefined都返回了false,这是因为它们的类型就是自己本身,并不是Object创建出来它们,所以返回了false。

  • 手动实现一个 instanceof 方法

function myInstanceof(left, right) {
    //基本数据类型直接返回false
    if(typeof left !== 'object' || left === null) return false;
    //getProtypeOf是Object对象自带的一个方法,能够拿到参数的原型对象
    let proto = Object.getPrototypeOf(left);
    while(true) {
        //查找到尽头,还没找到
        if(proto == null) return false;
        //找到相同的原型对象
        if(proto == right.prototype) return true;
        proto = Object.getPrototypeof(proto);
    }
}

console.log(myInstanceof("111", String)); //false
console.log(myInstanceof(new String("111"), String));//true

constructor

  • constructorprototype 对象上的属性,指向构造函数
  • 根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用 constructor 属性的。
var num  = 123;
var str  = 'abcdef';
var bool = true;
var arr  = [1, 2, 3, 4];
var json = {name:'wenzi', age:25};
var func = function(){ console.log('this is function'); }
var und  = undefined;
var nul  = null;
var date = new Date();
var reg  = /^[a-zA-Z]{5,20}$/;
var error= new Error();

function Person(){}
var tom = new Person();

// undefined和null没有constructor属性
console.log(
    tom.constructor==Person,
    num.constructor==Number,
    str.constructor==String,
    bool.constructor==Boolean,
    arr.constructor==Array,
    json.constructor==Object,
    func.constructor==Function,
    date.constructor==Date,
    reg.constructor==RegExp,
    error.constructor==Error
);
//所有结果均为true

toString()

  • 可以通过toString() 来获取每个对象的类型。
  • 为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用
var toString = Object.prototype.toString;

toString.call(123); //"[object Number]"
toString.call('abcdef'); //"[object String]"
toString.call(true); //"[object Boolean]"
toString.call([1, 2, 3, 4]); //"[object Array]"
toString.call({name:'wenzi', age:25}); //"[object Object]"
toString.call(function(){ console.log('this is function'); }); //"[object Function]"
toString.call(undefined); //"[object Undefined]"
toString.call(null); //"[object Null]"
toString.call(new Date()); //"[object Date]"
toString.call(/^[a-zA-Z]{5,20}$/); //"[object RegExp]"
toString.call(new Error()); //"[object Error]"

一个获取变量准确类型的函数

function getDataType(obj) {
  let type = typeof obj;

  if (type !== 'object') {
    return type;
  }
  //如果不是object类型的数据,直接用typeof就能判断出来

  //如果是object类型数据,准确判断类型必须使用Object.prototype.toString.call(obj)的方式才能判断
  return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1');
}

对象转原始类型是根据什么流程运行的?

对象转原始类型,会调用内置的[ToPrimitive]函数,对于该函数而言,其逻辑如下:

如果Symbol.toPrimitive()方法,优先调用再返回 调用valueOf(),如果转换为原始类型,则返回 调用toString(),如果转换为原始类型,则返回 如果都没有返回原始类型,会报错

var obj = {
  value: 3,
  valueOf() {
    return 4;
  },
  toString() {
    return '5'
  },
  [Symbol.toPrimitive]() {
    return 6
  }
}
console.log(obj + 1); // 输出7

JS 中的比较运算

data-type.jpg

JS 中的 '真' 值

  • JS 中除了 '假' 值就是 '真' 值
  • '假' 值共有七个
    • undefined
    • null
    • false
    • NaN
    • ''
    • 0
    • -0
  • 在条件判断的隐式转换中:"假" 值会转换为 false,"真" 值会转换为 true;

经常会问到的关于数据类型的面试题

0.1 + 0.2 为什么不等于 0.3 ?

  • 原因:
    • JS的精确度区间 约为正负 2^53,超出限制会截断。所以你看到的 0.1 不是真的 0.1。
    • 计算机无法识别十进制,JS会将十进制转换为对应的二进制(二进制即:0 和 1)。
    • 0.1和0.2在转换成二进制后会无限循环,由于标准位数的限制后面多余的位数会被截掉,此时就已经出现了精度的损失,相加后因浮点数小数位的限制而截断的二进制数字在转换为十进制就会变成0.30000000000000004。
  • 解决方式:高精度计算函数
  • 关于这个问题的更详细解答都已经在这篇文章当中了--- 一文看懂 JS 中 0.1 + 0.2 !== 0.3

[] == ![]

  • 类型转换都是先 valueOf() 然后再 toString()

  • 右边

    • 由于 ! 优先级比 == 高,先执行 !, 所有 typeOf()objectBoolean 都是 true
    • ![] 得到 false
    • 进行 相等性判断
    • false 转化为数字 0
  • 左边

    • 执行 [].valueOf() 原始值 还是 []
    • 执行 [].toString() 得到 ""
    • "" 转化为数字 0

所以:0 == 0 ,答案是 true

如何让if(a == 1 && a == 2)条件成立

var a = {
  value: 0,
  valueOf: function() {
    this.value++;
    return this.value;
  }
};
console.log(a == 1 && a == 2);//true

写在最后

如果你觉得这篇文章对你有益,烦请点赞以及分享给更多需要的人!

欢迎关注【全栈道路】及微信公众号【全栈道路】,获取更多好文及免费书籍!
有需要【百度】&【字节跳动】&【京东】&【猿辅导】内推的也请留言哦,你将享受VIP级极速内推服务~

往期好文

创建个性化的 Github 个人主页

面试官问你<img>是什么元素时你怎么回答

特殊的JS 浮点数的存储与计算

[万字长文]百度和好未来面试经含答案 | 掘金技术征文

前端实用正则表达式&小技巧,一股脑全丢给你🏆 掘金技术征文|双节特别篇

冷门的 HTML tabindex 详解

几行代码教你解决微信生成海报及二维码

Vue3.0 响应式数据原理:ES6 Proxy

[前端面试]前端缓存问题看这篇,让面试官爱上你

如何优雅地画一条细线

[三分钟小文]前端性能优化-HTML、CSS、JS部分

[三分钟小文]前端性能优化-页面加载速度优化

[三分钟小文]前端性能优化-网络传输层优化