在 JavaScript 编程中,我们经常会遇到边界数据类型判断,在大厂面试中,总是逃不过现场秀一段手写代码,此时往往需要考虑好代码的边界类型,让面试官看到你严谨的编程逻辑和深入思考的能力,面试才算加分!因此掌握它,看来还是很有必要的!
首先JS数据类型有如下图所示 8 种类型
以上又可分为基础类型和引用类型
基础类型:undefined、Null、Boolean、Number、String、BigInt、Symbol
引用类型:统称为 Object 类型。细分的话,有:Object 类型、Array 类型、Date 类型、RegExp 类型、Function 类型 、Math 类型等。
依据存储方式不同,数据类型大致可以分成两类:
基础类型存储在栈内存,被引用或拷贝时,会创建一个完全相等的变量;
引用类型存储在堆内存,存储的是地址,多个引用指向同一个内存地址。
let a = {
name: 'Miss U',
age: 9
}
let b = a;
console.log(a.name); //Miss U
b.name = 'Baby';
console.log(a.name); //Baby
console.log(b.name); //Baby
当b的name被修改后,a的name也随之改变,这里就体现了引用类型的“共享”的特性,即这两个值都存在同一块内存中共享,一个发生了改变,另外一个也随之跟着变化。
let a = {
name: 'father',
age: 80
}
function change(obj) {
obj.age = 3;
obj = {
name: 'son',
age: 18
}
return obj;
}
let b = change(a);
console.log(b.age); // 18
console.log(a.age); // 3
15行输出 b.age = 18,16行输出3,这里跟你预想的是否一样呢?
数据类型检测
在日常的业务开发中,经常会遇到 数据类型检测问题,当然也是面试中常问的,
比如:如何判断数组?怎么检测数据类型?等等。类似的题目会很多,而且在平常开发过程中我们也会经常用到。
数据类型的判断方法其实有很多种,比如 typeof 和 instanceof等,接下来就展开具体说一说。
第一种判断方法:typeof
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof null // 'object'
typeof [] // 'object'
typeof {} // 'object'
typeof 1n // 'bigint'
typeof BigInt('1') // 'bigint';
typeof Object(1n) // 'object'
typeof console // 'object'
typeof console.log // 'function'
抛出问题:此处 typeof null 为什么是 'object' 呢?
简单来说,typeof null
的结果为Object,
它是从娘胎里带出来的bug
,😂在javascript 的最初版本中,使用的 32
位系统,js
为了性能优化,使用低位来存储变量的类型信息。
在判断数据类型时,是根据机器码低位标识来判断的,而null
的机器码标识为全0
,而对象的机器码低位标识为000
。所以typeof null
的结果被误判为Object
。
那么问题来了,后续版本包括ES6为什么不修复这个bug呢?
这个问题问的好,其实Es6已有提案,被拒了,因为历史遗留代码太多了,改了容易得罪人😂,所以bug就成了feature了。
注意:引用数据类型 Object,用 typeof 来判断的话,除了 function 会判断为 Function 以外,其余都是 'object',是无法判断出来的
第二种判断方法:instanceof
首先明确instanceof是干嘛的?
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);
console.log(auto instanceof Car);
// expected output: true
console.log(auto instanceof Object);
// expected output: true
String 和 Date 对象同时也属于Object 类型(他们是由 Object 类派生出来的)
console.log(String.prototype.__proto__ === Object.prototype)// true
console.log(Date.prototype.__proto__ === Object.prototype) // true
如果通过 字面量 的方式创建字符串,那么无法通过 instanceof 判断某个变量是否是字符串
const str = 'aaaa'
console.log(str instanceof String) // false
console.log(str instanceof Object) // false
通过 new 方式,是可以使用 instanceof 判断 变量是否是字符串。
const str = new String('aaa')
console.log(str instanceof String) // true
console.log(str instanceof Object) // true
console.log(str.__proto__ === String.prototype) // true
对于复杂数据类型 对象 和 数组,无论是通过字面量的方式创建,还是 new 的方式,都是可以通过 instanceof 来判断数据类型的
class Person {
constructor(name) {
this.name = name
this.type = 'man'
}
}
const p1 = new Person('小白')
const p2 = { name: '肉丝' }
console.log(p1) // { name: '小白', type: 'man' }
console.log(p2) // { name: '肉丝' }
console.log(p1 instanceof Person) // true
console.log(p1 instanceof Object) // true
console.log(p2 instanceof Object) // true
const arr1 = [1, 2]
const arr2 = new Array('a', 'b')
console.log(arr1 instanceof Array) // true
console.log(arr2 instanceof Array) // true
那么问题又来了,如果让我们自己实现一个 instanceof 的底层实现,应该怎么写呢?
此刻,双手🙌🏻呈上代码,如写的不对,欢迎指错。
function myInstanceof(obj, objType) {
// 首先用typeof来判断基础数据类型,如果是,直接返回false
if(typeof obj !== 'object' || obj === null) return false;
// getProtypeOf是Object对象自带的API,能够拿到参数的原型对象
let proto = Object.getPrototypeOf(obj);
while(true) { //循环往下寻找,直到找到相同的原型对象
if(proto === null) return false;
if(proto === objType.prototype) return true;//找到相同原型对象,返回true
proto = Object.getPrototypeof(proto);
}
}
// 验证myInstanceof
console.log(myInstanceof(new Array('2','3'), Array)); // true
console.log(myInstanceof(123, Number)); // false
console.log(myInstanceof(new Number(123), Number)); //true
总结一下:
instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型;
typeof 它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了 function 类型以外,其他的也无法判断。
所以工作中常常需要结合这两个一起去检测数据类型,未免显得有些繁琐,所以,我一般更推荐第三种方法。
第三种判断方法:Object.prototype.toString
对于 Object.prototype.toString()
方法,会返回一个表示该对象的字符串,格式如"[object XXX]"
注意,XXX的首字母大写哦。
Object.prototype.toString.call(null) //"[object Null]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('Miss U') // “[object String]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString({}) // "[object Object]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call(function(){}) // ”[object Function]"
Object.prototype.toString.call([]) //"[object Array]"
Object.prototype.toString.call(/123/g) //"[object RegExp]"
Object.prototype.toString.call(new Date()) //"[object Date]"
Object.prototype.toString.call(document) //[object HTMLDocument]"
Object.prototype.toString.call(window) //"[object Window]"
OK,最后,给大家呈上一段通用检测数据类型的方法
function getPrototype(obj){
let type = typeof obj;
if (type !== "object") { // 先进行typeof判断,如果是基础数据类型,直接返回
console.log(obj,':',res)
return type;
}
// 对于typeof返回结果是object的,再进行如下的判断,正则返回结果
const res = Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1')
console.log(obj,'=',res);
return res;
}
getPrototype([]) // "Array" typeof []是object,因此toString返回
getPrototype('abc') // "string" typeof 直接返回
getPrototype(window) // "Window" toString返回
getPrototype(null) // "Null"首字母大写,typeof null是object,需toString来判断
getPrototype(undefined) // "undefined" typeof 直接返回
getPrototype() // "undefined" typeof 直接返回
getPrototype(function(){}) // "function" typeof能判断,因此首字母小写
getPrototype(/123/g) //"RegExp" toString返回
好了,今天的内容就分享到这,感谢观看!
⭐️⭐️山河远阔,国泰民安,祖国生日快乐!
— END —
听说早在中秋节过后就有小伙伴调侃:“一点也不想干活,好想早点为我们祖国母亲庆生!”
据说现在“爱国”三大表现是酱紫的
☟☟☟
1.祖国母亲生日临近,完全无心工作,只想为母亲庆生
2.觉得七天根本不足以表达我对祖国母亲的爱
3.希望祖国母亲过完这次生日后,再过个农历哒
不管你们是不是,反正我是中枪了…😄
其实我们应该觉得很幸福了,当一个假期接着另一个假期的时候是很有盼头的!我们祖国母亲马上 就过生日了,而我们也迎来了今年最后一个大假!
回家
旅游
学习
这个假期你打算怎么安排呢?
相信现在的你已经安排好自己的国庆假期了,来吧,保持队型,跟上节奏,留言区共享一下你的国庆假期是怎么安排的?
参考资料:
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=601#/detail/pc?id=6174