js检测数据类型四种办法

 

    • typeof类型检测

作用:用于判断一个一个表达式,(对象或者原始值),返回一个字符串。

var a;

var b=null;

var c=true;

var d=1;

var e='s';

var f=[1,2];

var g={name:'kk'};

var h=function(){};

var i=new h();

console.log(typeof(a));

console.log(typeof(b));

console.log(typeof(c));

console.log(typeof(d));

console.log(typeof(e));

console.log(typeof(f));

console.log(typeof(g));

console.log(typeof(h));

console.log(typeof(i));

结果

 

typeof返回结果

可以发现

typeof返回的内容有undefinded,boolen,number,string,object,function. null返回的是object而undefined还是undefined。数组返回的也是object,typeof不区分对象是由谁创建的,包括内置对象 Instanceof

instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。

使用规则:

object instanceof constructor

要求前面是个对象,后面是一个构造函数。而且返回的是布尔型的,不是true就是false。

常用使用:

由于typeof只能判断类型,所以,数组和对象返回的都是object,这时就需要使用instanceof来判断是否是

用法:o instanceof c,如果o继承自c.propotype(可以不是直接继承),则返回true。如果o不是对象的话则返回false

var a;

var b=null;

var c=true;

var d=1;

var e='s';

var f=[1,2];

var g={name:'kk'};

var h=function(){};

var i=new h();

var j=/\s/;

console.log(a instanceof Object);

console.log(b instanceof Object);

console.log(c instanceof Boolean);

console.log(c instanceof Object);

console.log(d instanceof Object);

console.log(e instanceof Object);

console.log(f instanceof Object);

console.log(f instanceof Array);

console.log(g instanceof Object);

console.log(h instanceof Object);

console.log(i instanceof h);

console.log(j instanceof RegExp);

结果

 

instanceof

可以发现

instanceof是检测对象用的,可以检测对象的特定类型,只要左边不是对象就返回false,如 var a=true; a instanceof Boolean==>false, function的instanceof是object

instanceof/constructor
->检测某一个实例是否属于某一个类
->我们使用instanceof/constructor可以检测数组和正则了
    console.log([] instanceof Array);//->true
    console.log(/^$/ instanceof RegExp);//->true
    console.log([] instanceof Object);//->true
    console.log([].constructor === Array);//->true
    console.log([].constructor === Object);//->false 我们的constructor可以避免instanceof检测的时候,用Object也是true的问题
    console.log({}.constructor === Object);
    局限性:
1)用instanceof检测的时候,只要当前的这个类在实例的原型链上(可以通过原型链__proto__找到它),检测出来的结果都是true
    var oDiv = document.getElementById("div1");
 //HTMLDivElement->HTMLElement->Element->Node->EventTarget->Object
    console.log(oDiv instanceof HTMLDivElement);//->true
    console.log(oDiv instanceof Node);//->true
    console.log(oDiv instanceof Object);//->true
2)基本数据类型的值是不能用instanceof来检测的
->console.log(1 instanceof Number);//->false
    数组创建的两种方式(对象、正则、函数...)
    ->对于引用数据类型来说,我们两种方式创建出来的都是所属类的实例,而且都是对象数据类型的值,是没有区别的
    var ary = [];
    var ary = new Array;
    ->对于基本数据类型来说,虽然不管哪一种方式创建出来的都是所属类的一个实例(在类的原型上定义的方法都可以使用),但是字面量方式创建出来的是基本数据类型,而实例方式创建出来的是对象数据类型
    var num1 = 1;
    var num2 = new Number("1");
    console.log(typeof num1,typeof num2);//->"number" "object"
   3)在类的原型继承中,instanceof检测出来的结果其实是不准确的
    function Fn() {}
    var f = new Fn();
    console.log(f instanceof Array);//->false f不是一个数组,它就是一个普通的实例(普通的对象)
   ->虽然我们的Fn继承了Array,但是f没有length和数字索引哪些东西,所以f应该不是数组才对,但是用instanceof检测的结果却是true,因为f虽然不是数组,但是在f的原型链上可以找到Array
    function Fn() {
    }
    Fn.prototype = new Array;//->Fn子类继承了Array这个父类中的属性和方法
    var f = new Fn;
    console.log(f instanceof Array);//->true

3.Object.propotype.toString.call(obj).slice(8,-1)

类属型法
很多对象重写了toSting,需要调用Object对象上的,返回形式[object class].

