当前位置: 首页 > news >正文

刷题 | 牛客 - 前端面试手撕题 - 中等 - 1-2/20 知识点解答

知识点总结:

DOM0级标准事件(onclick)——》dom.onclick。 如 document.querySelector('ul').onclick

事件委托,event.target 指当前点击的这个元素


FED1 事件委托

描述

请补全JavaScript代码,要求如下:
1. 给"ul"标签添加点击事件
2. 当点击某"li"标签时,该标签内容拼接"."符号。如:某"li"标签被点击时,该标签内容为".."
注意:
1. 必须使用DOM0级标准事件(onclick)


解答

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><style>/* 填写样式 */ul {list-style: none;padding: 0;}li{cursor: pointer;font-size: 20px;padding: 5px;}li: hover{background-color: #f0f0f0;}</style>
</head><body><ul><li>.</li><li>.</li><li>.</li></ul><!-- 填写标签 --><script type="text/javascript">// 填写JavaScriptdocument.querySelector('ul').onclick = event => {// 法一:事件委托 + tagNameif(event.target.tagName === 'LI'){event.target.innerHTML += '.';} // 法二:事件委托+innerText,潜在问题:点击到非li的ul区域内,会在空白处添加.event.target.innerText += '.'// 法三:事件委托 + nodeName,将其转小写event = event || window. Event;if(event.target.nodeName.toLowerCase() === 'li'){event.target.innerText += '.'}}// 拓展:选中所有的 li,遍历每个li, 给它绑定点击事件// 结果:对本事件无效。原因:样例输入也是 DOM0级,每个事件只能绑定一次,后面会覆盖前面的。/* const lis = document.querySelectorAll('li');lis.forEach(li => {li.onclick = function(){this.innerHTML += '.';}}) */</script>
</body></html>

分析:

法一:事件委托 + tagName

if (event.target.tagName === 'LI') { event.target.innerHTML += '.'; }

  • tagName 返回元素的标签名(HTML 下总是大写,比如 "UL", "LI")。

  • 所以必须写成 "LI" 才能匹配。

  • 优点:直观、常见。

  • 缺点:大小写敏感,不同文档类型(HTML vs XHTML)可能行为不同。


法二:事件委托 + innerText

event.target.innerText += '.';

  • 不做判断,直接加点。

  • 风险:如果点到的是 <ul> 空白区event.target 变成 <ul>,那就会在整个 <ul> 的文本后面加点,逻辑错误。

  • 优点:代码简洁。

  • 缺点:不安全,容易误操作。


法三:事件委托 + nodeName.toLowerCase()

event = event || window.event; // 兼容性写法

if (event.target.nodeName.toLowerCase() === 'li') { event.target.innerText += '.'; }

✨ 重点解释:

  1. nodeName 是什么?

    • DOM 节点的属性,返回该节点的名字

    • 对于 元素节点nodeType === 1),返回的就是标签名

    • 在 HTML 文档中,返回的大写字符串(例如 "LI", "UL")。

    • 在 XML / XHTML 中,nodeName 会区分大小写(比如你写 <Li> 就真的是 "Li")。

  2. 为什么转小写?

    • 保证兼容性,不受 HTML / XHTML / XML 的大小写差异影响。

    • 写成 nodeName.toLowerCase() === 'li' 就能稳定匹配。

  3. tagName 的区别:

    • tagName 只对 元素节点有意义。

    • nodeName 对 所有节点都有意义:

      • 元素节点:返回标签名 (DIV, LI)

      • 文本节点:返回 #text

      • 注释节点:返回 #comment

    • 所以 nodeName 更通用。

  4. 这里的作用:

    • 确认点击目标确实是 <li> 元素,而不是 <ul> 或其它节点。

    • toLowerCase() 保证跨浏览器 / 文档类型下都能稳定匹配。


✅ 总结:

  • 法一:常规做法,简单直接,但大小写固定要写 "LI"

  • 法二:简洁,但有潜在 bug(点空白会误加)。

  • 法三:最稳妥的写法 ——

    • nodeName 判断,适用于更多情况。

    • 转小写避免大小写不一致问题。

    • 推荐在写通用组件 / 库的时候用这种方式。


知识点:事件委托(event) & DOM0级标准事件(onclick)

在 HTML 中,tagName 默认返回 大写

关键点: event.target 

  • event.target  表示  实际被点击的那个元素  

