【JavaScript入门笔记05 数据类型Ⅵ 日期和时间】

笔记参考javascript.info中文站

  • 日期和时间
        • 1. 创建
        • 2. 组件
        • 3. 一些特性
          • 3.1  自动校准(Autocorrection)
          • 3.2  日期转化为数字,日期差值
          • 3.3  `Date.now()`
        • 4. 基准测试(Benchmarking)
        • 5. 对字符串调用:`Date.parse`
  • JSON 方法,toJSON
        • 1. JSON.stringify
        • 2. 排除和转换:replacer
        • 3. 格式化:space
        • 4. 自定义 “toJSON”
        • 5. JSON.parse
        • 6. 使用 reviver

日期和时间

日期和时间是一个内建的对象,有自己的方法,用来记录处理时间和日期

1. 创建

调用 new Date() 来创建一个新的 Date 对象

举个例子:

// 无参数,默认当前的日期时间
let now = new Date();
alert( now ); // 显示当前的日期时间

// 带参数,0 表示 01.01.1970 UTC+0
let Jan01_1970 = new Date(0);
alert( Jan01_1970 );

Date() 的参数如果是 Number 类型,那么指的是 “时间戳” ,也就是自 1970-01-01 00:00:00 以来经过的毫秒数,当然也可以是负数

Date() 的参数如果是 String类型,那就会自动解析成我们生活中常用的时间形式

let date = new Date("2017-01-26");
alert(date);
// 未指定具体时间,所以假定时间为格林尼治标准时间(GMT)的午夜零点
// 并根据运行代码时的用户的时区进行调整

Date() 的参数也可以很全面:

new Date(year, month, date, hours, minutes, seconds, ms)
  • year 是四位数字,表示年份,也可以是两位,表示19xx年
  • month 是数字,表示月份,取值从 0 到 11 ,表示一到十二月
  • date 是数字,表示日期,如果缺失默认为1
  • 如果 hours/minutes/seconds/ms 缺失,则均为默认值 0。
let date = new Date(2011, 0, 1, 2, 3, 4, 567);
alert( date ); // 1.01.2011, 02:03:04.567

2. 组件

我们可以通过这些方法来获取某些特定的时间:

  • getFullYear():获取年份(4 位数)
  • getMonth():获取月份,从 0 到 11。
  • getDate():获取当月的具体日期,从 1 到 31,这个方法名称可能看起来有些令人疑惑。
  • getHours(),getMinutes(),getSeconds(),getMilliseconds():获取对应的时间
  • getDay():获取星期几,从 0 到 6
  • getTime():获取时间戳
  • getTimezoneOffset():获取UTC 与本地时区之间的时差,以分钟为单位

我们可以通过下面这些方法来修改获取到的时间的某些数据:

  • setFullYear(year, [month], [date]):设置年份,也可以设置月份和日期
  • setMonth(month, [date]):设置月份,也可以设置日期
  • setDate(date):设置日期
  • setHours(hour, [min], [sec], [ms]):设置小时,也可以设置其后的时间
  • setMinutes(min, [sec], [ms]):设置分钟,也可以设置其后的时间
  • setSeconds(sec, [ms]):设置秒数,也可以设置毫秒
  • setMilliseconds(ms):设置毫秒数
  • setTime(milliseconds):使用自 1970-01-01 00:00:00 UTC+0 以来的毫秒数来设置整个日期

3. 一些特性

3.1  自动校准(Autocorrection)

超出日期 / 时间范畴的数据会被自动校准

如月份如果填了 13 ,那么就会被校准成第二年的 2 月,因为月份是 0 - 11
再如日期填了 0,那就会被校准成上个月的最后一天,因为日期是 1 - 31

3.2  日期转化为数字,日期差值

日期对象被强制转化成数字类型和使用 date.getTime() 方法效果相同,都是转化成时间戳,因此我们可以利用这点对日期进行差值计算,也就是类似计时器的效果:

let start = new Date(); // 开始测量时间

// do the job
for (let i = 0; i < 100000; i++) {
  let doSomething = i * i * i;
}

let end = new Date(); // 结束测量时间

alert( `The loop took ${end - start} ms` );

这很常用

3.3  Date.now()

在使用计时器的功能时,我们不需要日期对象只需要时间戳,因此使用 getDate() 有些浪费性能,因此我们可以使用 Date.now() 方法,这样更快更节省内存

let start = Date.now(); // 从 1 Jan 1970 至今的时间戳

4. 基准测试(Benchmarking)

有时我们需要测试一些操作,以知晓他们的运行效率,而有些操作非常消耗CPU性能,就需要谨慎对待,这样的操作就被称作基准测试

举个例子:
我们要测试这两种计算时间差的方法那个更快

// 我们有 date1 和 date2,哪个函数会更快地返回两者的时间差?
function diffSubtract(date1, date2) {
  return date2 - date1;
}

// or
function diffGetTime(date1, date2) {
  return date2.getTime() - date1.getTime();
}

单次运行时间太短看不出来,因此我们要多运行几万次

function bench(f) {
  let date1 = new Date(0);
  let date2 = new Date();

  let start = Date.now();
  for (let i = 0; i < 100000; i++) f(date1, date2);
  return Date.now() - start;
}

alert( 'Time of diffSubtract: ' + bench(diffSubtract) + 'ms' );
alert( 'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' );

看起来第二种方式更快,因为不需要进行类型转换,只需要调用一个对象方法

但问题是,CPU的很多运行是并行的,我们不能确定在处理方法一的时候是不是有一些预热操作也算在了方法一的时间内,导致方法一时间更长,因此我们需要更严谨的比较方式

比如说将整个测试过程多运行十几次
再比如将两种方法交替操作

5. 对字符串调用:Date.parse

Date.parse(str) 方法可以从一个字符串中读取日期,格式为 YYYY-MM-DDTHH:mm:ss.sssZ
其中Z是可选字符,为 +-hh:mm 格式的时区。
也可以更简洁些,只有日期甚至只有年份

该方法会自动解析字符串表示的日期时间,随后返回时间戳

let ms = Date.parse('2012-01-26T13:51:50.417-07:00');

alert(ms); // 1327611110417  (时间戳)


JSON 方法,toJSON

我们可以使用 toString() 方法将对象转化成特定格式的字符串以输出,但如果对象的属性发生变化,那么字符串也需要有相应的变化,每次写 toString() 格式的时候都很麻烦,属性嵌套对象就更麻烦,因此有了 JSON 方法

1. JSON.stringify

JSON(JavaScript Object Notation)是表示值和对象的通用格式,最初用于 Javascript 对象的表述,后来很多其它语言也用它的库

在 Javascript 中,JSON有两种方法:

  • JSON.stringify 将对象转换为 JSON
  • JSON.parse 将 JSON 转换回对象

举个例子:

let student = {
  name: 'John',
  age: 30,
  isAdmin: false,
  courses: ['html', 'css', 'js'],
  spouse: null
};

let json = JSON.stringify(student);

alert(typeof json); // string
alert(json); // 直接把对象照抄成字符串

而且 JSON.stringify原始类型同样有效,对数组也有效

JSON.stringify 转化后的字符串有两个特点,所有原对象的键都要加双引号,原本就有引号的值会变成双引号,无论以前是哪种引号。

JSON.stringify不是什么都转化,函数属性(方法)、Symbol 类型的键和值、存储 undefined 的属性会被跳过,不会变成字符串

当然,嵌套对象是支持转换的

但注意,不支持循环引用的转化,会报错

let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  participants: ["john", "ann"]
};

meetup.place = room;       // meetup 引用了 room
room.occupiedBy = meetup; // room 引用了 meetup

JSON.stringify(meetup); // Error: Converting circular structure to JSON

2. 排除和转换:replacer

JSON.stringify 还有两个可选参数:

let json = JSON.stringify(value[, replacer, space])

replacer 是要编码的属性数组或映射函数 function(key, value)

属性数组的例子:

let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  participants: [{name: "John"}, {name: "Alice"}],
  place: room // meetup 引用了 room
};

room.occupiedBy = meetup; // room 引用了 meetup

alert( JSON.stringify(meetup, ['title', 'participants']) );
// {"title":"Conference","participants":[{},{}]}
// participants是空的是因为属性列表应用于所有对象,包括嵌套

有时属性列表会过长,修改起来很困难,这时就需要用到函数映射 replacer()
这个函数可以自动辨别,规则自定义,例如上面的例子,自循环的就返回 undefined,都则返回正常的 value。

另外注意,该函数在调用之初会先检查一次整个对象本身,如果有必要,函数甚至会直接跳过这个对象

3. 格式化:space

JSON.stringify(value, replacer, spaces) 的第三个参数是用于优化格式的空格数量

举个例子:

let user = {
  name: "John",
  age: 25,
  roles: {
    isAdmin: false,
    isEditor: true
  }
};

alert(JSON.stringify(user, null, 2));
/* 两个空格的缩进:
{
  "name": "John",
  "age": 25,
  "roles": {
    "isAdmin": false,
    "isEditor": true
  }
}
*/

/* 对于 JSON.stringify(user, null, 4) 的结果会有更多缩进:
{
    "name": "John",
    "age": 25,
    "roles": {
        "isAdmin": false,
        "isEditor": true
    }
}
*/

4. 自定义 “toJSON”

就像 toString() 方法可以让对象在转换成字符串或其他调用该方法的时候自定义变成什么样子,对象也有 toJSON() 方法可以进行 JSON 转换,例如 JSON.stringify() 实际就会自动调用 toJSON()

举个例子:

let room = {
  number: 23,
  toJSON() {
    return this.number;
  }
};

let meetup = {
  title: "Conference",
  room
};

alert( JSON.stringify(room) ); // 23

alert( JSON.stringify(meetup) );
/*
  {
    "title":"Conference",
    "room": 23
  }
*/

转换成什么样子完全可以自定义,当然也可以不定义,让属性值自动转化成字符串

let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  date: new Date(Date.UTC(2017, 0, 1)),
  room
};

alert( JSON.stringify(meetup) );
/*
  {
    "title":"Conference",
    "date":"2017-01-01T00:00:00.000Z",  // 这里已经变成时间戳了,是转换成字符串的自然结果
    "room": {"number":23}
  }
*/

5. JSON.parse

JSON.parse(str, [reviver]) 方法会将 JSON 转化成原始格式,如对象、数组、原始类型

其中 str 是需要转化的字符串,reviver 是可选函数参数( function(key,value)

举个例子:

let userData = '{ "name": "John", "age": 35, "isAdmin": false, "friends": [0,1,2,3] }';

let user = JSON.parse(userData);
alert( user.friends[1] ); // 1

可以看到,即使是嵌套函数也能正常解析

但前提是格式严格正确,比如引号必须是双引号,属性名必须有双引号,只能是字符串不能有 new

其实有一个库 JSON5,它支持不太严格的JSON转换


6. 使用 reviver

reviver 是可选函数参数( function(key,value)),它用来解决一些 JSON 转化回去过程中出现的特殊格式问题,比如日期对象的时间戳会被转化成字符串,从而无法正常使用:

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

let meetup = JSON.parse(str);
alert( meetup.date.getDate() ); // Error!

这时就可以用 reviver 函数对特定类型进行特殊处理,如将属性名是 date 的属性值单独放进一个新建的 Date 对象中:

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() ); // 现在正常运行了!

语法稍微有一点怪,但使用此时增多后便会觉得可读性也可以,因为这个函数识别度很高

你可能感兴趣的:(JavaScript入门笔记,javascript,前端,开发语言)