编写javascript代码的时候常常要判断变量,字面量的类型,可以用typeof,instanceof,Array.isArray(),等方法,究竟哪一种最方便,最实用,最省心呢?本问探讨这个问题。

1. typeof

1.1 语法

typeof返回一个字符串,表示未经计算的操作数的类型。

语法:typeof(operand) | typeof operand
参数:一个表示对象或原始值的表达式,其类型将被返回
描述:typeof可能返回的值如下:

类型 结果
Undefined “undefined”
Null “object”
Boolean “boolean”
Number “number”
Bigint “bigint”
String “string”
Symbol “symbol”
宿主对象(由JS环境提供) 取决于具体实现
Function对象 “function”
其他任何对象 “object”

从定义和描述上来看,这个语法可以判断出很多的数据类型,但是仔细观察,typeof null居然返回的是“object”,让人摸不着头脑,下面会具体介绍,先看看这个效果:

  1. // 数值
  2. console.log(typeof 37) // number
  3. console.log(typeof 3.14) // number
  4. console.log(typeof(42)) // number
  5. console.log(typeof Math.LN2) // number
  6. console.log(typeof Infinity) // number
  7. console.log(typeof NaN) // number 尽管它是Not-A-Number的缩写,实际NaN是数字计算得到的结果,或者将其他类型变量转化成数字失败的结果
  8. console.log(Number(1)) //number Number(1)构造函数会把参数解析成字面量
  9. console.log(typeof 42n) //bigint
  10. // 字符串
  11. console.log(typeof '') //string
  12. console.log(typeof 'boo') // string
  13. console.log(typeof `template literal`) // string
  14. console.log(typeof '1') //string 内容为数字的字符串仍然是字符串
  15. console.log(typeof(typeof 1)) //string,typeof总是返回一个字符串
  16. console.log(typeof String(1)) //string String将任意值转换成字符串
  17. // 布尔值
  18. console.log(typeof true) // boolean
  19. console.log(typeof false) // boolean
  20. console.log(typeof Boolean(1)) // boolean Boolean会基于参数是真值还是虚值进行转换
  21. console.log(typeof !!(1)) // boolean 两次调用!!操作想短语Boolean()
  22. // Undefined
  23. console.log(typeof undefined) // undefined
  24. console.log(typeof declaredButUndefinedVariabl) // 未赋值的变量返回undefined
  25. console.log(typeof undeclaredVariable ) // 未定义的变量返回undefined
  26. // 对象
  27. console.log(typeof {a: 1}) //object
  28. console.log(typeof new Date()) //object
  29. console.log(typeof /s/) // 正则表达式返回object
  30. // 下面的例子令人迷惑,非常危险,没有用处,应避免使用,new操作符返回的实例都是对象
  31. console.log(typeof new Boolean(true)) // object
  32. console.log(typeof new Number(1)) // object
  33. console.log(typeof new String('abc')) // object
  34. // 函数
  35. console.log(typeof function () {}) // function
  36. console.log(typeof class C { }) // function
  37. console.log(typeof Math.sin) // function 

1.2 迷之null

javascript诞生以来,typeof null都是返回‘object’的,这个是因为javascript中的值由两部分组成,一部分是表示类型的标签,另一部分是表示实际的值。对象类型的值类型标签是0,不巧的是null表示空指针,它的类型标签也被设计成0,于是就有这个typeof null === ‘object’这个‘恶魔之子’。

曾经有ECMAScript提案让typeof null返回‘null’,但是该提案被拒绝了。

1.3 使用new操作符

除Function之外所有构造函数的类型都是‘object’,如下:

  1. var str = new String('String');
  2. var num = new Number(100)
  3. console.log(typeof str) // object
  4. console.log(typeof num) // object
  5. var func = new Function()
  6. console.log(typeof func) // function 

1.4 语法中的括号

typeof运算的优先级要高于“+”操作,但是低于圆括号

  1. var iData = 99
  2. console.log(typeof iData + ' Wisen') // number Wisen
  3. console.log(typeof (iData + 'Wisen')) // string 

1.5 判断正则表达式的兼容性问题

  1. typeof /s/ === 'function'; // Chrome 1-12 , 不符合 ECMAScript 5.1
  2. typeof /s/ === 'object'; // Firefox 5+ , 符合 ECMAScript 5.1 

1.6 错误

