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返回
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?