# 一、基础认知
提示
更多,我们可以查看 MDN:内置对象 (opens new window)
# 1、数据类型
JavaScript 的数据类型,共有八种。
(ES6 又新增了第两个 Symbol 类型、BigInt 类型)
原始类型(primitive type) | 数值(number) | 整数和小数 |
布尔值(boolean) | 真假 | |
字符串(string) | 文本 | |
合成类型(complex type) | 广义对象(object) | 各种值组成的集合(初始化值可统一设置为 null) |
undefined | 表示“未定义”或不存在 | |
null | 表示空值 |
# 2、引用数据类型
对象是最复杂的数据类型,广义上的对象可以是:
我们常说的 狭义的对象 也就是 普通对象(object)
- 普通对象(object)
- 数组对象(array)
- 函数对象(function)
- 日期对象、DOM 对象、全局对象、自定义对象
- ……
对象 与 引用数据类型
换个角度,对象(object)有可以叫做引用数据类型:
- 传参方式不同,基本数据类型是值传递,而引用数据类型是地址传递。
- 储存方式不同,基本数据类型是栈存储,而引用数据类型是堆存储。
# 3、原始数据类型
- 数字
JavaScript 内部,所有数字都是以 64 位浮点数形式储存(数值范围为 21024 到 2-1023,或者说范围为 Number.MAX_VALUE
到 Number.MIN_VALUE
),其底层根本没有整数
Math.pow(2, 1024) # Infinity
Math.pow(2, -1075) # 0
1 === 1.0 # true
-0 === +0 # true
拓展:toFixed 方法
将数字四舍五入为指定的小数位数,并返回字符串
const num = 2.35
num.toFixed(1) // 返回 '2.4'
- 字符串
# 1、字符集
'\251' # "©" 000-377
'\xA9' # "©" 00到FF
'\u00A9' # "©" 0000到FFFF
# 2、换行
var longString = 'Long \
long \
long \
string';
拓展:字符串 与 base64
Base64 简介:
将任意值转成 0 ~ 9、A ~ Z、a-z、+和/这 64 个字符组成的可打印字符。
ASCII 码 0 到 31 的符号无法打印、文本格式传递二进制数据等这些都可以有它解决
Base64 应用:
比如:将图片转换为 base64 数据:具体实现可以查看FormData (opens new window)
# 4、Symbol 和 BigInt
Symbol 类型是 ES6 新增的一种数据类型,可以用来创建独一无二的标识符,常用于防止产生
const foo = Symbol('feature')
const bar = Symbol('feature')
console.log(foo == bar) // false
- 防止 属性名/常量名 冲突
应用示例
const VIDEO = Symbol('play video')
const AUDIO = Symbol('play audio')
const IMAGE = Symbol('paly image')
function play(type) {
switch (type) {
case VIDEO:
console.log('播放视频')
break
case AUDIO:
console.log('播放音频')
break
case IMAGE:
console.log('播放图片')
break
}
}
play(AUDIO)
// 对象属性变量
let obj = {
name: '张三',
age: 20,
[Symbol('feature')]: '成熟稳重',
[Symbol('feature')]: '富贵家庭'
}
console.log(obj.age) // 20
console.log(obj[foo]) // 'zhangsna'
// 定义常量 ✨
const MY_CONSTANT = Symbol('unique_constant')
默认情况下 Symbol 是非枚举数据,可以通过 getOwnPropertySymbols() 来获取 Symbol 数据。
提示
Symbol 是一个函数,可以防止产生属性污染(想想 vue3 中的 data 选项的定义),其内部可以传一个字符串用于描述该函数的作用。
其防污染的第一步就是:不可迭代、不可枚举,并且 JavaScript 中如果对象内置了 Symbol.iterator 则表明该对象可迭代
- 打印隐藏的属性值
let obj = {
name: '李华',
age: 20,
[Symbol('level')]: '优秀',
[Symbol('level')]: '98分'
}
// 常规打印
console.log(Object.keys(obj))
console.log(obj[Symbol.iterator]) // undefined
const keyArr1 = Object.getOwnPropertyNames(obj) // 常规属性数组
for (let key of keyArr1) {
console.log(obj[key])
}
const keyArr2 = Object.getOwnPropertySymbols(obj) // Symbol属性数组
for (let key of keyArr2) {
console.log(obj[key])
}
# 二、类型检测
# 判断数组和对象
// 1、typeof ❌
typeof [] === 'object' // true
typeof {} === 'object' // true
// 2、Object.prototype.toString.call() ✅
// toString可以获取对象的类型信息字符串
Object.prototype.toString.call([]) === '[object Array]' // true
Object.prototype.toString.call({}) === '[object Object]' // true
// 3、instanceof
[] instanceof Array // true
{} instanceof Object // true
// 4、Array.isArray()
// 该方法为新增的ES6方法
Array.isArray([]) // true
Array.isArray({}) // false
# 1、typeof
typeof 用于检测数据类型的操作符,它返回的是一个字符串:
- 原始类型:
number
、boolean
、string
- 广义对象:
object
、function
- 其他:
undefined
其中,typeof null 返回 'object',这是一个历史遗留问题,目前已经无法修复;
还有,typeof NaN 返回 'number',是因为 NaN 被归类为 number 类型的特殊值;
// 注意事项:
const arr = [1, 2, 3]
const obj = { a: 1, b: 2 }
const userList = null
typeof arr / obj / null === 'object' // 注意结果为字符串形式
// ES6方法:isArray
Array.isArray(arr) // true
# 2、instanceof
简单的理解就是,某地实例对象是否是某个构造函数产生:
function P() {}
var foo = new P() // 实例对象
foo instanceof P // true
instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象
的原型链上:
object instanceof constructor 其中,object 是实例对象,constructor 是构造函数,返回 true 表示 object 是 constructor 的实例;
instanceof 解决了 typeOf 的一些问题,它可以深度的检测对象(数组、函数、对象)的类型:
console.log([] instanceof Array) // true
console.log(function () {} instanceof Function) // true
console.log({} instanceof Object) // true
# 3、in
prop in object
in 用于检测指定的属性在指定的对象或其原型链中,它返回的是一个布尔值:
// 1、属性
const car = { make: 'Honda', model: 'Accord', year: 1998 }
'make' in car // true
'toString' in car // true
// 2、索引
var trees = new Array('redwood', 'bay', 'cedar', 'oak', 'maple')
4 in trees // true
'length' in trees // true
# 三、类型转换
在开发中,有时需要我们手动的进行一些类型转换:
具体可参考 🍗:数据类型的转换 (opens new window)
# valueOf 方法
valueOf 方法在 JavaScript 中有一些特定的应用场景,尤其是当对象被用于进行算术运算时,因为 JavaScript 引擎需要将对象转换为原始值时,它会首先查找对象是否有 valueOf 方法。
valueOf() 可以获取/修改 Object、Date、String、Number 等数据类型的原始值
- 对象运算
new Date() 运算
- 示例
const date1 = new Date('2023-01-01')
const date2 = new Date('2023-01-10')
console.log(date1) // Sun Jan 01 2023 08:00:00 GMT+0800 (中国标准时间)
console.log(date2.valueOf()) // 1673308800000
// 使用了经过隐式转换valueOf()的值
console.log(date2 - date1) // 输出: 777600000 (相差9天的毫秒数)
- 内部处理逻辑
内部直接实现了 valueOf() 方法
// 等同于
Date.prototype.valueOf = function () {
return this.getTime() // 返回的时间戳
}
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
valueOf() {
return this.age
}
}
// 此时可以基于Person的age进行比较了
const zhangsan = new Person('zhangsan', 27)
const wangwu = new Person('wangwu', 25)
console.log(zhangsan < wangwu) // false
# 1、布尔值
- 双非 !!
var x = Boolean(value)
// 等效于
var x = !!value
- Boolean()
转换规则相对简单,除了以下几个以外,其他的值转换后都为 true:
- 原始类型:
0
、false
、""
- 其他类型:
undefined
、null
- 特殊值:
NaN
// 注意:
Boolean({}) // true
Boolean([]) // true
# 2、字符串
隐式转换
1 + '2' // '12'
'1' + 2 // '12'
'1' + '2' // '12'
1.1 + '2' // '1.12'
- toString
// 常见的
Object.prototype.toString()
Array.prototype.toString()
Number.prototype.toString()
- String()
String([1, 2, 3]) // "1,2,3"
# 3、数字 ✨
这里我们可以将 parseInt() / Number() 与 isNaN 方法进行配合使用
隐式转换
'4' - '3' // 1
'4' + '3' // '43'
typeof ('4' - '3') // 'number'
typeof ('4' + '3') // 'string'
- 一元加+
var x = Number(value)
// 等效于
var x = +value
- parseInt(string, radix)
// 如果遇到非数字字符,会停止解析,并返回已解析的部分
parseInt('120px') // 120 🤔
// 如果第一个字符不是数字字符或正负号,它将返回 NaN
parseInt('px') // NaN
// 如果parseInt的参数不是字符串,则会先转为字符串再转换
parseInt(1.22) // 1
// 如果字符串以0x或0X开头,parseInt会将其按照十六进制数解析
parseInt('0x10') // 16
- Number()
只要有一个字符无法转成数值,整个字符串就会被转为 NaN
Number(Object) 运算
- 示例
Number({ a: 1 }) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5
- 内部处理逻辑
先使用 toString() 转换为 字符串
// 等同于
if (typeof obj.valueOf() === 'object') {
Number(obj.toString())
} else {
Number(obj.valueOf())
}
// 原始类型值
Number('') // 0
Number(false) // 0
Number(null) // 0
Number(undefined) // NaN
Number('120px') // NaN
# 四、null 和 undefined
# 两者区别
null 和 undefined 都是 JavaScript 中的数据类型之一,但:
== 和 === 的区别
上述 null == undefined
结果为 true 的原因之一就是 ==
的比较时存在隐式转换的。
// 1、== 存在隐式转换
2 == '2' // ture
// 2、=== 不允许隐式转换
2 === '2' // false
// 在js中有两个表示空值的数据类型
null == undefined // true
!null // true
!undefined // true
# 1、null
null 是 1995 年 JavaScript 诞生的时候就有的,表示“无/没有(no object)”,和 C 语言一样可以自动转换为 0
,并且和 java 一样把它当成的是一个对象看待。
还有一个比较有意思的是:null 还是原型链的顶层,所有对象都继承与 Object 原型对象,而 Object 原型对象的原型是 null
5 + null // 5
Number(null) // 0
typeof null // 'object' 🚩
typeof null === 'object' // true
# 2、undefined
后来,Brendan Eich 觉得 null 最好还是不要被认定为对象,因为当时的 JavaScript 没有错误处理机制,如果 null 自动转为 0,很不容易发现错误。
因此,Brendan Eich 又设计了 undefined —— 表示"无/未定义(no value)"的原始值,转为数值时为 NaN。
NaN 和任何值都不相等,包括自身(可以通过 isNaN()函数解决)
产生 NaN 的场景
- 对一个数值执行了一些无意义的操作时
- 非数字类型的值进行数学运算时
- 使用 parseFloat() 或 parseInt() 方法解析字符串
Math.sqrt(-1)
'hello' / 2
parseFloat('hello')
5 + undefined // NaN
Number(undefined) // NaN
typeof undefined // undefined
typeof undefined === 'undefined' // true
# 3、使用/产生 👀
null 的应用
null 通常是我们手动设置,返回 null 是意料之中的情况
// 提前定义一个未知的对象 / 尚未加载的数据
let obj = null
const useStore = defineStore('storeId', {
state: () => {
return {
// 用于尚未加载的数据(对象)
user: null as UserInfo | null
// 用于初始化空列表(数组)
userList: [] as UserInfo[]
}
}
})
undefined 产生
首先,不同于 null 的是 undefined 是一种数据类型。
其次,产生 undefined 通常是代码存在些许问题,我们可以使用逻辑判断避免 undefined 对我们的开发造成影响
null 和 undefined 都是虚值,js 会把虚值强制转换为 false
以下是产生 undefined 的几种可能情况:
// 1、对象属性不存在
let obj = {
id: 1,
age: 22
}
console.log(obj.rank) // undefined
// 2、变量声明后没有赋值
let a
console.log(a) // undefined
// ========
// 3、函数调用没有传参
function f(x) {
console.log(x) // undefined
}
f()
// 4、函数的默认返回值
function f() {
//
}
console.log(f()) // undefined
# 五、条件赋值-表达式
在 JavaScript 中,我们可以通过 条件、&& 、|| 、 if 语句 、 布尔否定! 等来组成一个布尔表达式。
提示
&& 的优先级高于 ||,所以在使用 && 时
++1 是先自增再做其他运算,1++ 是先做其他运算再自增。
# 1、真假值 ✨
除了以下几个以外,其他做为条件语句的[ condition ]时皆为真值:
- 原始类型:
0
、false
、""
- 其他类型:
undefined
、null
- 特殊值:
NaN
// 注意:
Boolean({}) // true
Boolean([]) // true
# 2、&& 运算符
- 表达式
// 如果A 和 B同时为真是,才为真 --》 范围限制
if (A && B) {
//
}
- 逻辑运算
&&:成功扩张策略
// 如果A为真 就用 B ✨,不然就用A(A可以是某个属性等)
let data = A && B
1 && 2 // 结果:2
1 && 0 // 结果:0
0 && 1 // 结果:0
undefined && false // 结果:undefined
# -- 简易 if
应用: && 🌈 简易 if 判断(赋值)
描述:如果存在,就进行修饰
注意:{}、[]是真值
let user = {
name: 'Alice',
age: 25
}
// 方式1:&&运算符
let isAdmin = user && user.isAdmin
// 方式2:if判断
if (user) {
let isAdmin = user.isAdmin
}
# -- 可选链 ?
拓展:可选链操作符 👀?(代码块)
item.children && item.children.includes(class)
// 等效于
item.children?.includes(class)
this.dataShow = this.dataStore.filter(item => {
if(item.meta.title.includes(newValue)) return item
return item.children?.some(i => i.meta.title.includes(newValue))
})
# 3、|| 运算符
- 表达式
// 只要A 和 B有一个为真,就为真 --》 容错判断
if(A || B)
- 逻辑运算
||:成功防守策略
// 如果A为真 就用 A ✨,不然就用B(A可以是某个属性等)
let data = A || B
1 || 0 // 结果:1
1 || 2 // 结果:1
0 || 1 // 结果:1
undefined && false // 结果:false
# -- 设置默认值
应用:|| 🌈 设置默认值(赋值)
描述:如果没有,就设置默认值
const { data: res } = await axios({ method, url, data })
const index = (currentPage - 1) * pageSize
// 方式1:|| 运算符
result = {
data: res.data.slice(index, index + pageSize),
total: res.total || 0
}
// 方式2:三目运算符
result = {
data: res.data.slice(index, index + pageSize),
total: res.total ? res.total : 0 // 🤔
}
# -- 空值合并??
拓展:空值合并运算符 👀 ??
-运算符?? (opens new window) 和 运算符 || (opens new window) 使用是非常类似的。
- 运算符?? :左侧为
null
、undefined
时,使用右侧值 - 运算符|| :左侧为
null
、undefined
、0
、false
、""
、NaN
时,使用右侧值
简单一句话:空值合并运算符 ?? 不会过滤掉(接受)
0
、false
、""
、NaN
const state = {
token: localStorage.getItem('token') ?? ''
}
对象 →