一, 前言
有些东西很好用, 但是你未必知道; 有些东西你可能用过, 但是你未必知道原理.
实现一个目的有多种途径, 俗话说, 条条大路通罗马. 发散一下大家的思维以及拓展一下知识面.
二, 实现一个简短的 sleep 函数
sleep 函数主要用来做延迟执行的, 很多编程语言都有 sleep 函数, 但是 JavaScript 没有这个函数, 我们实现一下:
1, 简单版本
- function sleep(sleepTime){
- for(var start = +new Date;+new Date - start<sleepTime;){}
- }
- var t1 = +new Date();
- sleep(3000);
- var t2 = +new Date();
- console.log(t2-t1);
优点: 简单粗暴, 通俗易懂.
缺点: 确实 sleep 了, 但是卡死了, CPU 会飙升, 精确度不准
2,promise 版本
- // promise 版本
- function sleep(sleepTime){
- return new Promise(resolve => setTimeout(resolve,sleepTime));
- }
- var t1 = +new Date();
- sleep(3000).then(()=>{
- var t2 = +new Date();
- console.log(t2-t1);
- })
优点: 实际上用了 setTimeout, 没有形成进程阻塞, 不会造成性能和负载问题.
缺点: 虽然解决了回调函数的嵌套, 但是还是不美观, 而且异步不彻底, 过程中停止执行.
3,generator 版本
- // generotor 版本
- function sleep(sleepTime){
- return function(cb){
- setTimeout(cb.bind(this), sleepTime);
- }
- }
- function* genSleep(){
- var t1 = +new Date();
- yield sleep(3000);
- var t2 = +new Date();
- console.log(t2-t1);
- }
- async(genSleep);
- function async(gen){
- const iter = gen();
- function nextStep(it){
- if(it.done) return ;
- if (typeof it.value === "function") {
- it.value(function(ret) {
- nextStep(iter.next(ret))
- })
- } else {
- nextStep(iter.next(it.value));
- }
- }
- nextStep(iter.next());
- }
优点: 跟 promise 一样优点, 代码变得更简单干净.
缺点: 就是每次都要执行 next() 显得很麻烦, 虽然有 https://github.com/tj/co (第三方包) 可以解决, 但就多包了一层, 不好看, 错误也必须按 https://github.com/tj/co 的逻辑来处理, 不爽.
co 之所以这么火并不是没有原因的, 当然不是仅仅实现 sleep 这么无聊的事情, 而是它活生生的借着 generator/yield 实现了很类似 async/await 的效果! 这一点真是让我三观尽毁刮目相看.
- const co = require("co")
- function sleep(sleepTime) {
- return function(cb) {
- setTimeout(cb.bind(this), sleepTime)
- }
- }
- co(function*() {
- const t1 = +new Date()
- yield sleep(3000)
- const t2 = +new Date()
- console.log(t2 - t1)
- })
4,async/await 版本
- function sleep(delay) {
- return new Promise(reslove => {
- setTimeout(reslove, delay)
- })
- }
- !async function test() {
- const t1 = +new Date()
- await sleep(3000)
- const t2 = +new Date()
- console.log(t2 - t1)
- }()
优点: 同 Promise 和 Generator 优点. Async/Await 可以看做是 Generator 的语法糖, Async 和 Await 相较于 * 和 yield 更加语义, 另外各个函数都是扁平的, 不会产生多余的嵌套, 代码更加清爽易读.
缺点: ES7 语法存在兼容性问题, 有 babel 一切兼容性都不是问题
5, 开源的版本
在 JavaScript 优雅的写 sleep 等于如何优雅的不优雅, 这里有 C++ 实现的模块: https://github.com/ErikDubbelboer/node-sleep
- const sleep = require("sleep")
- const t1 = +new Date()
- sleep.msleep(3000)
- const t2 = +new Date()
- console.log(t2 - t1)
优点: 能够实现更加精细的时间精确度, 而且看起来就是真的 sleep 函数, 清晰直白.
缺点: 缺点需要安装这个模块 https://github.com/ErikDubbelboer/node-sleep .
前端知识点: Async/Await 是目前前端异步书写最优雅的一种方式
二, 优雅获取时间戳
上面实现 sleep 函数, 我们可以发现代码有 +new Date() 获取时间戳的用法, 这只是其中的一种, 下面就说一下其他两种以及 +new Date() 的原理.
1, 普通版
var timestamp=new Date().getTime()
优点: 具有普遍性, 大家都用这个
缺点: 应该没有吧
2, 进阶版
var timestamp = (new Date()).valueOf()
valueOf 方法返回对象的原始值 (Primitive,'Null','Undefined','String','Boolean','Number'五种基本数据类型之一), 可能是字符串, 数值或 bool 值等, 看具体的对象.
优点: 说明开发者原始值有一个具体的认知, 让人眼前一亮.
缺点: 应该没有吧
3,Date.now() 方法
Date.now()
Date.now() 方法返回自 1970 年 1 月 1 日 00:00:00 UTC 到当前时间的毫秒数. 类型为 Number. 因为 now() 是的一个静态函数, 所以必须以 Date.now() 的形式来使用.
优点: 简单明了.
缺点: 兼容性问题, ECMA-262 第五版中被标准化.
兼容性不支持时的兼容性代码:
- if (!Date.now) {
- Date.now = function now() {
- return new Date().getTime();
- };
- }
4, 终极版
var timestamp = +new Date()
优点: 对 JavaScript 隐式转换掌握的比较牢固的一个表现
缺点: 应该没有吧
我们来分析一下, 为什么 + new Date() 拿到的时间戳?
那就是隐式转换, 实质上还是调用了 valueOf() 的方法.
注意:
(1) 一元 + 运算符
一元 + 运算符将其操作数转换为 Number 类型并反转其正负. 注意负的 +0 产生 -0, 负的 -0 产生 +0.
+new Date() 相当于 ToNumber(new Date())
(2)toString 用来返回对象的字符串表示.
- var obj = {
- };
- console.log(obj.toString());//[object Object]
- var arr = [];
- console.log(arr.toString());//"" 空字符串
- var date = new Date(); // Tue May 28 2019 22:05:58 GMT+0800 (中国标准时间)
- console.log(date.toString());//"Tue May 28 2019 22:05:58 GMT+0800 (中国标准时间)"
(3)valueOf() 方法返回对象的原始值
valueOf() 方法返回对象的原始值, 可能是字符串, 述职或 boolean 值, 看具体的对象.
- var obj = {
- name: "saucxs"
- }
- console.log(obj.valueOf()) //Object {name: "saucxs"}
- var arr1 = [1,3]
- console.log(arr1.valueOf()) //[1,3]
- var date = new Date()
- console.log(date.valueOf())//1456638436303
- // 如代码所示, 三个不同的对象实例调用 valueOf 返回不同的数据
原始值指的是'Null','Undefined','String','Boolean','Number','Symbol' 6 种基本数据类型之一, 上面已经提到过这个概念, 这里再次申明一下.
最后分解一下其中的过程:+new Date():
(1) 运算符 new 的优先级高于一元运算符 +, 所以过程可以分解为: var time=new Date();+time
(2) 根据上面提到的规则相当于: ToNumber(time)
(3)time 是个日期对象, 根据 ToNumber 的转换规则, 所以相当于: ToNumber(ToPrimitive(time))
(4) 根据 ToPrimitive 的转换规则: ToNumber(time.valueOf()),time.valueOf() 就是 原始值 得到的是个时间戳, 假设 time.valueOf()=1503479124652
(5) 所以 ToNumber(1503479124652) 返回值是 1503479124652 这个数字.
前端知识点: 隐式转换的妙用
来源: https://www.cnblogs.com/chengxs/p/10949075.html