前端知识库(lizh)
  • README
  • Bugs
    • 前端调试随笔
    • 浏览器常见问题概览
    • 浏览器兼容问题概览
    • HTML常见问题概览
    • CSS常见问题概览
    • JS常见问题概览
    • 移动端兼容性问题概览
    • 微信小程序开发
    • NodeJs常见问题概览
    • Mac常见问题概览
    • 微信开发遇坑指南
    • Npm包常见问题概览
    • 其他问题汇总
  • Css探索系列
    • CSS基础知识
    • CSS常见问答
    • CSS常见问答02
    • CSS应用示例
    • CSS应用示例02
    • 由Z Index引发的层叠上下文思考
    • 由浮动塌陷引发的块级格式上下文思考
    • CSS探索系列 Flex布局
    • CSS探索系列 Margin
    • CSS探索系列 Auto关键字
    • CSS探索系列 Gradient
    • CSS探索系列 Line Height
    • CSS探索系列 元素居中
    • CSS探索系列 动画
    • 为什么使用PostCSS处理CSS?
    • 重新认识伪类与伪元素
    • 自定义表单伪元素样式
    • 如何理解Css中的Display属性
    • 视口和软键盘对视口的影响
    • 关于Css
  • Frontend
    • 00 关于Web前端
    • 01 前端知识概览
    • 02 常用前端库概览
    • 基础 00 前端常见问题01
    • 基础 01 浏览器缓存
    • 基础 02 浏览器工作原理
    • 基础 03 谈谈前端跨源问题及解决方法
    • 进阶 01 Web性能优化
    • 进阶 02 搜索引擎优化(SEO)
    • 进阶 03 前端模块化编程
    • 进阶 04 规范代码:Linter、Prettier、EditorConfig
    • 进阶 11 前端自动化测试
    • 高级 01 前端安全
    • Vue2.X原理篇
    • Vue3初步了解及迁移指南
    • 重读Vue教程
    • React17.X原理篇
    • 你必须知道的React问题
    • 重读React教程
    • 聊一聊Cookie的一些问题
    • 如何理解HTTP响应的状态码
    • HTTP的历史演变及概述
    • Webpack4.X原理篇
    • Webpack基础入门篇
    • Webpack构建优化篇
    • TypeScript使用指南
    • 代码规范
      • 前端规范
      • HTML
      • CSS
      • JS
  • Html探索系列
    • HTML基础知识
    • HTML基础知识02
    • HTML常见问答
    • HTML经典实践用例
    • HTML元素的宽高及位置详解
    • Video元素的使用和常见问题总结
    • Html探索系列 Meta标签
    • DOCTYPE:文档类型与浏览器模式
    • DHTML(动态网页)简介
    • HTML标签详解
    • HTML布局的几种方式
    • HTML全局属性
    • 关于Html
  • Js探索系列
    • 基础知识
    • 常见问答
    • 应用示例
    • 趣味示例
    • 基础篇 05 AJAX
    • 基础篇 06 Window对象
    • 基础篇 07 Error、JSON、Math、Console对象
    • 基础篇 08 History、URL、Screen、Navigator、Location对象
    • 基础篇 09 文档对象模型(DOM)
    • 基础篇 10 Document对象
    • 基础篇 11 Element对象
    • 基础篇 12 Event对象
    • 基础篇 13 键盘、鼠标、触摸事件
    • 基础篇 15 CSS对象模型(CSSOM)
    • 进阶篇 01 Prototype对象和继承
    • 进阶篇 02 Promise对象
    • 进阶篇 07 迭代器(Iterator)
    • 进阶篇 08 Generator和Async函数
    • 进阶篇 09 JavaScript异步编程
    • Date对象和日期时间字符串格式
    • Canvas基础入门篇
    • Canvas进阶篇
    • SVG基础入门篇
    • 四种判断数据类型方法的优缺点
    • 深入理解JavaScript的浅拷贝和深拷贝
    • 谈谈JavaScript的作用域和上下文
    • 复制内容到剪贴板
    • 关于Javascript
  • NodeJs
    • 关于Node.Js
    • Node.Js:三种调试方法
    • Npm包管理器简介及一些机制
    • NPM:Package.Json详解(中文)
    • NPM:从零开始,开发一个软件包
    • NPM:常用命令
    • Node.Js:Fs(文件系统)
    • Node.Js:Global(全局变量)
    • Node.Js:HTTP
    • Node.Js:Module(模块)
    • Node.Js:Path(路径)
    • Node.Js:Readline(逐行读取)
  • Research
    • 极细边框(1px边框)实现方式
    • 如何监控前端异常?
    • H5页面跳转和刷新
    • Web主题切换和个性化定制方法总结
    • Vue SSR(服务端渲染)的简单实现
    • 基于Create React App打造代码规范化的React+Ts项目
    • H5可视化编辑
    • Web常用功能
    • Javascript加密混淆
    • Vue如何导入TypeScript
    • 移动端PDF预览
    • 纯CSS绘制箭头
    • 网站性能测量和优化方法
  • Tech
    • GOOGLE浏览器的搜索技巧
    • Curl的用法指南
    • Sublime3插件篇
    • Charles安装及使用
    • Nginx基础使用
    • 排序算法(Javascript)
    • 代码整洁之道(摘录笔记)
    • Java的24种设计模式与7大原则
    • 观察者和发布订阅模式
  • Tools
    • Git
      • Git基础教程
      • Git常见问题
    • Gitbook
      • Gitbook入门篇
      • Gitbook插件篇
      • Gitbook进阶篇
