整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

JavaScript漫谈之理解类型操作符typeof

JavaScript漫谈之理解类型操作符typeof

本文中,将简述JavaScript类型系统和数据类型,以及如何使用typeof操作符执行类型检查。

还讲解了使用typeof操作符进行某些数据类型检查是不完善的,并介绍其他几种类型检查的方法。

每种编程语言都有自己的类型系统和数据类型,但各种编程语言的数据结构常有不同之处。使用JavaScript时,其引擎会在脚本执行期间隐式强制转换执行值的类型。类型检查对于编写可预测的JavaScript程序是非常有必要的。


JavaScript中的typeof操作符就是用于基础的类型检查

typeof操作符返回字符串,表示未经计算的操作数的类型(来自MDN)

一、JavaScript的数据类型

在此之前,需要了解JavaScript有哪些数据类型,最新标准定义了8种数据类型:

  • 7种原始类型:
  • Boolean
  • Null
  • Undefined
  • Number
  • String
  • BigInt
  • Symbol
  • 其他为引用类型:
  • Object
  • 数组array()、函数function()、正则表达RegExp特殊的物体:
  • array是一种特殊的对象,它是一个有序的编号值集合
  • function是一种特殊类型的对象,具有与之关联的可执行脚本块有关

JavaScript还有几个对象类构造函数,用于创建其他类型的对象:

  • Date—用于创建日期对象
  • RegExp—用于创建正则表达式
  • Error—用于创建JavaScript错误

二、typeof功能

1、语法

typeof使用一元操作符(只需要一个操作数)的计算结果作为其操作数的类型结果字符串。

另一种替代语法就是将操作数放入括号中使用,这种对JavaScript表达式返回的值进行类型检查非常有用。

typeof 参数
typeof(参数)

参数:一个表示对象或原始值的表达式,其类型将被返回

typeof "sueRimn"; // 'string'
typeof 22; // 'number'
typeof NaN; // 'number'
typeof Infinity; // 'number'
typeof true; // 'boolean'
typeof false; // 'boolean'
typeof [1, 2]; // 'object'
typeof {age: 22}; // 'object'
typeof null; // 'object'
typeof undefined; // 'undefined' 
typeof String; // 'function'
typeof Boolean; // 'function' 
typeof Number; // 'function'
typeof Object; // 'function'
typeof Function; // 'function'
typeof person; // 'function'

undefined是它自己的JavaScript类型

在ES6之前,typeof不管操作数是否声明,总是返回一个字符串,即对于未声明的标识符,总是返回而不是抛出错误。

在ES6中,使用let或const声明的块级作用域变量在初始化之前与typeof操作符使用,将抛出错误。

原因是:块级作用域变量在被初始化之前一直保留在临时死区。

console.log(typeof name==='undefined'); // ReferenceError
const name='sueRimn';


2、一般类型检查

在JavaScript中执行类型检查主要使用typeof操作符

function add(a, b) {
 if (typeof a !=='number' || typeof b !=='number') {
 throw '参数必须是数值'
 }
 return a + b;
}

以下是对类型检查的简单摘要:


检测值是否存在在不同环境是这样的:

if (typeof window !=='undefined') {
 // 浏览器
};
if (typeof process !=='undefined') {
 // Node.js
}
if (typeof $ !=='undefined') {
 // jQuery 
}

3、其他类型检查

对于某些值需要额外的类型检查才可以区分,例如null和[]在使用typeof操作符执行type-check时都是"object"类型,但是区分它们需要额外的操作。

一些其他数据类型值检查方法

  • 使用instanceof
  • 检查对象的constructor属性
  • 使用对象的toString()方法检查对象类

(1)检测是否为空

使用typeof操作符检查值是否为空并不好,检查值是否为空的最佳方法是对值与关键字进行严格相等比较。

以上代码呈现的结果是不一样的,所以使用严格相等操作符是非常重要的。

(2)检测NaN

任何涉及NaN的算术运算都将对NaN求值,如果想为任何形式的算术运算使用值,那么需要确保该值不是NaN。