function classof(o){

if(o===null) return 'Null';

if(o===undefined) return 'Undefined';

return Object.prototype.toString.call(o).slice(8,-1);

}

var a;

var b=null;

var c=false;

var d=1;

var e='s';

var f=[1,2];

var g={name:'kk'};

var h=function(){};

var i=new h();

var j=/\s/;

console.log(classof(a));

console.log(classof(b));

console.log(classof(c));

console.log(classof(d));

console.log(classof(e));

console.log(classof(f));

console.log(classof(g));

console.log(classof(h));

console.log(classof(i));

console.log(classof(j));

结果

 

类属性法

可以发现

能够识别内置对象但不能识别自定义对象的类别。 返回Null,Undefined,Boolean,Number,String,Array,Object,自定义对象。

4.constructor

原理:返回构造函数的名称

Function.prototype.getName=function(){

if('name' in this) return this.name;

return this.name=this.toString().match(/function\s*([^(]*)\(/);

}

function typeAndValue(x){

if(x===null) return 'Null';

if(x===undefined) return 'Undefined';

return x.constructor.getName();

}

var a;

var b=null;

var c=false;

var d=1;

var e='s';

var f=[1,2];

var g={name:'kk'};

var h=function(){};

var i=new h();

var j=/\s/;

console.log(typeAndValue(a));

console.log(typeAndValue(b));

console.log(typeAndValue(c));

console.log(typeAndValue(d));

console.log(typeAndValue(e));

console.log(typeAndValue(f));

console.log(typeAndValue(g));

console.log(typeAndValue(h));

console.log(typeAndValue(i));

console.log(typeAndValue(j));

结果

 

constructor法

可以看出

能够返回自定义对象与内置对象的名称 null与undefined没有constructor需要自己处理有的对象没有constructor需要特殊处理

原型链上没有constructor的情况

Function.prototype.getName=function(){

if('name' in this) return this.name;

return this.name=this.toString().match(/function\s*([^(]*)\(/);

}

function typeAndValue(x){

if(x===null) return 'Null';

if(x===undefined) return 'Undefined';

return x.constructor.getName();

}

function Range(from,to){

this.from=from;

this.to=to;

}

Range.prototype={

 toString:function(){

   return '('+this.from+','+this.to+')'

}

}

var r=new Range(3,4);

console.log(typeAndValue(r))

console.log(Object.prototype.toString(r).slice(8,-1));

结果

 

都为object

几种方法的综合

function type(o){

 var t,c,n

 if(o===null) return 'Null';//处理Null

 if(o!==o) return 'NaN';//处理NaN特殊

 if((t=typeof(o))!=='object') return t;//处理string,boolean,number,function,undefined。

 if((c=classof(o))!=='Object') return c//处理内置对象Date

 if(o.constructor && typeof(o.constructor)==='function'&&(n=o.constructor.getName())) {return n;}//处理自定义对象

 return 'object';

 

}

function classof(o){

 return Object.prototype.toString.call(o).slice(8,-1);

}

Function.prototype.getName=function(){

 if('name' in this) return this.name;

 return this.name=this.toString().match(/function\s*([^(]*)\(/)[1];

}

var a;

var b=null;

var c=false;

var d=1;

var e='s';

var f=[1,2];

var g={name:'kk'};

var h=function(){};

var i=new h();

var j=/\s/;

console.log(type(a));

console.log(type(b));

console.log(type(c));

console.log(type(d));

console.log(type(e));

console.log(type(f));

console.log(type(g));

console.log(type(h));

console.log(type(i));

console.log(type(j));

结果

 

 

综合

可以发现

  • 以typeof处理基本类型(undefined,boolean,number,string,function)第一个字母是小写的。
  • 以类属性法处理内置对象Object.prototype.toString.call(obj).slice(8,-1)(Date,RegExp),第一个字母大写的

以constructor法处理自定义类别和普通的Object(Object,h),注意Object是首字母大写

回顾 JS 的数据类型

我们先来回顾一下最新的 ECMAScript 标准的8种数据类型:

7种原始类型(或称作基本类型):Boolean、Null、Undefined、Number、BigInt、String、Symbol 和 Object。

基本类型(基本数值、基本数据类型)是一种既非对象也无方法的数据。在 JavaScript 中,共有7种基本类型:string,number,bigint,boolean,null,undefined,symbol (ECMAScript 2016新增)。

多数情况下,基本类型直接代表了最底层的语言实现。

所有基本类型的值都是不可改变的。但需要注意的是,基本类型本身和一个赋值为基本类型的变量的区别。变量会被赋予一个新值,而原值不能像数组、对象以及函数那样被改变。

何谓不可改变

var bar = "baz";

bar[0] = "a";

console.log(bar[0]); // "b"

console.log(bar);// "baz"

基本类型的包装对象

除了 null 和 undefined 外,所有基本类型都有其对应的包装对象:

  • String 为字符串基本类型。
  • Number 为数值基本类型。
  • BigInt 为大整数基本类型。
  • Boolean 为布尔基本类型。
  • Symbol 为字面量基本类型。

这个包裹对象的 valueOf() 方法返回基本类型值。

var s_prim = "foo";

var s_obj = new String(s_prim);

console.log(typeof s_prim);//"string"

console.log(typeof s_obj);//"object"

console.log(typeof s_obj.valueOf());//"string"

typeof 可能的返回值表

类型

结果

Undefined

“undefined”

Null

“object” (历史遗留问题)

Boolean

“boolean”

Number

“number”

BigInt

“bigint”

String

“string”

Symbol (ECMAScript 2015 新增)

“symbol”

宿主对象(由 JS 环境提供)

取决于具体实现

Function 对象 (按照 ECMA-262 规范实现 [[Call]])

“function”

其他任何对象

“object”

下表总结了 typeof 可能的返回值:

分析 typeof 的误区

我之前在分析 typeof 函数和对象的时候,是根据这种方式来分析的:

以前面的 String 为例:

var s_obj = new String(s_prim);

console.log(typeof String); // "function"

// 使用 typeof 调用函数的时候,是把函数当做函数对象来调用的,又因为所有的函数对象都是由 Function 构造出来的,所以:String.__Proto__ === Function.prototype,所以结果返回了 "function"console.log(typeof s_obj);  // "object"// 1. 因为 s_obj 是一个对象,它是由 String 构造出来的,所以:S_obj.__proto__ === String.prototype;// 2. 又因为所有的构造函数都是由 Object 派生出来的,所以:String.prototype.__proto__ === Object.prototype;// 3. 所以 s_obj 返回 "object";

因为原型链是这样的。但是问题点出在,typeof 是提前把函数的原型链终止了吗?

// 因为Function.prototype.__proto__ === Object.prototype;

这么看来,如果不提前终止原型链,那么 typeof 的结果应该返回 “object” 才对啊。

一小段 JS 历史

带着这个问题,我们来看一点 JavaScript 的历史。

大家都知道“typeof null”的bug,它是在JavaScript的第一版就存在的。在这个版本中,值以32位的单位存储,包括一个小型类型标记(1-3位和值的实际数据。类型标记存储在单元的较低位上。一共有5种类型:

  • 000: object,表示这个数据是一个对象的引用。
  • 1: int,表示这个数据是一个31位的有符号整型。
  • 010: double,表示这个数据是一个双精度浮点数的引用。
  • 100: string,表示这个数据是一个字符串的引用。
  • 110: boolean,表示这个数据是一个布尔值。

两个值比较特殊:

undefined (JSVAL_VOID) 的值是 −2^30 整型(一个超出整型范围的数)。

null (JSVAL_NULL) 是机器码空指针。或者是:一个对象类型标记加上一个为零的引用。

 

在 javascript 的最初版本中,使用的 32 位系统,为了性能考虑使用低位存储了变量的类型信息:

  • 000:对象
  • 1:整数
  • 010:浮点数
  • 100:字符串
  • 110:布尔

有 2 个值比较特殊:

  • undefined:用 - (−2^30)整型(一个超出整型范围的数)表示。
  • null:对应机器码的NULL指针,一般是全零。

所以,typeof 在判断 null 的时候就出现问题了,由于null的所有机器码均为0,因此直接被当做了对象来看待;

现在应该很清楚为什么 typeof 认为 null 是一个对象了:它检查了类型标记和类型标记表示的对象。

mozilla 的 typeof 的源码

回顾完第一版的历史,我们再来看一下 1998 年 mozilla 的 typeof 的源码:

JS_PUBLIC_API(JSType)

JS_TypeOfValue(JSContext *cx, jsval v){

    JSType type = JSTYPE_VOID;

    JSObject *obj;

    JSObjectOps *ops;

    JSClass *clasp;

    CHECK_REQUEST(cx);

    // 检查 v 是否是 undefined

    if (JSVAL_IS_VOID(v)) {

        type = JSTYPE_VOID;

    }

    // 检查 v 是否有 object 的类型标记

    else if (JSVAL_IS_OBJECT(v)) {

        obj = JSVAL_TO_OBJECT(v);

        // 如果 obj 是可调用的,或者是内部的 [[Class]] 属性标记了它是一个函数,就返回 function

        if (obj &&

            (ops = obj->map->ops,

             ops == &js_ObjectOps

             ? (clasp = OBJ_GET_CLASS(cx, obj),

                clasp->call || clasp == &js_FunctionClass)

             : ops->call != 0)) {

            type = JSTYPE_FUNCTION;

        } else {

            type = JSTYPE_OBJECT;

        }

    } else if (JSVAL_IS_NUMBER(v)) {

        type = JSTYPE_NUMBER;

    } else if (JSVAL_IS_STRING(v)) {

        type = JSTYPE_STRING;

    } else if (JSVAL_IS_BOOLEAN(v)) {

        type = JSTYPE_BOOLEAN;

    }

    return type;

}

ES6 是如何解释的

我们再来看一下ES6的typeof 是如何对待函数和对象类型的

如果一个对象(Object)没有实现[[Call]]内部方法,那么它就返回object

如果一个对象(Object实现了[[Call]]内部方法,那么它就返回function

不愧是标准,简单直接。

[[Call]] 是什么

执行与此对象关联的代码。通过函数调用表达式调用。内部方法的参数是一个 this 值和一个包含调用表达式传递给函数的参数的列表。实现此内部方法的对象是可调用的。

这是翻译的原话,简单点说,一个对象如果支持了内部的[[Call]]方法,那么它就可以被调用,就变成了函数,所以叫做函数对象。

相应地,如果一个函数对象支持了内部的[[Construct]]方法,那么它就可以使用new或super来调用,这时我们就可以把这个函数对象称为:构造函数。

typeof 原理就大致分析到这里

这两个方法都可以用来判断变量类型

区别:前者是判断这个变量是什么类型,后者是判断某一个实例是不是某种类型返回的是布尔值

(1)typeof  

typeof返回一个数据类型的字符串,而且都是小写的字符串,返回值有'number','boolean','string','function','object','undefined'这几个

typeof 100; //'number'

typeof (1==1); //'boolean'

typeof 'onepixel'; //'string'

typeof {} ; //'object'

typeof onepixel; // 'undefined'

typeof parseInt; // 'function'

typeof [];//'object'

typeof new Date(); //'object'

缺陷:

1.不能判断变量具体的数据类型比如数组、正则、日期、对象,因为都会返回object,不过可以判断function,如果检测对象是正则表达式的时候,在Safari和Chrome中使用typeof的时候会错误的返回"function",其他的浏览器返回的是object.

2.判断null的时候返回的是一个object,这是js的一个缺陷,所以null也成为 "薛定谔的对象";判断NaN的时候返回是number

(2)instanceof

可以用来检测这个变量是否为某种类型,返回的是布尔值,并且可以判断这个变量是否为某个函数的实例,它检测的是对象的原型

instanceof 可以判断一个引用是否属于某构造函数;另外,还可以在继承关系中用来判断一个实例是否属于它的父类型。

instanceof的判断逻辑是: 从当前引用的proto一层一层顺着原型链往上找,能否找到对应的prototype。找到了就返回true

[] instanceof Array; //true

{} instanceof Object;//true

new Date() instanceof Date;//true

function Person(){};

[] instanceof Object; //true

new Date() instanceof Object;//tru

new Person instanceof Object;//true

var array = new Array()

array instanceof Array //true

共同点:基本数据类型都可以判断

不同点:instanceof可以判断这个变量是否为某个函数的实例,而typeof不能

用法:typeof经常用来检测一个变量是不是最基本的数据类型,instanceof简单说就是判断一个引用类型的变量具体是不是某种类型的对象。

联系:typeof和instanceof的目的都是检测变量的类型,两个区别在于typeof只能用于检测基本数据类型,instanceof可以检测基本数据类型,也可以检测某些引用数据类型,因为instanceof只能通过true或者false来判断,不能直接看出来是什么类型,所以需要另外一种直观的方法:

解决方案

因为js中的一切都是对象,任何都不例外,对所有值类型应用 Object.prototype.toString.call() 方法结果如下:

console.log(Object.prototype.toString.call(123)) //[object Number]
console.log(Object.prototype.toString.call('123')) //[object String]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(function(){})) //[object Function]

判断是否为函数

function isFunction(value) {
return Object.prototype.toString.call(value) === '[object Function]';

}

判断是否为数组:
function isArray(value) {
  return Object.prototype.toString.call(value) === '[object Array]';
}

利用这个特性,可以写一个比typeof instanceof更准确的判断方法:

var type = function (o) {

    var s = Object.propertype.toString.call(o)

    return s.match(/object(.∗?)object(.∗?)/)[1].toLowerCase()

}

type({}); // "object"

type([]); // "array"

type(5); // "number"

type(null); // "null"

type(); // "undefined"

type(/abcd/); // "regex"

type(new Date()); // "date"复制代码

其他方法总结:

数组:

var a = [1, 2, 3]

var b = {}

Array.isArray(a)  //  true

Array.isArray(b)  // false

面试题中经常会考js数据类型检测,js中常用的四种方法判断数据类型

1.typeof

console.log(typeof "");

console.log(typeof 1);

console.log(typeof true);

console.log(typeof null);

console.log(typeof undefined);

console.log(typeof []);

console.log(typeof function(){});

console.log(typeof {});

看看控制台输出什么

 

可以看到,typeof对于基本数据类型判断是没有问题的,但是遇到引用数据类型(如:Array)是不起作用的。

2.instanceof

console.log("1" instanceof String);

console.log(1 instanceof Number);

console.log(true instanceof Boolean);

// console.log(null instanceof Null);

// console.log(undefined instanceof Undefined);

console.log([] instanceof Array);

console.log(function(){} instanceof Function);

console.log({} instanceof Object);

暂且不考虑null和undefined(这两个比较特殊),看看控制台输出什么

 

可以看到前三个都是以对象字面量创建的基本数据类型,但是却不是所属类的实例,这个就有点怪了。后面三个是引用数据类型,可以得到正确的结果。如果我们通过new关键字去创建基本数据类型,你会发现,这时就会输出true,如下:

 

接下再来说说为什么null和undefined为什么比较特殊,实际上按理来说,null的所属类就是Null,undefined就是Undefined,但事实并非如此:控制台输出如下结果:

 

浏览器压根不认识这两货,直接报错。在第一个例子你可能已经发现了,typeof null的结果是object,typeof undefined的结果是undefined

 

尤其是null,其实这是js设计的一个败笔,早期准备更改null的类型为null,由于当时已经有大量网站使用了null,如果更改,将导致很多网站的逻辑出现漏洞问题,就没有更改过来,于是一直遗留到现在。作为学习者,我们只需要记住就好。

3.constructor

console.log(("1").constructor === String);

console.log((1).constructor === Number);

console.log((true).constructor === Boolean);

//console.log((null).constructor === Null);

//console.log((undefined).constructor === Undefined);

console.log(([]).constructor === Array);

console.log((function() {}).constructor === Function);

console.log(({}).constructor === Object);

 

这里依然抛开null和undefined)乍一看,constructor似乎完全可以应对基本数据类型和引用数据类型,都能检测出数据类型,事实上并不是如此,来看看为什么:

function Fn(){};

Fn.prototype=new Array();

var f=new Fn();

console.log(f.constructor===Fn);

console.log(f.constructor===Array);

 

首先声明了一个构造函数,并且把他的原型指向了Array的原型,所以这种情况下,constructor也显得力不从心了。

看到这里,是不是觉得绝望了。没关系,终极解决办法就是第四种办法,看过jQuery源码的人都知道,jQuery实际上就是采用这个方法进行数据类型检测的。

4.Object.prototype.toString.call()

var a = Object.prototype.toString;

console.log(a.call("aaa"));

console.log(a.call(1));

console.log(a.call(true));

console.log(a.call(null));

console.log(a.call(undefined));

console.log(a.call([]));

console.log(a.call(function() {}));

console.log(a.call({}));

 

 

可以看到,所有的数据类型,这个办法都可以判断出来。那就有人质疑了,假如我把他的原型改动一下呢?如你所愿,我们看一下:

可以看到,依然可以得到正确的结果。

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值