js中有两种数据类型,分为基本数据类型和引用数据类型。
- 基本数据类型
- Number
- String
- Boolean
- Undefined
- Null
- Symbol(ES6新增)
- 引用数据类型
- Object
- Array
- Function
- Regexp
判断数据类型的方法:
1. typeof 对于基本数据类型判断是没有问题的,但是遇到引用数据类型(如:Array)是不起作用的,会把引用数据类型都判断为Object
var arr = [];
var str = "aa";
console.log(typeof arr); //Object
console.log(typeof str); //String
注意:typeof判断Null的结果是object
因为JavaScript是用二进制来存储值的,typeof是通过前三位来判断数据类型的,object和null的前三位都是000,所以会把null判断成object
- 1:整型(int)
- 000:引用类型(object)
- 010:双精度浮点型(double)
- 100:字符串(string)
- 110:布尔型(boolean)
另外还用两个特殊值:
- undefined,用整数−2^30(负2的30次方,不在整型的范围内)
- null,机器码空指针(C/C++ 宏定义),前三位三位也是000
var n = null;
console.log(typeof n); //object
2. instanceof 判断引用数据类型是否是构造函数创建的,返回布尔值
// instanceof
var arr = [];
var obj = {};
console.log(arr instanceof Array); //true
console.log(arr instanceof Object); //true
console.log(obj instanceof Array); //false
console.log(obj instanceof Object); //true
instanceof中,用数组去对比object返回的也是true
console.log("str" instanceof String); //false
console.log(111 instanceof Number); //false
instanceof不能判断基本数据类型,结果都是false
3. constructor 完全可以应对基本数据类型和引用数据类型
function Aa(c) {
this.num = c;
}
Aa.prototype = Array.prototype;
let zx = new Aa(111);
console.log(zx.constructor == Array); //true
如果声明了一个构造函数,并且把他的原型指向了 Array 的原型,所以这种情况下,constructor也显得力不从心
4. Object.prototype.toString.call() 完美的解决方案
console.log(Object.prototype.toString.call([])); //[object Array]
console.log(Object.prototype.toString.call("")); //[object String]
console.log(Object.prototype.toString.call({})); //[object Object]
基本数据类型和应用数据类型的区别
基本数据类型(存放在栈内存中):基本数据类型是指存放在栈中的简单数据段,数据大小确定,内存空间大小可以分配,它们是直接按值存放的,所以可以直接按值访问
var a = 10;
var b = a;
b = 20;
console.log(a); // 10
console.log(b); // 20
应用数据类型(存放在堆内存中的对象,每个空间大小不一样,要更具情况进行特定的配置) :引用类型是存放在堆内存中的对象,变量其实是保存的在栈内存中的一个指针(保存的是堆内存中的引用地址),这个指针指向堆内存。
引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象。
var obj1 = new Object();
var obj2 = obj1;
obj2.name = "我";
console.log(obj1.name); // 我
说明这两个引用数据类型指向了同一个堆内存对象。obj1赋值给obj2,实际上这个堆内存对象在栈内存的引用地址复制了一份给了obj2,但是实际上他们共同指向了同一个堆内存对象,所以修改obj2其实就是修改那个对象,所以通过obj1访问也能访问的到。
var a = [1,2,3,4,5];
var b = a;//传址 ,对象中传给变量的数据是引用类型的,会存储在堆中;
var c = a[0];//传值,把对象中的属性/数组中的数组项赋值给变量,这时变量C是基本数据类型,存储在栈内存中;改变栈中的数据不会影响堆中的数据
alert(b);//1,2,3,4,5
alert(c);//1
//改变数值
b[4] = 6;
c = 7;
alert(a[4]);//6
alert(a[0]);//1
从上面我们可以得知,当我改变b中的数据时,a中数据也发生了变化;但是当我改变c的数据值时,a却没有发生改变。
这就是传值与传址的区别。因为a是数组,属于引用类型,所以它赋予给b的时候传的是栈中的地址(相当于新建了一个不同名“指针”),而不是堆内存中的对象。而c仅仅是从a堆内存中获取的一个数据值,并保存在栈中。所以b修改的时候,会根据地址回到a堆中修改,c则直接在栈中修改,并且不能指向a堆内存中。