使用typeof操作符检查NaN值是否返回"number"。要检查NaN值,可以使用全局函数isNaN(),或者ES6中的Number.isNaN()函数:

NaN值非常特殊,通过比较,它永远不等于任何其他值,包括它自己:

可以使用以下方法在非ES6环境下检测NaN:

function isNan(value) {
 return value !==value;
}

最后,你可以利用ES6中的Object.is()函数来测试值是否为NaN。

以下函数作用是检查两个值是否相同:

function isNan(value) {
 return Object.is(value, Number.NaN);
}

(3)检测数组

使用typeof检查数组将返回object。这里介绍几种检测数组的方法,并进行对比:

  • 使用constructor属性(不推荐)
function isArray(value) {
 return typeof value=='object' && value.constructor===Array;
}
  • 使用instanceof(不推荐,因为对象的原型可以更改)
function isArray(value) {
 return value instanceof Array;
}
  • 使用Object.prototype.toString()(推荐,类似于ES6 Array.isArray())
function isArray(value) {
 return Object.prototype.toString.call(value)==='[object Array]';
}

object.prototype.tostring()方法对于检查任何JavaScript值的对象类型都非常有用

  • 使用ES6 Array.isArray()

量的数据类型转换:将一种数据类型转换为另外一种数据类型。

通常有三种形式的类型转换:

  • 转换为字符串类型
  • 转换为数字型
  • 转换为布尔型

你会专门把某个数据类型转换成 null 或者 undefined 吗?不会,因为这样做,没有意义。

typeof 运算符

我们先来讲一下 typeof,再讲类型转换。

typeof()表示“获取变量的数据类型”,返回的是小写,语法为:(两种写法都可以)

// 写法1typeof 变量;// 写法2typeof(变量);

typeof 这个运算符的返回结果就是变量的类型。那返回结果的类型是什么呢?是字符串。

返回结果

typeof 的代码写法返回结果typeof 数字numbertypeof 字符串stringtypeof 布尔型booleantypeof 对象objecttypeof 方法functiontypeof nullobjecttypeof undefinedundefined

备注 1:为啥 typeof null的返回值也是 object 呢?因为 null 代表的是空对象

备注 2:typeof NaN的返回值是 number,上一篇文章中讲过,NaN 是一个特殊的数字。

返回结果举例

console.log(type []); // 空数组的打印结果:objectconsole.log(type {}); // 空对象的打印结果:object

代码解释:这里的空数组[]、空对象{} ,为啥他们在使用 typeof 时,返回值也是 object呢?因为这里的 返回结果object指的是引用数据类型。空数组、空对象都是引用数据类型 Object

变量的类型转换的分类

类型转换分为两种:显示类型转换、隐式类型转换。

显示类型转换

  • toString()
  • String()
  • Number()
  • parseInt(string)
  • parseFloat(string)
  • Boolean()

隐式类型转换

  • isNaN ()
  • 自增/自减运算符:++、—-
  • 正号/负号:+a、-a
  • 加号:+
  • 运算符:-、*、/

隐式类型转换(特殊)

  • 逻辑运算符:&&、||、! 。非布尔值进行与或运算时,会先将其转换为布尔值,然后再运算,但运算结果是原值。具体可以看下一篇文章《运算符》。
  • 关系运算符:<、> <=>=等。关系运算符,得到的运算结果都是布尔值:要么是true,要么是false。具体可以看下一篇文章《运算符》。

针对上面这两种类型转换,这篇文章来详细介绍。

其他的简单类型 —> String

方法一(隐式类型转换):字符串拼接

格式:变量+”” 或者 变量+”abc”

举例:

var a=123; // Number 类型console.log(a + ''); // 转换成 String 类型console.log(a + 'haha'); // 转换成 String 类型

上面的例子中,打印的结果,都是字符串类型的数据。实际上内部是调用的 String() 函数。也就是说,c=c + "" 等价于 c=String(c)。

方法二:调用 toString()方法

语法:

变量.toString()

【重要】该方法不会影响到原变量,它会将转换的结果返回。当然我们还可以直接写成a=a.toString(),这样的话,就是直接修改原变量。

注意:null 和 undefined 这两个值没有 toString()方法,所以它们不能用方法二。如果调用,会报错。

另外,Number 类型的变量,在调用 toString()时,可以在方法中传递一个整数作为参数。此时它将会把数字转换为指定的进制,如果不指定则默认转换为 10 进制。例如:

var a=255;//对于Number调用toString()时可以在方法中传递一个整数作为参数//此时它将会把数字转换为指定的进制,如果不指定则默认转换为10进制a=a.toString(2); // 转换为二进制console.log(a); // 11111111console.log(typeof a); // string

方法三(强制转换):使用 String()函数

语法:

String(变量)

使用 String()函数做强制类型转换时:

  • 对于 Number 和 Boolean 而言,本质上就是调用 toString()方法。
  • 但是对于 null 和 undefined,则不会调用 toString()方法。它会将 null 直接转换为 “null”。将 undefined 直接转换为 “undefined”。

prompt():用户的输入

我们在 JS 基础的第 01 篇里,就讲过,prompt()就是专门用来弹出能够让用户输入的对话框。重要的是:用户不管输入什么,都当字符串处理。

其他的数据类型 —> Number 【重要】

使用 Number() 函数

情况一:字符串 —> 数字

  • 1.如果字符串中是纯数字,则直接将其转换为数字。
  • 2.只要字符串中包含了非数字的内容(小数点按数字来算),则转换为 NaN。
  • 3.如果字符串是一个空串或者是一个全是空格的字符串,则转换为 0。

情况二:布尔 —> 数字

  • true 转成 1
  • false 转成 0

情况三:null —> 数字

  • 结果为:0

情况四:undefined —> 数字

  • 结果为:NaN

补充:怎么理解这里的 NaN 呢?可以这样理解,使用 Number() 函数之后,如果无法转换为数字,就会转换为 NaN

使用 parseInt()函数:字符串 -> 整数

parseInt()的作用是将字符串中的有效的整数内容转为数字。parse 表示“转换”,Int 表示“整数”(注意Int的拼写)。例如:

parseInt("5");

得到的结果是数字 5。

parseInt()的转换情况如下。

情况一:字符串 —> 数字

  • 1.只保留字符串最开头的数字,后面的中文自动消失。
  • 2.如果字符串不是以数字开头,则转换为 NaN。
  • 3.如果字符串是一个空串或者是一个全是空格的字符串,转换时会报错。

情况二:Boolean —> 数字

  • 结果为:NaN

情况三:Null —> 数字

  • 结果为:NaN

情况四:Undefined —> 数字

  • 结果为:NaN

Number() 函数和 parseInt() 函数的区别:

就拿Number(true) 和 parseInt(true)/parseFloat(true)来举例,二者在使用时,是有区别的:

  • Number(true) :千方百计地想转换为数字。
  • parseInt(true)/parseFloat(true) :先转为字符串,再提取出最前面的数字部分;没提取出来,那就返回 NaN。

parseInt()具有以下特性

(1)只保留字符串最开头的数字,后面的中文自动消失。例如:

console.log(parseInt("2017在公众号上写了6篇文章")); //打印结果:2017console.log(parseInt("2017.01在公众号上写了6篇文章")); //打印结果仍是:2017   (说明只会取整数)console.log(parseInt("aaa2017.01在公众号上写了6篇文章")); //打印结果:NaN (因为不是以数字开头)

(2)如果对非 String使用 parseInt()或 parseFloat(),它会先将其转换为 String 然后再操作。【重要】

比如:

