事件冒泡

事件冒泡(Event Bubbling) 是 DOM 事件传播的一种机制,指当一个元素触发事件后,事件会按照 “从触发元素到顶层元素” 的顺序逐级向上传播,就像气泡从水底逐渐浮到水面一样。

1
2
3
4
5
<div id="grandparent" onclick="console.log('祖父元素被点击')">
<div id="parent" onclick="console.log('父元素被点击')">
<button id="child" onclick="console.log('子元素被点击')">点击我</button>
</div>
</div>

控制台输出:

1
2
3
子元素被点击
父元素被点击
祖父元素被点击

点击事件从按钮(最内层元素)依次向上传播到父元素和祖父元素。
这会对父元素造成污染, 我们可以使用 event.stopPropagation() 方法:来阻止事件冒泡

应用:事件委托
事件委托(Event Delegation):利用冒泡机制,将子元素的事件处理逻辑统一绑定到父元素上,实现 “一次绑定,多个元素共享

不会冒泡的事件:

  • focus、blur、submit、reset、change、input,load、unload、error,resize。scroll、mouseenter、mouseleave

MessageChannel

MessageChannel 是 JavaScript 中用于创建两个通信端口(port1 和 port2)的 API,这两个端口之间可以通过发送消息进行双向通信,形成一个私有、异步的通信通道。

  • 俩个端口通信是双向的,都可以发送和接受消息
  • 消息通过 postMessage() 发送,通过 onmessage 接收
  • 消息传递是异步的,不会阻塞主线程

私有的,高效地,可传输二进制数据

async、 await 实现原理

基于 promise 和 generator 实现的一个异步操作的语法糖。
Generator 函数(生成器)是一种可以暂停执行和恢复执行的函数,通过 yield 暂停异步操作,next 恢复执行,但需要手动嵌套调用 next()。
当 async 函数被调用时:

  • async 会返回一个新的 Promise()
  • 进入函数内部执行同步代码,知道遇到 await
  • await 会暂停当前上下文,执行 await 之后的回调函数
  • 若执行成功,将 await 的结果返回,继续执行后续代码
  • 若失败可由 try/catch 抛出异常

基于 Promise 实现处理异步操作的状态,用 Generator 来实现代码的阻塞

Proxy

Proxy 是 JavaScript 用于创建对象代理的内置对象,可以拦截自定义对象的基本操作,从而实现对对象的控制

Proxy 就是将目标对象的操作会先经过这个中间层来进行一些自定义逻辑(验证、拦截、日志记录等)
语法const proxy = new Proxy(target, handler);

  • target:目标对象,可以使对象、数组、函数
  • handler:配置对象,用于拦截对象的而做出的操作
  • proxy:返回的对象,通过 proxy 来对目标对象进行操作
    示例:
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
// 目标对象
const user = { name: "张三", age: 20 };

// 创建代理
const userProxy = new Proxy(user, {
// 拦截属性读取
get(target, prop) {
console.log(`读取了属性 ${prop}`);
// 可以自定义返回值,比如对不存在的属性返回默认值
return prop in target ? target[prop] : "属性不存在";
},

// 拦截属性设置
set(target, prop, value) {
console.log(`设置了属性 ${prop} = ${value}`);
// 可以添加验证逻辑
if (prop === "age" && (value < 0 || value > 150)) {
throw new Error("年龄必须在 0-150 之间");
}
target[prop] = value; // 允许设置
return true; // 表示设置成功
},
});

// 通过代理操作对象
console.log(userProxy.name); // 输出:读取了属性 name → 张三
userProxy.age = 25; // 输出:设置了属性 age = 25
userProxy.age = 200; // 抛出错误:年龄必须在 0-150 之间
console.log(userProxy.gender); // 输出:读取了属性 gender → 属性不存在

Proxy 本身不能自动监听嵌套对象的内部变化,但通过递归创建代理的方式,可以实现对对象中所有层级的属性操作进行监听,包括嵌套对象的引用和内部变化。

解构

用于从数组或对象中提取值,并将其赋值给变量
解构数组

  • const [x, y, z] = arr;
  • const [first, , third] = [1, 2, 3];
  • const [a, …rest] = [1, 2, 3, 4];
  • const [x, y = 20] = [10]; // 数组只有一个元素

解构对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 方式一 解构方式(按属性名匹配)
const user = { name: "张三", age: 20, gender: "男" };
const { name, age } = user; // 等价于 { name: name, age: age }
const { name: userName, age: userAge } = user;

// 嵌套对象解构
const obj = {
a: 1,
b: { c: 2, d: 3 },
};
const {
a,
b: { c: cVal },
} = obj;
console.log(a, cVal); // 1 2

数组解构依赖普通对象时会报错,因为数组解构对象依赖可迭代对象
实现方法:

  1. 将对象转化为数组
  2. 让对象实现迭代器接口

作用域

作用域(Scope) 是指变量、函数和对象的可访问范围,它决定了代码中不同部分对变量的可见性和访问权限。

  1. 不同作用域中的同名变量不会相互干扰
  2. 变量只能在其声明的作用域及嵌套的子作用域中被访问。
  3. 作用域会影响变量的生命周期

全局作用域:全局作用域中声明的变量(全局变量)在代码任何地方都能访问,生命周期与程序一致
函数作用域:函数内声明的变量(var/let/const)只能在函数内部及嵌套函数中访问,函数执行结束后,其作用域内的变量通常会被销毁(闭包除外)。
块级作用域


作用域链:当访问一个变量时,JavaScript 会按以下规则查找:

先在当前作用域中查找,如果找到则使用。
如果没找到,就向上查找父级作用域。
依次类推,直到全局作用域。如果全局作用域也没有,就会报错(xxx is not defined)。这种由内到外的查找链条,称为作用域链


变量提升是指变量声明会被 “提升” 到其作用域的顶部,但赋值不会。不同声明方式的提升规则不同:

var:声明会提升到作用域顶部,默认值为 undefined。
let/const:声明也会提升,但会形成 “暂时性死区”(TDZ),在声明前访问会报错。


作用域与闭包的关系:闭包(Closure)是作用域的特殊应用:当内层函数引用了外层函数作用域中的变量,且内层函数被返回到外层作用域之外时,外层函数的作用域不会被销毁,内层函数依然能访问其中的变量。允许内层函数长期访问外层作用域的变量。

bind、call、apply

bind、call、apply 都是 JavaScript 中函数对象的方法,用于改变函数执行时的 this 指向

  • call():立即执行函数。传递参数的方式是逐个传入
  • apply():立即执行函数。传递参数的方式是数组或类数组对象(参数放在数组中)。
  • bind():不会立即执行函数,而是返回一个新的函数(绑定了新 this 的函数副本)。可以预先传入部分参数(柯里化,Currying),剩余参数在调用新函数时传入

柯里化

指的是将一个接收多个参数的函数,转换为一系列只接收单一参数(或部分参数)的函数链。其核心思想是 “分步传递参数”,每次传递部分参数并返回一个新函数,直到所有参数传递完毕后才执行最终逻辑。

柯里化本质是利用闭包保存每次传递的参数,直到参数齐全后执行函数。

Vue3 响应式设计原理

当数据发生变化时,依赖该数据的视图会自动更新。核心基于 ES6 的 Proxy API,配合 依赖收集机制 实现更高效、更全面的响应式。使用 Proxy 作为核心拦截器

  1. 当我们使用 reactive 将对象或数组转化为响应式数据时,内部通过 Proxy 创建代理对象,并通过 Reflect 反射操作原对象,确保不破坏原对象的默认行为
  2. 依赖收集:当响应式数据的属性被访问时(如在组件渲染或 effect 中读取 obj.xxx),track 函数基于 WeakMap 数据结构来存储会记录数据与关联的函数一个依赖关系
  3. 更新函数:当响应式数据的属性被修改时(如 obj.xxx = newValue),trigger 函数会从依赖表中找到该属性对应的所有副作用函数,并执行它们
  4. 副作用函数: