JavaScript有两种数据类型,分别是基本数据类型和引用数据类型。其中基本数据类型包括Undefined、Null、Boolean、Number、String和Symbol(ES6新增,表示独一无二的值),而引用类型统称为Object对象、主要包括对象、数组和函数。
基本数据类型
1.基本数据类型的值是不可变的。
var str = "abc"; str[0] = "d"; // 字符串是可以通过[]访问的
console.log(str[1]="e"); // e console.log(str[0]); // a console.log(str); // abc
在JavaScirpt中,数字、字符串、布尔值、Null和Undefined的值是不可改变的,就算在代码中动态地修改它的值,它的原始值并不会发生改变,如果需要修改值,都是通过定义一个变量来保存这个新值,因为它的返回值就是修改后的值。
2.原始数据类型注解存储在栈(Stack)中的简单数据段,占据空间小,大小固定,属于被频繁使用数据,所以放入栈中存储。
3.值的比较可以用【==】或【===】运算符。【==】只进行值的比较,【===】不仅进行值的比较,还要进行数据类型的比较。
var num = 1; // Number类型 var str = "1"; // String类型 console.log(num == str); // true,只比较值 console.log(num === str); // false,不仅比较值,还比较数据类型
引用数据类型
1.引用数据类型的值是可变的。
var num = [1,2,3]; num[0] = "a";
console.log(num); // ["a", 2, 3]
在JavaScript中,数组和对象的值是可变的,也就是说当动态修改里面的值的时候,原始的值也会发生相应的改变。
2.引用数据类型同时保存在栈内存和堆内存。
引用数据类型存储在堆(Heap)中的对象,占据空间大,大小不固定。如果存储在栈中,将会影响程序运行的性能。因此引用数据类型在栈中只存储指针,该指针指向堆中该实体的起始位置。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
3.引用数据类型比较的是引用地址。当从一个变量向另一个变量赋值引用类型的值时,同样也会将存储在变量上的对象的值赋值一份放到为新变量分配的空间中。
var obj1 = {age: 22}; var obj2 = obj1; // 将obj1赋值给obj2,实际上是将obj1指向的内存地址赋值给obj2 console.log(obj1 === obj2); // true,obj1和obj2指向同一块内存空间 obj2.age = 18; // obj2修改属性的同时,obj1也一起发生了改变,因为它们指向同一个对象,任何修改操作都会相互影响 console.log(obj1 === obj2); // true,obj1和obj2还是指向同一块内存空间
数据类型的检测
在JavaScript中,要检测一个变量的数据类型,主要有5种方法。
1.【typeof】运算符
【typeof】返回一个表示数据类型的字符串,返回结果包括7种:"number"、"boolean"、"string"、"symbol"、"object"、"undefined"、"function"。
typeof 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 无效
使用【typeof】运算符时,对于数组和对象,返回的都是"object",没有什么卵用。因此【typeof】主要是用来判断基本数据类型的,当然了,除了Null。那么这时就到【instanceof】运算符上场了。
2.【instanceof】运算符
【instanceof】运算符是用来判断A是否为B的实例,表达式为:A instanceof B。如果A是B的实例,则返回true,否则返回false。【instanceof】运算符实际上是用来测试一个对象在其原型链中是否存在一个构造函数的prototype属性的。
[] instanceof Array; // true {} instanceof Object; // true new Date() instanceof Date; // true new RegExp() instanceof RegExp; // true
但是对于基本数据类型来说,字面量方式创造出来的结果和实例方法创建是有一定的区别的。
console.log(1 instanceof Number); // false console.log(new Number(1) instanceof Number); // true
从严格意义上来讲,只有实例创建出来的对象才是标准的对象数据类型值,也是标准的Number这个类的一个实例;对于字面量创建出来的结果是基本的数据类型值,不是严谨的实例,但是由于JavaScript的松散特点,导致了可以使用Number.prototype上提供的方法。只要在当前实例的原型链上,我们用其检测出来的结果都是true。在类的原型继承中,我们最后检测出来的结果未必是准确的(坑爹)。
var arr = [1, 2, 3]; function fn(){} console.log(arr instanceof Array); // true console.log(arr instanceof Object); // true console.log(fn instanceof Function); // true console.log(fn instanceof Object); // true
另外,【instanceof】运算符也不能用来检测null和undefined。对于这两种特殊的数据类型,对应的类是Null和Undefined,浏览器把这两个类保护起来了,不允许访问使用。
而对于数组的类型判断,其实还可以用ES6新增的Array.isArray()。
Array.isArray([]); // true
3.【===】严格运算符
【===】运算符只能用于判断null和undefined,因为这两种类型的值都是唯一的。
var a = null; var b; console.log(typeof a); // "object" console.log(typeof b); // "undefined" console.log(a === null); // true console.log(b === undefined); // true
4.【constructor】属性
【constructor】的作用与【instanceof】非常相似,但是【constructor】检测Object与【instanceof】不一样,还可以处理基本数据类型的检测。
var arr = [1,2]; var reg = /^$/; // 正则表达式 console.log(arr.constructor === Array); // true console.log(arr.constructor === RegExp); // false console.log((1).constructor === Number); // true console.log(reg.constructor === RegExp); // true console.log(reg.constructor === Object); // false
因为null和undefined是无效的对象,因此是不会有constructor存在的,这两种类型的数据需要通过其它方式判断,比如上面的【===】严格运算符。
另外,函数的constructor是不稳定的。这个主要体现在把类的原型进行重写,在重写的过程中很有可能出现把之前的constructor给覆盖了的情况,这样检测出来的结果就是不准确的。
function Fn(){} Fn.prototype = new Array(); var f = new Fn;
console.log(f.constructor); // Array
5.【Object.prototype.toString.call()】函数
【Object.prototype.toString.call()】是最准确、最常用的方式。首先获取Object原型上的toString()方法,让方法执行,然后让toString()方法中的this指向第一个参数的值。为什么要这样写而不是用对象直接调用toString()方法,是因为每个类重写了toString()方法,而实际要调用的是Object的原型方法toString(),不理解的话建议去看看原型链继承相关的知识。
Object上的toString的作用是返回当前方法执行的主体(方法中的this)所属类的详细信息,即"[object Object]",其中第一个object代表当前实例是对象数据类型的(固定值,只有这一个候选值),第二个Object代表的是this所属的类是Object,候选值有String、Number、Date等。
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的引用
关于toString的重要补充:toString的本意是转换为字符串,一般用来将值转换为字符串。但是对于Number、String、Boolean、Array、RegExp、Date、Function原型上的toString属性(值是函数),都是用于把当前的数据类型转换为字符串,Object上的toString也是同样的,返回的是数据类型的字符串,而不是值的字符串。
"这世上没有平白无故的闪闪发光。"