var a=168.23;console.log(parseInt(a)); //打印结果:168  (因为是先将c转为字符串"168.23",然后然后再操作)var b=true;console.log(parseInt(b)); //打印结果:NaN (因为是先将a转为字符串"true",然后然后再操作)var c=null;console.log(parseInt(c)); //打印结果:NaN  (因为是先将b转为字符串"null",然后然后再操作)var d=undefined;console.log(parseInt(d)); //打印结果:NaN  (因为是先将b转为字符串"undefined",然后然后再操作)

(3)自动带有截断小数的功能:取整,不四舍五入

例 1:

var a=parseInt(5.8) + parseInt(4.7);console.log(a);

打印结果:

9

例 2:

var a=parseInt(5.8 + 4.7);console.log(a);

打印结果:

10;

(4)带两个参数时,表示在转换时,包含了进制转换。

代码举例:

var a='110';var num=parseInt(a, 16); // 【重要】将 a 当成 十六进制 来看待,转换成 十进制 的 numconsole.log(num);

打印结果:

272

如果你对打印结果感到震惊,请仔细看上面的代码注释。就是说,无论 parseInt() 里面的进制参数是多少,最终的转换结果是十进制。

我们继续来看下面的代码,打印结果是多少。

var a='5';var num=parseInt(a, 2); // 将 a 当成 二进制 来看待,转换成 十进制 的 numconsole.log(num); // 打印结果:NaN。因为 二进制中没有 5 这个数,转换失败。

parseFloat()函数:字符串 —> 浮点数(小数)

parseFloat()的作用是:将字符串转换为浮点数

parseFloat()和 parseInt()的作用类似,不同的是,parseFloat()可以获得有效的小数部分。

代码举例:

var a='123.456.789px';console.log(parseFloat(a)); // 打印结果:123.456

parseFloat() 的几个特性,可以参照 parseInt()。

转换为 Boolean

将其他的数据类型转换为 Boolean,可以使用 Boolean()函数。情况如下:

  • 情况一:数字 —> 布尔。除了 0 和 NaN,其余的都是 true。也就是说,Boolean(NaN)的结果是 false。
  • 情况二:字符串 —-> 布尔。除了空串,其余的都是 true。全是空格的字符串,转换结果也是 true。字符串'0'的转换结果也是 true。
  • 情况三:null 和 undefined 都会转换为 false。
  • 情况四:引用数据类型会转换为 true。注意,空数组[]和空对象{},转换结果也是 true,这个一点,很多人都不知道。

PS:转换为 Boolean 的这几种情况,很重要,开发中会经常用到。

知识补充:其他进制的数字

  • 16 进制的数字,以0x开头
  • 8 进制的数字,以0开头
  • 2 进制的数字,0b开头(不是所有的浏览器都支持:chrome 和火狐支持,IE 不支持)

比如070这个字符串,如果我调用 parseInt()转成数字时,有些浏览器会当成 8 进制解析,有些会当成 10 进制解析。

所以,比较建议的做法是:可以在 parseInt()中传递第二个参数,来指定当前数字的进制。例如:

var a="070";a=parseInt(a, 8); //将 070 当成八进制来看待,转换结果为十进制。console.log(a); // 打印结果:56。这个地方要好好理解。

隐式类型转换

重点:隐式类型转换,内部调用的都是显式类型的方法。下面来详细介绍。

isNaN() 函数

语法:

isNaN(参数);

解释:判断指定的参数是否为 NaN(非数字类型),返回结果为 Boolean 类型。也就是说:任何不能被转换为数值的参数,都会让这个函数返回 true

执行过程

(1)先调用Number(参数)函数;

(2)然后将Number(参数)的返回结果和NaN进行比较。

代码举例:

console.log(isNaN('123')); // 返回结果:false。console.log(isNaN('abc')); // 返回结果:true。因为 Number('abc') 的返回结果是 NaNconsole.log(isNaN(null)); // 返回结果:falseconsole.log(isNaN(undefined)); // 返回结果:trueconsole.log(isNaN(NaN)); // 返回结果:true

自增/自减运算符:++、—-

举例 1

var a="666";a++;console.log(typeof a); // 打印结果: numberconsole.log(a); // 打印结果:667

