# 浏览器基础知识点

# 1.事件机制

# 1.1 自定义事件实现

  • createEvent,设置事件类型,是 html 事件还是鼠标事件
  • initEvent 事件初始化,事件名称,是否允许冒泡,是否阻止自定义事件
  • dispatchEvent 触发事件

# 1.2 前端事件流

事件流描述的是从页面中接受事件的顺序:事件捕获阶段-->处于目标阶段-->事件冒泡阶段

  • 捕获阶段:addevenListener 最后这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序
    • 实际目标 div 在捕获阶段不会接受事件,也就是在捕获阶段,事件从 document 到 html 再到 body 就停止了
  • 目标阶段
    • 事件在 div 发生并处理,但是事件处理会被看成是冒泡阶段的一部分。
  • 冒泡阶段:addevenListener 最后这个布尔值参数如果是 false,表示在冒泡阶段调用事件处理程序。
    • 事件又传播回文档

阻止冒泡事件:event.stopPropagation()

function stopBubble(e) {
  if (e && e.stopPropagation) {
    e.stopPropagation()
  } else {
    //ie
    window.event.cancelBubble = true
  }
}
1
2
3
4
5
6
7
8

阻止默认行为:event.preventDefault()

function stopDefault(e) {
  if (e && e.preventDefault) {
    e.preventDefault()
  } else {
    //ie
    window.event.returnValue = false
  }
}
1
2
3
4
5
6
7
8

事件如何先冒泡后捕获

在 dom 标准事件模型中,是先捕获后冒泡。但是如果要实现先冒泡后捕获的效果,对于同一个事件,监听捕获和冒泡,分别对应相应的处理函数,监听到捕获事件,先暂缓执行,直到冒泡事件被捕获后在执行捕获事件。

那些事件不支持冒泡事件:

  • 鼠标事件:mouserleave mouseenter
  • 焦点事件:blur focus
  • UI 事件:scroll resize

# 1.3 事件委托(提高性能)

不在事件的(直接在 dom)上设置监听函数,而是在其父元素上设置监听函数。通过事件冒泡,父元素可以监听到子元素上事件的触发。

  • 目的

    • 通过动态绑定事件,新添加的子元素也会监听函数,减少内存消耗,提高性能
  • 写法

    • 通过判断事件发生元素 dom 的类型,来做出不同的响应。
    • 举例:最经典的就是 ul 和 li 标签的事件监听,比如我们在添加事件的时候,采用事件委托机制,不会在 li 标签上直接添加,而是在 ul 父元素上添加

# 1.4 输入框 change 和 input 事件

  • onchange 事件: 要在 input 失去焦点的时候才触发
  • oninput 事件: 要在用户输入时触发,他是元素之发生变化时立即触发

# 1.5 document load / ready

  • 共同点:
    • 这两种事件都代表的是页面文档加载时触发。
  • 异同点:
    • ready 事件的触发,表示文档结构已经加载完成(不包含图片等非文字媒体文件)。
    • onload 事件的触发,表示页面包含图片等文件在内的所有元素都加载完成。

事件的触发过程是怎么样的?知道什么是事件代码吗?

# 事件触发三个阶段

  • window 往事件触发处传播,遇到注册的捕获事件会触发
  • 传播到事件触发处时触发注册事件
  • 从事件触发处往 window 传播,遇到注册的冒泡事件会触发

事件触发一般来说会按照上面的顺序进行,但是也有特例,如果给一个 body 中的子节点同时注册冒泡和捕获事件,事件触发会按照注册的顺序执行

// 以下会先打印冒泡然后捕获
node.addEventListener(
  "click",
  (event) => {
    console.log("冒泡")
  },
  false
)
node.addEventListener("click", (event) => {
  console.log("捕获")
})
1
2
3
4
5
6
7
8
9
10
11

# 注册事件

通常我使用addEventListener注册事件,该函数的第三个参数可以是布尔值,也可以是对象。对于布尔值useCapture参数来说,该参数默认值为false,useCapture决定了注册的事件是捕获事件还是冒泡事件。对于对象来说,可以使用以下几个属性

  • capture :布尔值,和useCapture
  • once: 布尔值,值为true表示该回调只会调用一次,调用后会移除监听
  • passive:布尔值,表示永远不会调用preventDefault

一般来说,如果我们只希望事件只触发在目标上,这时候可以使用stopPropagation来阻止事件的进一步传播。通常我们认为stopPropagation是用来阻止事件冒泡的,其实该函数可以阻止捕获事件。stopImmediatePropagation同样也能实现阻止事件,但是还能阻止该事件目标执行别的注册事件。

node.addEventListener(
  "click",
  (event) => {
    event.sotpImmediatePropagation()
    console.log("冒泡")
  },
  false
)
// 点击node只会执行上面的函数,该函数不会执行
node.addEventListener(
  "click",
  (event) => {
    console.log("捕获")
  },
  true
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 事件代理

如果一个节点中的子节点是动态生成的,那么子节点需要注册事件的话应该注册在该父节点上

<ul id="ul">
	<li>1</li>
    <li>2</li>
	<li>3</li>
	<li>4</li>
	<li>5</li>
</ul>
<script>
	let ul = document.querySelector('#ul')
	ul.addEventListener('click', (event) => {
		console.log(event.target);
	})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13

事件代理的方式相较于直接给目标注册事件来说,有以下优点:

  • 节省内存
  • 不需要给子节点注销事件

# 3.存储

有几种方式可以实现存储功能,分别有什么优缺点?什么是 Service Worker? CookielocalStoragesessionStorageindexDB

# Service Worker

Service Worker 实现缓存功能一般分为三个步骤:首先需要先注册 Service Worker,然后监听到install事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询时候存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。

// install.js
if (navigator.serviceWorker) {
  navigator.serviceWorker
    .register("sw.js")
    .then(function (registration) {
      console.log("service worker 注册成功")
    })
    .catch(function (err) {
      console.log("service worker 注册失败")
    })
}
// sw.js
// 监听 `install`事件,回调中缓存所需文件
self.addEventListener("install", (e) => {
  e.waitUntil(
    caches.open("my-cache").then(function (cache) {
      return cache.addAll(["./index.html", "./index.js"])
    })
  )
})
// 拦截所有请求事件
// 如果缓存中已经有请求的数据就直接用缓存,否则去请求数据
self.addEventListener("fetch", (e) => {
  e.respondWith(
    caches.match(e.request).then(function (response) {
      if (response) {
        return response
      }
      console.log("fetch source")
    })
  )
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 6.什么是内存溢出和内存泄漏

给的不够用,用了不归还

  • 内存溢出:
    • 在程序申请内存是,没有足够的内存空间供其使用,出现 out of memoryp;比如申请了一个 integer,但是给他存了 long 才能存下的数据,那就是内存溢出。
  • 内存泄漏:
    • 在程序申请内存后,无法释放已申请的内存空间,一次内存泄漏的危害可以忽略,但是内存泄漏堆积的后果很严重,无论多少内存,迟早会被占光
    • 内存泄漏会导致一系列问题,比如:运行缓慢,崩溃,高延迟
  • 哪些操作会造成内存泄漏
    • 闭包中的 this,对象函数
    • 匿名函数返回函数
    • setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏
上次更新: 2023/1/7 下午3:34:52