面试官:随便打开一个网页,如何统计标签总数?

680 阅读3分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

问题

这年头,面试官都不问热门面试题了,可恶的面试官总喜欢另辟蹊径,今天我就看到两道很有意思的题目,学习记录一下。

  • 随便打开一个网页,统计标签总数。
  • 随便打开一个网页,统计页面中出现最多的三种标签及出现次数。

1.随便打开一个网页,统计标签总数

答案:

new Set([...document.getElementsByTagName('*')].map(v => v.tagName)).size

这个题考查了

  • DOM API 的使用熟练度
  • 伪数组转数组
  • 数组去重

知道答案后,一想,不过如此嘛。

但是我刚看到这个题时,大脑是一片空白的,没有思路,只想到估计有个DOM API可以先获取到所有的元素,但具体是哪一个,确实想不起了,天天写 vue,原生的东西都忘完了啊。

获取所有元素

打开控制台,输入:

document.getElementsByTagName('*')
或者 
document.querySelectorAll("*")

获取一个伪数组 HTMLCollection

HTMLCollection 接口表示一个包含了元素(元素顺序为文档流中的顺序)的通用集合(generic collection),还提供了用来从该集合中选择元素的方法和属性。

直接调数组方法会报错:

image.png

伪数组转数组

可以使用 Array.from拓展运算符,效果都是一样的。

Array.from(document.getElementsByTagName('*'))
或者
[...document.getElementsByTagName('*')]

然后就拿到元素名 tagName 了。

然后调数组 map 方法,拿到所有元素集合

[...document.getElementsByTagName('*')].map(v => v.tagName)

image.png

数组去重

Set 实现数组去重。

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

new Set([...document.getElementsByTagName('*')].map(v => v.tagName))

image.png

最后调一下 Set 的 size 属性,返回 Set 实例的成员总数,拿到页面标签总数。

new Set([...document.getElementsByTagName('*')].map(v => v.tagName)).size

2.随便打开一个网页,统计页面中出现最多的三种标签及出现次数

答案:

const arr = [...document.getElementsByTagName("*")].map(tag=>tag.tagName)
const obj = arr.reduce((pre, i)=>{
  pre[i] = (pre[i] || 0) + 1;
  return pre;
}, {})
const res = Object.entries(obj).sort((a, b)=>(b[1] - a[1])).slice(0, 3)
console.table(res)

image.png

这题是上一题的升级版,考察了:

  • 计算数组中每个元素出现的次数
  • Object.entries
  • 根据数组某一项进行排序

用上一题中的方法,先拿到所有的元素集合。

const arr = [...document.getElementsByTagName("*")].map(tag=>tag.tagName)

计算数组中每个元素出现的次数

用 reduce 方法计算数组中每个元素出现的次数,返回一个对象

const obj = arr.reduce((pre, i)=>{
  pre[i] = (pre[i] || 0) + 1;
  return pre;
}, {})

不使用 reduce 方法,也能实现,但使用 reduce 更简洁,而且可恶的面试官多半要让你用 reduce 实现。

const obj = {}
arr.forEach(item => {
  if (!obj[item]) {
    obj[item] = 0
  }
  obj[item]++
})

image.png

Object.entries()

Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。

接着操作上一步的对象
const res = Object.entries(obj)
console.log(res)

根据数组某一项进行排序

然后根据数组第1项(元素个数)排序,截取前3个:

image.png

不使用 Object.entries()

如果不用 Object.entries(),也能做到,但是写起来就要复杂一些了,而且返回的数据结构不一样:

image.png

作为面试官,估计是想考察 Object.entries() 这个 API 的,我也是第一次使用。

不得不感叹,面试官的脑洞是真滴牛,学到了。

参考资料

ES6 Set
Array.prototype.reduce()
Object.entries()
Array.prototype.sort()