ECMAScript 2015之前,typeof总能保证对任何所给的操作数都返回一个字符串,即使是没有声明,没有赋值的标示符,typeof也能返回undefined,也就是说使用typeof永远不会报错。

但是ES6中加入了块级作用域以及let,const命令之后,在变量声明之前使用由let,const声明的变量都会抛出一个ReferenceError错误,块级作用域变量在块的头部到声明变量之间是“暂时性死区”,在这期间访问变量会抛出错误。如下:

  1. console.log(typeof undeclaredVariable) // 'undefined'
  2. console.log(typeof newLetVariable) // ReferenceError
  3. console.log(typeof newConstVariable) // ReferenceError
  4. console.log(typeof newClass) // ReferenceError
  5.  
  6. let newLetVariable
  7. const newConstVariable = 'hello'
  8. class newClass{} 

1.7 例外

当前所有浏览器都暴露一个类型为undefined的非标准宿主对象document.all。typeof document.all === 'undefined'。景观规范允许为非标准的外来对象自定义类型标签,单要求这些类型标签与已有的不同,document.all的类型标签为undefined的例子在web领域被归类为对原ECMA javascript标准的“故意侵犯”,可能就是浏览器的恶作剧。

总结:typeof返回变量或者值的类型标签,虽然对大部分类型都能返回正确结果,但是对null,构造函数实例,正则表达式这三种不太理想。

2. instanceof

2.1 语法

instanceof运算符用于检测实例对象(参数)的原型链上是否出现构造函数的prototype。

语法:object instanceof constructor
参数:object 某个实例对象
          constructor 某个构造函数
描述:instanceof运算符用来检测constructor.property是否存在于参数object的原型链上。

  1. // 定义构造函数
  2. function C() {
  3. }
  4. function D() {
  5. }
  6. var o = new C()
  7. console.log(o instanceof C) //true,因为Object.getPrototypeOf(0) === C.prototype
  8. console.log(o instanceof D) //false,D.prototype不在o的原型链上
  9. console.log(o instanceof Object) //true 同上
  10.  
  11. C.prototype = {}
  12. var o2 = new C()
  13. console.log(o2 instanceof C) // true
  14. console.log(o instanceof C) // false C.prototype指向了一个空对象,这个空对象不在o的原型链上
  15. D.prototype = new C() // 继承
  16. var o3 = new D()
  17. console.log(o3 instanceof D) // true
  18. console.log(o3 instanceof C) // true C.prototype现在在o3的原型链上

需要注意的是,如果表达式obj instanceof Foo返回true,则并不意味着该表达式会永远返回true,应为Foo.prototype属性的值可能被修改,修改之后的值可能不在obj的原型链上,这时表达式的值就是false了。另外一种情况,改变obj的原型链的情况,虽然在当前ES规范中,只能读取对象的原型而不能修改它,但是借助非标准的__proto__伪属性,是可以修改的,比如执行obj.__proto__ = {}后,obj instanceof Foo就返回false了。此外ES6中Object.setPrototypeOf(),Reflect.setPrototypeOf()都可以修改对象的原型。

instanceof和多全局对象(多个iframe或多个window之间的交互)

浏览器中,javascript脚本可能需要在多个窗口之间交互。多个窗口意味着多个全局环境,不同全局环境拥有不同的全局对象,从而拥有不同的内置构造函数。这可能会引发一些问题。例如表达式[] instanceof window.frames[0].Array会返回false,因为Array.prototype !== window.frames[0].Array.prototype。

起初,这样可能没有意义,但是当在脚本中处理多个frame或多个window以及通过函数将对象从一个窗口传递到另一个窗口时,这就是一个非常有意义的话题。实际上,可以通过Array.isArray(myObj)或者Object.prototype.toString.call(myObj) = "[object Array]"来安全的检测传过来的对象是否是一个数组。

2.2 示例

String对象和Date对象都属于Object类型(它们都由Object派生出来)。

