js易忘知识点总结

181 阅读5分钟

window apis

getComputedStyle() 返回元素所有CSS属性(只读的)
postmessage() 不能传递function类型数据,不能传递不能json化数据
open() 无origin时,自动加当前origin
beforeunload() 无法自定义弹窗
IntersectionObserver 一种异步观察目标元素与其祖先元素或顶级文档视窗交叉状态的方法
ResizeObserver 接口监视内容盒或边框盒或者边界尺寸的变化
MutationObserver 监视对 DOM 树所做更改的能力

element apis

requestFullscreen() 进入全屏,与F11全屏不能互通
exitFullScreen() 退出全屏
getBoundingClientRect() 提供了元素的大小及其相对于视口的位置

字符编码

字符编码那点事:快速理解ASCII、Unicode、GBK和UTF-8

drag

ondragenter和ondragover事件的默认行为是拒绝接受任何被拖放的元素。 因此,我们必须阻止浏览器这种默认行为, e.preventDefault()。

ondragleave 有可能会反复触发,项目中实验了下解决方案为判断event.target是否为当前离开元素dom。

多层嵌套的对象

1. a && a.b && a.b.c
// 如果属性不为 null,可以用对象解构
2. var {b:{c:{d:{e} = {}} = {}} = {}} = a
3. let e; try { e = a.b.c.d.e } catch (error) {}
4. lodash之类的库

运算符

~~ : 是按位取反的意思,可以用来强制转int, 向下取整

~:-1的取反操作等于0

&:偶数 & 1 = 0 , 奇数 & 1 = 1

<<: 左移1位是乘2,2位是乘2的2次方,依次类推

>>: 右移1位是除2,2位是除2的2次方,依次类推

**: N次方,例如 i**3

如何转化类数组成数组

常见的类数组

  • getElementsByTagName/ClassName()获得的HTMLCollection
  • querySelector获得的nodeList
  • arguments

那这导致很多数组的方法就不能用了,必要时需要我们将它们转换成数组,有哪些方法呢?

Array.prototype.slice.call()

function sum(a, b) {
  let args = Array.prototype.slice.call(arguments);
  console.log(args.reduce((sum, cur) => sum + cur));//args可以调用数组原生的方法啦
}
sum(1, 2);//3

Array.from()

function sum(a, b) {
  let args = Array.from(arguments);
  console.log(args.reduce((sum, cur) => sum + cur));//args可以调用数组原生的方法啦
}
sum(1, 2);//3

for of, for in, forEach比较

forEach

forEach无法中途跳出forEach循环,break、continue和return都不奏效。
forEach 只支持同步代码,forEach 并不会去处理异步的情况。

for in

for...in 循环只遍历可枚举属性

像 Array和 Object使用内置构造函数所创建的对象都会继承自Object.prototype和String.prototype的不可枚举属性,例如 String 的 indexOf() 方法或 Object的toString()方法。

循环将遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性(更接近原型链中对象的属性覆盖原型属性)。

for...in不应该用于迭代一个 Array,因为无法预计索引顺序。

for in 循环的输出顺序问题

先遍历出整数属性(integer properties,按照升序),然后其他属性按照创建时候的顺序遍历出来。

var obj = {
  a:1,
  '2':1,
  '1':1,
  d:1,
  c:1,
  '3':1
}
obj.b=66;

for (var key in obj) { console.log(key ); }    //"1", "2", "3", "a", "d", "c", "b"
Object.keys(obj);                              //["1", "2", "3", "a", "d", "c", "b"]

Object.keys()和for in 具有相同的排列顺序

for of

有着同for...in一样的简洁语法,但是没有for...in那些缺点。
不同于forEach方法,它可以与break、continue和return配合使用。
支持异步

遍历对象时,推荐使用for in,使用for of会报错

原生具备 Iterator 接口的数据结构如下。

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

JSON

JSON.stringify()还能这么玩

Date

valueOf() 返回date 的毫秒表示
getTime() 返回Date对象与'1970/01/01 00:00:00'之间的毫秒值(北京时间的时区为东8区,起点时间实际为:'1970/01/01 08:00:00') 

if/else优雅写法

日常开发经常会遇到复杂的条件判断, 一般做法就是用if/else, 如何使用更优雅的方式来实现呢?

if(status === 1) {
    sendLog('processing')
    jumpTo('IndexPage')
  } else if(status === 2) {
    sendLog('fail')
    jumpTo('FailPage')
  } else if(status === 3) {
    sendLog('fail')
    jumpTo('FailPage')
  } else {
    sendLog('other')
    jumpTo('Index')
  }

