最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 假如易立竞问你如何判断 JavaScript 中的数据类型?

    正文概述 掘金(童欧巴)   2021-06-09   327

    美味值:?????

    口味:芥末虾仁球

    为了和易老师对线,我们先来简单复习下。

    JavaScript 的数据类型包括原始类型和对象类型:

    • 原始类型:Null、Undefined、Number、String、Boolean、Symbol、BigInt
    • 对象类型:Object

    我们习惯把对象称为引用类型,当然还有很多特殊的引用类型,比如 Function、Array、RegExp、Math、Date、Error、Set、Map、各种定型数组 TypedArray 等。

    假如易立竞问你如何判断 JavaScript 中的数据类型?

    原始类型值保存在栈中,对象类型值保存在堆中,在栈中保留了对象的引用地址,当 JavaScript 访问数据的时候,通过栈中的引用访问。

    在 JavaScript 中,原始类型的赋值会完整复制变量值,而对象(引用)类型的赋值是复制引用地址。

    再来两道常考面试题练练手

    let a = {
        name: '前端食堂',
        age: 2
    }
    let b = a
    console.log(a.name)
    b.name = '童欧巴'
    console.log(a.name)
    console.log(b.name)
    
    // 前端食堂
    // 童欧巴
    // 童欧巴
    

    第一题 So Easy,闭着眼睛也能答对。

    let a = {
        name: '前端食堂',
        age: 2
    }
    const expand = function(b) {
        b.age = 18
        b = {
            name: '童欧巴',
            age: 25
        }
        return b
    }
    let c = expand(a)
    console.log(c.age)
    console.log(a.age)
    console.log(a)
    
    // 25
    // 18
    // {name: "前端食堂", age: 18}
    

    这道题可能有些同学会答错,我们来一起分析一下:

    expand 函数传进来的参数 b,其实传递的是对象在堆中的内存地址值,通过调用 b.age = 18 可以改变 a 对象的 age 属性。

    但是 return 又把 b 变成了另一个内存地址,将 {name: "童欧巴", age: 25} 存入,导致最后返回 a 的值就变成了 {name: "童欧巴", age: 25}

    接下来让我们以热烈的掌声,欢迎易老师闪亮登场!

    假如易立竞问你如何判断 JavaScript 中的数据类型?

    • typeof
    • instanceof
    • constructor
    • Object.prototype.toString.call()

    1.typeof

    typeof 'a' // 'string'
    typeof 1   // 'number' 
    typeof true // 'boolean'
    typeof undefined // 'undefined'
    typeof Symbol('a') // 'symbol'
    typeof 1n // 'bigint'
    
    typeof null // 'object'
    
    typeof function() {} // 'function'
    typeof [] // 'object'
    typeof {} // 'object'
    typeof /a/ // 'object'
    typeof new Date() // 'object'
    typeof new Error() // 'object'
    typeof new Map() // 'object'
    typeof new Set() // 'object'
    

    两条结论:

    1. typeof 可以判断除了 null 以外的原始类型。
    2. typeof 只能判断对象类型中的 Function,其他判断不出来,都为 object。

    typeof 检测 null 时返回 object,是最初 JavaScript 语言的一个 Bug,为了兼容老代码一直保留至今。

    如果想了解更多,请戳下面链接。

    • 链接

    Tips

    这里不得不提一下 NaN,毕竟我们都知道它戏比较多。

    typeof NaN // number
    

    F**k NaN!

    2.instanceof

    检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

    也就是使用 a instanceof B 判断的是:a 是否为 B 的实例,即 a 的原型链上是否存在 B 的构造函数。

    console.log(1 instanceof Number) // false
    console.log(new Number(1) instanceof Number) // true
    
    const arr = []
    console.log(arr instanceof Array) // true
    console.log(arr instanceof Object) // true
    
    const Fn = function() {
        this.name = '构造函数'
    }
    Fn.prototype = Object.create(Array.prototype)
    let a = new Fn()
    console.log(a instanceof Array) // true
    

    两条结论:

    1. instanceof 可以准确判断对象(引用)类型,但是不能准确检测原始类型。
    2. 由于我们可以随意修改原型的指向导致检测结果不准确,所以这种方法是不安全的。

    好,满足。

    虽然 instanceof 不能检测原始类型,但是有一种方法可以让其用于检测原始类型。

    Symbol.hasInstance 允许我们自定义 instanceof 的行为。

    class PrimitiveNumber {
      static [Symbol.hasInstance] = x  => typeof x === 'number';
    }
    123 instanceof PrimitiveNumber; // true
    
    class PrimitiveString {
      static [Symbol.hasInstance] = x => typeof x === 'string';
    }
    'abc' instanceof PrimitiveString; // true
    
    class PrimitiveBoolean {
      static [Symbol.hasInstance] = x => typeof x === 'boolean';
    }
    false instanceof PrimitiveBoolean; // true
    
    class PrimitiveSymbol {
      static [Symbol.hasInstance] = x => typeof x === 'symbol';
    }
    Symbol.iterator instanceof PrimitiveSymbol; // true
    
    class PrimitiveNull {
      static [Symbol.hasInstance] = x => x === null;
    }
    null instanceof PrimitiveNull; // true
    
    class PrimitiveUndefined {
      static [Symbol.hasInstance] = x => x === undefined;
    }
    undefined instanceof PrimitiveUndefined; // true
    

    代码来源下面链接。

    • 有没有一种方法可以将instanceof用于原始JavaScript值?

    手写 instanceof

    const myInstanceof = function(left, right) {
        if (typeof left !== 'object' || left === null) return false
        let proto = Reflect.getPrototypeOf(left)
        while (true) {
            if (proto === null) return false
            if (proto === right.prototype) return true
            proto = Reflect.getPrototypeOf(proto)
        }
    }
    
    const arr = []
    console.log(myInstanceof(arr, Array)) // true
    console.log(myInstanceof(arr, Object)) // true
    console.log(myInstanceof(arr, RegExp)) // false
    

    要理解 instanceof 的工作原理,就必须理解原型链,对 JavaScript 原型链掌握的不够深刻的同学可以戳下面链接学习。

    • JavaScript深入之从原型到原型链
    • 如何回答面试中的JavaScript原型链问题

    3.constructor

    对于数值直接量,直接使用 constructor 是会报错的,这个错误来自于浮点数的字面量解析过程,而不是 "." 作为存取运算符的处理过程。

    在 JS 中,浮点数的小数位是可以为空的,因此 1. 和 1.0 会解析成相同的浮点数。

    // 所以需要加上一个小括号,小括号运算符能够把数值转换为对象
    (1).constructor // ƒ Number() { [native code] }
    // 或者
    1..constructor // ƒ Number() { [native code] }
    
    const a = '前端食堂'
    console.log(a.constructor) // ƒ String() { [native code] }
    console.log(a.constructor === String) // true
    
    const b = 5
    console.log(b.constructor) // ƒ Number() { [native code] }
    console.log(b.constructor === Number) // true
    
    const c = true
    console.log(c.constructor) // ƒ Boolean() { [native code] }
    console.log(c.constructor === Boolean) // true
    
    const d = []
    console.log(d.constructor) // ƒ Array() { [native code] }
    console.log(d.constructor === Array) // true
    
    const e = {}
    console.log(e.constructor) // ƒ Object() { [native code] }
    console.log(e.constructor === Object) // true
    
    const f = () => 1
    console.log(f.constructor) // ƒ Function() { [native code] }
    console.log(f.constructor === Function) // true
    
    const g = Symbol('1')
    console.log(g.constructor) // ƒ Symbol() { [native code] }
    console.log(g.constructor === Symbol) // true
    
    const h = new Date()
    console.log(h.constructor) // ƒ Date() { [native code] }
    console.log(h.constructor === Date) // true
    
    const i = 11n
    console.log(i.constructor) // ƒ BigInt() { [native code] }
    console.log(i.constructor === BigInt) // true
    
    const j = /a/
    console.log(j.constructor) // ƒ RegExp() { [native code] }
    console.log(j.constructor === RegExp) // true
    
    
    String.prototype.constructor = 'aaa'
    console.log(a.constructor === String) // false
    
    const k = null
    console.log(k.constructor) // Cannot read property 'constructor' of null
    
    const l = undefined
    console.log(l.constructor) // Cannot read property 'constructor' of undefined
    

    两条结论:

    1. 除了 null 和 undefined,constructor 可以正确检测出原始类型和对象(引用)类型。
    2. 由于我们可以随意修改 constructor 导致检测结果不准确,所以这种方法是不安全的。

    4.Object.prototype.toString

    toString() 方法返回一个表示该对象的字符串,我们可以改变它的 this 指向,将 this 指向要检测的值,即可返回当前检测值的信息。

    Object.prototype.toString({}) // '[object Object]'
    
    Object.prototype.toString.call({}) // '[object Object]'
    Object.prototype.toString.call('a') // '[object String]'
    Object.prototype.toString.call(1) // '[object Number]'
    Object.prototype.toString.call(true) // '[object Boolean]'
    Object.prototype.toString.call(null) // '[object Null]'
    Object.prototype.toString.call(undefined) // '[object Undefined]'
    Object.prototype.toString.call(Symbol('a')) // '[object Symbol]'
    Object.prototype.toString.call(11n) // '[object BigInt]'
    Object.prototype.toString.call(/a/) // '[object RegExp]'
    Object.prototype.toString.call(new Date()) // '[object Date]'
    Object.prototype.toString.call([0, 1, 2]) // '[object Array]'
    Object.prototype.toString.call(function() {}) // '[object Function]'
    Object.prototype.toString.call(new Error()) // '[object Error]'
    Object.prototype.toString.call(new Set()) // '[object Set]'
    Object.prototype.toString.call(new Map()) // '[object Map]'
    

    封装检测数据类型的通用方法

    封装方法的时候注意大小写。

    方案有很多种,这里简单提供两个思路。

    const getType = function(obj) {
        let type = typeof obj
        if (type !== 'object') {
            return type
        }
        return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1').toLowerCase()
    }
    
    getType({}) // object
    getType('a') // string
    getType(1) // number
    getType(true) // boolean
    getType(null) // null
    getType(undefined) // undefined
    getType(Symbol('a')) // symbol
    getType(11n) // bigint
    getType(/a/) // regexp
    getType(new Date()) // date
    getType([0, 1, 2]) // array
    getType(function() {}) // function
    getType(new Error()) // error
    getType(new Map()) // map
    getType(new Set()) // set
    

    当然,换个姿势,这样也可以实现。

    Object.prototype.toString.call('1').slice(8, -1).toLowerCase()
    // 'string'
    

    聊到这,基本上就是满分答案了。

    如果你觉得哪里有遗漏,欢迎在评论区补充。

    最后一个易老师的问题留给大家:

    ❤️爱心三连击

    1.如果你觉得食堂酒菜还合胃口,就点个赞支持下吧,你的是我最大的动力。

    2.关注公众号前端食堂,吃好每一顿饭!

    3.点赞、评论、转发 === 催更!


    起源地下载网 » 假如易立竞问你如何判断 JavaScript 中的数据类型?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元