但是,使用对象文字符号创建的对象在这里是一个例外,虽然原型未定义,但是instanceof of Object返回true。

  1. var simpleStr = "This is a simple string";
  2. var myString = new String();
  3. var newStr = new String("String created with constructor");
  4. var myDate = new Date();
  5. var myObj = {};
  6. var myNonObj = Object.create(null);
  7.  
  8. console.log(simpleStr instanceof String); // 返回 false,虽然String.prototype在simpleStr的原型链上,但是后者是字面量,不是对象
  9. console.log(myString instanceof String); // 返回 true
  10. console.log(newStr instanceof String); // 返回 true
  11. console.log(myString instanceof Object); // 返回 true
  12.  
  13. console.log(myObj instanceof Object); // 返回 true, 尽管原型没有定义
  14. console.log(({}) instanceof Object); // 返回 true, 同上
  15. console.log(myNonObj instanceof Object); // 返回 false, 一种创建非 Object 实例的对象的方法
  16.  
  17. console.log(myString instanceof Date); //返回 false
  18.  
  19. console.log( myDate instanceof Date); // 返回 true
  20. console.log(myDate instanceof Object); // 返回 true
  21. console.log(myDate instanceof String); // 返回 false 

注意:instanceof运算符的左边必须是一个对象,像"string" instanceof String,true instanceof Boolean这样的字面量都会返回false。

下面代码创建了一个类型Car,以及该类型的对象实例mycar,instanceof运算符表明了这个myca对象既属于Car类型,又属于Object类型。

  1. function Car(make, model, year) {
  2. this.make = make;
  3. this.model = model;
  4. this.year = year;
  5. }
  6. var mycar = new Car("Honda", "Accord", 1998);
  7. var a = mycar instanceof Car; // 返回 true
  8. var b = mycar instanceof Object; // 返回 true 

不是...的实例

要检测对象不是某个构造函数的实例时,可以使用!运算符,例如if(!(mycar instanceof Car))

instanceof虽然能够判断出对象的类型,但是必须要求这个参数是一个对象,简单类型的变量,字面量就不行了,很显然,这在实际编码中也是不够实用。

总结:obj instanceof constructor虽然能判断出对象的原型链上是否有构造函数的原型,但是只能判断出对象类型变量,字面量是判断不出的。

3. Object.prototype.toString()

3.1. 语法

toString()方法返回一个表示该对象的字符串。

语法:obj.toString()
返回值:一个表示该对象的字符串
描述:每个对象都有一个toString()方法,该对象被表示为一个文本字符串时,或一个对象以预期的字符串方式引用时自动调用。默认情况下,toString()方法被每个Object对象继承,如果此方法在自定义对象中未被覆盖,toString()返回“[object type]”,其中type是对象的类型,看下面代码:

  1. var o = new Object();
  2. console.log(o.toString()); // returns [object Object] 

注意:如ECMAScript 5和随后的Errata中所定义,从javascript1.8.5开始,toString()调用null返回[object, Null],undefined返回[object Undefined]

3.2. 示例

覆盖默认的toString()方法

可以自定义一个方法,来覆盖默认的toString()方法,该toString()方法不能传入参数,并且必须返回一个字符串,自定义的toString()方法可以是任何我们需要的值,但如果带有相关的信息,将变得非常有用。

下面代码中定义Dog对象类型,并在构造函数原型上覆盖toString()方法,返回一个有实际意义的字符串,描述当前dog的姓名,颜色,性别,饲养员等信息。

  1. function Dog(name,breed,color,sex) {
  2. this.name = name;
  3. this.breed = breed;
  4. this.color = color;
  5. this.sex = sex;
  6. }
  7. Dog.prototype.toString = function dogToString() {
  8. return "Dog " + this.name + " is a " + this.sex + " " + this.color + " " + this.breed
  9. }
  10.  
  11. var theDog = new Dog("Gabby", "Lab", "chocolate", "female");
  12. console.log(theDog.toString()) //Dog Gabby is a female chocolate Lab 

4. 使用toString()检测数据类型

