Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

深入 JS 类型检测原理 #12

Open
lurenacm opened this issue Apr 23, 2021 · 0 comments
Open

深入 JS 类型检测原理 #12

lurenacm opened this issue Apr 23, 2021 · 0 comments

Comments

@lurenacm
Copy link
Owner

JS 中的类型

  • 基本类型,number,string,null,undefined,Boolean,es6 新增的 symbol,es11
    中的 bigint 总共 7 种基本类型
  • 引用类型:object,function

数据类型的检测方式

数据类型的检测的方法中共有四种typeof, instanceof, Object.prototype.toString.call(), constructor

一、typeof

typeof 检测原理是:在计算机底层 根据js数据类型的二进制的值进行检测的

  • typeof 检测类型后的返回值是一个字符串,ES5 中对于一个为定义的变量判断类型也会抛出字符串undefined,而不是报错。但是用 let, const 声明的变量也能会导致暂时性死区,抛出 ReferenceError
typeof undefined    //"undefined"
typeof a // "undefined"
typeof b // "ReferenceError" 
let b

typeof 12   //"number"
typeof NaN  //"number"
typeof ''   //"string"
typeof 1n    //"bigint"
typeof function(){}     //"function"
  • (缺点) typeof 可以检测除了 null 类型以外的数据型。null 被检测成 object 这是一个历史遗留的 bug。
  • (缺点) typeof 不能检测出具体的 object 类型,因为对象类型的二进制开头都是 000。比如 typeof [] //"object" 检测数组,正则,日期等。
  • 其他类型的二进制,000 对象,00000... null,1 整数,010 浮点数 ,100 字符串,110 布尔值 , -2^30 undefined
typeof {}   // "object"
typeof []   // "object"
typeof /^/  // "object"

typeOf 判断函数为什么可以判断出是 function 类型而不是 object

JS 中虽然函数也是对象,但是 typeOf 判断函数是会调用 call 方法来判断。所以能判断出function

思考:typeof null 检测出来的结果为什么是 object

typeof 是根据二进制值来判断数据类型的,null 的二进制值是 000,而 object 类型的二进制值都是 000 开头的,所以 typeof 检测 null 是也被检测成 object,这是一个历史里留下来的bug

思考:0.1 + 0.2 !== 0.3 成立原因

二、滥竽充数的 instanceof

instanceof 检测机制是:判断右侧的类型是否出现在左侧实例的原型链上,出现了返回的结果就是true 否则是 false

  • instanceof 判断返回的结果是一个 Boolean 值。
  • instanceof 是用来检测当前的实例是否属于某个类的。可以用来解决 typeof 无法检测具体的对象类型的问题。
let ary = []
console.log(ary instanceof Array)   // true

let reg = /^/
console.log(reg instanceof RegExp)  // true
  • (缺点) 只要当前类出现在实例的原型链上检测的结果就是 true,那么 Object 类型检测的结果永远都是 true,如果实例的原型被修改了,即使检测的结果是 true,也是不准确的。
  • (缺点) instanceof 不能检测基本数据类型。
// 出现在原型链上的类型都被判断成true
let ary = []
console.log(ary instanceof Object)   // true

function fn(){}
fn.prototype = Array.prototype  // 原型被修改了
let f = new fn()
console.log(f instanceof Array) // true
console.log(f instanceof Function) // false

// 不能检测基本数组类型
console.log(1 instanceof Number)    //false
console.log('' instanceof String)   //false
console.log(false instanceof Boolean)   //false

思考,模拟实现 instanceof

思路:根据左侧实例的原型链上是否出现右侧的类型进行判断结果。即实例.__proto__ === 类.prototype,则是true 否则是 false。对原型和原型链不熟悉的可以看 面试 | 你不得不懂得 JS 原型和原型链

function _instanceof(example, classP) {
    let proto = Object.getPrototypeOf(example),
        classPrototype = classP.prototype
    while (true) {
        if (proto === classPrototype) {
            return true
        }
        if (proto === null) {
            return false
        }
        proto = Object.getPrototypeOf(proto)
    }

_instanceof([], Array)  //true
_instanceof('', Array)  // false
_instanceof('', Object) // true

三、滥竽充数的 constructor

instanceof 检测机制是:constructor 存放的就是构造函数本身,通过存放的构造函数来检测类型。不熟悉 constructor的也可以看 面试 | 你不得不懂得 JS 原型和原型链

  • constructor 可以检测基本类型,这点比 instanceof 好用
let ary = []
ary.constructor === Array   // true,ary.constructor ==> Array.prototype.constructor
ary.constructor === String  // false
ary.constructor === Object  // false

let a = 1
a.constructor === Number    // true

let s = ''
s.constructor === String    // true
  • (缺点) constructorinstanceof 有一样的缺陷,就是 constructor 可以被修改也就是重定向
Number.prototype.constructor = 'abc'
let a = 1
a.constructor === Number    // false

四、Object.prototype.toString.call(),标准检测类型

检测机制是:利用 Object.prototype.toString 返回实例所属类的信息,通过改变 toString 中的 this 指向来返回指定参数的类型。

  • Object.prototype.toString 返回的格式固定是 '[object xxx]',可以检测任意类型
Object.prototype.toString.call(1)       //"[object Number]"
Object.prototype.toString.call('')      //"[object String]"
Object.prototype.toString.call(null)        //"[object Null]"
Object.prototype.toString.call(undefined)       //"[object Undefined]"
Object.prototype.toString.call(Symbol())        //"[object Symbol]"
Object.prototype.toString.call([])          //"[object Array]"
Object.prototype.toString.call(function(){})        //"[object Function]"
Object.prototype.toString.call({})      //"[object Object]"
  • (缺点) 不能直接提取出数据类型,还需要转变才拿到直接的类型

五、其他快捷判断方式

isNumber(), isNaN(), Array.isArray()。。。

六、封装一个万能的检测方法 toType()

1. jQuery 中的类型检测

(function () {
    // jquery 中的 toType
    let class2type = {},
        toString = class2type.toString

    // 设定类型映射表
    let arrType = ['Number', 'String', 'Boolean', 'Symbol', 'Array', 'Date', 'Error', 'RegExp', 'Function', 'Object']
    arrType.forEach(item => {
        class2type[`[object ${item}]`] = item.toLowerCase()
    });

    function toType(checkType) {
        if (checkType === null || checkType == undefined) {
            return checkType + ''
        }
        return typeof checkType == 'object' || typeof checkType == 'function' ? class2type[toString.call(checkType)] : typeof checkType
    }
    window.toType = toType
})()

toType(1)   //"number"
toType('')  //"string"
toType(null)    //"null"
toType(undefined)   //"undefined"
toType({})  //"object"
toType(function(){})    //"function"
toType([])  //"array"

本质上还是利用了Object.prototype.toString.call()

2. 自封装一个更简单的 selfType()

(function () {
    function selfType(checkType) {
        let res = {}.toString.call(checkType).split(' ')[1].split(']')[0].toLowerCase() // 可以使用正则
        return res
    }
    window.selfType = selfType
})()

selfType('')        //"string"
selfType(1)     //"number"
selfType(null)      //"null"
selfType(undefined)     //"undefined"
selfType(function(){})     //"function"
selfType({})    //"object"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant