Sporadic Knowledge

216 阅读8分钟

时间戳

1,new Date('/') 接收的是当地时间,没写时分秒自动补0,输入与输出一致。

2,new Date('-') 接收的是UTC时间,没写时分秒自动补0,输出本地时间;写了时分秒也就是YYYY-MM-DD hh:mm:ss格式时接收的是当地时间,输入与输出一致。

3,Unix时间戳(Unix timestamp)定义为从1970年01月01日00时00分00秒(UTC)起至现在经过的总秒数。

4,JavaScript中提供的Date对象可以将所有时间都存为一个整数,表示从1970年1月1日00:00:00起的总毫秒数。

5,北京时间 = GMT+8 = UTC+8。

6,两个日期对象相减,得到相差的毫秒数;相加为两个日期字符串的相加。

上两个个图自己体会一下: 1,new Date()比较的其实时间是根据本地的时间,中国就是北京时间=UTC+8=new Date('1970-01-01 08:00:00')

2, new Date('1970-01-01')new Date('1970-01-01 08:00:00').getTime()结果相同,new Date('1970/01/01')new Date('1970-01-01') ,输出结果后者多了8小时即new Date('1970-01-01')-new Date('1970/01/01') --> 28800000

当 async/await 遇上 forEach

问题描述

var getNumbers = () => {
  return Promise.resolve([1, 2, 3])
}
var multi = num => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (num) {
        resolve(num * num)
      } else {
        reject(new Error('num not specified'))
      }
    }, 1000)
  })
}
async function test () {
  var nums = await getNumbers()
  nums.forEach(async x => {
    var res = await multi(x)
    console.log(res)
  })
}
test()

我们的预期执行效果是每隔一秒打印一个数字,依次打印:1,4,9 而实际的执行效果则是过了一秒后同时打印出了1,4,9

问题分析

可以通过结果看出,三个promise是并行的,而我们想要的是串行。其实根本原因是这里直接运行的三个promise,即

async function test() {
  new Promise((resolve) => {
    setTimeout(() => {
      resolve(1)
    }, 1000);
  }).then(e => console.log(e))
  new Promise((resolve) => {
    setTimeout(() => {
      resolve(4)
    }, 1000);
  }).then(e => console.log(e))
  new Promise((resolve) => {
    setTimeout(() => {
      resolve(9)
    }, 1000);
  }).then(e => console.log(e))
}

如果想要按照目标执行,实际上只需要加一个await

async function test() {
  await new Promise((resolve) => {
    setTimeout(() => {
      resolve(1)
    }, 1000);
  }).then(e => console.log(e))
  await new Promise((resolve) => {
    setTimeout(() => {
      resolve(4)
    }, 1000);
  }).then(e => console.log(e))
  await new Promise((resolve) => {
    setTimeout(() => {
      resolve(9)
    }, 1000);
  }).then(e => console.log(e))
}

for(let index = 0; index < nums.length; index++) {
  await (async x => {
  var res = await multi(x)
    console.log(res)
  })(nums[index])
}

或 直接用for in 替换foreach

async function test () {
  var nums = await getNumbers()
  for(let x of nums) {
    var res = await multi(x)
    console.log(res)
  }
}

由此可见,for inforeach实现方式不一样,这里具体为什么有区别,可能是因为for in将回调函数会作为数组迭代器的回调执行。

其实await/async的使用是为了避免promise的回调地狱,我们往往对其的使用场景为一个请求的参数依赖于另一个请求的返回值,因此用其实await/async的方式将异步写成同步,加强代码的可阅读性,而上述场景需要同时发出多个请求,实际上可以通过Promise.all等API来实现。

运算符及在JS中的运用

  1. 按位与(AND) &

&以特定的方式组合操作二进制数中对应的位,如果对应的位都为1,那么结果就是1, 如果任意一个位是0 则结果就是0。

// 1的二进制表示为: 00000000 00000000 00000000 00000001
// 3的二进制表示为: 00000000 00000000 00000000 00000011
// -----------------------------
// 1的二进制表示为: 00000000 00000000 00000000 00000001
console.log(1 & 3)     // 1
  1. 按位或(OR) |

| 运算符跟 & 的区别在于如果对应的位中任一个操作数为1 那么结果就是1。

// 1的二进制表示为: 00000000 00000000 00000000 00000001
// 3的二进制表示为: 00000000 00000000 00000000 00000011
// -----------------------------
// 3的二进制表示为: 00000000 00000000 00000000 00000011
console.log(1 | 3)     // 3
  1. 按位异或(XOR) ^

^ 如果对应两个操作位有且仅有一个1时结果为1,其他都是0。

// 1的二进制表示为: 00000000 00000000 00000000 00000001
// 3的二进制表示为: 00000000 00000000 00000000 00000011
// -----------------------------
// 2的二进制表示为: 00000000 00000000 00000000 00000010
console.log(1 ^ 3)     // 2
  1. 按位非(NOT) ~

~ 运算符是对位求反,1变0, 0变1,也就是求二进制的反码。

// 1的二进制表示为: 00000000 00000000 00000000 00000001
// 3的二进制表示为: 00000000 00000000 00000000 00000011
// -----------------------------
// 1反码二进制表示: 11111111 11111111 11111111 11111110
// 由于第一位(符号位)是1,所以这个数是一个负数。JavaScript 内部采用补码形式表示负数,即需要将这个数减去1,再取一次反,然后加上负号,才能得到这个负数对应的10进制值。
// -----------------------------
// 1的反码减1:     11111111 11111111 11111111 11111101
// 反码取反:       00000000 00000000 00000000 00000010
// 表示为10进制加负号:-2
console.log(~ 1)     // -2
  • 简单记忆:一个数与自身的取反值相加等于-1。
  1. 左移(Left shift)<<

<<运算符使指定值的二进制数所有位都左移指定次数,其移动规则:丢弃高位,低位补0即按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。

// 1的二进制表示为: 00000000 00000000 00000000 00000001
// -----------------------------
// 2的二进制表示为: 00000000 00000000 00000000 00000010
console.log(1 << 1)     // 2
  1. 有符号右移>>

>>该操作符会将指定操作数的二进制位向右移动指定的位数。向右被移出的位被丢弃,拷贝最左侧的位以填充左侧。由于新的最左侧的位总是和以前相同,符号位没有被改变。所以被称作“符号传播”。

// 1的二进制表示为: 00000000 00000000 00000000 00000001
// -----------------------------
// 0的二进制表示为: 00000000 00000000 00000000 00000000
console.log(1 >> 1)     // 0
  1. 无符号右移>>>

>>>该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃,左侧用0填充。因为符号位变成了 0,所以结果总是非负的。(译注:即便右移 0 个比特,结果也是非负的。) 对于非负数,有符号右移和无符号右移总是返回相同的结果。例如, 9 >>> 2 得到 29 >> 2 相同,对负数进行无符号右移时,相当于先取二进制反码后+1(不是取反),再无符号右移。

默认值

js给默认值一般的写法(除了if else,三元表达式)外常用的为解构赋值的时候提供默认值以及||以下两种,并且最近es针对该场景又出了一种新的运算符??,下面我们来看看它们直接的区别:

var arr = [{title: null},{title: undefined},{},{title: ''},{title: 0},{title: NaN},{title: false}]
console.log(arr.map(({title = 'default'}) => title))    -->    // [null, "default", "default", "", 0, NaN, false]
console.log(arr.map(({title}) => title || 'default'))	-->	   // ["default", "default", "default", "default", "default", "default", "default"]
console.log(arr.map(({title}) => title ?? 'default'))	-->	   // ["default", "default", "default", "", 0, NaN, false]

由此可见 解构赋值提供默认值的形式仅对 undefined 产生作用,等同于title !== undefined ? title : 'default' ||则是将参数做了一层Boolean类型的转换,等同于title ? title : 'default' ??仅对 undefinednull 产生作用,等同于title !== undefined && title !== null ? title : 'default' 我们平时做默认值处理时,若没有添加ts做类型规定,当参数的正常范围包括 0 , false 等会被 Boolean 转化为 false 时,就会采用默认值,这并不是我们想要的结果,因此我们推荐采用解构赋值,或者 ?? 运算符来做判断,这里值得注意的是解构赋值的形式并不会对 null 做处理。

取整(正数向下取整,负数向上取整)

console.log(~~ 6.88)      // 6
console.log(~~ -6.88)     // -6
console.log(6.88 >> 0)    // 6
console.log(-6.88 >> 0)   // -6
console.log(6.88 << 0)    // 6
console.log(-6.88 << 0)   // -6
console.log(6.88 | 0)     // 6
console.log(-6.88 | 0)    // -6
// 不可对负数取整
console.log(6.88 >>> 0)   // 6
console.log(-6.88 >>> 0)  // 4294967290

值交换

var a = 5
var b = 8
a ^= b
b ^= a
a ^= b
console.log(a)   // 8
console.log(b)   // 5

和indexOf结合判断元素是否存在于数组内

var a = [1,2,3]
console.log(!~a.indexOf(0))		// true

判断number是否为2的正数幂

var n = 1024
console.log(!(n&(n-1))) 		// true