由 GitBook 提供支持
在本页
  • typeof
  • instanceof
  • constructor
  • toString
  • 常见问题
  • 总结
  • 参数链接

这有帮助吗?

  1. Js探索系列

四种判断数据类型方法的优缺点

上一页SVG基础入门篇下一页深入理解JavaScript的浅拷贝和深拷贝

最后更新于1年前

这有帮助吗?

JavaScript 是一种弱类型或者说动态语言。这意味着你不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。这也意味着你可以使用同一个变量保存不同类型的数据:

let a = 42
a = "bar"
a = true
a = {}

最新的 ECMAScript 标准定义了 8 种数据类型:

  • 基本类型(7种)

    基本类型也称为简单类型,存储于栈内存中,数据大小确定,内存空间大小可以分配,是按值存储的,可以直接访问。

  • 引用类型(1种)

    Object 是唯一的复杂数据类型。 Object、Function 、Array、RegExp、Date 这些引用类型值最终都可以归结为 Object 复杂数据类型。

    引用类型存储于堆内存中。而存储在变量处的值,是一个指针,指向存储对象的内存处,即按址访问。

除 Object 以外的所有类型都是不可变的(值本身无法被改变)。例如,与 C 语言不同,JavaScript 中字符串是不可变的(如:JavaScript 中对字符串的操作一定返回了一个新字符串,原始字符串并没有被改变)。我们称这些类型的值为"原始值”。

鉴于ECMAScript 是弱类型的语言,因此需要有一种手段来检测给定变量的数据类型。对于这个问题,JavaScript 也提供了多种方法,但遗憾的是,没有一种是完美,每种方法都有自己的优缺点。

let objT = {
    str: '',
    num: 1,
    bool: true,
    undef: undefined,
    nul: null,
    symbol: Symbol(),
    bigInt: BigInt(10),

    fun: new Function(),

    arr: [],
    numN: new Number(),
    boolN: new Boolean(),
    strN: new String(),
    date: new Date(),
    reg: new RegExp(),
}

typeof

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

typeof variable

返回该类型的字符串(全小写字母)形式表示,包括以下 8 种:string、number、boolean、symbol、bigint、undefined、function、object 等。

有些时候,typeof 操作符会返回一些令人迷惑但技术上却正确的值:

  • 对于 null ,返回'object '

    // JavaScript 诞生以来便如此
    typeof null === 'object';

    在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回'object'。

  • 引用类型Function,返回'function'

    let func1 = new Function()
    let func2 = function () {}
    
    typeof func1 //"function"
    typeof func2 //"function"
  • 对于其他基本类型,均可以返回正确的结果

  • 对于其他引用类型,一律返回'object'

  • 除 Function 外的所有构造函数的类型都是'object'

    // 除 Function 外的所有构造函数的类型都是 'object'
    let str = new String('String');
    let num = new Number(100);
    let func = new Function();
    
    typeof str; // 返回 'object'
    typeof num; // 返回 'object'
    typeof func; // 返回 'function'
  • 对正则表达式字面量的类型判断在某些浏览器中不符合标准:

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

    尽管规范允许为非标准的外来对象自定义类型标签,但它要求这些类型标签与已有的不同。document.all 的类型标签为 'undefined' 的例子在 Web 领域中被归类为对原 ECMA JavaScript 标准的“故意侵犯”。

在 ECMAScript 2015 之前,typeof 总能保证对任何所给的操作数返回一个字符串。即便是没有声明的标识符,typeof 也能返回 'undefined'。使用 typeof 永远不会抛出错误。

for (let k in objT) {
    console.log(typeof objT[k])
}

let objT = {
    str: '', //string
    num: 1, //number
    bool: true, //boolean
    undef: undefined, //undefined
    nul: null, //object
    symbol: Symbol(), //symbol
    bigInt: BigInt(10), //bigint

    fun: new Function(), //function

    //以下都为object
    arr: [],
    numN: new Number(),
    boolN: new Boolean(),
    strN: new String(),
    date: new Date(),
    reg: new RegExp()
}

instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。即用来判断 object 是否为 constructor 的实例,如果 object 是 constructor 的实例,则返回 true,否则返回 false。表达式为:

object instanceof constructor
  • 不能用来判断7种基本类型值,因为基本数据类型没有原型链

    objT.str instanceof String //false
    objT.num instanceof Number //false
    objT.bool instanceof Boolean //false
    objT.undef instanceof undefined //Uncaught TypeError: Right-hand side of 'instanceof' is not an object
    objT.nul instanceof null //Uncaught TypeError: Right-hand side of 'instanceof' is not an object
    objT.symbol instanceof Symbol //false
    objT.bigInt instanceof BigInt //false
  • 不能正确判断 Object 类型

    instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。

    [] instanceof Array // true
    [] instanceof Object; // true
    
    function Person(){}
    new Person() instanceof Person //true
    new Person() instanceof Object;// true
  • prototype 属性的值是可变的

    function A( ){} 
    let a = new A()
    a instanceof A //true
    
    //修改构造函数的prototype
    A.prototype = null
    a instanceof A //false
    
    //修改对象的原型链
    b.__proto__ = null
    b instanceof A //false

    构造函数的 prototype 属性的值有可能会改变,改变之后的值很有可能不存在于 object 的原型链上。

    对象 obj 的原型链的也是可以修改的。虽然在目前的ES规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的 __proto__ 伪属性,是可以实现的。

  • 不能用于跨窗口判断

    在浏览器中,我们的脚本可能需要在多个 frame 或 window 之间的交互。多个窗口意味着多个全局环境,不同的全局环境拥有不同的全局对象,从而拥有不同的内置类型构造函数。这可能会引发一些问题。比如:

    document.body.appendChild(document.createElement('iframe'))
    let xArray = window.frames[0].Array
    let arr = new xArray(1,2,3)
    
    arr instanceof xArray //true
    arr instanceof Array //false

    针对数组的这个问题,ES5 提供了 Array.isArray() 方法 。该方法用以确认某个对象本身是否为 Array 类型,而不区分该对象在哪个环境中创建。

constructor

constructor属性返回一个指向创建了该对象原型的函数引用。需要注意的是,该属性的值是那个函数本身。所有对象都会从它的原型上继承一个 constructor 属性。