switch

switch (status) {
    case 1:
      sendLog('processing')
      jumpTo('IndexPage')
      break
    case 2:
    case 3:
      sendLog('fail')
      jumpTo('FailPage')
      break
    default:
      sendLog('other')
      jumpTo('Index')
  }

对象映射

const actions = {
  '1': ['processing', 'IndexPage'],
  '2': ['fail', 'FailPage'],
  '3': ['fail', 'FailPage'],
  'default': ['other', 'Index']
}
const clickHandler = (status) => {
  let action = actions[status] || actions['default'], 
    LogName = action[0],
    pageName = action[1]
  sendLog(LogName)
  jumpTo(pageName)
}

map

const actions = new Map([
  ['1', ['processing', 'IndexPage']],
  ['2', ['fail', 'FailPage']],
  ['3', ['fail', 'FailPage']],
  ['default', ['other', 'Index']]
])

const clickHandler = (status) => {
  let action = actions.get(status) || actions.get('default')
  sendLog(action[0])
  jumpTo(action[1])
}

新需求

原先只是判断status的状态, 若现在还需同时判断用户:

if(identity == 'guest') {
    if(status === 1) {
      // to do something
    } else if (status === 2) {
      // to do something
    } else if (status === 3) {
      // to do something
    } else {
      // to do something
    }
  } else if(identity == 'master') {
    if(status === 1) {
      // to do something
    } else if (status === 2) {
      // to do something
    } else {
      // to do something
    }
  }
}

map

const actions = {new Map([
  ['guest_1', () => {/* to do something */}],
  ['guest_2', () => {/* to do something */}],
  ['guest_3', () => {/* to do something */}],
  ['master_1', () => {/* to do something */}],
  ['master_2', () => {/* to do something */}],
  ['master_3', () => {/* to do something */}],
  ['default', () => {/* to do something */}],
])}

上述代码的逻辑是:

  • 把两个条件拼接成字符串
  • 以拼接的条件字符串作为key, 以处理函数作为值的Map对象进行查找并执行

当然, 也可以用Object对象来实现

const actions = {
  'guest_1': () => {/* to do something */},
  'guest_2': () => {/* to do something */},
  'guest_3': () => {/* to do something */},
  'master_1': () => {/* to do something */},
  'master_2': () => {/* to do something */},
  'master_3': () => {/* to do something */},
  'default': () => {/* to do something */}
}

map(以ObjectKey

const actions = new Map([
  [{identity: 'guest', status: 1}, () => {/* to do something */}],
  [{identity: 'guest', status: 2}, () => {/* to do something */}]
  [{identity: 'guest', status: 3}, () => {/* to do something */}]
])

const clickHandler = (identity, status) {
  let action = [...actions].filter((key, value) => {key.identity === identity && key.status === status})
  action.forEach(([key, value]) => {value.call(this)})
}

假如在guest情况下, status 1~4 的处理逻辑是一样的:

const actions = new Map([
  [{identity: 'guest', status: 1}, functionA],
  [{identity: 'guest', status: 2}, functionA],
  [{identity: 'guest', status: 3}, functionA],
  [{identity: 'guest', status: 4}, functionA],
  [{identity: 'guest', status: 5}, functionB],
])

const clickHandler = (identity, status) {
  let action = [...actions].filter((key, value) => {key.identity === identity && key.status === status})
  action.forEach(([key, value]) => {value.call(this)})
}

这样写, 基本也满足需求了, 但重复的写4次functionA, 但如果identity的状态有3种,status的状态有30种呢? 如果是此种情况, 也可以考虑用正则表达式, 如:

const actions = new Map([
  [/^guest_[1-4]$/, functionA],
  [/^guest_5$/, functionA],
])

const clickHandler = (identity, status) {
  let action = [...actions].filter((key, value) => {key.test(`${identity}_${status}`)})
  action.forEach(([key, value]) => {value.call(this)})
}

假如需求变成, 凡是guest的情况, 都要发送一个日志埋码, 不同的status的情况, 也要单独做处理. 那么我们可以考虑这样写:

const actions = new Map([
  [/^guest_[1-4]$/, functionA],
  [/^guest_5$/, functionA],
  [/^guest_.$/, functionC],
])

const clickHandler = (identity, status) {
  let action = [...actions].filter((key, value) => {key.test(`${identity}_${status}`)})
  action.forEach(([key, value]) => {value.call(this)})
}