JS 数据类型你了解多少?
JS 数据类型你了解多少?
数据类型概念
JavaScript 的数据类型有下图所示的 8 种:

数据类型大致可以分成两类来进行存储:
- 基础类型存储在栈内存,被引用或拷贝时,会创建一个完全相等的变量;
- 引用类型存储在堆内存,存储的是地址,多个引用指向同一个地址,这里会涉及一个“共享”的概念。
题目一:初出茅庐
let a = {
name: 'lee',
age: 18
}
let b = a;
console.log(a.name); // 第一个console 'lee'
b.name = 'son';
console.log(a.name); // 第二个console 'son'
console.log(b.name); // 第三个console 'son'
在执行了 b.name='son' 之后,会发现 a 和 b 的属性 name 都是 'son',第二个和第三个打印结果是一样的,这里就体现了引用类型的“共享”的特性,即这两个值都存在同一块内存中共享,一个发生了改变,另外一个也随之跟着变化。
题目二:渐入佳境
let a = {
name: 'Julia',
age: 20
}
function change(o) {
o.age = 24;
o = {
name: 'Kath',
age: 30
}
return o;
}
let b = change(a); // 注意这里没有new
console.log(b.age); // 第一个console
console.log(a.age); // 第二个console
通过上述代码可以看到第一个 console 的结果是 30,b 最后打印结果是 {name: "Kath", age: 30};第二个 console 的返回结果是 24,而 a 最后的打印结果是 {name: "Julia", age: 24}。
原因在于:函数传参进来的 o,传递的是对象在堆中的内存地址值,通过调用 o.age = 24确实改变了 a 对象的 age 属性;12 行把参数 o 的地址重新返回了,将 {name: "Kath", age: 30} 存入其中,最后返回 b 的值就变成了 {name: "Kath", age: 30}。而如果把 return o 去掉,那么 b 就会返回 undefined。
数据类型检测
第一种判断方法:typeof
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof null // 'object'
typeof [] // 'object'
typeof {} // 'object'
typeof console // 'object'
typeof console.log // 'function'
虽然 typeof null 会输出 object,但这只是 JS 存在的一个悠久 Bug,不代表 null 就是引用数据类型,并且 null 本身也不是对象。如果需要在 if 语句中判断是否为 null,直接通过 ‘===null’ 来判断就好。
第二种判断方法:instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
语法
object instanceof constructor
参数
object 某个实例对象,constructor 某个构造函数
// 定义构造函数
function C(){}
function D(){}
var o = new C();
o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof D; // false,因为 D.prototype 不在 o 的原型链上
o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 true
C.prototype instanceof Object // true,同上
C.prototype = {};
var o2 = new C();
o2 instanceof C; // true
o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上.
D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上
几个例子:
[] instanceof Array; //true
{} instanceof Object;//true
new Date() instanceof Date;//true
function Person(){};
new Person() instanceof Person;
[] instanceof Object; //true
new Date() instanceof Object;//tru
new Person instanceof Object;//true
从上面的例子中,我们发现虽然 instanceof 能够正确判断 [] 是 Array 的实例对象,但不能辨别 [] 不是 Object 的实例对象,为什么呢,这还需要从 js 的原型链说起,我们首先来分析一下 []、Array、Object 三者之间的关系,从instanceof判断能够得出:[].__proto__ ->Array.prototype, 而 Array.prototype.__proto__ 指向了Object.prototype,Object.prototype.__proto__ 指向了 null , 标志着原型链的结束。

手写 instanceof
function myInstanceof(left, right) {
// 先用typeof来判断基础数据类型,如果是,直接返回false
if (typeof left !== 'object' || left === null) {
return false;
}
// getProtypeOf是Object对象自带的API,能够拿到参数的原型对象
let proto = Object.getPrototypeOf(left);
// 循环往下寻找,直到找到相同的原型对象
while (true) {
if (proto === null) {
return false;
}
if (proto === right.prototype) {
// 找到相同原型对象,返回true
return true;
}
proto = Object.getPrototypeOf(proto)
}
}
console.log(myInstanceof(new Number(123), Number)) // true
console.log(myInstanceof(123, Number)) // false
console.log([] instanceof Object) // true
console.log(myInstanceof([], Object)) // true
现在我们知道了两种判断数据类型的方法,那么它们之间有什么差异呢?我总结了下面两点:
-
instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型;
-
而 typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了 function 类型以外,其他的也无法判断。
第三种判断方法:Object.prototype.toString
toString() 是 Object 的原型方法,调用该方法,可以统一返回格式为 “[object Xxx]” 的字符串,其中 Xxx 就是对象的类型。对于 Object 对象,直接调用 toString() 就能返回 [object Object];而对于其他对象,则需要通过 call 来调用,才能返回正确的类型信息。
Object.prototype.toString({}) // "[object Object]"
Object.prototype.toString.call({}) // 同上结果,加上call也ok
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('1') // "[object String]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call(null) //"[object Null]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(/123/g) //"[object RegExp]"
Object.prototype.toString.call(new Date()) //"[object Date]"
Object.prototype.toString.call([]) //"[object Array]"
Object.prototype.toString.call(document) //"[object HTMLDocument]"
Object.prototype.toString.call(window) //"[object Window]"
从上面这段代码可以看出,Object.prototype.toString.call() 可以很好地判断引用类型,甚至可以把 document 和 window 都区分开来。
function getType(obj){
let type = typeof obj;
if (type !== "object") { // 先进行typeof判断,如果是基础数据类型,直接返回
return type;
}
// 对于typeof返回结果是object的,再进行如下的判断,正则返回结果
return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1'); // 注意正则中间有个空格
}
/* 代码验证,需要注意大小写,哪些是typeof判断,哪些是toString判断?思考下 */
getType([]) // "Array" typeof []是object,因此toString返回
getType('123') // "string" typeof 直接返回
getType(window) // "Window" toString返回
getType(null) // "Null"首字母大写,typeof null是object,需toString来判断
getType(undefined) // "undefined" typeof 直接返回
getType() // "undefined" typeof 直接返回
getType(function(){}) // "function" typeof能判断,因此首字母小写
getType(/123/g) //"RegExp" toString返回
浙公网安备 33010602011771号