# 一、基础认知

提示

  更多,我们可以查看 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_VALUENumber.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 类型

  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 用于检测数据类型的操作符,它返回的是一个字符串

  • 原始类型:numberbooleanstring
  • 广义对象:objectfunction
  • 其他: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' // 注意结果为字符串形式

// 拓展:isArray
Array.isArray(arr) // true

# 2、instanceof

  instanceof 用于检测某些对象实例 / 原型对象是否是某个构造函数产生的,它返回的是一个布尔值

object instanceof constructor

function P() {}
var foo = new P()

foo instanceof P // true

# 3、in

prop in object

  in 用于检测指定的属性在指定的 实例对象(Object)或其 原型链(Prototype)中,它返回的是一个布尔值

// 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:

  • 原始类型:0false""
  • 其他类型: undefinednull
  • 特殊值: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 ]时皆为真值:

  • 原始类型:0false""
  • 其他类型: undefinednull
  • 特殊值: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) 使用是非常类似的。

  • 运算符?? :左侧为 nullundefined时,使用右侧值
  • 运算符|| :左侧为 nullundefined0false""NaN时,使用右侧值

简单一句话:空值合并运算符 ?? 不会过滤掉(接受) 0false""NaN

const state = {
  token: localStorage.getItem('token') ?? ''
}
更新于 : 7/8/2024, 10:21:14 AM