JavaScript数据类型,类型转换和类型判断
数据类型
- 原始类型:
Number
、String
、Boolean
、Null
、Undefined
、Symbol
、BigInt
- 引用类型:
Object
引用数据类型可统称为一种,在JS中万物皆对象,除了基本数据类型其他都是对象。数组是对象、函数是对象、正则表达式也是对象。如Function、Array、Date等都是基于Object的。
基本数据类型
基本数据类型的主要特点是赋值方式是传值,并且值存在 栈 中。
由操作系统自动分配释放存放函数的参数值、局部变量的值等,其操作方式类似于数据结构中的栈;
数据类型 | 实例 | 说明 |
---|---|---|
Number | 12 ,-1 ,17.23 ,3.1E-2 |
数值型数据 |
BigInt | 12n ,-2n ,0n |
表示任意精度格式的整数,BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。 |
String | "message" ,“str” ,“name” |
字符型数据,需加英文双引号或单引号或反引号 |
Boolean | true , false |
布尔型数据,不用加引号,表示逻辑真与假 |
Undefined | undefined |
JavaScript中未定义的值 |
Null | null |
表示空值 |
Symbol | let var_symbol = Symbol(); let var_symbol = Symbol.for('symbol'); |
Symbol 指的是独一无二的值。每个通过 Symbol() 生成的值都是唯一的。可以防止命名冲突 |
Number
Number型数据即为数值型数据用来表示整数和浮点数,分为正数、负数、小数等。
整数型数值可以使用十进制、八进制以及十六进制标识
- 十进制:直接使用数字表示,例如:
123
、456
等。 - 八进制:以
0
开头,例如:010
、077
等。 - 十六进制:以
0x
或0X
开头,例如:0xFF
、0x3F
等。
1 | let numn1=1; //十进制,输出值为1 |
注意: 当你在JavaScript中使用八进制或十六进制数值时,它们会被自动转换为十进制表示。例如,010
会被解释为十进制的8
,但是081
会被解释成81
(因为8进制里面没有8
) ,0xFF
会被解释为十进制的255
。
浮点数是指包含小数点的实数,且可用科学计数法来表示。科学计数法一般用于值极大或者极小的时候。
1 | //Number--浮点数 |
如果浮点数看起来是整数, JavaScript会 自动将浮点数转换为整数。原因是JavaScript总是希望使用更少的内存,因为浮点数使用的内存是整数值的两倍。
范围: Number
类型只能表示$-(2^{53}-1)到(2^{53}-1)$ 之间的值
Number.MIN_VALUE
的值为5e-324
,这是 Number 类型能够表示的最接近于零的正数。这意味着任何小于 5e-324 的正数都会被 JavaScript 解释为 0。Number.MAX_VALUE
的值为1.7976931348623157e+308
,这是 Number 类型能够表示的最大值。这意味着任何大于 1.7976931348623157e+308 的数值都会被 JavaScript 解释为 Infinity。- 当数值范围超过最大值或最小值时,可以使用
Infinity
和-Infinity
来表示无限数
1 | //Number.MIN_VALUE和Number.MAX_VALUE |
特殊的Number类型:
Infinity
- 当一个数太大,以至于无法用
Number.MAX_VALUE
表示时,JavaScript 会返回Infinity
。 - 当一个正数除以0时,结果为
Infinity
。 - 使用
Number.POSITIVE_INFINITY
可以访问这个值。例如:1 / 0
返回Infinity
。
- 当一个数太大,以至于无法用
-Infinity
- 当一个数太小,以至于无法用
Number.MIN_VALUE
表示时,JavaScript 会返回-Infinity
。 - 当一个负数除以0或一个正数除以
-0
时,结果为-Infinity
。 - 使用
Number.NEGATIVE_INFINITY
可以访问这个值。例如:-1 / 0
返回-Infinity
。
- 当一个数太小,以至于无法用
NaN
(虽然 Not a Number,但是是Number类型)- 表示值不是数字(Not a Number) 。他是一个特殊的数值,表示无效数字。当一个操作或计算机结果不是一个有效的数字时,JavaScript会返回
NaN
- 任何与NaN的运算都会返回NaN
- NaN不等于任何值,包括其本身
- 表示值不是数字(Not a Number) 。他是一个特殊的数值,表示无效数字。当一个操作或计算机结果不是一个有效的数字时,JavaScript会返回
BigInt
BigInt 类型的数据必须添加后缀n。
BigInt数据类型提供了一种方法来表示大于$2^{53}-1$的整数。BigInt可以表示 任意大的整数(不能表示小数) 。
Number类型只能安全的支持$-9007199254740991(-(2^{53}-1))$ 和 $9007199254740991(2^{53}-1)$之间的整数,任何超过这个范围的数值都会失去精度;而BigInt
可以解决这个问题
- 在整数的末尾追加n:
console.log(9007199254740999n); //9007199254740999
- 调用
BigInt()
构造函数:1
2let bigInt = BigInt("9007199254740999");
//传递给BigInt()的参数将自动转换为BigInt 9007199254740999n
注意:
- BigInt 除了不能使用一元加号运算符外,其他的运算符都可以使用
- BigInt 和 Number 之间不能进行混合操作
String
字符串是零个或多个字符的序列。JavaScript不区分单个字符和字符串,任何字符或字符串以英文单引号(''
)或双引号(""
)开头和结尾。
JavaScript字符串是不可变的,意味着一旦被创建就无法修改。
- 以双引号开头的字符串必须是双引号结尾,以单引号开头的字符串也必须是单引号结尾,以什么引号开头就以什么引号结尾
- 无论单引号或是双引号必须成对使用
- 单引号/双引号可以互相嵌套,但是不以自已嵌套自已(单套双或双套单)
- 必要时可以使用转义符 `` ,输出单引号或双引号
转义字符 | 描述 |
---|---|
\b |
退格符 |
\f |
换页符 |
\n |
换行符 |
\r |
回车符 |
\t |
制表符(Tab) |
\v |
垂直制表符 |
' |
单引号 |
" |
双引号 |
`` | 反斜杠 |
Boolean
boolean
型数据表示的是布尔型数据,它有两个固定的值 true
和 false
,表示肯定的数据用 true
,表示否定的数据用 false
,且任何时刻都只能使用两种状态中的一种,不能同时出现。
注意: Boolean型变量赋值时,不能在 true 或 false 外面加引号,否则就变成字符串(string
)了
Undefined
Undefined型是未定义类型,未定义是比较特殊的类型,只有一个值 undefined
。默认情况下,当声明变量时但未赋值的情况下,变量的默认值为 undefined
。JavaScript 中变量的值决定了变量的数据类型。( Undefined是一种数据类型,只有一个值undefined
)
1 | // 只声明了变量,并末赋值 |
Null
Null型数据表示空值,作用是表明数据空缺的值,只有一个值null。一般在设定已存在的变量(或对象的属性)为空时较为常见。(Null是一种数据类型,只有一个值null)
注意: console.log(null == undefined)
结果是true
Symbol
Symbol 本质上是一种唯一标识符,可用作对象的唯一属性名,这样其他人就不会改写或覆盖你设置的属性值。在创建symbol类型数据 时的参数只是作为标识使用,所以 Symbol()
也是可以的。
- 声明:
let id = Symbol('id')
- 即使是用同一个变量(标识)生成的值也不相等:
- 隐藏性,
for···in
和object.keys()
不能访问 - 但是也有能够访问的方法:
Object.getOwnPropertySymbols
, 该方法会返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
1 | let id = Symbol("id"); |
- 全局注册并登记的方法:
Symbol.for()
:有时候希望能够多次使用同一个symbol值的情况。
1 | let name1 = Symbol.for('name'); //检测到未创建后新建 |
- 通过symbol对象获取到参数值:
Symbol.keyFor()
1 | let name1 = Symbol.for('name'); |
引用数据类型
堆(heap):用来保存栈中简单数据字段对指针的引用
存储复杂类型,一般由程序员分配释放,若程序员不释放,由垃圾回收机制 回收;
复杂数据类型引用放在栈里面,实际数据存放到堆里面。
Object
Array
Function
Date
RegExp
Map
Set
WeakMap
WeakSet
Promise
Error
Blob
File
ArrayBuffer
- . . .
包装类型
在JavaScript中,一些操作并不直接适用于原始类型的值,而是需要将它们转换为相应的对象。这就引入了”包装类”的概念。这些包装类是由JavaScript自动创建的临时对象,用于实现对原始值进行操作。
一般来说,有三种包装类:
Number
、String
和Boolean
。当我们使用原始数据类型调用相关属性或方法时,JavaScript会 自动将 其转换为相应的包装对象,以便于对其进行操作。
所有的包装类都有 valueOf()
和 toString()
方法,除了Math 和 Error 对象没有valueOf
方法
包装类才有组包与拆包
组包与拆包
组包是由简单变复杂,拆包是由复杂变简单。
组包
基本数据类型 转成 引用数据 类型
- 显式组包:
new 包装类()
- 隐式组包:
1 | let test=1; |
拆包
引用数据 类型 转成 基本数据 类型
调用
包装类.valueOf()
隐式组包 和 隐式拆包:
let test = 1; test.toString(); // '1' 组包 test = new Number(test);
typeof test;// number 隐式拆包了
显式组包 和 显式拆包:
test = new Number(test);// 显式组包
test = test.valueof();//显式拆包
Boolean
toString()
方法返回指定的布尔对象的字符串形式。
1 | let flag1 = new Boolean(true); |
valueOf()
方法返回一个Boolean对象的原始值
1 | let x = new Boolean(); |
Number
一些方法和Global
上的方法功能一样,一般都是用Global
上的方法即可。
1 | //例如: Number.parseInt() 、Number.parseFloat() |
String
String
包装类 隐式 或 显式 组包以后可以使用一些String
原型上的一些api:
charAt(index)
方法从一个字符串中返回指定的字符。index
默认为0index
大于字符串总长度,返回空字符串
indexOf(searchValue[, fromIndex])
查找字符串中是否存在某个字符串,并返回所在索引 。fromIndex
默认为0,从某个位置开始查找- 如果未找到该值,则返回-1
lastIndexOf
和indexOf
类似,是查找最后一次出现的索引位置slice(begin[, end])
方法提取一个字符串的一部分,并返回一新的字符串。begin
开始位置end
结束位置,默认值是从begin
一直到字符串结束
concat
将一个或多个字符串与原字符串连接合并,形成一个新的字符串并返回。toUpperCase()
将调用该方法的字符串值转换为大写形式,并返回。toLowerCase()
会将调用该方法的字符串值转为小写形式,并返回。trim()
方法会从一个字符串的两端删除空白字符
类型转换
JS中只存在 三种 类型转换
- to string
- to boolean
- to number
然后具体到不同的数据类型向这三种类型转换,如原始数据类型(string
, number
, boolean
, undefined
, null
) 和引用数据类型(object
)
按转换类型划分
其他类型转成Number
数据类型 | 转为值 |
---|---|
String | 纯数字的字符串转为对应的数字,空字符串和空格字符串是0 ,其他字符变成NaN |
Boolean | true 为1,false 为0 |
Object | 调用valueOf() 的返回结果,没有则返回toString() 结果,若对象没有valueOf() 和toString() 则返回NaN |
null | 返回0 |
undefined | 返回NaN |
Number([])
返回0
,Number([3])
返回3
,Number([1, 2])
返回NaN
。
显式转换
Number()
:可以把任意值转换成数值,如果要转换的字符串中有一个不是数值的字符,返回 NaNparseInt()
/parseFloat()
:parseFloat
会解析第一个.
遇到第二个.
或者非数字结束,如果解析的内容里只有整数,解析成整数
1 | let a = '12.3px' |
隐式转换
isNaN()
函数用于判断是否是一个非数字类型
-
,*
,/
,%
,**
(除了+
)都可以将其他类型在和数字进行运算时转成数字类型
1 | console.log('2' - 1) // 1 |
- +和-(-转过来是负的) 都可以把其他类型(尤其是字符串)转成数字类型
1 | console.log(+'21') // 21 |
其他类型转成String
数据类型 | 转为值 |
---|---|
Number | 转为对应数字的字符串形式,NaN 是“NaN” |
Boolean | true 为"true" ,false 为"false" |
Object | 返回toString() 的返回值,默认是“[object Object]” |
null | 返回"null" |
undefined | 返回"undefined" |
显式转换
- toString(), 注意,不可以转
null
和underfined
,但NaN可以转:NaN.toString()
结果是'NaN'
- String(),都可以转
隐式转换
- 用“字符串(通常是空字符串)
+
”拼接,如:12+''
会转成字符串'12'
其他类型转成Boolean
数据类型 | 转为true的值 | 转为false的值 |
---|---|---|
Number | 任何非零数字(包括无穷大) | 0 、-0 和NaN |
String | 任何非空字符 | “” (空字符串) |
Object | 任何对象 | 无 |
null | 无 | null |
undefined | 无 | undefined |
转为false的:
""
空字符串0 || -0 || 0n
各种0null
undefined
NaN
其余都是true,注意
'0'
,'<一到多个空格>'
也是true哦,它们不是空字符串。
显式转换
Boolean()
:都可以转,包括null
,undefined
,NaN
(这三个都是false)!!
:经过两次取反可以将一个值转成布尔类型。如!!null
的结果是false
隐式转换
在if
,while
,逻辑运算符或逻辑判断时会自动转成Boolean。
显式转换
to string的原始类型转换
- toString(), 注意,不可以转
null
和underfined
,但NaN可以转:NaN.toString()
结果是'NaN'
1 | console.log(1..toString()) // '1' |
- String(),都可以转
1 | console.log(String(1)) // '1' |
to number的原始类型转换
原始类型 | 类型转换规则 |
---|---|
string | ("" 空字符串, "<空白符>" , "<多个空白符>" ) -> 0 ; "number" 纯数字字符串 -> number ; "<16进制数>" -> <16 进制数> -> 十进制数; 其他字符串 → NaN |
number | 无需转换(NaN ->NaN ) |
boolean | true -> 1 , false -> 0 |
null | 0 |
undefined | NaN |
Number()
:可以把任意值转换成数值,如果要转换的字符串中有一个不是数值的字符,返回 NaNparseInt()
/parseFloat()
:parseFloat
会解析第一个.
遇到第二个.
或者非数字结束,如果解析的内容里只有整数,解析成整数
to boolean的原始类型转换
转换为false
""
0
||-0
||0n
null
undefined
NaN
转换为
true
- 非空字符串
- 非
0
和非NaN
的数值
对象 to {string, number, boolean}的 To Primitive类型转换
在ES5版本, 当我们显式或者隐式地将对象转换为原始值(primitive value)的时候, 通常是默认调用对象自身或者原型链上的toString()
或者valueOf()
方法, 将其转换原始值. 或者可以自定义覆盖对象的这两个方法来控制对象的to primitive行为, 不过不建议这样做.
ES6 为开发者提供了官方的
[Symbol.toPrimitve]
接口来自定义对象的to primitive操作, 当对象发生显式或者隐式类型转换操作的时候, 会自动调用其预先定义的[Symbol.toPrimitive]
方法, 同时会忽略对象自身的toString()
和valueOf()
方法.
toString()
和valueOf()
方法, 这两个方法可以自定义,也可以是原型方法.。一些JavaScript的内置对象会有自己的内置toString()
和valueOf()
方法, 位于其原型上, 汇总如下:
类型 | toString | valueOf |
---|---|---|
object | “[object <type>] “ |
指向自身 |
function | 函数的字符串形式 | 指向自身 |
array | “arr[0], arr[1], ... “ 或者 "" (数组为空) |
指向自身 |
date | 包含本地时间信息的字符串 | 从1970年 1 月 1 日开始至今的毫秒数 |
regexp | 正则表达式的字符串表示形式 | 指向自身 |
error | 错误名+错误信息: “<err>.name:<err>.message “ |
指向自身 |
总结:只有Date重写了自身的valueOf()方法,其余都是返回自身。除了Number
、Boolean
、String
、Array
、Date
、RegExp
、Function
这几种实例化对象之外,其他对象返回的都是该对象的类,都是继承的Object.prototype.toString
方法。
1 | let obj = new Object({}); |
对象 to boolean
一般情况下, 对象to boolean都是直接转换为 true , 而且不会调用对象的 [Symbol.toPrimitive]
, toString
, valueOf
这三个方法。
注意: document.all
对象在浏览器中转为布尔值的时候为false
, 这是一种极特殊情况,在vscode中使用这个对象的时候,会提示已经被弃用了,了解即可。
对象to string | number
ES5时的转换方式
在ES5的时候, 对象的类型转换是通过内置或者自定义的toString
, valueOf
方法进行to primitive类型转换的。
对象to string
调用对象的
toString()
方法:首先,尝试调用对象的toString()
方法。检查
toString()
的返回值:- 如果
toString()
方法返回一个原始值(如字符串、数字、布尔值等),则将这个原始值用作对象的字符串表示形式。 - 如果
toString()
方法返回一个对象,那么程序将进入下一步。
- 如果
调用对象的
valueOf()
方法:如果toString()
方法返回一个对象,则尝试调用对象的valueOf()
方法。检查
valueOf()
的返回值:- 如果
valueOf()
方法返回一个原始值,那么这个原始值将被用作对象的字符串表示形式。 - 如果
valueOf()
方法返回一个对象,那么JavaScript将会抛出一个错误,因为无法将对象转换为字符串。
- 如果
总结来说,对象到字符串的转换遵循以下顺序:
- 调用对象的
toString()
方法。 - 如果
toString()
返回原始值,使用该值。 - 如果
toString()
返回对象,调用对象的valueOf()
方法。 - 如果
valueOf()
返回原始值,使用该值。 - 如果
valueOf()
返回对象,抛出错误。
对象to number
顺序和对象to string相反。
调用对象的
valueOf()
方法:首先,尝试调用对象的valueOf()
方法。检查
valueOf()
的返回值:- 如果
valueOf()
方法返回一个原始值(如字符串、数字、布尔值等),则将这个原始值用作对象的数字表示形式。 - 如果
valueOf()
方法返回一个对象,那么程序将进入下一步。
- 如果
调用对象的
toString()
方法:如果valueOf()
方法返回一个对象,则尝试调用对象的toString()
方法。检查
toString()
的返回值:- 如果
toString()
方法返回一个原始值,那么这个原始值将被用作对象的数字表示形式。 - 如果
toString()
方法返回一个对象,那么JavaScript将会抛出一个错误,因为无法将对象转换为数字。
- 如果
总结来说,对象到数字的转换遵循以下顺序:
- 调用对象的
valueOf()
方法。 - 如果
valueOf()
返回原始值,使用该值进行原始值到数字的转换。 - 如果
valueOf()
返回对象,调用对象的toString()
方法。 - 如果
toString()
返回原始值,使用该值进行原始值到数字的转换。 - 如果
toString()
返回对象,抛出错误。
ES6的转换方式
在ES6, 开发者可以通过官方提供的
[Symbol.toPrimitive]
接口去定义对象 to primitive 的行为。 需要注意的是, 如果自定义了对象的[Symbol.toPrimitive]
的方法, 那么, 当对象发生 to primitive类型转换的时候, 那么只会调用[Symbol.toPrimitive]
方法, 而无视ES5中提供的toString()
,valueOf()
方法。
比如下面将对象显式转换为string
的代码中, 只有 [Symbol.toPrimitive]()
会被调用:如果返回结果为对象, 则直接报错(不会去调用toString
或者是valueOf
方法); 如果返回结果为原始值, 则将将该原始值进行to string
操作, 作为最终的对象 to primitive结果:
1 | const obj = { |
隐式转换
对于某些运算符, 当A
- 宽松相等运算符
==
,!=
- 关系运算符(
>
,<
,<=
,>=
) - 逻辑运算符(
&&
,||
,!
) if
,while
,for
,? :
+ (condition)中的条件表达式(condition)- 加性运算符
+
- 算数运算符(
-
,*
,/
,%
) - 一元
+
,-
操作
一元加号,减号
+
即强制转换为Number类型。直接参考显示类型转换中的to number-
与+
类似,不过会将结果取负号
1 | +"1" // 1 |
加法
在求值时,它首先将两个操作数强制转换为基本类型。然后,检查两个操作数的类型:
- 如果有一方是字符串,另一方则会被转换为字符串,并且它们连接起来。
- 如果双方都是
BigInt
,则执行BigInt
加法。如果一方是BigInt
而另一方不是,会抛出TypeError
。 - 否则,双方都会被转换为数字,执行数字加法。
1 | 1 + "1" // "11" |
减法
减法不涉及字符串拼接,故需要都转换为Number
类型。
- 如果有一个操作数为
string
,boolean
,null
,undefined
中一种,则在后台调用Number()
将其转化为数值,再进行减法运算。 - 如果有一个操作数是对象 ,则调用对象的
NaN
,减法结果是NaN
。如果对象没有valueOf()
方法,调用它的toString()
方法,并将得到的字符串转化为数值。 (参考上文一元加号的运算法则)
1 | []-1 //-1 过程[]->''->0 即相当于 0-1 |
相等判断
==
相等
注意:特例NaN不等于自身
ECMAScript® 2023 Language Specification (ecma-international.org) 对于==
的规定:
若
Type(x)
与Type(y)
相同,则进行严格相等 (===
) 的比较。若
x
为null
且y
为undefined
,返回true
。若
x
为undefined
且y
为null
,返回true
。这里只需记住
document.all
与null
或undefined
,返回true
即可。若
Type(x)
为Number
且Type(y)
为String
,返回比较x == ToNumber(y)
的结果。若
Type(x)
为String
且Type(y)
为Number
,返回比较ToNumber(x) == y
的结果。若
Type(x)
为String
且Type(y)
为BigInt
:- 使用与
BigInt()
构造函数相同的算法将字符串转换为BigInt
。如果转换失败,返回false
,然后再进行比较。
- 使用与
和第7步类似(顺序交换即可):
- 若
Type(x)
为BigInt
且Type(y)
为String
,尝试将字符串y
转换为BigInt
,转换成功则比较两个BigInt
值。
- 若
若
Type(x)
为Boolean
,返回比较ToNumber(x) == y
的结果。若
Type(y)
为Boolean
,返回比较x == ToNumber(y)
的结果。若
Type(x)
为String
或Number
,且Type(y)
为Object
,返回比较x == ToPrimitive(y)
的结果。若
Type(x)
为Object
且Type(y)
为String
或Number
,返回比较ToPrimitive(x) == y
的结果。BigInt
与Number
类型比较:
a. 如果Number
类型的值为无穷值(Infinity
或-Infinity
),直接返回false
。
b. 如果两个值转换为数学上的值相同,返回true
。(数学上-0
与+0
均为0
)返回
false
。
===
全等
类型必须相同。
NaN不等于自身。
1 | null == undefined //true |
Object.is()
全等的缺陷:NaN === NaN //false
,-0 === +0 //true
1 | Object.is(NaN,NaN) //true |
Object.is()
与===
的区别:
- 处理 0 的特殊情况:如果
a
或b
严格等于0
,函数会检查1/a
是否等于1/b
。这是因为1/0
的结果是Infinity
,而1/-0
的结果是-Infinity
,这可以区分+0
和-0
。 - 处理 NaN 的特殊情况:如果
a
和b
都不等于它们自己(这是 NaN 的一个特性),则函数返回true
,表示两个值都是NaN
。 - 一般比较:如果上述两种特殊情况都不满足,函数使用
===
运算符进行比较,这是一种严格相等的比较。
1 | //实现Object.is() |
面试题
题目一
console.log([] == !{})
结果是?
!
运算符优先级高于==
,故先进行!
运算。!{}
运算结果为false
,结果变成[] == false
比较。- 根据4.1中的规则(引用类型与基本类型比较),等式左边
ToPrimitive([]) == ''
。按照上面规则进行原始值转换,[]
会先调用valueOf
函数,返回this
。不是原始值,继续调用toString
方法,x = [].toString() = ''
。 故结果为'' == 0
比较。 - 等式左边为
string
,右边为number
,等式左边x = ToNumber('') = 0
。所以结果变为:0 == 0
,打印true
,比较结束。
题目二
let result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;
result的结果是?
1 | //拆解 |
题目三
1 | let arr= [] |
知识点1:在js中,数组底层也是通过键值对存储的。当我们以索引形式为数组添加元素时,会发生以下过程。自动将传入的索引值转换为CanonicalNumericIndexString (规范数字索引字符串)
- 将索引转换为数值类型
- 再将索引转换为字符串类型
- 最终添加至数组
故
arr[0]
与arr["0"]
事实上等价知识点2:数组也是一个对象,可以添加属性。数组的索引范围为 $[0,2^{32}-1)$ ,而向对象中添加属性时,若不在此范围,就相当于向其身上添加属性。
在JavaScript中,数组的索引通常是数字,并且它们被用来按顺序存储元素。然而,JavaScript对象(包括数组)的键(即索引)可以是字符串或任何值,只要它能够被转换为字符串(除了
undefined
),这就意味着你可以使用非数字的键来设置数组的元素。
let arr = [];
这行代码创建了一个空数组arr
。arr[0] = 1;
这里将数字1
赋值给数组的第一个元素,使用数字0
作为索引。arr["0"] = 2;
这行代码将数字2
赋值给数组的第二个元素,使用字符串"0"
作为索引。在JavaScript中,字符串索引和数字索引是等价的,所以这实际上是创建了数组的第二个元素。arr["1"] = 3;
这里将数字3
赋值给数组的第三个元素,使用字符串"1"
作为索引。同样,字符串索引会被转换为数字索引。arr[-1] = 4;
这行代码将数字4
赋值给数组的一个特殊索引-1
。在大多数情况下,数组的索引不能为负数,但在这里,由于使用了方括号语法,JavaScript 允许将任何值(包括负数)作为索引。这将创建一个索引为-1
的属性,但它不是数组的常规索引,并且不会影响数组的length
属性。arr[{}] = 5;
这行代码尝试将数字5
赋值给一个对象作为索引。在JavaScript中,对象作为索引时会被转换为字符串[object Object]
,因此这将创建一个非数字索引的属性,其值为5
。- 最终,当你打印
arr
时:console.log(arr);
你将得到一个包含多个元素和属性的数组对象,其中一些是数组的索引,一些是对象的属性。数组的 length
属性将反映其索引数组的长度,即 [0, 3]
。这是因为 arr["0"]
和 arr[0]
引用相同的位置,而 arr["1"]
会创建一个新的索引为 1
的元素,使得数组长度变为 2
。arr[-1]
和 arr[{}]
不会影响数组的 length
,但它们会被添加为对象的属性。
请注意,数组的 length
是 2
,因为只有索引 0
和 1
被赋值,而 -1
和 {}
创建的是非标准索引,它们不会出现在数组的索引序列中,但作为对象属性存在。
类型判断
typeof
typeof是一元运算符,可以用来返回一个变量或表达式的数据类型。它可以准确地判断除null
之外的原始类型,并且可以判断function
类型。
但是没法用来区分引用数据类型的,因为所有的引用数据类型都会返回”object” 。
注意: null
、 NaN
、 document.all
的判断
1 | console.log(typeof "hello"); // 输出: "string" |
instanceOf
语法:obj instanceof Type
判断 obj
是不是 Type
类的实例 ,只可用来判断引用数据, 右操作数必须是函数或者 class
用于判断一个对象是否是某个构造函数(或其子类)的实例,只能判断引用类型。它通过原型链的查找来判断对象的类型。
手写instanceOf
:
1 | function myInstanceof(Fn, obj) { |
1 | let arr = [1, 2, 3]; |
Object.prototype.toString.call()
利用函数动态 this
的特性。
Object.prototype.toString()
方法可以返回一个对象的字符串表示形式。它会读取数据结构内部属性来读取数据的类型 class。通过调用call()
方法,可以改变toString()
中this
的指向,使其返回 "[object 类型]"
的形式,其中 类型可以是任意数据类型。
1 | Object.prototype.toString.call(123); // "[object Number]" |
constructor
constructor
作用和 instanceof
非常相似。但 constructor
检测 Object 与 instanceof 不一样,还可以处理 基本数据类型 的检测。
constructor 指向创建 该实例对象的构造函数
注意: null
和 undefined
没有 constructor
,以及 constructor
可以被改写,不太可靠
1 | const arr = [1, 2, 3]; |
isxxx
isPrototypeof()
用于判断一个对象是否为另一个对象的原型。
prototypeObj.isPrototypeOf(object)
,如果 prototypeObj
是 object
的原型对象,isPrototypeOf
方法返回 true
,否则返回 false
功能基本等同于 instanceof
注意:isPrototypeOf
方法只能用于判断对象类型,不能用于判断基本数据类型。如果 prototypeObj
不是一个对象,isPrototypeOf
方法会抛出 TypeError
异常。
1 | const obj = { name: "云牧", age: 18 } |
getPrototypeOf()
getPrototypeOf()
返回一个对象的原型,只能用于判断对象类型。
1 | const obj = { name: "云牧", age: 18 } |
Array.isArray()
Array.isArray()
方法用于判断一个对象是否为数组,且只能判断数组类型。
1 | console.log(Array.isArray([1, 2, 3])); // 输出: true |
Number.isNaN()
Number.isNaN()
方法用于判断是否为NaN。
1 | console.log(Number.isNaN(NaN)) // true |
加餐:ES6中NaN的加强
NaN 和 Number.NaN 特点
typeof
判断类型是数字number
- 自己不等于自己
isNaN
如果非数字,隐式转换传入结果如果是 NaN,就返回 true,反之返回 false
1 | console.log(isNaN(NaN)) // true |
Number.isNaN
判断一个值是否是数字,并且值是否等于 NaN
1 | console.log(Number.isNaN(NaN)) // true |
indexOf 和 includes
indexOf
不可查找 NaN, includes
则可以查找 NaN
1 | const arr = [NaN] |
Number.isFinite()
Number.isFinite()
可以判断一个值是否为有限数
1 | console.log(Number.isFinite(123)); // true |
鸭子类型检测
检查自身属性的类型或者执行结果的类型
通常作为候选方案
1 | const isObject = value => |
1 | function kindof(obj) { |
Symbol.toStringTag
- 原理:
Object.prototype.toString
会读取该值 - 适用场景:需自定义类型
- 注意事项:兼容性
1 | class MyArray { |
等比较
原理:与某个固定值进行比较
适用场景:undefined
、 window
、 document
、 null
等
1 | _.isNull = function(obj) { |
void 0
始终返回 undefined
,void
后面接任意值都是返回 undefined
, 这是为了兼容 IE,因为在 IE 中 undefined
值可以被改写