JavaScript 中常见的几种数据类型:
目录
null既然属于基本数据类型,为什么用typeof返回的是object呢?
一、分类
基本类型:string,number,boolean,symbol(ES6新增)基本类型中也有两个特殊的类型,即:undefined,null
引用类型:Object,Function,Array,RegExp,Date,...
二、判断
1、typeof
typeof 返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、object、undefined、function等6种数据类型
typeof '123' string // 有效
typeof 1 number // 有效
typeof true boolean // 有效
typeof Symbol('1') symbol // 有效
typeof undefined undefined // 有效
typeof {a:1,b:2} object // 有效
function c(){console.log('123')}
typeof c function // 有效
typeof null object // 无效
typeof [] ; //object 无效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效
总结:typeof 可以对JS基础数据类型做出准确的判断,而对于引用类型返回的基本上都是object, 其实返回object也没有错,因为所有对象的原型链最终都指向了Object,Object是所有对象的`祖宗`。 但当我们需要知道某个对象的具体类型时,typeof 就显得有些力不从心了
null既然属于基本数据类型,为什么用typeof返回的是object呢?
js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息?
000:对象
010:浮点数
100:字符串
110:布尔
1:整数
但是对于 undefined 和 null 来说,这两个值的信息存储是有点特殊的。
null:所有机器码均为0
undefined:用 −2^30 整数来表示
所以,typeof 在判断 null 的时候就出现问题了,由于 null 的所有机器码均为0,因此直接被当做了对象来看待。
js小数运算出现的问题(精度丢失)
var num1 = 0.1
var num2 = 0.2
var num3 = num1 + num2
console.log(num3) // 0.30000000000000004
本质原因:计算机对于数据都是转换为二进制存储的,但是对于某些浮点数计算机无法将其精确表达为二进制
像0.5这种数字,可以很快计算得到2进制结果,但是0.1和0.2这样的数字,是永远不可能计算得到准确的2进制结果的,因为一直乘2,就没有得到整数的时候,此时在转换2进制的过程中,会形成无限死循环。
计算机内部在存储无限死循环数据时,必须要有一个限度,采取舍去的原则,所以,0.1和0.2在计算机内部存储的对应的2进制数字,本来就不精准,所以相加得到的2进制结果,也就不精准了,那转换成10进制后,是会有一定的误差的,所以结果不是精准的0.3。
计算机内部对于2进制小数,根据IEEE754标准(是一个仔细制定的表示浮点数及其运算的标准),小数部分最多会保留52位:
所以上面那段代码,在计算机中的运算过程其实是如下所示这样的
0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001
+
0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001
=
0.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011
所以,不精准是正确的,只是偶尔会有两个不精准的数字相加,正好得到一个精准的值。
解决方法一
将需要运算的小数扩大10倍、100倍、。。。将小数扩大到整数,然后进行运行,最后再缩小扩大的倍数。例:
var num1 = 0.1
var num2 = 0.2
var num3 = (num1 * 10 + num2 * 10) / 10
console.log(num3) // 0.3
解决方法二
通过js中Number的内置方法toFixed,强制保留小数点后位数。例:
var num1 = 0.1
var num2 = 0.2
var num3 = num1 + num2
console.log(num3.toFixed(3)) // 0.300 - 强制保留小数点后3位
解决方法三
封装数学运算方法,当需要进行数学运算的时候,不直接进行,而调用自己封装的方法来实现数学运算。
2、instanceof
instanceof 是用来判断 A 是否为 B 的实例对,表达式为:A instanceof B,如果A是B的实例,则返回true,否则返回false。 在这里需要特别注意的是:instanceof检测的是原型,我们用一段伪代码来模拟其内部执行过程:
instanceof (A,B) = {
var L = A.__proto__;
var R = B.prototype;
if (L === R) {
//A的内部属性__proto__指向B的原型对象
return true ;
}
return false ;
}
从上述过程可以看出,当 A 的 __proto__ 指向 B 的 prototype 时,即A的原型链上可以找到B时,就认为A就是B的实例,我们再来看几个例子:
[] instanceof Array; //true
{} instanceof Object; //true
new Date() instanceof Date; //true
function Person(){};
new Person() instanceof Person; // true
[] instanceof Object; //true
new Date() instanceof Object; //true
new Person instanceof Object; //true
[] 的 __proto__ 直接指向Array.prototype, 间接指向Object.prototype, 所以按照 instanceof 的判断规则,[] 就是Object的实例。当然,类似的new Date()、new Person() 也会形成这样一条原型链,因此,instanceof 只能用来判断两个对象是否属于原型链的关系, 而不能获取对象的具体类型。
3、constructor
当一个函数F被定义时,JS引擎会为F添加prototype原型,然后再在prototype上添加一个constructor属性,并让其指向F的引用。如下所示:
当执行 var f = new F() 时,F被当成了构造函数,f是F的实例对象,此时F原型上的constructor传递到了f上,因此f.constructor == F
可以看出,JS在函数F的原型上定义了constructor,当F被当作构造函数用来创建对象时,创建的新对象就被标记为了“F” 类型,使得新对象有名有姓,可以追溯。
同理,JS中的数据类型也遵守这个规则
细节问题:
- null和undefined是无效的对象,因此是不会有constructor存在的,这两种类型的数据需要通过typeof来判断。
- JS对象的constructor是不稳定的,这个主要体现在自定义对象上,当开发者重写prototype后,原有的constructor会丢失,constructor会默认为Object
4、Object.prototype.toString
toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型格式为[object,xxx],xxx是具体的数据类型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,... 基本上所有对象的类型都可以通过这个方法获取到。
Object.prototype.toString.call( '' ) ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call( true ) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call( null ) ; // [object Null]
Object.prototype.toString.call( new Function()) ; // [object Function]
Object.prototype.toString.call( new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call( new RegExp()) ; // [object RegExp]
Object.prototype.toString.call( new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window是全局对象global的引
需要注意的是,必须通过Object.prototype.toString.call来获取,而不能直接 new Date().toString(), 从原型链的角度讲,所有对象的原型链最终都指向了Object, 按照JS变量查找规则,其他对象应该也可以直接访问到Object的toString方法,而事实上,大部分的对象都实现了自身的toString方法,这样就可能会导致Object的toString被终止查找,因此要用call来强制执行Object的toString方法。
5、===
- 可以判断 undefined,null
var a;
console.log(a, typeof a, typeof a === 'undefined', a === undefined); // undefined 'undefined' true true
console.log(undefined === 'undefined'); // false
a = 3;
console.log(typeof a === 'number'); // true
a = 'atguigu';
console.log(typeof a === 'string'); // true string小写
a = true;
console.log(typeof a === 'boolean'); // true
a = null;
console.log(typeof a, a === null); // object true
// 2、对象数据类型
var b1 = {
b2:[1, 'abc', console.log],
b3:function(){
console.log('b3');
return function(){
return 'xfzhang';
}
}
};
// 判断是否是函数还有一种方式
console.log(typeof b1.b3 === 'function'); // true
三、相关问题
实例:实例对象
类型:类型对象
1、undefined 与 null的区别
undefined 代表定义未赋值
null 代表定义并赋值了,只是值为null
2、什么时候给变量赋值为null呢?
初始赋值,表明将要赋值为对象
结束前,让对象称为垃圾对象(被垃圾回收器回收)
3、严格区分变量类型和数据类型?
数据的类型:① 基本类型 ② 对象类型(一般对象类型就是引用类型)
变量的类型(变量内存值的类型): ① 基本类型:保存的就是基本类型的数据 ② 引用类型:保存的是地址值