JavaScript 数据类型
类型简介
JS 数据类型整体分为两大类:基本数据类型、引用数据类型
- 基本数据类型:
- number 数字型
- string 字符串型
- boolean 布尔型
- symbol 标志型(不常用)
- undefined 未定义型
- null 空类型
- 引用数据类型:
- Object
- Function
- Array
引用数据类型还有:
Date
、RegExp
、Map
、Set
、Error
、Promise
等
基本数据类型与引用数据类型在内存中的存储不同(操作系统层面):
- 栈: 访问速度快,基本数据类型存放到栈
- 堆: 存储容量大,引用数据类型存放到堆
数字型
数字是最基本的数据类型,JavaScript 中的数字型不区分整型和浮点型。JS 是弱数据类型,变量类型只有在赋值之后,才能确认
- 数学运算符也叫算术运算符,主要包括加、减、乘、除、取余(求模)
+
:求和、-
:求差、*
:求积、/
:求商、%
:取模(取余数)
数字包装类型
number 有专门的构造函数Number
,称为包装类型,除了字面量初始化数字型变量,也可以使用Number()
构造函数
let num1 = 99
let num2 = new Number(88) // 这里是对象
一般使用前者较多,num1
照样可以调用Number
构造函数原型链上的方法,这个过程称为自动装箱,如:
const num = 99
const result = num.toFixed(2)
console.log(result) // "99.00"
在这个示例中,尽管num
是一个原始的数字类型,你仍然可以调用toFixed()
方法。这是因为:
- JavaScript 引擎为数字
99
创建了一个Number
包装对象 - 在这个
Number
对象上调用了toFixed(2)
方法 toFixed()
方法返回了一个字符串"99.00"
,该字符串被赋值给了变量result
Number
包装对象被销毁
包装类型是临时的,它们只在需要的时候被创建,一旦方法调用结束就会被销毁
NaN
NaN
代表非数字(Not-a-Number)是一个特殊的值,用于表示一个操作数或操作数的结果不是合法数字。NaN
是粘性的,任何对 NaN
的操作都会返回 NaN
以下是一些可能导致NaN
的情况:
- 无效的算术操作: 当对一个或多个非数字值进行算术运算时,结果会是
NaN
"hello" + 10 // NaN
Number({}) * 10 // NaN
- 使用
NaN
作为操作数: 任何涉及NaN
的操作都会产生NaN
10 * NaN // NaN
- 错误的类型转换: 尝试将非数字字符串转换为数字,如果字符串格式不正确,会得到
NaN
Number("123abc") // NaN
- 科学计数法表示的过大数值: 当数值过大,超出了 JS 能表示的数字范围时,可能会返回
NaN
Number("1e500") // NaN
由于NaN
与任何值都不相等(包括自己)不能使用==
或===
来检测NaN
,正确的方式是使用全局的isNaN()
函数
isNaN("hello") // true
isNaN(123) // false
isNaN(0 / 0) // true
字符串型
通过单引号 (' ')、双引号 (" ") 或反引号 ( `` ) 包裹的数据都叫字符串,JavaScript 中的单引号和双引号没有本质上的区别,推荐使用单引号
特点如下:
- 单引号与双引号可以互相嵌套,但是不能自已嵌套自已,必要时可以使用转义符
\
来输出单引号或双引号 - 通过
+
运算符,可以实现字符串的拼接 - 字符串的值是不可变的,这意味着一旦字符串被创建就不能被改变,不过字符串是可以整体赋新值
- 字符串取单字符可以使用
charAt()
和[]
模板字符串
反引号 (``) 包裹,又称为模板字符串,用于拼接字符串和变量,内容拼接变量时用${}
包住变量
- 模板字符串可以直接包含换行符
const text = `
Hello,
How are you?
Hope you are doing great.
`
- 表达式可以嵌入到模板字符串中,表达式会在运行的过程中求值
const a = 5
const b = 10
const result = `The sum of ${a} and ${b} is ${a + b}.`
console.log(result) // The sum of 5 and 10 is 15.
标签函数
标签函数是一个特殊的函数调用形式,主要用于处理模板字符串。当在模板字符串之前放置一个函数名时,这个函数就会作为标签函数被调用
// 第一个参数是数组,包含了模板字符串中所有静态的部分(即不包含插值表达式的部分)
// 从第二个参数开始,是模板字符串中所有插值表达式求值后的值
function capitalize(strings, ...values) {
return strings.map((str, i) =>
str + (i < values.length ? ` ${values[i].charAt(0).toUpperCase() + values[i].slice(1)}` : '')
).join('')
}
const world = 'world'
const greeting = capitalize`Hello, ${world}`
console.log(greeting) // Hello, World
- 模板字符串的
.raw
属性提供了模板字符串的原始形式,它将转义序列视为普通文本
const html = String.raw`<div>\n <p>Hello, World!</p>\n</div>`
console.log(html) // <div>\n <p>Hello, World!</p>\n</div>
字符串包装类型
string
类型也有其对应的包装类型String
,这也是为什么string
类型能使用字符串方法的原因,同样涉及到自动装箱,与number
类型类似
其属性及方法主要有:
length
:获取字符串的长度split('分隔符')
:将字符串拆分成数组substring(需要截取的第一个字符的索引[,结束的索引号])
:字符串截取startsWith(检测字符串[,检测位置索引号])
:检测是否以某字符开头endsWith(搜索的字符串[,检测终止位置索引号])
:检测是否以某字符结尾includes(搜索的字符串[,检测位置索引号])
:判断一个字符串是否包含在另一个字符串中trim()
:字符串的两端清除空格,返回一个新的字符串,而不修改原始字符串toUpperCase()
用于将字母转换成大写toLowerCase()
:用于将就转换成小写indexOf()
:检测是否包含某字符replace(regexp)
:替换字符串,支持正则匹配match(regexp)
:查找字符串,支持正则匹配
symbol
Symbol
的值是唯一的,独一无二的,主要用于防止属性名冲突,如向第三方对象中添加属性
const symbol1 = Symbol()
const symbol2 = Symbol(42)
const symbol3 = Symbol('foo')
console.log(typeof symbol1) // "symbol"
console.log(symbol2 === 42) // false
console.log(symbol3.toString()) // "Symbol(foo)"
console.log(Symbol('foo') === Symbol('foo')) // false
- 可以使用
description
获取传入的描述参数
const symbol = Symbol('foo')
console.log(symbol.description) // "foo"
for、keyFor
Symbol.for(key)
:接受一个字符串作为参数,并搜索一个具有该字符串描述的全局 Symbol
注册表
- 如果找到了,就返回这个
Symbol
- 如果没有找到,就创建一个新的
Symbol
并将其添加到全局注册表中,然后返回这个新创建的Symbol
这意味着,当你使用相同的字符串作为参数调用 Symbol.for
时,你总是会得到相同的 Symbol
对象
let mySymbol = Symbol.for('mySymbol')
let anotherMySymbol = Symbol.for('mySymbol')
console.log(mySymbol === anotherMySymbol) // true
Symbol.keyFor(sym)
:接受一个 Symbol
对象作为参数,并返回该 Symbol
对象的全局描述字符串
- 如果提供的不是
Symbol
对象,或者该Symbol
对象没有在全局注册表中创建,则返回undefined
这个方法通常用于反向查找 Symbol
对应的字符串键
let mySymbol = Symbol.for('mySymbol')
let anotherMySymbol = Symbol.for('mySymbol')
console.log(Symbol.keyFor(mySymbol)) // 'mySymbol'
console.log(Symbol.keyFor(anotherMySymbol)) // 'mySymbol'
遍历Symbol
Symbol 不能使用 for-in
、for-of
遍历,可以使用 Object.getOwnPropertySymbols(obj)
或Reflect.ownKeys(obj)
获取所有Symbol
属性。此外,这也意味着,如果对象属性不想被遍历,可以使用Symbol
来保护
const myObject = {
// 普通属性
visibleProperty: 'This is visible',
// 使用 Symbol 创建的属性
[Symbol('hiddenProperty1')]: 'This is hidden1',
[Symbol('hiddenProperty2')]: 'This is hidden2'
}
for (const key in myObject) {
console.log(key + ' = ' + myObject[key])
}
// 只输出: visibleProperty = This is visible
for (const value of Object.values(myObject)) {
console.log(value)
}
// 只输出: This is visible
const symbols = Object.getOwnPropertySymbols(myObject)
console.log(symbols) // [ Symbol(hiddenProperty1), Symbol(hiddenProperty2) ]
// 打印 Symbol 属性的值
symbols.forEach(sym => {
console.log(sym.description + ' = ' + myObject[sym])
})
// 输出: hiddenProperty1 = This is hidden1
// 输出: hiddenProperty2 = This is hidden2
布尔类型
布尔类型只有两个固定的值:true
、false
。与string、number
类似,boolean
也有包装类型Boolean
未定义与空类型
undefined
和null
都是表示没有值的特殊值,但它们的含义和用途不同:
undefined:
- 当声明一个变量但未赋值则默认值为
undefined
- 函数没有返回值时,其返回值也是
undefined
typeof undefined
的结果是"undefined"
null:
null
表示赋予变量空值,用来表示一个变量希望被赋予一个空或者不存在的值null
常用于初始化一个变量,表示它在逻辑上是一个空或者无效的引用typeof null
的结果是"object"
(历史遗留问题,不建议依赖这个特性)
不同点:
undefined == null
(true)、undefined === null
(false)undefined
强转数字类型为NaN
,null
强制数字类型为0
undefined
通常用于表示变量已声明但尚未初始化null
通常用于表示变量应该引用一个不存在的或者为空的对象
数据类型检测
通过typeof
关键字检测数据类型,返回表示数据类型的字符串
typeof用法格式 | 返回的字符串的值 | 含义 |
---|---|---|
typeof x | undefined | 该值未定义 |
typeof(x) | boolean | 该值为布尔类型 |
string | 该值为字符串类型 | |
number | 该值为数值类型 | |
object | 该值为对象(引用数据类型)或者null | |
function | 该值为函数类型 |
运算符与表达式
表达式
表达式指的是由运算符组成的式子,JS 引擎会将其计算出一个结果
//表达式一定会有运算结果
console.log(1 + 2)
let num = 1 + 2
运算符
算数运算与赋值运算符
算数运算符 | 含义 | 赋值运算符 | 含义 |
---|---|---|---|
+ | 求和 | = | 直接赋值 |
- | 求差 | += | a = a + b |
* | 求积 | *= | a = a * b |
/ | 求商 | /= | a = a / b |
% | 取模(取余数) | %= | a = a % b |
连接符(+)
用于字符串的拼接,+
号只要遇到字符串,就是连接符(数字相加,字符相连)
document.write('Hello' + 'World') // HelloWorld
let first = 'Hello'
let second = 'World'
document.write(first + second) // HelloWorld
一元/三元运算符
一元运算符:++
(自增)、-
(自减)
- 前置
let num = 1;
console.log(++num + 1 + ++num);//6
console.log(num);//3
- 后置
let num = 1;
console.log(num++ + 1 + num++);//4
console.log(num);//3
三元运算符:条件 ? 满足条件执行的代码 : 不满足条件执行的代码
// 数字补零
num = num < 10 ? '0' + num : num
比较运算符
比较运算符 | 含义 |
---|---|
> | 左边是否大于右边 |
< | 左边是否小于右边 |
>= | 左边是否大于或等于右边 |
<= | 左边是否小于或等于右边 |
== | 左右两边值是否相等(强转) |
=== | 左右两边是否类型和值都相等(无强转) |
!== | 左右两边是否不全等 |
逻辑运算符
符号 | 名称 | 特点 |
---|---|---|
&& | 逻辑与 | 符号两边都为true 结果才为true |
|| | 逻辑或 | 符号两边有一个为true 就 true |
! | 逻辑非 | 取反 |
逻辑与、逻辑或可以作为短路运算符来使用:
- 逻辑中断:存在于逻辑运算符
&&
和||
中,左边如果满足一定条件会中断代码执行,也称为逻辑短路 - 当逻辑表达式结果不是布尔值时,会隐式转换为布尔值,但是最后运算结果,返回的不是布尔值,而是进行隐式转换之前的结果
逻辑运算符使用规则如下:
false && anything
:&&
左边结果为false
则中断, 否则返回右边代码的值true || anything
:||
左边结果为true
则中断,否则返回右边代码的值&&
常常用来代替if(flag){}
代码块
console.log(1 || 2) // 1,或运算,左边为true,逻辑中断,不用计算右边,直接输出或运算符左边的原结果,即1
console.log(1 || 0) // 1,或运算,左边为true,逻辑中断,不用计算右边,直接输出或运算符左边的原结果,即1
console.log(0 || 1) // 1,或运算,左边为false,右边计算为true,输出或运算符右边的原结果,即1
console.log(0 || 0) // 0,或运算,左边为false,右边计算为false,输出或运算符右边的原结果,即0
console.log(1 && 2) // 2,与运算,左边为true,右边计算为true,输出与运算符右边的原结果,即2
console.log(1 && 0) // 0,与运算,左边为true,右边计算为false,输出与运算符右边的原结果,即0
console.log(0 && 1) // 0,与运算,左边为false,逻辑中断,不用计算右边,直接输出与运算符左边的原结果,即0
//实际例子举例
let age = 10
console.log(true && age++) // 10,++在右边,不参与运算
console.log(true && ++age) // 12,++在左边,参与运算
console.log(age) // 12
运算符优先级
优先级 | 运算符 | 顺序 |
---|---|---|
1 | 小括号 | () |
2 | 一元运算符 | ++ 、-- 、! |
3 | 算数运算符 | 先* / % 后+ - |
4 | 关系运算符 | > 、 >= 、< 、<= |
5 | 相等运算符 | == 、!= 、=== 、!== |
6 | 逻辑运算符 | 先&& 后` |
7 | 赋值运算符 | = |
8 | 逗号运算符 | , |
类型转换
JavaScript 是弱数据类型,只有变量赋值时,才确定为何种数据类型
显示转换
转为数字型
方法 | 含义 |
---|---|
Number(数据) | 返回数字类型。字符串有非数字则为NaN 。null 转换为0 , undefined 为NaN |
parseInt(数据) | 只保留整数。如果为数字开头字符串则保留整数数字,非数字开头返回NaN |
parseFloat(数据) | 可以保留小数。如果数字开头的字符串,可以保留小数 |
parseInt(string, radix)
详解:
参数 | 说明 |
---|---|
string | 要解析的字符串 |
radix | 1) 可选。表示要解析的数字的基数。该值介于2 ~ 36 之间2) 如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以0x 或0X 开头,将以16 为基数;以1 ~ 9 的数字开头,parseInt() 将把它解析为十进制的整数3) 如果该参数 < 2 or> 36 ,则 parseInt() 返回NaN |
示例如下:
parseInt('689090',8) //6 只解析小于8的
parseInt('8f89090',8) //NaN 8不在8进制范围内
parseInt('8000',0) //8000 以10为基数解析
//经典案例
["1", "2", "3"].map(parseInt) // [1, NaN, NaN]
//其实就是["1", "2", "3"].map((item, index)=>{return parseInt(item, index)})
转为字符型
方法 | 含义 |
---|---|
String(数据) | 返回字符串类型 |
toString(进制数) | 可以有进制转换,返回字符串类型 |
转为布尔型
Boolean(数据)
:返回true
或者false
空字符、0、-0、undefined、null、false、NaN
转换为布尔值后为false
,其余为true
隐式转换
某些运算符被执行时,系统内部自动将数据类型进行转换,这种转换称为隐式转换,如:
+
号作为运算符时,两边只要有一个是字符串,都会把另外一个转成字符串- 除了
+
以外的算术运算符,如- * /
等都会把数据转成数字类型 - 把
+
号作为正号解析可以转换成数字型 - 逻辑非
!
转换为布尔类型