目前来看toString()方法能够基本满足javascript数据类型的检测需求,可以通过toString()来检测每个对象的类型。为了每个对象都能通过Object.prototype.toString()来检测,需要以Function.prototype.call()或者Function.prototype.apply()的形式来检测,传入要检测的对象或变量作为第一个参数,返回一个字符串"[object type]"。

  1. // null undefined
  2. console.log(Object.prototype.toString.call(null)) //[object Null] 很给力
  3. console.log(Object.prototype.toString.call(undefined)) //[object Undefined] 很给力
  4.  
  5. // Number
  6. console.log(Object.prototype.toString.call(Infinity)) //[object Number]
  7. console.log(Object.prototype.toString.call(Number.MAX_SAFE_INTEGER)) //[object Number]
  8. console.log(Object.prototype.toString.call(NaN)) //[object Number],NaN一般是数字运算得到的结果,返回Number还算可以接受
  9. console.log(Object.prototype.toString.call(1)) //[object Number]
  10. var n = 100
  11. console.log(Object.prototype.toString.call(n)) //[object Number]
  12. console.log(Object.prototype.toString.call(0)) // [object Number]
  13. console.log(Object.prototype.toString.call(Number(1))) //[object Number] 很给力
  14. console.log(Object.prototype.toString.call(new Number(1))) //[object Number] 很给力
  15. console.log(Object.prototype.toString.call('1')) //[object String]
  16. console.log(Object.prototype.toString.call(new String('2'))) // [object String]
  17.  
  18. // Boolean
  19. console.log(Object.prototype.toString.call(true)) // [object Boolean]
  20. console.log(Object.prototype.toString.call(new Boolean(1))) //[object Boolean]
  21.  
  22. // Array
  23. console.log(Object.prototype.toString.call(new Array(1))) // [object Array]
  24. console.log(Object.prototype.toString.call([])) // [object Array]
  25.  
  26. // Object
  27. console.log(Object.prototype.toString.call(new Object())) // [object Object]
  28. function foo() {}
  29. let a = new foo()
  30. console.log(Object.prototype.toString.call(a)) // [object Object]
  31.  
  32. // Function
  33. console.log(Object.prototype.toString.call(Math.floor)) //[object Function]
  34. console.log(Object.prototype.toString.call(foo)) //[object Function]
  35.  
  36. // Symbol
  37. console.log(Object.prototype.toString.call(Symbol('222'))) //[object Symbol]
  38.  
  39. // RegExp
  40. console.log(Object.prototype.toString.call(/sss/)) //[object RegExp] 

上面的结果,除了NaN返回Number稍微有点差池之外其他的都返回了意料之中的结果,都能满足实际开发的需求,于是我们可以写一个通用的函数来检测变量,字面量的类型。如下:

  1. let Type = (function () {
  2. let type = {};
  3. let typeArr = ['String', 'Object', 'Number', 'Array', 'Undefined', 'Function', 'Null', 'Symbol', 'Boolean', 'RegExp', 'BigInt'];
  4. for (let i = 0; i < typeArr.length; i++) {
  5. (function (name) {
  6. type['is' + name] = function (obj) {
  7. return Object.prototype.toString.call(obj) === '[object ' + name + ']'
  8. }
  9. })(typeArr[i])
  10. }
  11. return type
  12. })()
  13. let s = true
  14. console.log(Type.isBoolean(s)) // true
  15. console.log(Type.isRegExp(/22/)) // true 

除了能检测ECMAScript规定的八种数据类型(七种原始类型,BooleanNullUndefinedNumberBigIntStringSymbol,一种复合类型Object)之外,还能检测出正则表达式RegExpFunction这两种类型,基本上能满足开发中的判断数据类型需求。

5. 判断相等

