JavaScript中的类型

待兔 等级 611 0 0

JavaScript中的类型

一、关于类型

什么叫做类型?简单地说,类型就是把内存中的一个二进制序列赋予某种意义。比如,二进制序列0100 0000 0111 0000 0001 0101 0100 1011 1100 0110 1010 0111 1110 1111 1001 1110如果看作是64位无符号整数类型就是4643234631018606494 而按照IEEE 754规定的浮点数二进制表示规则(见附1)双精度浮点类型则是257.331。

变量类型

大部分计算机语言使用变量来存储和表示数据,一些语言会给变量规定一个类型,在整个程序中(不论是编译时还是运行时),这个类型都不能被改变。与此相对,JavaScript和一些其它语言的变量可以存储任何类型,它们使用无类型的变量。变量类型是否存在,是跟语法无关的,例如C#中也提供了var类型的变量,但是,下面的语句在C#中会出错:

var a=1;
a=”string”;

原因是C#的var关键字只是省略了变量类型声明,而根据初始化表达式自动推断变量类型,所以C#的var变量仍然是有类型的。而JavaScript中,任何时刻你都可以把任何值赋值给特定变量,所以JavaScript变量是无类型的。

强类型和弱类型

按照计算机语言的类型系统的设计方式,可以分为强类型和弱类型两种。二者之间的区别,就在于计算时是否可以不同类型之间对使用者透明地隐式转换。从使用者的角度来看,如果一个语言可以隐式转换它的所有类型,那么它的变量、表达式等在参与运算时,即使类型不正确,也能通过隐式转换来得到正确地类型,这对使用者而言,就好像所有类型都能进行所有运算一样,所以这样的语言被称作弱类型。与此相对,强类型语言的类型之间不一定有隐式转换(比如C++是一门强类型语言,但C++中double和int可以互相转换,但double和任何类型的指针之间都需要强制转换)

为什么要有类型

类型可以帮助程序员编写正确的程序,它在实际编写程序的过程中体现为一种约束。一般规律是,约束越强越不容易出错,但编写程序时也越麻烦。变量有类型的强类型语言约束最强,典型代表是C++,变量无类型的弱类型语言约束最弱,典型代表是JavaScript。在JavaScript中,因为约束比较弱,所以容易出现这种错误:

var a =200;

var b ="1";

var c= a + b;

你可能期望c是201,但实际上它是"2001",这个错误在强类型语言中决不会出现。然而正是因为JavaScript没有这些约束,所以可以很方便地拼接数字和字符串类型。所以,约束和灵活性对语言的设计者而言,永远是需要平衡的一组特性。

静态类型和动态类型

类型是一种约束,这种约束是通过类型检查来发生作用的。在不同语言中,类型检查在不同的阶段发生作用,这样又可以分为编译时检查和运行时检查。对于JavaScript这样的解释型语言,也有跟编译过程比较相似的阶段,即词法分析和语法分析,解释型语言的类型检查若在语法分析或者之前的阶段完成,也可以认为类似于编译时检查。所以更合理的说法是静态类型检查和动态类型检查。

有趣的是,很多语言虽然编译时检查类型,但是它的类型信息仍可以在运行时获得,如C#中使用元数据来保存类型信息,在运行阶段,使用者可以通过反射来获取和使用类型的信息。

JavaScript在设计的各个方面都以灵活性优先,所以它使用动态类型检查,并且除了在进行极少数特定操作时,JavaScript不会主动检查类型。你可以在运行时获得任何一个变量或者表达式的类型信息并且通过程序逻辑检查它的正确性。

二、JavaScript标准规定的类型

JavaScript标准中规定了9种类型:Undefined Null Boolean String Number Object Reference List Completion

其中,Reference List Completion三种类型仅供语言解析运行时使用,无法从程序中直接访问,这里就暂不做介绍。下面我们可以了解下这六种类型:

Undefined类型

Undefined类型只有一个值undefined,它是变量未被赋值时的值,在JS中全局对象有一个undefined属性表示undefined,事实上undefined并非JavaScript的关键字,可以给全局的undefined属性赋值来改变它的值。

