JS 的数据类型分为:基本数据类型和引用类型
其中基本数据类型存放在栈内存中,引用类型存放在堆内存中
两种类型的区别在于 存储位置的不同
堆和栈的概念存在于数据结构和操作系统内存中,在数据结构中:
在数据结构中:
在操作系统中,内存被分为栈区和堆区:
typeof
typeof对基本数据类型和 function 判断是准的,但对于 []、object 和 null就会判断成object。
console.log(typeof 2); //number
console.log(typeof NaN); //number
console.log(typeof "2"); //string
console.log(typeof true); //boolean
console.log(typeof undefined); //undefined
console.log(typeof 123n); //bigint
console.log(typeof Symbol('张三')); // symbol
console.log(typeof function () {}); //functionconsole.log(typeof []); //object
console.log(typeof {}); //object
console.log(typeof null); //object
那么,typeof 是根据什么判断的呢?
在 JavaScript 第一个版本中,所有值都存储在 32 位的单元中,每个单元包含一个小的 类型标签(1-3 bits) 以及当前要存储值的真实数据。类型标签存储在每个单元的低位中,共有五种数据类型:
码值 | 类型 |
---|---|
000 | 对象 |
010 | 浮点数 |
100 | 字符串 |
110 | 布尔 |
1 | 整数 |
有两种特殊数据类型:
那也就是说 null 的类型标签也是 000,和 Object 的类型标签一样,所以会被判定为 Object。所以 typeof null 的结果是 object。
instanceof
instanceof对引用类型的判断是准确的,对于基本数据类型都是 false.
console.log(2 instanceof Number); // false
console.log(NaN instanceof Number); // false
console.log("2" instanceof String); // false
console.log(true instanceof Boolean); // false
console.log(Symbol('张三') instanceof Symbol); // falseconsole.log(Object instanceof Object); // true
console.log(function () {} instanceof Function); // true
console.log([] instanceof Array); // true
如何实现instanceof ?
function myInstanceof(left, right) {// 获取对象的原型let proto = Object.getPrototypeOf(left)// 获取构造函数的 prototype 对象let prototype = right.prototype;// 判断构造函数的 prototype 对象是否在对象的原型链上while (true) {if (!proto) return false;if (proto === prototype) return true;// 如果没有找到,就继续从其原型上找,Object.getPrototypeOf方法用来获取指定对象的原型proto = Object.getPrototypeOf(proto);}
}
constructor
一般的数据类型和引用的都可以判断,但是 undefined 和 null 除外
console.log(('').constructor == String)console.log((true).constructor == Boolean)console.log(({}).constructor == Object)console.log(([]).constructor == Array)console.log((undefined).constructor == undefined) //报错console.log((null).constructor == null) //报错
但是注意,constructor 是可以被改变的。
Object.prototype.toString.call()
可以判断任何数据类型。
原理:使用 Object 对象的原型方法 toString 来判断数据类型:
var a = Object.prototype.toString;console.log(a.call(2)); // [object Number]
console.log(a.call(true)); // [object Boolean]
console.log(a.call('str')); // [object String]
console.log(a.call([])); // [object Array]
console.log(a.call(function () {})); // [object Function]
console.log(a.call({})); // [object Object]
console.log(a.call(undefined)); // [object Undefined]
console.log(a.call(null)); // [object Null]
同样是检测对象 obj 调用 toString 方法,obj.toString()的结果和 Object.prototype.toString.call(obj)的结果不一样,这是为什么?
这是因为 toString 是 Object 的原型方法,而 Array、function 等类型作为 Object 的实例,都重写了 toString 方法。不同的对象类型调用 toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法(function 类型返回内容为函数体的字符串,Array 类型返回元素组成的字符串…),而不会去调用 Object 上原型 toString 方法(返回对象的具体类型),所以采用 obj.toString()不能得到其对象类型,只能将 obj 转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用 Object 原型上的 toString 方法。
原型链
obj.__proto__ === Array.prototype; // true
通过 isPrototypeOf
Array.prototype.isPrototypeOf(obj)
通过 ES6 的 Array.isArray()做判断
Array.isArrray(obj);
通过 instanceof 做判断
obj instanceof Array
通过 Object.prototype.toString.call()做判断
Object.prototype.toString.call(obj).slice(8,-1) === 'Array';
通过原型链做判断
obj.__proto__ === Array.prototype;
通过 Array.prototype.isPrototypeOf
Array.prototype.isPrototypeOf(obj)