前端基础学习之关于 JavaScript 的数据类型

7中数据类型

  • 空(Null)
  • 未定义(Undefined)
  • 数字(Number)
  • 字符串(String)
  • 布尔值(Boolean)
  • 符号(Symbol)
  • 对象(Object)

Undefined

只有一个值“undefined”。得到undefined的几种方式:

  • 引用已声明但未初始化的变量;
  • 引用未定义的对象属性;
  • 执行无返回值函数;
  • 执行 void 表达式;
  • 全局常量 window.undefined 或 undefined。

“void 0” 表达式常用来代表不执行任何操作,实例如下:

x>0 && x<5 ? fn() : void 0;

判断变量值是否为undefined常用的方式:

if(typeof x === 'undefined') {
  ...
}

注意,直接使用if(!x)和if(x===undefined)是不可取的,因为只要变量 x 的值为 undefined、空字符串、数值 0、null 时都会判断为真。直接使用三等号===,当x为undefined,那么是会抛出错误 “ReferenceError: x is not defined” 导致程序执行终止。

Null

表示空值,只有唯一值null。

Boolean

Boolean 数据类型只有两个值:true 和 false,分别代表真和假。
需要注意,我们在if语句中进行条件判断时,if语句实惠进行类型转换的,将判断条件的变量转换成 Boolean 数据类型,而 0、空字符串、null、undefined 在转换时都会返回 false。如下代码,传入0时,返回的是undefined,而不是我们期望的"星期日"。

function getWeek(week) {
  const dict = ['日', '一', '二', '三', '四', '五', '六'];
  if(week) return `星期${dict[week]}`;
}

Number

两个重要值

Number 是数值类型,有 2 个特殊数值得注意一下,即 NaN 和 Infinity。

  • NaN(Not a Number)通常在计算失败的时候会得到该值。要判断一个变量是否为 NaN,则可以通过 Number.isNaN 函数进行判断。
  • Infinity 是无穷大,加上负号 “-” 会变成无穷小,在某些场景下比较有用,比如通过数值来表示权重或者优先级,Infinity 可以表示最高优先级或最大权重。

进制转换

将其他进制的整数转换成十进制显示的时候可以使用 parseInt 函数,此函数有来两个参数,第一个为数字或字符串,第二个为进制数,默认为10,当进制数转换失败时会返回 NaN。

['0', '1', '2'].map(parseInt) // [0, NaN, NaN]

将十进制转换成其他进制时,可以通过 toString 函数来实现。

(10).toString(2) // "1010"

精度问题

在JavaScript中,我们进行浮点数运算时,往往会遇到精度问题。如下:

0.1 + 0.2 // 0.30000000000000004//并不是期望的0.3
Math.pow(Math.pow(5, 1/2), 2) // 5.000000000000001

原因

JavaScript进行计算时,JavaScript 引擎会先将十进制数转换为二进制,然后进行加法运算,再将所得结果转换为十进制。

解决方法

两种方式消除无限小数位。

  • 先转换成整数进行计算,然后再转换回小数,这种方式适合在小数位不是很多的时候。
  • 舍弃末尾的小数位。比如对上面的加法就可以先调用 toPrecision 截取 12 位,然后调用 parseFloat 函数转换回浮点数。parseFloat((0.1 + 0.2).toPrecision(12)) // 0.3

String

最常用的类型,相关的方法比较多,不一一介绍,可查看官方文档

千位分隔符的两种实现方式:

//从右往左遍历数值每一位,每隔 3 位添加分隔符。将字符串数据转化成引用类型数据,即用数组来实现。
function sep(n) {
  let [i, c] = n.toString().split(/(\.\d+)/)
  return i.split('').reverse().map((c, idx) => (idx+1) % 3 === 0 ? ',' + c: c).reverse().join('').replace(/^,/, '') + c
}
//通过引用类型,即用正则表达式对字符进行替换来实现
function sep2(n){
  let str = n.toString()
  str.indexOf('.') < 0 ? str+= '.' : void 0
  return str.replace(/(\d)(?=(\d{3})+\.)/g, '$1,').replace(/\.$/, '')
}

