数据类型
JS 的数据类型分为:基本数据类型和引用类型
- 数字(number)
- 字符串(string)
- 空值(null)
- 未定义(undefined)
- 布尔值(boolean)
- 符号(symbol, ES6 中新增)
- 大整数(BigInt, ES2020 引入)
- 对象 (object)
其中基本数据类型存放在栈内存中,引用类型存放在堆内存中
- 栈:原始数据类型(Undefined、Null、Boolean、Number、String、symbol、BigInt)
- 堆:引用数据类型(对象、数组和函数)
两种类型的区别在于 存储位置的不同
- 栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
- 堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
堆和栈的概念存在于数据结构和操作系统内存中,在数据结构中:
在数据结构中:
- 栈中数据的存取方式为先进后出。
- 堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。
在操作系统中,内存被分为栈区和堆区:
- 栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
- 堆区内存一般由开发着分配释放,若开发者不释放,程序结束时可能由垃圾回收机制回收。
如何判断数据类型
-
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 () {}); //function console.log(typeof []); //object console.log(typeof {}); //object console.log(typeof null); //object
那么,typeof 是根据什么判断的呢?
在 JavaScript 第一个版本中,所有值都存储在 32 位的单元中,每个单元包含一个小的 类型标签(1-3 bits) 以及当前要存储值的真实数据。类型标签存储在每个单元的低位中,共有五种数据类型:
码值 类型 000 对象 010 浮点数 100 字符串 110 布尔 1 整数 有两种特殊数据类型:
- undefined的值是 (-2)30(一个超出整数范围的数字);
- null 的值是机器码 NULL 指针(null 指针的值全是 0)
那也就是说 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); // false console.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)