既然说道这里,不妨说一说另一个开发中常见的问题,判断一个变量是否等于一个值。ES5中比较两个值是否相等,可以使用相等运算符(==),严格相等运算符(===),但它们都有缺点,== 会将‘4’转换成4,后者NaN不等于自身,以及+0 !=== -0。ES6中提出”Same-value equality“(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法,它用来比较两个值是否严格相等,与严格比较运算(===)行为基本一致。

  1. console.log(5 == '5') // true
  2. console.log(NaN == NaN) // false
  3. console.log(+0 == -0) // true
  4. console.log({} == {}) // false
  5.  
  6. console.log(5 === '5') // false
  7. console.log(NaN === NaN) // false
  8. console.log(+0 === -0) // true
  9. console.log({} === {}) // false

Object.js()不同之处有两处,一是+0不等于-0,而是NaN等于自身,如下:

  1. let a = {}
  2. let b = {}
  3. let c = b
  4. console.log(a === b) // false
  5. console.log(b === c) // true
  6. console.log(Object.is(b, c)) // true 

注意两个空对象不能判断相等,除非是将一个对象赋值给另外一个变量,对象类型的变量是一个指针,比较的也是这个指针,而不是对象内部属性,对象原型等。

javascript中判断数据类型的更多相关文章

  1. 我所知道的JavaScript中判断数据类型

    相信一提到怎么判断js的数据类型,大家都会想到的是typeof.instanceof,那么为什么有了typeof的存在还要有instanceof? typeof? 根据MDN:typeof操作符返回一 ...

  2. JavaScript 中的数据类型

    Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...

  3. javaScript中的数据类型

    一.综述 javaScript中的数据类型分为两类: 简单类型:Boolean,Number,String 引用类型:Object 其他:undefined代表变量没有初始化,null代表引用类型为空 ...

  4. Javascript中的数据类型之旅

    虽然Javascript是弱类型语言,但是,它也有自己的几种数据类型,分别是:Number.String.Boolean.Object.Udefined.Null.其中,Object属于复杂数据类型, ...

  5. 【译】Javascript中的数据类型

    这篇文章通过四种方式获取Javascript中的数据类型:通过隐藏的内置[[Class]]属性:通过typeof运算符:通过instanceof运算符:通过函数Array.isArray().我们也会 ...

  6. 鉴定JavaScript中的数据类型

    众所周知,JavaScript是一门弱类型的语言,但是这并不代表JavaScript中没有数据类型.JavaScript中常见的数据类型有string.number.object等等,通常我们使用ty ...

  7. JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

    这篇文章主要介绍了JavaScript中判断整字类型最简洁的实现方法,本文给出多个判断整数的方法,最后总结出一个最短.最简洁的实现方法,需要的朋友可以参考下 我们知道JavaScript提供了type ...

  8. 面试说:聊聊JavaScript中的数据类型

    前言 请讲下 JavaScript 中的数据类型? 前端面试中,估计大家都被这么问过. 答:Javascript 中的数据类型包括原始类型和引用类型.其中原始类型包括 null.undefined.b ...

  9. Javascript中判断数组的正确姿势

    在 Javascript 中,如何判断一个变量是否是数组? 最好的方式是用 ES5 提供的 Array.isArray() 方法(毕竟原生的才是最屌的): var a = [0, 1, 2]; con ...

随机推荐

  1. day 20

    目录 一.继承初体验 二.寻找继承关系 三.继承背景下对象属性查找顺序 四.派生 五.子类派生出新的属性,并重复父类的属性 六.新式类与经典类(了解) 一.继承初体验 父类: class Parent ...

  2. 02-23 决策树CART算法

    目录 决策树CART算法 一.决策树CART算法学习目标 二.决策树CART算法详解 2.1 基尼指数和熵 2.2 CART算法对连续值特征的处理 2.3 CART算法对离散值特征的处理 2.4 CA ...

  3. HTML块元素与内联元素嵌套规则

    HTML存在许多种类型的标签,有的标签下面只允许特定的标签存在,这就叫HTML嵌套规则.不按HTML嵌套规则写,浏览器就不会正确解析,会将不符合嵌套规则的节点放到目标节点的下面,或者变成纯文本.关于H ...

  4. 教你用Vue写一个开心消消乐

    之前做过一个算法题,算法要求就是写一个开心消消乐的逻辑算法,当时也是考虑了一段时间才做出来.后来想了想,既然核心算法都有了,能不能实现一个开心消消乐的小游戏呢,于是花了两天时间做了一个小游戏出来. 效 ...

  5. 白话系列之实现自己简单的mvc式webapi框架

    前言:此文为极简mvc式的api框架,只当做入门api的解析方式,并且这里也不算是mvc框架,因为没有view层,毕竟现在大部分都属于前后端分离,当然也可以提供view层,因为只是将view当做文本返 ...

  6. 嗜血的硅谷与Hygge的欧洲

    听到太多抱怨工作累,加班多,996的声音.我们是否从底层来理智的分析这一过程.那些抱怨加班多.996的人有多少离开了那个让他发声的工作,去找一份轻松悠闲的工作度日? 我们是一边抱怨着工作,一边担心着离 ...

  7. ESP8266开发之旅 基础篇① 走进ESP8266的世界

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  8. 微信小程序自定义弹窗(可通用)

    效果图 .wxml <cover-view class='mask' wx:if='{{isShow}}'> <cover-view class='modal'> <co ...

  9. python3 requests_html 爬取智联招聘数据(简易版)

    PS重点:我回来了-----我回来了-----我回来了 1. 基础需要: python3 基础 html5 CS3 基础 2.库的选择: 原始库  urllib2  (这个库早些年的用过,后来淡忘了) ...

  10. Java IO_003.Reader与Writer--字符流以及编码对数据的操作(读取与写入)

    Java IO之Reader与Writer对象常用操作(包含了编码问题的处理) 涉及到文件(非文件夹)内容的操作,如果是纯文本的情况下,除了要用到File(见之前文章),另外就必须用到字符输入流或字符 ...