JS数据类型
字数统计:2.1k字目录
参考:
类型检测
浅谈 instanceof 和 typeof 的实现原理
在JavaScript中,如何判断数组是数组?
分类
基本数据类型
Number
、String
、Boolean
、Undefined
、Null
Symbol [ES6]
Number
Number类型包含整数和浮点数(浮点数数值必须包含一个小数点,且小数点后面至少有一位数字)两种值。
NaN: 非数字类型。特点:① 涉及到的任何关于NaN的操作,都会返回NaN ②NaN不等于自身。
isNaN() 函数用于检查其参数是否是非数字值。isNaN(123) //false isNaN("hello") //true
String
字符串有length属性。
字符串转换:转型函数String(),适用于任何数据类型(null,undefined 转换后为null和undefined);toString()方法(null,defined没有toString()方法)。
Boolean
该类型只有两个值,true和false
Undefined
只有一个值,即undefined值。使用var声明了变量,但未给变量初始化值,那么这个变量的值就是undefined。
Null
null类型被看做空对象指针,前文说到null类型也是空的对象引用。
复杂数据类型
Object
Object本质上是由一组无序的名值对组成的。
js中对象是一组属性与方法的集合。这里就要说到引用类型了,引用类型是一种数据结构,用于将数据和功能组织在一起。引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。
引用类型
Object
、Array
、Function
、Date
、RegExp
等
基本类型与引用类型
基本类型又叫原始类型(primitive type)
栈:原始数据类型(Undefined,Null,Boolean,Number、String)
堆:引用数据类型(Object、Array、Function)
两种类型的区别是 存储位置不同
:
原始数据类型
直接存储在栈(stack)
中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
引用数据类型
存储在堆(heap)
中的对象,占据空间大、大小不固定,如果存储在栈中,将会影响程序运行的性能。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
变量
ECMAScript中用var关键字来定义变量,因为js是弱类型的,所以无法确定变量一定会存储什么值,也就不知道变量到底会是什么类型,而且变量的类型可以随时改变。
这就是ECMAScript是松散类型的来由,所谓松散类型就是可以用来保存任何类型的数据。
ps:
es6中新增了let命令来声明变量、const命令声明一个只读的常量。
let的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
const一旦声明,常量的值就不能改变。1
2
3
4
5
6
7
8
9
10
11
12
13
14const是用来定义常量的,而且定义的时候必须初始化,且定义后不可以修改。
对于基本类型的数据来说,自然很好理解了,例如 const PI = 3.14。
如果定义的时候不初始化值的话就会报错,错误内容就是没有初始化。
const p = {name: 'www', age: '22'}
p.name = 'dfsfs'
p: // {name: 'dfsfs', age: '22' }
因为对象是引用类型的,P中保存的仅是对象的指针,这就意味着,const仅保证指针不发生改变,
修改对象的属性不会改变对象的指针,所以是被允许的。也就是说const定义的引用类型只要指针不发生改变,
其他的不论如何改变都是允许的。
即使对象的内容没发生改变,指针改变也是不允许的。
类型检测
typeof
、Object.prototype.toString()
typeof
typeof返回一个表示数据类型的字符串,返回结果包括:number
、boolean
、string
、symbol
、object
、undefined
、function
等7种数据类型,但不能判断null
、array
等。1
2
3
4
5
6
7
8
9
10typeof Symbol(); // symbol 有效
typeof ''; // string 有效
typeof 1; // number 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof new Function(); // function 有效
typeof null; //object 无效
typeof [] ; //object 无效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效
instanceof
instanceof
主要的作用就是判断一个实例是否属于某种类型
。是用来判断A是否为B的实例,表达式为:A instanceof B
,如果A是B的实例,则返回true,否则返回false。instanceof
运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype
属性,但它不能检测null
和 undefined
。
1 | [] instanceof Array; //true |
1 | let person = function () {} |
instanceof 实现原理
1 | function new_instance_of(leftVaule, rightVaule) { |
其实 instanceof 主要的实现原理就是只要右边变量的prototype
在左边变量的原型链上即可。因此,instanceof
在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype
,如果查找失败,则会返回 false
,告诉我们左边变量并非是右边变量的实例。
constructor
constructor
作用和instanceof
非常相似。但constructor
检测 Object
与instanceof
不一样,还可以处理基本数据类型的检测
。1
2
3let a = []
console.log(a.constructor);
// ƒ Array() { [native code] }
不过函数的 constructor
是不稳定的,这个主要体现在把类的原型进行重写,在重写的过程中很有可能出现把之前的constructor
给覆盖了,这样检测出来的结果就是不准确的。1
2
3
4
5
6
7
8
9
10//定义一个数组
const a = [];
//作死将constructor属性改成了别的
a.contrtuctor = Object;
console.log(a.constructor == Array);
//false (哭脸)
console.log(a.constructor == Object);
//true (哭脸)
console.log(a instanceof Array);
//true (instanceof火眼金睛)
可以看出,constructor
属性被修改之后,就无法用这个方法判断数组是数组了,除非你能保证不会发生constructor
属性被改写的情况,否则用这种方法来判断数组也是不靠谱的。
Object.prototype.toString.call()
toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]]。
这是一个内部属性,其格式为 [object xxx] ,其中 xxx 就是对象的类型。1
2
3
4
5
6
7
8
9
10var gettype=Object.prototype.toString;
gettype.call('aaaa') 输出 [object String]
gettype.call(2222) 输出 [object Number]
gettype.call(true) 输出 [object Boolean]
gettype.call(undefined) 输出 [object Undefined]
gettype.call(null) 输出 [object Null]
gettype.call({}) 输出 [object Object]
gettype.call([]) 输出 [object Array]
gettype.call(function(){}) 输出 [object Function]
用Array对象的isArray方法判断
当参数为数组的时候,isArray方法返回true,当参数不为数组的时候,isArray方法返回false。1
2
3
4const a = [];
const b = {};
Array.isArray(a);//true
Array.isArray(b);//false
我试着在调用这个方法之前重写了Object.prototype.toString方法:1
2
3
4
5Object.prototype.toString = ()=>{
console.log('Hello Howard');
}
const a = [];
Array.isArray(a);//true
并不影响判断的结果。
我又试着修改了constructor对象:1
2
3
4const a = [];
const b = {};
a.constructor = b.constructor;
Array.isArray(a);//true
OK,还是不影响判断的结果。
可见,它与instance运算符判断的方法以及Object.prototype.toString
法并不相同,一些列的修改并没有影响到判断的结果。
Array.isArray
是ES5标准中增加的方法,部分比较老的浏览器可能会有兼容问题,所以为了增强健壮性,建议还是给Array.isArray方法进行判断,增强兼容性,重新封装的方法如下:1
2
3
4
5if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
封装可以分辨所有数据类型的方法
1 | var typeName = { |