后序我会更新一系列的前端面试题,感兴趣的可以关注一手
Vue 2 的响应式原理是通过使用 Object.defineProperty() 方法来劫持对象的属性,从而实现对数据的监听和响应。
分为以下几点
1.数据劫持
var data = { msg: 'Hello, Vue!' };
Object.defineProperty(data, 'msg', {
get() {
console.log('获取数据');
return val;
},
set(newVal) {
console.log('设置数据');
val = newVal;
},
});
在上述示例中,我们通过 Object.defineProperty() 方法定义了一个名为 ‘msg’ 的属性,并为它提供了 getter 和 setter。当我们读取 ‘msg’ 属性时,会执行 get() 函数;当我们修改 ‘msg’ 属性时,会执行 set() 函数。
2.依赖收集
var dep = new Dep();
var watcher = new Watcher();
Object.defineProperty(data, 'msg', {
get() {
if (Dep.target) {
dep.addSub(Dep.target);
}
return val;
},
set(newVal) {
val = newVal;
dep.notify();
},
});
在上述示例中,我们定义了一个名为 ‘dep’ 的依赖管理器,以及一个名为 ‘watcher’ 的监听器。当 ‘msg’ 属性被获取时,会判断当前是否存在正在监听的 Watcher 对象,如果存在则将该 Watcher 添加到 ‘dep’ 的订阅列表中。
3.派发更新
var Dep = function() {
this.subs = [];
};
Dep.prototype.addSub = function(sub) {
this.subs.push(sub);
};
Dep.prototype.notify = function() {
for (var i = 0; i < this.subs.length; i++) {
this.subs[i].update();
}
};
在上述示例中,我们定义了一个名为 ‘Dep’ 的依赖管理器,并添加了添加订阅者和通知订阅者更新的方法。当属性值发生改变时,会调用 ‘Dep’ 的 notify() 方法,遍历依赖列表并触发每个 Watcher 对象的 update() 方法。
!!!值得注意的是:Vue 2 的响应式原理仅适用于初始化过程中已经存在的属性,无法监听新添加的属性或删除的属性。如果需要监听动态添加或删除的属性,可以使用 Vue.set() 或 Vue.delete() 进行操作。
Vue 3 的响应式原理是使用 Proxy 对象实现的。Proxy 是 ES6 引入的一个功能,它可以拦截并自定义对象的操作。
在 Vue 3 中,当你创建一个响应式对象时,Vue 内部会使用 Proxy 对象来包装这个对象。Proxy 对象通过拦截对响应式对象的访问,使得 Vue 可以追踪到对响应式对象的修改,并触发相应的更新。
在 Vue 3 中,使用 reactive() 函数和 Proxy 对象来创建响应式对象。具体而言,当你通过 reactive() 函数将一个普通对象转换为响应式对象时,Vue 会使用 Proxy 对象对这个对象进行包装。
响应式对象的创建:
import { reactive } from 'vue';
const state = reactive({
count: 0,
name: 'John',
});
上述代码中,以 reactive() 函数将普通对象 { count: 0, name: ‘John’ } 转换为一个响应式对象 state。现在,当你修改 state 中的属性时,Vue 会捕获到这个操作,并触发相应的更新。
如何触发更新?
当你修改了响应式对象的属性时,Vue 会根据依赖收集器(Dependency Tracking)中的关联关系,将相关的代码块进行重新执行,进而更新视图。
依赖收集器的建立:
在 Vue 3 中,可以使用 effect() 函数来建立依赖收集器。effect() 函数接收一个函数作为参数,这个函数内部可能会访问响应式对象的属性。当响应式对象的属性发生变化时,与其相关联的 effect() 函数会重新执行。
示例:
import { reactive, effect } from 'vue';
const state = reactive({
count: 0,
});
effect(() => {
console.log('Count changed:', state.count);
});
上述代码中,我们通过 effect() 函数创建了一个依赖收集器。在 effect() 函数内部,我们访问了 state.count 属性。当 state.count 属性发生变化时,在控制台输出相应的消息。
响应式对象的属性访问和更新:
使用响应式对象的属性与普通对象类似,可以通过点符号或方括号来访问和更新属性的值。
示例:
console.log(state.count); // 访问 count 属性的值
state.count = 1; // 修改 count 属性的值
上述代码中,我们访问了响应式对象 state 的 count 属性,并将其修改为新的值。
!!!需要注意的是,响应式系统只能追踪到在初始化过程中访问过的属性。如果后续新增属性需要响应式,可以使用 Vue.set() 方法或者直接赋值一个新的响应式对象。
1.Proxy vs Object.defineProperty:
2.性能优化:
3.依赖收集:
4.生命周期钩子:
5.Computed 和 Watch:
6.总结
总的来说,Vue 3 的响应式系统经过重新设计和优化,使用了 Proxy 来取代 Object.defineProperty,提供了更好的性能和更广泛的拦截能力。它还引入了更轻量级的依赖收集器,并对生命周期钩子和计算属性、侦听器做了一些改进。这些变化使得 Vue 3 更加高效、灵活和易用。
作用域
作用域链
作用域链的执行机制
具体来说,当一个函数被创建时,会创建一个保存变量和函数声明的内部对象,称为“活动对象”(Activation Object)。函数执行时,会创建一个称为“执行环境”(Execution Context)的内部对象,它包含了当前执行中的代码所需的全部信息,包括变量、函数等。
在作用域链中,每个执行环境都有一个关联的“变量对象”(Variable Object),它用于存储该执行环境中定义的变量和函数。执行环境的变量对象作为作用域链的一部分,形成了一个链式结构。
当访问一个变量时,解释器首先在当前执行环境的变量对象中查找,如果找不到,则沿着作用域链向上一级执行环境的变量对象中查找,直到找到变量或抵达全局执行环境。如果在最顶层的全局执行环境中也找不到该变量,则视为未定义
作用域链的底层原理是什么?
1.词法环境(Lexical Environment):
2.标识符解析(Identifier Resolution):
具体流程
在实际的查找过程中,当解释器遇到一个变量引用时,会根据当前的词法环境和作用域链进行标识符解析。如果在当前词法环境中找到了对应的变量,则返回该变量的值;否则,解释器会按照作用域链的顺序依次查找,直到找到变量或抵达全局词法环境。
需要注意的是,一旦变量被找到后,解释器会停止进一步的查找,这就是标识符解析的过程。通过作用域链的层层嵌套和标识符解析的机制,JavaScript 实现了变量在不同作用域中的访问和使用。
原型(Prototype):
原型链(Prototype Chain):
创作不易,要是本文章对广大读者有那么一点点帮助 不妨三连支持一下,您的鼓励就是博主创作的动力