obj.constructor === Constructor
  • null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。

    objT.undef.constructor //Cannot read property 'constructor' of undefined
    objT.nul.constructor //Cannot read property 'constructor' of null
  • 其他基本数据类型访问时,JavaScript会自动调用其构造函数来生成一个对象

    (0).constructor === Number // true
    true.constructor === Boolean // true
    'a'.constructor === String // true
    
    // 相当于
    (new Number(0)).constructor === Number
    (new Boolean(true)).constructor === Boolean
    (new String('a')).constructor === String
  • 对象的constructor是可变的

    function A( ){} 
    let a = new A()
    a.constructor === A //true
    
    //修改对象的constructor
    a.constructor = null
    a.constructor === A //false
  • 不能用于跨窗口判断

    与instanceof类似,constructor在不同的窗口拥有不同的内置构造函数。

    document.body.appendChild(document.createElement('iframe'))
    let xArray = window.frames[0].Array
    let arr = new xArray(1,2,3)
    
    arr.constructor === Array //false
    arr.constructor === xArray //true

toString

toString方法是最为可靠的类型检测手段,它会将当前对象转换为字符串并输出。 toString属性定义在Object.prototype上,因而所有对象都拥有toString方法。 但Array, Date等对象会重写从Object.prototype继承来的toString, 所以最好用Object.prototype.toString来检测类型。

//目前是最可靠的了,它总能返回正确的值
Object.prototype.toString.call(variable) 
  • 无法检测用户自定义类型

    toString也不是完美的,它无法检测用户自定义类型。 因为Object.prototype是不知道用户会创造什么类型的, 它只能检测ECMA标准中的那些内置类型。

    function A( ){} 
    let a = new A()
    
    Object.prototype.toString.call(a) //[object Object]
  • 避免了跨窗口问题

    因为返回值是字符串,也避免了跨窗口问题。当然IE弹窗中还是有Bug,不必管它了。

常见问题

内部属性 [[Class]] 是什么?

每个对象在创建时都会带有一个 [[class]] 的内部属性,这个属性无法直接访问,可通过 Object.prototype.toString() 来查看。

Object.prototype.toString.call([])     // '[object Array]'
Object.prototype.toString.call(/a/i )  // '[object RegExp]'

一般来说,对象的内部 [[class]] 属性和创建该对象的构造函数是相对应的,但是也有例外,如,null|undefined。总的来说, [[class]] 属性代表的是数据的类型值。

对象的 [[class]] 属性:

Object.prototype.toString.call({})         // '[object Object]'
Object.prototype.toString.call(new Date()) // '[object Date]'

基本类型值的 [[class]] 属性:

Object.prototype.toString.call("abc")     // '[object String]'
Object.prototype.toString.call(1234)      // '[object Number]'
Object.prototype.toString.call(true)      // '[object Boolean]'

Object.prototype.toString.call(null)      // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'

总结

  • typeof 只适用于基本数据类型,对于 null 还有 Bug;

  • instanceof 适用于构造函数创建的对象,它是基于原型链运作的;

  • constructor 适用于 ECMA 内置 JavaScript 类型和构造函数创建的对象,无法判断 null、undefined;

  • toString 适用于 ECMA 内置 JavaScript类型(包括基本数据类型和内置对象)的类型判断;

  • instanceof和constructor是不安全的,且都有跨窗口问题。

总之,如果你要判断的是基本数据类型或 JavaScript 内置对象,使用 toString; 如果要判断的是自定义类型,请使用 instanceof 或 constructor。

参数链接

曾有一个 ECMAScript 的修复提案(通过选择性加入的方式),但。该提案会导致 typeof null === 'null'。

当前所有的浏览器都暴露了一个类型为 undefined 的非标准宿主对象 。

但在加入了块级作用域的 和 之后,在其被声明之前对块中的 let 和 const 变量使用 typeof 会抛出一个 。块作用域变量在块的头部处于“”,直至其被初始化,在这期间,访问变量将会引发错误。

Boolean
Null
Undefined
Number
BigInt
String
Symbol
Object
被拒绝了
document.all
let
const
ReferenceError
暂存死区
判断JS数据类型的四种方法
如何检查 JavaScript 变量类型?