Skip to content

typeof 、instanceof是否正确判断类型? instanceof 原理 #51

Open
@TieMuZhen

Description

@TieMuZhen

typeof 是否正确判断类型

首先 typeof 能够正确的判断基本数据类型,但是除了null, typeof null输出的是对象。

但是对象来说,typeof 不能正确的判断其类型, typeof 一个函数可以输出 'function',而除此之外,输出的全是 object,这种情况下,我们无法准确的知道对象的类型。

instanceof是否正确判断类型

instanceof可以准确的判断复杂数据类型,但是不能正确判断基本数据类型

instanceof原理

instanceof是通过原型链判断的,A instanceof B, 在A的原型链中层层查找,是否有原型等于B.prototype,如果一直找到A的原型链的顶端(null;即Object.prototype.__proto__),仍然不等于B.prototype,那么返回false,否则返回true。其原理代码如下:

// L instanceof R
function instance_of(L, R) {//L 表示左表达式,R 表示右表达式
    var O = R.prototype;// 取 R 的显式原型
    L = L.__proto__;    // 取 L 的隐式原型
    while (true) { 
        if (L === null) //已经找到顶层
            return false;  
        if (O === L)   //当 O 严格等于 L 时,返回 true
            return true; 
        L = L.__proto__;  //继续向上一层原型链查找
    } 
}

使instanceof既能判断基本类型又能判断复杂类型

Symbol.hasInstance被用于确定构造对象是否是其实例。instanceof的行为可以通过这个来定制。

class MyArray {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance)
  }
}
console.log([] instanceof MyArray);  // true

所以我们可以用这个方法来封装instanceof

/**
 * isComplex判断是否是复杂数据类型,如果是返回true,否则返回false
 * @param {*} data 需要被判断类型的数据
 */

function isComplex(data) {
    if (data && (typeof data === 'object' || typeof data === 'function')) {
        return true;
    }
    return false;
}


/**
 * 定义自己的基本数据类型
 */

class PrimitiveString {
    static [Symbol.hasInstance](data) {
        return typeof data === 'string';
    }
}

class PrimitiveNumber {
    static [Symbol.hasInstance](data) {
        return typeof data === 'number';
    }
}

class PrimitiveUndefined {
    static [Symbol.hasInstance](data) {
        return typeof data === 'undefined';
    }
}

class PrimitiveBool {
    static [Symbol.hasInstance](data) {
        return typeof data === 'boolean';
    }
}

class PrimitiveNull {
    static [Symbol.hasInstance](data) {
        return data === null;
    }
}

class PrimitiveSymbol {
    static [Symbol.hasInstance](data) {
        return typeof data === 'symbol';
    }
}


/**
 * 测试 
 */

let num = 2;
console.log(num instanceof PrimitiveNumber);    //true
console.log('isComplex: ', isComplex(num));

let str = 'Yvette';
console.log(str instanceof PrimitiveString);    //true
console.log('isComplex: ', isComplex(str));

let flag = false;
console.log(flag instanceof PrimitiveBool);     //true
console.log('isComplex: ', isComplex(flag));

let und = undefined;
console.log(und instanceof PrimitiveUndefined); //true
console.log('isComplex: ', isComplex(und));

let nul = null;
console.log(nul instanceof PrimitiveNull);      //true
console.log('isComplex: ', isComplex(nul));

let sym = Symbol(10);
console.log(sym instanceof PrimitiveSymbol);    //true
console.log('isComplex: ', isComplex(sym));

console.log('isComplex: ', isComplex(isComplex)); //true

判断数据类型除了typeof、instanceof还有constructor、Object.prototype.toString共四种

使用constructor

console.log('22'.constructor === String)             // true
console.log(true.constructor === Boolean)            // true
console.log([].constructor === Array)                // true
console.log(document.constructor === HTMLDocument)   // true
console.log(window.constructor === Window)           // true
console.log(new Number(22).constructor === Number)   // true
console.log(new Function().constructor === Function) // true
console.log((new Date()).constructor === Date)       // true
console.log(new RegExp().constructor === RegExp)     // true
console.log(new Error().constructor === Error)       // true

1、nullundefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。
2、函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object

使用Object.prototype.toString

console.log(Object.prototype.toString.call(bool));//[object Boolean]
console.log(Object.prototype.toString.call(num));//[object Number]
console.log(Object.prototype.toString.call(str));//[object String]
console.log(Object.prototype.toString.call(und));//[object Undefined]
console.log(Object.prototype.toString.call(nul));//[object Null]
console.log(Object.prototype.toString.call(arr));//[object Array]
console.log(Object.prototype.toString.call(obj));//[object Object]
console.log(Object.prototype.toString.call(fun));//[object Function]

function Person(){}
function Student(){}
Student.prototype = new Person()
var haoxl = new Student()
console.log(Object.prototype.toString.call(haoxl));//[object Object]

在任何值上调用 Object 原生的 toString() 方法,都会返回一个 [object NativeConstructorName] 格式的字符串。对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。
但是它不能检测非原生构造函数的构造函数名

参考文献

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions