面试官:JS的数据类型你了解多少?(一万字总结)

前言

作为JavaScript的入门知识点,Js数据类型在整个JavaScript的学习过程中其实尤为重要。最常见的是边界数据类型条件判断问题。

我们将通过这几个方面来了解数据类型:

概念
检测方法
转换方法

概念

undefined、Null、Boolean、String、Number、Symbol、BigInt为基础类型;

Object为引用类型,其中包括Array、RegExp、Date、Math、Function这几种常见的类型。

面试官:JS的数据类型你了解多少?(一万字总结)

数据类型大致分为两类来进行存储。

基础类型存储在栈内存,被引用或拷贝时,会创建一个完全相等的变量。

引用类型存储在堆内存,存储的是地址,多个引用指向同一个地址,这里会涉及一个“共享”的概念。

面试官:JS的数据类型你了解多少?(一万字总结)

示例题一:

let a = {
name:"maomin",
age:"20"
}
let b = a;
console.log(a.name); // "maomin"
b.name = "haojie";
console.log(a.name); // "haojie"
console.log(b.name); // "haojie"

这里就体现了引用类型的“共享”的特性,即这两个值都存在同一块内存中共享,一个发生了改变,另外一个也随之跟着变化。

示例题二:

let a = {

name: 'maomin',

age: 20

}

function change(o) {

o.age = 24;

o = {

name: 'haojie',

age: 30

}

return o;

}

let b = change(a);

console.log(b.age); // 30

console.log(a.age); // 24

函数传参进来的 o,传递的是对象在堆中的内存地址值,通过调用 o.age = 24确实改变了 a 对象的 age 属性;但是代码中{name:'haojie',age:30}却又把 o 变成了另一个内存地址,将{name: 'haojie', age: 30}存入其中,最后返回 b 的值就变成了 {name: 'haojie', age: 30}。

其实,上面两个例子很显明地阐述了在Vue.js组件中data属性必须是一个函数而不是一个对象,每个实例可以维护一份被返回对象的独立的拷贝。
数据类型检测
第一种检测方法: 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'

可以看到,前 6 个都是基础数据类型,而为什么第 6 个 null 的typeof是 object 呢?这里要和你强调一下,虽然 typeof null会输出 object,但这只是 JS 存在的一个悠久 Bug,不代表 null 就是引用数据类型,并且 null 本身也不是对象。因此,null 在 typeof 之后返回的是有问题的结果,不能作为判断 null 的方法。如果你需要在 if 语句中判断是否为null,直接通过 ===null来判断就好。

此外还要注意,引用数据类型 Object,用 typeof 来判断的话,除了 function 会正确判断以外,其余都是 object,是无法判断出来的。
第二种检测方法:instanceof

我们 new 一个对象,那么这个新对象就是它原型链继承上面的对象了,通过 instanceof我们能判断这个对象是否是之前那个构造函数生成的对象,这样就基本可以判断出这个新对象的数据类型。

let Car = function() {}

let benz = new Car();

benz instanceof Car; // true


let car = new String('maomin');

car instanceof String; // true


let str = 'haojie';

str instanceof String; // false

我们自己可以实现一个 instanceof 的底层实现:

function myInstanceof(target, typeObj) {

// 这里先用typeof来判断基础数据类型,如果是,直接返回false

if(typeof target !== 'object' || target === null) return false;

// getProtypeOf是Object对象自带的API,能够拿到参数的原型对象

let proto = Object.getPrototypeOf(target);

while(true) { //循环往下寻找,直到找到相同的原型对象

if(proto === null) return false;

if(proto === typeObj.prototype) return true;//找到相同原型对象,返回true

proto = Object.getPrototypeof(proto);

}

}

// 验证一下自己实现的myInstanceof

console.log(myInstanceof(new String('maomin'), String)); // true

console.log(myInstanceof('maomin', String)); // false

看到上述代码的实现,我们会总结这两个方法的差异性:

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]"

好,下面我们将实现一个完善的数据类型检测方法。

function getType(obj){

let type = typeof obj;

if (type !== "object") { // 先进行typeof判断,如果是基础数据类型,直接返回

return type;

}

// 对于typeof返回结果是object的,再进行如下的判断,正则返回结果

return Object.prototype.toString.call(obj).replace(/^

object(§+)

$/, '$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返回

数据类型转换

在日常的业务开发中,经常会遇到 JavaScript 数据类型转换问题,有的时候需要我们主动进行强制转换,而有的时候 JavaScript 会进行隐式转换,隐式转换的时候就需要我们多加留心。

'123' == 123 // true

'' == null // false

'' == 0 // true

[] == 0 // true

[] == '' // true

[] == ![] // true

null == undefined // true

Number(null) // 0

Number('') // 0

parseInt(''); // NaN

{}+10 // 10

let obj = {

[Symbol.toPrimitive]() {

return 200;

},

valueOf() {

return 300;

},

toString() {

return 'Hello';

}

}

console.log(obj + 200); // 400

上面的代码相信你并不陌生,基本涵盖了我们平常容易疏漏的一些情况,这就是在做数据类型转换时经常会遇到的强制转换和隐式转换的方式,
强制类型转换

强制类型转换方式包括Number()、parseInt()、parseFloat()、toString()、String()、Boolean(),这几种方法都比较类似,通过字面意思可以很容易理解,都是通过自身的方法来进行数据类型的强制转换。

。。。。。。。。。。。。。

作者:Vam的金豆之路

篇幅有限更多请见扩展链接:http://www.mark-to-win.com/tutorial/50891.html

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章