执行过程:

(1)先调用Number(参数)函数;

(2)然后将Number(参数)的返回结果进行 加 1 操作。

举例 2

var a='abc';a++;console.log(typeof a); // 打印结果:numberconsole.log(a); // 打印结果:NaN。因为 Number('abc')的结果为 NaN,再自增后,结果依然是 NaN

正号/负号:+a、-a

注意,这里说的是正号/负号,不是加号/减号。

任何值做+a、-a、/a运算时,运算结果都会自动转换为 Number 类型。 内部调用的是 Number() 函数。

举例

var a='666';var b=+a;console.log(typeof a); // 打印结果:string。说明 a 的数据类型保持不变。console.log(a); // 打印结果:666console.log(typeof b); // 打印结果:number。说明 b 的数据类型发生了变化。console.log(b); // 打印结果:666

加号:+

情况一:字符串 + 数字

  • 当加号的两边,只要有一个是字符串的时候,就会调用 String() 函数将数字转为字符串,然后再计算。导致最终的运算结果是字符串。

情况二:Boolean + 数字

  • Boolean 型和数字型相加时, true 按 1 来算 ,false 按 0 来算。这里其实是先调 Number() 函数,将 Boolean 类型转换为 Number类型,然后再和 数字相加。

情况三: null + 数字

  • 等价于:0 + 数字

情况四: undefined + 数字

  • 计算结果:NaN

运算符:-、*、/

1、任何非 Number 类型的值做-、*、/运算时,会将这些值转换为Number然后再运算(内部调用的是 Number() 函数),运算结果是 Number 类型。(注:任何值 + 字符串是特例,运算结果是字符串)

比如:

    result1=true + 1;  // 2=1+ 1    result2=true + false; // 1=1+ 0    result3=1 + null; // 1=1+ 0    result4=100 - '1' // 99

2、任何的值和字符串做加法运算,都会先转换为字符串,然后再做拼串操作。

比如:

    result1=1 + 2 + '3'  // 33    result2='1' + 2 + 3; // 123

3、任何值和NaN做运算的结果都是NaN。

页可见区域宽:document.body.clientWidth

网页可见区域高:document.body.clientHeight

网页可见区域宽:document.body.offsetWidth (包括边线的宽)

网页可见区域高:document.body.offsetHeight (包括边线的宽)

网页正文全文宽:document.body.scrollWidth

网页正文全文高:document.body.scrollHeight

网页被卷去的高:document.body.scrollTop

网页被卷去的左:document.body.scrollLeft

网页正文部分上:window.screenTop

网页正文部分左:window.screenLeft

屏幕分辨率的高:window.screen.height

屏幕分辨率的宽:window.screen.width

屏幕可用工作区高度:window.screen.availHeight

屏幕可用工作区宽度:window.screen.availWidth

HTML精确定位:scrollLeft,scrollWidth,clientWidth,offsetWidth

scrollHeight: 获取对象的滚动高度。

scrollLeft:设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离

scrollTop:设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离

scrollWidth:获取对象的滚动宽度

offsetHeight:获取对象相对于版面或由父坐标 offsetParent 属性指定的父坐标的高度

offsetLeft:获取对象相对于版面或由 offsetParent 属性指定的父坐标的计算左侧位置

offsetTop:获取对象相对于版面或由 offsetTop 属性指定的父坐标的计算顶端位置

event.clientX 相对文档的水平座标

event.clientY 相对文档的垂直座标

event.offsetX 相对容器的水平坐标

event.offsetY 相对容器的垂直坐标

document.documentElement.scrollTop 垂直方向滚动的值

event.clientX+document.documentElement.scrollTop 相对文档的水平座标+垂直方向滚动的量

IE,FireFox 差异如下:

IE6.0、FF1.06+:

clientWidth=width + padding

clientHeight=height + padding

offsetWidth=width + padding + border

offsetHeight=height + padding + border

IE5.0/5.5:

clientWidth=width - border

clientHeight=height - border