Null类型

Null类型也只有一个值null,但是JavaScript为它提供了一个关键字null来表示这个唯一的值。Null类型的语义是“一个空的对象引用”。

Boolean类型

Boolean有两种取值true和false

String类型

String类型的的正式解释是一个16位无符号整数类型的序列,它实际上用来表示以UTF-16编码的文本信息。

Number类型

JavaScript的Number共有18437736874454810627 (就是 264-253 +3)个值。JavaScript的Number以双精度浮点类型存储,除了9007199254740990表示NaN,它遵守IEEE 754(见附1)规定,占用64位8字节。

Object类型

JavaScript中最为复杂的类型就是Object,它是一系列属性的无序集合,Function是实现了私有属性[[call]]的Object,JavaScript的宿主也可以提供一些特别的对象。

三、JavaScript使用者眼中的类型:

前面讲了JS标准中规定的类型,然而一个不能忽略的问题是JS标准是写给JS实现者看的,对JS使用者而言,类型并不一定要按照标准来定义,比如,因为JS在进行.运算的时候,会自动把非Object类型转换为与其对应的对象,所以"str".length其实和(new String("str")).length是等效的,从这个角度而言,认为二者属于同一类型也未尝不可。我们利用JS中的一些语言特性,可以进行运行时的类型判别,但是这些方法判断的结果各不相同,孰好孰坏还需要您自己决定。

typeof——看上去很官方

typeof是JS语言中的一个运算符,从它的字面来看,显然它是用来获取类型的,按JavaScript标准的规定,typeof获取变量类型名称的字符串表示,他可能得到的结果有6种:string、bool、number、undefined、object、function,而且JavaScript标准允许其实现者自定义一些对象的typeof值。

在JS标准中有这样一个描述列表:

Type

Result

Undefined

"undefined"

Null

"object"

Boolean

"boolean"

Number

"number"

String

"string"