Symbol

定义

Symbol 是 ES6 中引入的新数据类型,它表示一个唯一的常量,通过 Symbol 函数来创建对应的数据类型,创建时可以添加变量描述,该变量描述在传入时会被强行转换成字符串进行存储。

一句话概括 Symbol 生成一个全局唯一的值。

常用于两个场景 避免常量值重复避免对象属性覆盖

补充知识:类型转换

类型转换定义

在处理不同数据类型运算或逻辑操作时会强制转换成同一数据类型。

通常强制转换的目标数据类型为 String、Number、Boolean 这三种。如下为转换关系:
转换关系

常见的触发类型转换的操作

  • 运算相关的操作符包括 +、-、+=、++、* 、/、%、<<、& 等。
  • 数据比较相关的操作符包括 >、<、== 、<=、>=、三等号===
  • 逻辑判断相关的操作符包括 &&、!、||、三目运算符。

Object

引用类型,即键值对的集合。

浅拷贝和深拷贝:

  • 由于引用类型在赋值时只传递指针,这种拷贝方式称为浅拷贝。
  • 而创建一个新的与之相同的引用类型数据的过程称之为深拷贝。

通过typeof查看数据类型的描述:

[undefined, null, true, '', 0, Symbol(), {}].map(it => typeof it)// ["undefined", "object", "boolean", "string", "number", "symbol", "object"]

发现 null 有些特殊,返回结果和 Object 类型一样都为"object",所以需要再次进行判断。按照上面分析的结论,我们可以写出下面的函数:

function clone(data) {
  let result = {}
  const keys = [...Object.getOwnPropertyNames(data), ...Object.getOwnPropertySymbols(data)]
  if(!keys.length) return data
  keys.forEach(key => {
    let item = data[key]
    if (typeof item === 'object' && item) {//避免null
      result[key] = clone(item)
    } else {
      result[key] = item
    }
  })
  return result
}

在遍历 Object 类型数据时,我们需要把 Symbol 数据类型也考虑进来,所以不能通过 Object.keys 获取键名或 for…in 方式遍历,而是通过 getOwnPropertyNames 和 getOwnPropertySymbols 函数将键名组合成数组,然后进行遍历。对于键数组长度为 0 的非 Object 类型的数据可直接返回,然后再遍历递归,最终实现拷贝。

由于嵌套调用,因此当对象数据嵌套时,会出现死循环。怎么避免这种情况呢?一种简单的方式就是把已添加的对象记录下来,这样下次碰到相同的对象引用时,直接指向记录中的对象即可。要实现这个记录功能,我们可以借助 ES6 推出的 WeakMap 对象,该对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。

function clone(obj) {
  let map = new WeakMap()//记录已经拷贝过的对象
  function deep(data) {
    let result = {}
    const keys = [...Object.getOwnPropertyNames(data), ...Object.getOwnPropertySymbols(data)]
    if(!keys.length) return data 
    const exist = map.get(data)
    if (exist) return exist //重新创建一个对象并加入 WeakMap 中
    map.set(data, result)//重新创建一个对象并加入 WeakMap 中
    keys.forEach(key => {
      let item = data[key]
      if (typeof item === 'object' && item) {
        result[key] = deep(item)
      } else {
        result[key] = item
      }
    })
    return result
  }
  return deep(obj)
}

总结

  • 7种类型:空(Null)、未定义(Undefined)、数字(Number)、字符串(String)、布尔值(Boolean)、符号(Symbol)、对象(Object)。
  • Number精度问题。
  • 进制转换parseInt和toString函数。
  • Symbol 生成一个全局唯一的值。
  • Object深拷贝,getOwnPropertyNames 和 getOwnPropertySymbols 函数。
  • WeakMap解决深拷贝递归调用死循环问题。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章