👉 在 DOM 里,点击“空白”其实也是点到某个元素。比如:

  • 点击 <li>  →  event.target 是这个 <li>

  • 点击 <ul> 的 padding / margin 区域  →  event.target<ul> 本身


  • 事件委托(event.target)

    • 只绑定一次事件在 ul 上,节省内存,适合元素很多、动态添加的场景。

  • 逐个绑定(①const lis = document.querySelectorAll('li');  ②lis.forEach(li => { li.onclick = function(){} })

    • 每个 <li> 单独有事件,代码直观,但如果 <li> 很多,性能稍差


FED2 数组去重

描述

请补全JavaScript代码,要求去除数组参数中的重复数字项并返回该数组。
注意:
1. 数组参数仅包含数字

示例1

输入:

_deleteRepeat([-1,1,2,2])

输出:

[-1,1,2]

法一:ES6 的 Set (最简洁,O(n))

return [...new Set(array)];
或者
return Array.from(new Set(array));

new Set(array) 返回类数组对象,Array.from 将类数组对象转为真正的数组。


法二:传统写法(进阶)(如 新数组 + includes)

const arr = [];
array.forEach(item => {if(!arr.includes(item)){arr.push(item);}
})

forEach 是元素值循环,即 item是元素值 —》forEach 第一个参数是 元素值,第二个参数是 索引。——》forEach((item, index) => ...)

for是索引循环

时间复杂度是 O(n²),对大数组性能不太好


法三:算法思路(排序+相邻比较)

array.sort((a, b)=> a - b);
const arr = array.filter((item, i) => i === 0 || item !== array[i - 1])l

先用.sort 进行排序,再用 filter 进行过滤,保留第一个元素 以及 之后的 元素与前一个元素不同的,即不重复的;否则,过滤掉。

时间复杂度是 O(n log n)
但是会改变原数组顺序,适合只关注结果、不关心顺序的情况。


法四:哈希表(性能更优)

const seen = {};
const arr = array.filter(item => {if(!seen[item]){seen[item] = true;return true;}return false;
});

使用 普通对象 当作哈希表,时间复杂度是O(n),性能更好。

缺点:对象键会被转为字符串,比如数字 1 和字符串 "1" 会冲突。——》解决:使用 Map


法五: ★ 对象数组去重(★ 实战场景:Map按 id 去重)

数组里存的是对象:[{id:1,name:'A'},{id:1,name:'A'},{id:2,name:'B'}] 。       

使用 Map 按 id 去重

const arr = [...new Map(array.Map(item => [item.id, item])
).values()];

保留第一次出现的对象,同时去除重复id的对象。

在实际项目中常见,如接口返回的用户列表去重。


总结最简洁 用 Set——》思路 用序 ——》性能 用 哈希表或Map ——》 实际开发常见 对象数组按 id 去重


所有的代码,以及其他方法(上述是几个重要的方法):

const arr = [...new Map(array.Map(item => [item.id, item])
).values()];<!DOCTYPE html>
<html><head><meta charset=utf-8></head><body><script type="text/javascript">const _deleteRepeat = array => {// 补全代码/* 数组去重 */// 法一:arr.includes + push(不重复者 添加到新数组中)[以下4种 即可]let arr = []let len = array.length;/*for(let i=0;i < len;i++){if(!arr.includes(array[i])){arr.push(array[i]);}}return arr; */// 注意点:forEach 中的参数 item 是对应的值/* array.forEach(item => {if(!arr.includes(item)){arr.push(item);}})return arr;*/// map 中属性名不可重复/* const map = new Map();array.forEach(item => {if(!map.has(item)){map.set(item, true);arr.push(item);}})return arr; */// 对象属性名不可重复/* const obj = {}array.forEach(item => {if(!obj[item]){obj[item] = true;arr.push(item);}})return arr;*/// 法二:Set + Array.from 方法(es6方法):new Set(array) 返回类数组,可以用 Array.from 转为真正的数组// return Array.from(new Set(array));return [...new Set(array)];// 法三:数组的 reduce 累加器(累加数,当前的值)/* const newArr = array.reduce((pre, cur) => {if(pre.includes(cur) === false){pre.push(cur);}return pre;}, []);return newArr;*/}</script></body>
</html>

知识点:数组去重总结,见 手撕代码 | 知识点总结 - 数组去重-CSDN博客


http://www.dtcms.com/a/422690.html

相关文章:

  • 建立自动化SSL证书更新机制与多层监控体系
  • 岚图汽车 x Apache Doris : 海量车联网数据实时分析实践
  • chrome-devtools-mcp windows 环境安装
  • IOT_通讯控制器(IO模块)
  • 分布式计数器系统完整解决方案
  • 音频类AI工具扩展
  • PyCharm 开发 Python 项目后,将其打包并部署到 Nginx 服务器
  • 在 Trae 国际版中添加 Chrome Dev MCP Server(Windows 实战指南)
  • 个人商城网站备案互联网域名是什么意思
  • 太原微信网站商城网站建设定制
  • VR 太阳光参数与快速渲染
  • 垃圾分类魔法互动墙-垃圾分类展厅设备-VR垃圾分类软件
  • 九、Proteus817实现51单片机DHT22温湿度读取
  • 家庭录像损坏了无法播放?视频修复让回忆重现
  • 【StarRocks】-- 深入理解 StarRocks 窗口函数 LAG()
  • [C++项目组件]Elasticsearch简单介绍
  • 网站建设公司的服务15年做哪些网站致富
  • 学做软件的网站有哪些怎么制作网站后台
  • Wyn 商业智能软件:3D 可视化大屏搭建与工具使用全指南
  • 【Linux】IPC——匿名管道的使用
  • 重庆市建设医院网站首页网站服务器租用一年多少钱啊
  • Process Explorer 第四章 · Autoruns 基础知识——通俗易懂
  • Spring Boot 3.x 开发 Starter 快速上手体验,通过实践理解自动装配原理
  • 如何通过配置扩展服务函数的返回对象
  • 手工生成DuckDB 1.4版c++插件的简单步骤
  • linux进程生命周期
  • 单机游戏大全网站开发wordpress模板获取数据库
  • wap网站设计方案做一款网站注意啥
  • Flask项目中CSRF Token实现的解决方案
  • 使用 Kubernetes(k8s) 搭建 Redis 3 主 3 从集群教程