JavaScript总结【3】数据类型

这篇博客深入探讨了JavaScript中的数据类型,包括数字类型、数组、可迭代对象、Map和Set等。讨论了0.1+0.2的问题及其解决方案,详细介绍了数组的方法、Map和Set的特性和用法,以及Date对象的操作。还涵盖了JSON的序列化和反序列化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JavaScript数据类型进阶

数字类型

常见数字类型

  1. 十进制:121

  2. 二进制:01011

  3. 八进制:0o377

  4. 十六进制:0xff

  5. 科学计数法:1.23e6

  6. NaN

    转换为字符串:.toString(base)

舍入

  1. Math.floor
  2. Math.ceil
  3. Math.round
  4. Math.trunc:移除小数点后的内容,IE浏览器不支持

0.1+0.2

简单理解,0.1、0.2在计算机中其实是无线循环的小数

  1. 理解这个之前需要知道计算机中浮点数的表示IEEE 754浮点数标准详解,在JavaScript中数字是以64位双精度浮点数来存储的,表示方法:

    符号指数尾数
    占位1占位11占位52

    整数部分作为隐藏位,所以最大值会到53次幂

  2. 在计算机中,数字都是以二进制存储的,所以我们要先将0.1 0.2 转化成二进制,对于十进制转二进制,整数部分除二取余,倒序排列,小数部分乘二取整,顺序排列得到:

    0.10.0 0011 0011 0011 0011 0011 0011 ...

    0.20.0 011 0011 0011 0011 0011 0011 0011 ...

  3. 使用IEEE 754 双精度64位浮点数来表示

// 0.1
e = -4; // 2^-4 = 0.0625
m = 1.1001100110011001100110011001100110011001100110011010 (52)

// 0.2
e = -3; // 2^-3 = 0.125
m = 1.1001100110011001100110011001100110011001100110011010 (52)
  1. 做加法运算是首先进行对阶,指数小的做右移操作,于是有
e = -3; m = 0.1100110011001100110011001100110011001100110011001101 (52)
// +
e = -3; m = 1.1001100110011001100110011001100110011001100110011010 (52)
得
e = -3; m = 10.0110011001100110011001100110011001100110011001100111 (52)
// 保留一位整数
e = -2; m = 1.00110011001100110011001100110011001100110011001100111 (53)
// 已经溢出,处理规则为保留偶数
e = -2; m=1.0011001100110011001100110011001100110011001100110100
// 那么最终结果位
0.1 + 0.2  === 1.0011001100110011001100110011001100110011001100110100 * 2 ^ -2
// 转化位小数
x === 0.010011001100110011001100110011001100110011001100110100
// 即:
x === 0.30000000000000004 

如何解决0.1+0.2问题

  1. 使用num.toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。它返回的是字符串,可以使用一元符号进行转换
let sum = 0.1 + 0.2;
alert( +sum.toFixed(2) ); // 0.3
  1. 将数字临时乘以一个数

isFinite 和 isNaN

  1. NaN是独一无二的,它不等于任何东西,包括它自身alert( NaN === NaN ); // false

  2. isNaN(value) 将其参数转换为数字,然后测试其是否为NaN,如果是返回true

  3. isFinite(value) 将其参数转换为数字,如果是常规数字,则返回 true,如果为NaN/Infinity/-Infinity返回false

**注意:**在所有数字函数中,包括 isFinite,空字符串或仅有空格的字符串均被视为 0

parseInt 和 parseFloat

  1. 它们可以从字符串中读取数字,直到无法读取为止。如果发生 error,则返回收集到的数字。函数 parseInt 返回一个整数,而 parseFloat 返回一个浮点数,如果未读取到返回NaN
alert( parseInt('100px') ); // 100
alert( parseFloat('12.5em') ); // 12.5
alert( parseInt('12.3') ); // 12,只有整数部分被返回了
alert( parseFloat('12.3.4') ); // 12.3,在第二个点出停止了读取
  1. 第二个参数:parseInt() 函数具有可选的第二个参数。它指定了数字系统的基数,因此 parseInt 还可以解析十六进制数字、二进制数字等的字符串
alert( parseInt('0xff', 16) ); // 255
alert( parseInt('ff', 16) ); // 255,没有 0x 仍然有效
alert( parseInt('2n9c', 36) ); // 123456

进制转换

  1. Number.parseInt(string , radix)
  2. Number.toString(radix)

其他数学函数

  1. Math.random()
  2. Math.max()
  3. Math.min()
  4. Math.pow(n, power)

数组