offsetWidth=width

offsetHeight=height

(需要提一下:CSS中的margin属性,与clientWidth、offsetWidth、clientHeight、offsetHeight均无关)

网页可见区域宽: document.body.clientWidth

网页可见区域高: document.body.clientHeight

网页可见区域宽: document.body.offsetWidth (包括边线的宽)

网页可见区域高: document.body.offsetHeight (包括边线的高)

网页正文全文宽: document.body.scrollWidth

网页正文全文高: document.body.scrollHeight

网页被卷去的高: document.body.scrollTop

网页被卷去的左: document.body.scrollLeft

网页正文部分上: window.screenTop

网页正文部分左: window.screenLeft

屏幕分辨率的高: window.screen.height

屏幕分辨率的宽: window.screen.width

屏幕可用工作区高度: window.screen.availHeight

屏幕可用工作区宽度: window.screen.availWidth

-------------------

技术要点

本节代码主要使用了Document对象关于窗口的一些属性,这些属性的主要功能和用法如下。

要得到窗口的尺寸,对于不同的浏览器,需要使用不同的属性和方法:若要检测窗口的真实尺寸,在Netscape下需要使用Window的属性;在IE下需要 深入Document内部对body进行检测;在DOM环境下,若要得到窗口的尺寸,需要注意根元素的尺寸,而不是元素。

Window对象的innerWidth属性包含当前窗口的内部宽度。Window对象的innerHeight属性包含当前窗口的内部高度。

Document对象的body属性对应HTML文档的标签。Document对象的documentElement属性则表示HTML文档的根节点。

document.body.clientHeight表示HTML文档所在窗口的当前高度。document.body. clientWidth表示HTML文档所在窗口的当前宽度。

实现代码

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<title>请调整浏览器窗口</title>

<meta http-equiv="content-type" content="text/html; charset=gb2312">

</head>

<body>

<h2 align="center">请调整浏览器窗口大小</h2><hr>

<form action="#" method="get" name="form1" id="form1">

<!--显示浏览器窗口的实际尺寸-->

浏览器窗口 的 实际高度: <input type="text" name="availHeight" size="4"><br>

浏览器窗口 的 实际宽度: <input type="text" name="availWidth" size="4"><br>

</form>

<script type="text/javascript">

<!--

var winWidth=0;

var winHeight=0;

function findDimensions() //函数:获取尺寸

{

//获取窗口宽度

if (window.innerWidth)

winWidth=window.innerWidth;

else if ((document.body) && (document.body.clientWidth))

winWidth=document.body.clientWidth;

//获取窗口高度

if (window.innerHeight)

winHeight=window.innerHeight;

else if ((document.body) && (document.body.clientHeight))

winHeight=document.body.clientHeight;

//通过深入Document内部对body进行检测,获取窗口大小

if (document.documentElement && document.documentElement.clientHeight && document.documentElement.clientWidth)

{

winHeight=document.documentElement.clientHeight;

winWidth=document.documentElement.clientWidth;

}

//结果输出至两个文本框

document.form1.availHeight.value=winHeight;

document.form1.availWidth.value=winWidth;

}

findDimensions();

//调用函数,获取数值

window.onresize=findDimensions;

//-->

</script>

</body>

</html>

源程序解读

(1)程序首先建立一个表单,包含两个文本框,用于显示窗口当前的宽度和高度,并且,其数值会随窗口大小的改变而变化。

(2)在随后的JavaScript代码中,首先定义了两个变量winWidth和winHeight,用于保存窗口的高度值和宽度值。

(3)然后,在函数findDimensions ( )中,使用window.innerHeight和window.innerWidth得到窗口的高度和宽度,并将二者保存在前述两个变量中。

(4)再通过深入Document内部对body进行检测,获取窗口大小,并存储在前述两个变量中。

(5)在函数的最后,通过按名称访问表单元素,结果输出至两个文本框。

(6)在JavaScript代码的最后,通过调用findDimensions ( )函数,完成整个操作。