判断数据类型的方式有哪些(原理及优缺点)?(typeof / instanceof 核心原理 / constructor / Object.prototype.toString)

目录

1、typeof 操作符

2、instanceof

3、constructor 构造器

4、Object.prototype.toString

5、通过对象的原型方法判断类型的应用

6、总结


结论先行

①  typeof 操作符(返回值都是字符串类型 

只能准确判断除 null 以外的基本数据类型;对于引用数据类型,typeof 的判断结果都是 object,函数返回 function;

②  instanceof 或者 constructor

instanceof  判断某个对象是否是某个构造函数的实例;

constructor 是查看自己的构造函数;

instanceof 和 constructor 可以用来判断引用数据类型,但是不能正确判断基本数据类型。

因为实例是一个对象或函数创建的,是引用类型。而如果想判断基本数据类型的话,需要通过基本数据类型对应的包装对象来判断。所以对于 null 和 undefined 这两个家伙就检测不了了,因为这两个特殊类型没有对应的包装对象;

② 所有引用类型都是 Object 的实例。

④ Object.prototype.toString.call() 返回对象的具体类型

使用 Object 对象的原型方法 toString 来判断数据类型,统一返回"[object Xxx]"的字符串

① toString() 方法是定义在 Object 的原型对象(Object.prototype)上的,Object 的每个实例化对象都可以共享 Object.prototype.toString() 方法

② Object.prototype.toString.call(xxx) 它是类型判断的终极解决方案,工作中也是比较常用而且准确!!

1、typeof 操作符

typeof 返回值都是字符串类型,例如: “number”、“boolean”、“string”、“undefined”、'symbol"、“function” 、“object” 

typeof 操作符可以判断基本类型的数据,但是也存在一些特例。比如 typeof null 返回的是“object” ;

为什么 typeof null === object? 

这只是 js 存在的一个bug,null 的机器码是 000,与 object 的类型标签一样。所以会被判断为Object。

typeof 5          // "number"
typeof true       // "boolean"
typeof 'name'     // "string"
typeof undefined  // "undefined"
typeof Symbol(1)  // "symbol"

typeof null       // "object"

注意:因为 typeof 是一个操作符而不是函数,所以不需要参数,但是可以使用参数,执行结果是一样的

let message = "some string"
typeof message  // "string"
typeof(message) // "string"

对于引用类型的数据,typeof 判断的结果均为 “object”, 函数返回 “function”

typeof []             // "object"
typeof new Date()     // "object"
typeof function(){}   // "function"

综上所述:

① typeof 操作符返回的是字符串类型

① typeof 只能准确判断除 null 以外的基本数据类型;

② 引用数据类型,typeof 的判断结果均为object,函数返回function;

2、instanceof

判断数据是否是某个对象的实例,返回一个布尔值。其内部运行机制是判断在其原型链中能否找到该类型的原型

// 判断 p 是否为 Person 的实例
function Person(name) {
 this.name = name
}
const p = new Person('sunshine')
p instanceof Person // true
// 这里的 p 是 Person 函数构造出来的,所以顺着 p 的原型链可以找到Object的构造函数
p.__proto__ === Person.prototype // true
p.__proto__.__proto__ === Object.prototype // true

缺点: 

对于基本类型的数据,instanceof 是不能直接判断它的类型的。

因为实例是一个对象或函数创建的,是引用类型,所以需要通过基本类型对应的 包装对象 来判断。所以对于 null 和 undefined 这两个家伙就检测不了了~

5 instanceof Number // false
new Number(5) instanceof Number  // true

因为原型链继承的关系,instanceof 会把数组都识别为 Object 对象,所有引用类型的祖先都是 Object 对象

let arr = [1, 2]
console.log(Object.prototype.toString.call(arr) === '[object Array]') // true
console.log(arr instanceof Array) // true
console.log(arr instanceof Object) // true
let fn = function(){}
console.log(fn instanceof Object)  // true

综上所述:

① instanceof 是判断某个变量是否为某个对象的实例;

② instanceof 可以检测所有能转换为实例对象的数据,所以对于 null 和 undefined 检测不了;

③ 所有引用类型都是 Object 的实例。


3、constructor 构造器

使用 constructor 可以查看目标构造函数,也可以进行数据类型判断。

但是不能判断 null 和 undefined,因为这两个特殊类型没有其对应的包装对象

constructor 和 instanceof 类似。constructor 返回结果的是自己的构造函数,而 instanceof 则是自己与构造函数比较返回布尔值

(5).constructor === Number     // true
"text".constructor === String  // true
true.constructor === Boolean   // true
({}).constructor === Object    // true
console.log({} instanceof Object) // true

// Uncaught TypeError: Cannot read property 'constructor' of undefined
undefined.constructor === undefined  // 报错
null.constructor === null            // 报错

综上所述:

① constructor 是查看自己的构造函数;

② instanceof 可以检测所有能转换为实例对象的数据,所以不能判断 null 和 undefined ,因为这两个特殊类型没有对应的包装对象;


4、Object.prototype.toString.call()

在判断数据类型时,我们称 Object.prototype.toString 为 “万能方法”、 “终极方法”,工作中也是比较常用而且准确!!

// 简单实现
var arr = [1, 2, 3]
Object.prototype.toString.call(arr) // "[object Array]"

// 封装实现 (返回对应类型)
function type(obj) {
  return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()
}

// 简易实现 Array.isArray() 方法
function isArray(obj) {
  return type(obj) === 'array'
}
console.log(isArray(arr)) // true

深度剖析

在 ECMAScript 中,Object 类型的每个实例都有 toString() 方法,返回对象的字符串表示

所以每个实例化的对象都可以调用 toString() 方法,并且其他数据类型也能使用 toString()

var obj = {a: 1}
console.log(obj.toString())     //"[object Object]"

var flag = true
console.log(flag.toString())     // "true"

var num = 1
console.log(num.toString());    // "1"

同样是检测对象 obj 调用 toString 方法,obj.tostring() 的结果 Object.prototype.tostring.call(obj) 的结果不一样,这是为什么? 

这是因为 toString 是 Object 的原型方法,而 Array、function 等类型作为 object 的实例,都重写了 toString 方法。

不同的对象类型调用 toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法 (function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串...) ,而不会去调用 Object 上原型 toString 方法(返回对象的具体类型),所以采用 obj.toString() 不能得到其对象类型,只能将 obj 转换为字符串类型。

因此,在想要得到对象的具体类型时,应该调用 Object 原型上的 tostring 方法。 

那么为什么都可以使用 toString() 方法,方法是哪里来?

我们顺着原型链,obj => obj.__proto__=> Object.prototype,可以发现,toString()方法是定义在 Object 的原型对象 Object.prototype上的,这样 Object 的每个实例化对象都可以共享Object.prototype.toString()方法

如果不通过原型链查找,直接调用 Object.prototype.toString() 也可以,因为Object.prototype 也是对象,所以返回了对象的字符串表示。通过 obj 对象调用 Object.prototype.toString() 方法。

我们再来分析一下不同类型的“对象”调用 toString() 方法,返回值有什么不同之处?

Object.prototype.toString.call/apply(obj) // "[object Object]"

Object 作为引用类型,它是一种数据结构,常被称为 Object类(但这种称呼并不妥当,JS中没有类,一切都是语法糖而已)。另外基于 Object 类型,JS 还实现了其他常用的对象子类型(就是不同类型的对象),我们可以说:Object类是所有子类的父类(祖先)

上面提到的定义在 Object.prototype 上的 toString() 方法,可以说是最原始的 toString() 方法了,其他类型都或多或少重写了 toString() 方法,导致不同类型的对象调用 toString() 方法产生返回值各不相同

Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call('') // "[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() // "[object Undefined]"
Object.prototype.toString.call(new Date()) // "[object Date]"
Object.prototype.toString.call(/at/) // "[object RegExp]"

注意:

纯数字不可以直接调用 toString方法,因为也可以代表数学中的小数点。

js 执行引擎在做语法解析的时候,会通过.分隔符来解析上下文,但是对于 1.toString() 这样的表达式,会理解为不合理的小数,故而报错。

如果想解决这个问题,可以采用以下形式,即 (1).toString()、1…toString()、1 .toString()

对于 Object 对象,直接使用 toString(),就能正确返回 [object Object];

其他对象都要通过 call 来调用,才能正确返回 【call 是函数的方法用来改变 this 指向,用 apply 也可以,如果不改变 this 指向为我们的目标变量,this 将永远指向调用的Object.prototype

1.toString()      // Uncaught SyntaxError: Invalid or unexpected token
(1).toString()    // "1"
1..toString()     // "1"
1 .toString()     // "1"

综上所述:

① toString() 方法是定义在 Object 的原型对象(Object.prototype)上的,Object 的每个实例化对象都可以共享 Object.prototype.toString() 方法

② Object.prototype.toString.call(xxx) 是类型判断的终极解决方案,工作中也是比较常用而且准确!!

5、通过对象的原型方法判断类型的应用

// Iview源码
function typeOf(obj) {
    const toString = Object.prototype.toString;
    const map = {
        '[object Boolean]': 'boolean',
        '[object Number]': 'number',
        '[object String]': 'string',
        '[object Function]': 'function',
        '[object Array]': 'array',
        '[object Date]': 'date',
        '[object RegExp]': 'regExp',
        '[object Undefined]': 'undefined',
        '[object Null]': 'null',
        '[object Object]': 'object'
    };
    return map[toString.call(obj)];
}
// element-ui源码
export function isString(obj) {
  return Object.prototype.toString.call(obj) === '[object String]';
}

export function isObject(obj) {
  return Object.prototype.toString.call(obj) === '[object Object]';
}

export function isHtmlElement(node) {
  return node && node.nodeType === Node.ELEMENT_NODE;
}

export const isFunction = (functionToCheck) => {
  var getType = {};
  return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
};

export const isUndefined = (val)=> {
  return val === void 0;
};

export const isDefined = (val) => {
  return val !== undefined && val !== null;
};

6、总结

①  typeof 操作符

返回值都是字符串类型 

① typeof 只能准确判断除 null 以外的基本数据类型;

② 对于引用数据类型,typeof 的判断结果均为 object,函数返回 function;

②  instanceof

判断数据是否是某个对象的实例,返回一个布尔值

其内部运行机制是判断在其原型链中能否找到该类型的原型

① instanceof 可以检测所有能转换为实例对象的数据,所以对于 null 和 undefined 检测不了;

② 所有引用类型都是 Object 的实例。

③  constructor

① constructor 是查看自己的构造函数;

② instanceof 可以检测所有能转换为实例对象的数据,所以不能判断 null 和 undefined ,因为这两个特殊类型没有对应的包装对象;

instanceof 和 constructor 可以准备判断引用数据类型,但是不能正确判断基本数据类型。

对于基本类型的数据,instanceof 是不能直接判断它的类型的。因为实例是一个对象或函数创建的,是引用类型,所以需要通过基本类型对应的包装对象来判断。所以对于 null 和 undefined 这两个家伙就检测不了了~

④ Object.prototype.toString.call() 返回对象的具体类型

使用 Object 对象的原型方法 toString 来判断数据类型,统一返回"[object Xxx]"的字符串

① toString() 方法是定义在 Object 的原型对象(Object.prototype)上的,Object 的每个实例化对象都可以共享Object.prototype.toString()方法

② Object.prototype.toString.call(xxx) 是类型判断的终极解决方案,工作中也是比较常用而且准确!!

你可能感兴趣的:(每日专栏,JavaScript,ES6,javascript,前端,原型模式,1024程序员节)