数组时一种有序集合,JavaScript中的数组可以存储任意数据类型

数组创建

数组创建的两种方法

  1. let arr = new Array()
  2. let arr = []

数组的长度

arr.length,他会自动更新,而且它是可写的

数组方法

改变原数组的方法不改变原数组的方法
pushconcat
shiftslice
popindexOf
unshiftlastIndexOf
spliceincludes
sortfind
reversefindIndex
filter
map
split
join
  1. 作为队列

    • push 在末端添加一个(或多个)元素,在原数组上修改,返回修改后数组的长度
    • shift 取出队列首端的一个元素,整个队列往前移,在原数组修改,返回取出的元素
  2. 作为栈

    • push
    • pop 从末端取出一个元素
  3. 搜索方法

    请注意,以下这些方法使用的是严格相等 === 比较。所以如果我们搜索 false,会精确到的确是 false 而不是数字 0

    • 常用arr.indexOf(item, from) 从索引 from 开始搜索 item,如果找到则返回索引,否则返回 -1
    • arr.lastIndexOf(item, from) 和上面相同,只是从右向左搜索
    • arr.includes(item, from) 从索引 from 开始搜索 item,如果找到则返回 true,如果没找到,则返回 false
    • arr.find(function(item, index, array) 寻找满足特定条件的对象,找到返回true并停止搜索,找不到返回undefined
    • arr.findIndex方法(与 arr.find 方法)基本上是一样的,但它返回找到元素的索引,而不是元素本身。并且在未找到任何内容时返回 -1
    • arr.filter(fn)语法与 find 大致相同,但是 filter 返回的是所有匹配元素组成的数组,它可以返回多个
  4. 数组操作

    首先,在数组和数组方法中,负向索引是允许的

    • unshift在数组的首端添加元素,可以是一个或多个
    • delete,数组时对象,可以使用如delete arr[1],会留下一个undefined空位
    • splice可以添加,删除和插入元素arr.splice(start[, deleteCount, elem1, ..., elemN]),它从索引 start 开始修改 arr:删除 deleteCount 个元素并在当前位置插入 elem1, ..., elemN。最后返回已被删除元素的数组。
    • slice可提取字符串的某个部分,并以新的字符串返回被提取的部分,不会改变原始数组。arr.slice([start], [end])会返回一个新数组,将所有从索引 startend(不包括 end)的数组项复制到一个新的数组。startend 都可以是负数,在这种情况下,从末尾计算索引
    • concat可以合并数组并但会合并的新数组
    • map通过arr.map它对数组的每个元素都调用函数,并返回结果数组。
    • sort对数组进行排序并返回排序后的数组,默认是转换成字符串比较,会造成2>13的情况,需要传入一个函数作为参数进行比较两个值返回01-1进行排序,可以是倒序也可以是顺序,看返回值怎么设定,注意,不一定要用1-1只要是正负数就可以了,正数代表大于,负数代表小于,使用箭头函数更佳
function compareNumeric(a, b) {
 if (a > b) return 1;
 if (a == b) return 0;
 if (a < b) return -1;
}

let arr = [ 1, 2, 13 ];
arr.sort(compareNumeric);
alert(arr);  // 1, 2, 13
  • arr.reverse方法用于颠倒 arr 中元素的顺序
  • split,可选第二个参数限制数组长度,超长不在运行,传入空字符串返回拆解的字母
  • join
  • reduce:应用函数时,上一个函数调用的结果将作为第一个参数传递给下一个函数
// 累加,可省略初始值(第二个参数)
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum + current, 0);
alert(result); // 15
  • reduceRightreduce只不过数组从右边开始遍历
  • Array.isArray(value)判断是否为数组
  1. push/popshift/unshift性能比较

    push/pop 方法运行的比较,而 shift/unshift 比较慢。因为操作数组的前端部分需要移动所有元素并修改索引

  2. 可以但不要用的几种方式

    • arr.test=1arr添加一个非数字的书写,可以实现,但不能通过poppush操作它,和length一样,它是属性,不会改变数组的长度
    • 制造空洞,比如:添加 arr[0],然后添加 arr[1000] (它们中间什么都没有)。使用pop得到的会是undefined
    • 以倒序填充数组,比如 arr[1000]arr[999] 等等。

数组的循环

  1. for
  2. for-of
  3. for-in,也可以遍历数组值,但不推荐,也会遍历出length等属性名(显示undefined)和其他自建的非数字索引,且速度慢
  4. arr.forEach(func)

数组的toString

arr.toStringString(arr)都会返回以逗号隔开的元素列表。如果嵌套了数组,他会展开

数组相等

数组时对象,不应该使用 == 运算符比较 JavaScript 中的数组

/* 这里都是对象,除非地址相同,否则永不相等 */
alert( [] == [] ); // false
alert( [0] == [0] ); // false
/* 这里是因为数组被转换成了空字符串'' */
alert( 0 == [] ); // true
alert('0' == [] ); // false

可迭代对象

遍历Array可以采用下标循环,遍历MapSet就无法使用下标。为了统一集合类型,ES6标准引入了新的iterable类型,ArrayMapSet都属于iterable类型。具有iterable类型的集合可以通过新的for ... of循环来遍历。

Symbol.iterator

  1. for_of运行机制
    • for..of 循环启动时,它会调用这个方法(如果没找到,就会报错)。这个方法必须返回一个 迭代器(iterator) —— 一个有 next 方法的对象。
    • 从此开始,for..of 仅适用于这个被返回的对象
    • for..of 循环希望取得下一个数值,它就调用这个对象的 next() 方法。
    • next()方法返回的结果的格式必须是{done: Boolean, value: any},当 done=true时,表示迭代结束,否则value` 是下一个值
  2. 实现迭代器range
/* 可迭代对象range实现 */
let range = {
  from: 1,
  to: 5
};

// 1. for..of 调用首先会调用这个:
range[Symbol.iterator] = function() {

  // ……它返回迭代器对象(iterator object):
  // 2. 接下来,for..of 仅与此迭代器一起工作,要求它提供下一个值
  return {
    current: this.from,
    last: this.to,

    // 3. next() 在 for..of 的每一轮循环迭代中被调用
    next() {
      // 4. 它将会返回 {done:.., value :...} 格式的对象
      if (this.current <= this.last) {
        return { done: false, value: this.current++ };
      } else {
        return { done: true };
      }
    }
  };
};

// 运行
for (let num of range) {
  alert(num); // 1, 然后是 2, 3, 4, 5
}

range 自身没有 next() 方法。相反,是通过调用 range[Symbol.iterator]() 创建了另一个对象,即所谓的“迭代器”对象,并且它的 next 会为迭代生成值。

隐藏以上代码可以简写为:

let range = {
  from: 1,
  to: 5,

  [Symbol.iterator]() {
    this.current = this.from;
    return this;  // 这里返回的时自己
  },

  next() {
    if (this.current <= this.to) {
      return { done: false, value: this.current++ };
    } else {
      return { done: true };
    }
  }
};

for (let num of range) {
  alert(num); // 1, 然后是 2, 3, 4, 5
}

显式调用迭代器

let str = "Hello";

// 和 for..of 做相同的事
// for (let char of str) alert(char);

let iterator = str[Symbol.iterator]();

while (true) {
  let result = iterator.next();
  if (result.done) break;
  alert(result.value); 
}

可迭代(iterable)和类数组(array-like)

  • Iterable 如上所述,是实现了 Symbol.iterator 方法的对象。
  • Array-like 是有索引和 length 属性的对象,所以它们看起来很像数组。

Array.from

全局方法 Array.from可以接受一个可迭代或类数组的值,并从中获取一个“真正的”数组。然后我们就可以对其调用数组方法了。

  • Array.from(obj[, mapFn, thisArg]):可选的第二个参数 mapFn 可以是一个函数,该函数会在对象中的元素被添加到数组前,被应用于每个元素,此外 thisArg 允许我们为该函数设置 this

Map and Set(映射和集合)

Map

Map是一个带键的数据项的集合,就像一个 Object 一样。 但是它们最大的差别是 Map 允许任何类型的键(key)。

map的属性和方法
  • new Map() —— 创建 map

  • map.set(key, value) —— 根据键存储值,每一次 map.set 调用都会返回 map 本身,所以我们可以进行“链式”调用

  • map.get(key) —— 根据键来返回值,如果 map 中不存在对应的 key,则返回 undefined

  • map.has(key) —— 如果 key 存在则返回 true,否则返回 false

  • map.delete(key) —— 删除指定键的值。

  • map.clear() —— 清空 map。

  • map.size —— 返回当前元素个数。

  • map.keys() —— 遍历并返回所有的键,主要返回的不是数组,而是MapIterator

  • map.values() —— 遍历并返回所有的值,主要返回的不是数组,而是MapIterator

    • map.entries() —— 遍历并返回所有的实体(key、value),对map使用for..of 在默认情况下使用的就是这个。
  • map.forEach(value,key,map)

创建map
  1. new Map()
let map = new Map()
map.set('1', 'str1')
map.set(1, 'num1')     
map.set(true, 'bool1')
  1. new Map([[key, value],[key,value]])
let map = new Map([
  ['1',  'str1'],
  [1,    'num1'],
  [true, 'bool1']
]);
  1. 从对象创建map new Map(Object.entries(obj))
// Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组
let obj = {
  name: "John",
  age: 30
}
let map = new Map(Object.entries(obj))
从map创建对象
// 创建一个普通对象(plain object)
// obj = { banana: 1, orange: 2, meat: 4 }
let map = new Map()
map.set('banana', 1)
map.set('orange', 2)
map.set('meat', 4)
let obj = Object.fromEntries(map.entries())
let obj = Object.fromEntries(map) // 也可以

Set

Set 是一个特殊的类型集合 —— “值的集合”(没有键),它的每一个值只能出现一次。

set的属性和方法
  • new Set(iterable) —— 创建一个 set,如果提供了一个 iterable 对象(通常是数组),将会从数组里面复制值到 set 中。
  • set.add(value) —— 添加一个值,返回 set 本身
  • set.delete(value) —— 删除值,如果 value 在这个方法调用的时候存在则返回 true ,否则返回 false
  • set.has(value) —— 如果 value 在 set 中,返回 true,否则返回 false
  • set.clear() —— 清空 set。
  • set.size —— 返回元素个数。
set迭代
  1. 我们可以使用 for..offorEach 来遍历 Set:
let set = new Set(["oranges", "apples", "bananas"]);

for (let value of set) alert(value);

// 与 forEach 相同:
set.forEach((value, valueAgain, set) => {
  alert(value);
});

forEach 的回调函数有三个参数:一个 value,然后是 同一个值 valueAgain,最后是目标对象.forEach 的回调函数有三个参数,是为了与 Map 兼容。当然,这看起来确实有些奇怪。但是这对在特定情况下轻松地用 Set 代替 Map 很有帮助,反之亦然。

  1. Map 中用于迭代的方法在 Set 中也同样支持:
  • set.keys() —— 遍历并返回所有的值(returns an iterable object for values),
  • set.values() —— 与 set.keys() 作用相同,这是为了兼容 Map
  • set.entries() —— 遍历并返回所有的实体(returns an iterable object for entries)[value, value],它的存在也是为了兼容 Map

弱映射和弱集合

如果把一个对象放入到数组中,那么只要这个数组存在,那么这个对象也就存在,即使没有其他对该对象的引用,不会进行垃圾回收掉。弱映射和弱集合在这方面有根本的不同

WeakMap

  1. WeakMap键必须是对象,不能是原始值
let weakMap = new WeakMap();
let obj = {name: "John"};
weakMap.set(obj, "ok"); // 以对象作为键
  1. weakMap 中使用一个对象作为键,并且没有其他对这个对象的引用 —— 该对象将会被从内存(和map(map中无法访问到它))中自动清除
let john = { name: "John" };
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; // 覆盖引用 john 被从内存中删除了!
  1. WeakMap 不支持迭代以及 keys()values()entries() 方法。所以没有办法获取 WeakMap 的所有键或值。WeakMap 只有以下的方法:

    • weakMap.get(key)

    • weakMap.set(key, value)

    • weakMap.delete(key)

    • weakMap.has(key)

WeakSet

  1. 只能向 WeakSet 添加对象(而不能是原始值)
  2. 对象只有在其它某个(些)地方能被访问的时候,才能留在 set 中
  3. Set 一样,WeakSet 支持 addhasdelete 方法,但不支持 sizekeys(),并且不可迭代

Date

创建时间

  1. new Date():// 当前的日期/时间

  2. new Date(number) // 时间戳:1970 年 1 月 1 日 UTC+0 之后经过的毫秒数

  3. new Date(datestring):如let date = new Date("2017-01-26")运行代码时会根据时区进行调整

  4. new Date(year, month, date, hours, minutes, seconds, ms):年必须是四位数,月为0-11,,日缺失默认为1,其他参数缺失默认为0

访问日期组件

  1. date.getFullYear()
  2. date.getMonth():返回的是0-11
  3. date.getDate()
  4. date.getHours()
  5. date.getMinutes()
  6. date.getSeconds()
  7. date.getMillisenconds()

设置日期组件

  1. date.setFullYear(year, [month], [date])
  2. date.setMonth(month, [date])
  3. date.setDate(date)
  4. date.setHours(hour, [min], [sec], [ms])
  5. date.setMinutes(min, [sec], [ms])
  6. date.setSeconds(sec, [ms])
  7. date.setMilliseconds(ms)
  8. date.setTime(milliseconds):时间戳

自动校准

// 1st Feb 2013
let date = new Date(2013, 0, 32);
// 1 Mar 2016 闰年,加两天他会自动调整,这里2可以是0甚至负值
let date = new Date(2016, 1, 28);
date.setDate(date.getDate() + 2);

转化

  1. 转化为时间戳
// 使用一元运算符转换
let date = new Date();
alert(+date); // 以毫秒为单位的数值,与使用 date.getTime() 的结果相同

// 时间测量
let date1 = new Date()
doSomething()
let date2 = new Date()
let runtime = date2 - date1

// 更快的时间测量方法
let start = Date.now(); // Date.now() 它相当于 new Date().getTime(),但它不会创建中间的 Date 对象并进行类型转化。因此它更快,而且不会对垃圾处理造成额外的压力。
doSomething()
let end = Date.now();
runtime = start - end
  1. 字符串解析为Date
  • Date.parse(str)返回时间戳
  • 其中str格式应该为YYYY-MM-DDTHH:mm:ss.sssZ,字符 "T" 是分隔符,可选字符 'Z'+-hh:mm 格式的时区。单个字符 Z 代表 UTC+0 时区。
  • 简短形式如 YYYY-MM-DDYYYY-MM,甚至可以是 YYYY
let ms = Date.parse('2012-01-26T13:51:50.417-07:00');
alert(ms); // 1327611110417

JSON

JSON(JavaScript Object Notation)是表示值和对象的通用格式

  • JSON.stringify()将对象转换为JSON,得到的 json 字符串是一个被称为 JSON 编码(JSON-encoded)序列化(serialized)字符串化(stringified)编组化(marshalled) 的对象
  • JSON.parse()将JSON转换回对象

JSON.stringify

  1. JSON 编码的对象与对象字面量的区别:

    • 字符串使用双引号。JSON 中没有单引号或反引号,单引号被转化为双引号
    • 对象属性名称也是双引号的
  2. JSON.stringify支持的数据类型

    转换后都是字符串类型

    • Object
    • Arrays JSON.stringify([1,2,3]) => ‘[1,2,3]’
    • Primitives:
      • strings JSON.stringify('test') => ‘ “test” ’
      • numbers JSON.stringify(1) => ‘1’
      • boolean JSON.stringify('test') => ‘false’
      • null JSON.stringify(null) => ‘null’
  3. 特定于 JavaScript 的对象属性会被 JSON.stringify 跳过:

    • 函数属性(方法)
    • Symbol 类型的属性
    • 存储 undefined 的属性
let user = {
 sayHi() { // 被忽略
   alert("Hello");
 },
 [Symbol("id")]: 123, // 被忽略
 something: undefined // 被忽略
};

alert( JSON.stringify(user) ); // {}(空对象)
  1. 支持嵌套,但不能循环引用,会报错
  2. 可选参数:replacerspace
    • replacer:要编码的属性数组或映射函数 function(key, value),解决循环引用问题
    • space:用于格式化的空格数量,用于格式化,美观
// 如果我们传递一个属性数组给它,那么只有这些属性会被编码
let room = {
  number: 23
};
let meetup = {
  title: "Conference",
  participants: [{name: "John"}, {name: "Alice"}],
  place: room // meetup 引用了 room
};
room.occupiedBy = meetup; // room 引用了 meetup
r = JSON.stringify(meetup, ['title', 'participants']); // '{"title":"Conference","participants":[{},{}]}'

// 使用replcer函数(不一定就交这个函数名)跳过循环引用
r2 = JSON.stringify(meetup, function replacer(key, value) {
  return (key == 'occupiedBy') ? undefined : value;
}); // '{"title":"Conference","participants":[{"name":"John"},{"name":"Alice"}],"place":{"number":23}}'
  1. toJSON 方法

    toString 进行字符串转换,对象也可以提供 toJSON 方法来进行 JSON 转换。如果可用,JSON.stringify 会自动调用它。

JSON.parse

  1. JSON.parse(str, [reviver])

    • str:要解析的 JSON 字符串
    • reviver:可选的函数 function(key,value),该函数将为每个 (key, value) 对调用,并可以对值进行转换
    • 可以用于嵌套对象
  2. 可选参数reviver

let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';

let meetup = JSON.parse(str, function(key, value) {
  if (key == 'date') return new Date(value);
  return value;
});

alert(meetup.date.getDate()); // 如果不转换它是字符串,没有这个方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值