Object (native and doesn't implement [[call]])

"object"

Object (native and implements [[call]])

"function"

Object (host)

Implementation-dependent

下面一个例子来自51js的Rimifon,它展示了IE中typeof的结果产生"date"和"unknown"的情况:

var xml=document.createElement("xml");
var rs=xml.recordset;
rs.Fields.Append("date", 7, 1);
rs.Fields.Append("bin", 205, 1);
rs.Open();
rs.AddNew();
rs.Fields.Item("date").Value = 0;
rs.Fields.Item("bin").Value = 21704;
rs.Update();
var date = rs.Fields.Item("date").Value;
var bin = rs.Fields.Item("bin").Value;
rs.Close();
alert(date);
alert(bin);
alert([typeof date, typeof bin]);
try{alert(date.getDate())}catch(err){alert(err.message)}

关于这个最为接近"类型"语义的判断方式,实际上有不少的批评,其中之一是它无法分辨不同的object,new String("abc")和new Number(123)使用typeof无法区分,由于JS编程中,往往会大量使用各种对象,而typeof对所有对象都只能给出一个模糊的结果"object",这使得它的实用性大大降低。

instanceof——原型还是类型?

instanceof的意思翻译成中文就是"是……的实例",从字面意思理解它是一个基于类面向对象编程的术语,而JS实际上没有在语言级别对基于类的编程提供支持。JavaScript标准虽然只字未提,但其实一些内置对象的设计和运算符设置都暗示了一个"官方的"实现类的方式,即从把函数当作类使用,new运算符作用于函数时,将函数的prototype属性设置为新构造对象的原型,并且将函数本身作为构造函数。

所以从同一个函数的new运算构造出的对象,被认为是一个类的实例,这些对象的共同点是:1.有同一个原型 2.经过同一个构造函数处理。而instanceof正是配合这种实现类的方式检查"实例是否属于一个类"的一种运算符。猜一猜也可以知道,若要检查一个对象是否经过了一个构造函数处理千难万难,但是检查它的原型是什么就容易多了,所以instanceof的实现从原型角度理解,就是检查一个对象的[[prototype]]属性是否跟特定函数的prototype一致。注意这里[[prototype]]是私有属性,在SpiderMonkey(就是Firefox的JS引擎)中它可以用__proto__来访问。

原型只对于标准所描述的Object类型有意义,所以instanceof对于所有非Object对象都会得到false,而且instanceof只能判断是否属于某一类型,无法得到类型,但是instanceof的优势也是显而易见的,它能够分辨自定义的"类"构造出的对象。

instanceof实际上是可以被欺骗的,它用到的对象私有属性[[prototype]]固然不能更改,但函数的prototype是个共有属性,下面代码展示了如何欺骗instanceof

function ClassA(){};

function ClassB(){};

var o = new ClassA();//构造一个A类的对象

ClassB.prototype = ClassA.prototype; //ClassB.prototype替换掉

alert(o instanceof ClassB)//true 欺骗成功 - -!

Object.prototype.toString——是个好方法?

Object.prototype.toString原本很难被调用到,所有的JavaScript内置类都覆盖了toString这个方法,而对于非内置类构造出的对象,Object.prototype.toString又只能得到毫无意义的[object Object]这种结果。所以相当长的一段时间内,这个函数的神奇功效都没有被发掘出来。

在标准中,Object.prototype.toString的描述只有3句

1. 获取this对象的[[class]]属性

2. 通过连接三个字符串"[object ", 结果(1), 和 "]"算出一个字符串

3. 返回 结果(2).

显而易见,Object.prototype.toString其实只是获取对象的[[class]]属性而已,不过不知道是不是有意为之,所有JS内置函数对象String Number Array RegExp……在用于new构造对象时,全都会设定[[class]]属性,这样[[class]]属性就可以作为很好的判断类型的依据。

因为Object.prototype.toString是取this对象属性,所以只要用Object.prototype.toString.call或者Object.prototype.toString.apply就可以指定this对象,然后获取类型了。

Object.prototype.toString尽管巧妙,但是却无法获取自定义函数构造出对象的类型,因为自定义函数不会设[[class]],而且这个私有属性是无法在程序中访问的。Object.prototype.toString最大的优点是可以让1和new Number(1)成为同一类型的对象,大部分时候二者的使用方式是相同的。

然而值得注意的是 new Boolean(false)在参与bool运算时与false结果刚好相反,如果这个时候把二者视为同一类型,容易导致难以检查的错误。

总结:

为了比较上面三种类型判断方法,我做了一张表格,大家可以由此对几种方法有个整体比较。为了方便比较,我把几种判断方式得到的结果统一了写法:

对象

typeof

instanceof

Object.prototype.toString

标准

"abc"

String

——

String

String

new String("abc")

Object

String

String

Object

function hello(){}

Function

Function

Function

Object

123

Number

——

Number

Number

new Number(123)

Object

Number

Number

Object

new Array(1,2,3)

Object

Array

Array

Object

new MyType()

Object

MyType

Object

Object

null

Object

——

Object

Null

undefined

Undefined

——

Object

Undefined

事实上,很难说上面哪一种方法是更加合理的,即使是标准中的规定,也只是体现了JS的运行时机制而不是最佳使用实践。我个人观点是淡化"类型"这一概念,而更多关注"我想如何使用这个对象"这种约束,使用typeof配合instanceof来检查完全可以在需要的地方达到和强类型语言相同的效果。

附1 IEEE 754 规定的双精度浮点数表示(来自中文wikipedia):

sign bit(符号): 用来表示正负号

exponent(指数): 用来表示次方数

mantissa(尾数): 用来表示精确度

收藏
评论区

相关推荐

JavaScript中的类型
JavaScript中的类型 一、关于类型 什么叫做类型?简单地说,类型就是把内存中的一个二进制序列赋予某种意义。比如,二进制序列0100 0000 0111 0000 0001 0101 0100 1011 1100 0110 1010 0111 1110 1111 1001 1110如果看作是64位无符号整数类型就是4
js 基础之弱类型/变量提升/TDZ/块作用域/重复定义/Object.freeze()
弱类型 在JS中变量类型由所引用的值决定 var web "helloWorld"; console.log(typeof web); //string web 99; console.log(typeof web); //number web {}; console.log(typeof web); //object 变量提升
utils.js 文件 工具类 方法分享
javascript / 时间解析工具 @param {(Object|string|number)} time @param {string} cFormat @returns {string | null} / export function parseTime(time, cFormat) { if (arguments.leng
JS - typeof 与 instanceof
一、typeof typeof 操作符返回一个字符串,表示未经计算的操作数的类型 使用方法如下: typeof operand typeof(operand) operand表示对象或原始值的表达式,其类型将被返回 举个例子 typeof 1 // 'number' typeof '1' // 'string'
一篇文章弄明白Javascript
在一片混沌中,一个叫Function的函数开天辟地,自己创建了自己。于是,一个叫JavaScript的星球诞生了。 图一:创建了自己的Function 新诞生
7个关于"this"面试题,你能回答上来吗?
作者:Shadeed 译者:前端小智 来源:dmitripavlutin 点赞再看,微信搜索【大迁世界(https://mp.weixin.qq.com/s/sY9ufGGKfcdaAQ7KJQs3HA)】,B站关注【前端小智(https://space.bilibili.com/31089477)】这个没有大厂背景,但有着一股向上积
Python中JSON的基本使用_Just do it !
JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。Python3 中可以使用 json 模块来对 JSON 数据进行编解码,它主要提供了四个方法: dumps、dump、loads、load。 dump和dumps dump和dumps对python对象进行序列化。将一个Python对象
Java的数值数据类型以及命名规范
一、Java中的数值数据类型 <table<tbody<tr<td width"75" valign"top" style"wordbreak: breakall;"<span style"backgroundcolor: rgb(255, 254, 213);"类型名<br</span</td<td width"299
你不知道的JSON Schema
1、JSON?JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。具有简洁、可读性高、支持广泛的特点。下面我们用JSON描述了一个商品的基本信息,包含了JSON的一些基本数据的类型 。json{ "productId": 1, "
前端面试题自检 JS CSS 部分
JS类型 JavaScript的简单数据类型Number , String , Boolean , Undefined , Null , Symbol typeof 操作符的返回值 number string boolean undefined object function
[C#]ArrayList、string、string[]之间的转换
1、ArrarList 转换为 string\[\] :  ArrayList list new ArrayList();  list.Add("aaa");  list.Add("bbb");  string\[\] arrString (string\[\])list.ToArray(typeof( string)) ;2、string\[\] 转换
面试官:JavaScript的数据类型你了解多少?
前言作为JavaScript的入门知识点,Js数据类型在整个JavaScript的学习过程中其实尤为重要。最常见的是边界数据类型条件判断问题。我们将通过这几个方面来了解数据类型: 概念 检测方法 转换方法 概念undefined、Null、Boolean、String、Number、Symbol、BigInt为基础类型;Ob
一篇文章带你了解JavaScript Object 对象
一、概念JavaScript 原生提供Object对象(注意起首的O是大写),介绍该对象原生的各种方法。JavaScript 的所有其他对象都继承自Object对象,即那些对象都是Object的实例。 二、Object()Object本身是一个函数,可以当作工具方法使用,将任意值转为对象。这个方法常用于保证某个值一定是对象。如果参数为空(或者为undefi
一篇文章带你了解JavaScript作用域
在JavaScript中,对象和函数也是变量。在JavaScript中,作用域是你可以访问的变量、对象和函数的集合。JavaScript 有函数作用域: 这个作用域在函数内变化。一、本地JavaScript变量一个变量声明在JavaScript函数内部,成为函数的局部变量。局部变量有局部作用域: 它们只能在函数中访问。JS://code here can n
一篇文章带你了解JavaScript类型转换
类型转换是一种将一种数据类型转换为另一种数据类型的方法。一、运算符的类型typeof运算符可以帮助你找到你的变量的类型。typeof运算符返回一个变量或表达式的类型。例:项目 JavaScript typeof运算符 typeof运算符返回变量或表达式的类型:document.write(typeof "" + "" +typeof "Json" + ""