面试题答案
一键面试关键实现思路
- 数据劫持:利用
Object.defineProperty()
方法,该方法可以定义或修改对象的属性特性。通过设置configurable
、writable
等特性,对数据对象的属性进行劫持,当属性值发生变化时触发相应的更新操作。 - 依赖收集:在数据读取时,收集依赖该数据的视图更新函数,这样当数据变化时,能够通知到所有依赖它的视图进行更新。
- 视图更新:当数据变化触发更新操作时,调用收集到的视图更新函数,实现视图的实时更新。
代码示例
// 定义一个简单的Observer类,用于数据劫持
class Observer {
constructor(data) {
this.data = data;
this.walk(data);
}
walk(data) {
Object.keys(data).forEach(key => {
this.defineReactive(data, key, data[key]);
});
}
defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
get() {
Dep.target && dep.addSub(Dep.target);
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify();
}
});
}
}
// 定义一个Dep类,用于依赖收集和通知
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
// 定义一个Watcher类,用于关联数据和视图更新函数
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm;
this.cb = cb;
this.getter = parsePath(expOrFn);
this.value = this.get();
}
get() {
Dep.target = this;
const value = this.getter.call(this.vm, this.vm);
Dep.target = null;
return value;
}
update() {
const oldValue = this.value;
this.value = this.get();
this.cb.call(this.vm, this.value, oldValue);
}
}
function parsePath(path) {
if (/[.$]/.test(path)) {
const segments = path.split(/[.$]/);
return function (obj) {
for (let i = 0; i < segments.length; i++) {
if (!obj) return;
obj = obj[segments[i]];
}
return obj;
}
}
return function (obj) {
return obj && obj[path];
}
}
// 简单示例使用
const data = {
message: 'Hello, MVVM!'
};
const observer = new Observer(data);
function updateView() {
document.getElementById('message').innerHTML = data.message;
}
new Watcher(data, 'message', updateView);
// 模拟视图变化更新数据
document.getElementById('input').addEventListener('input', function (e) {
data.message = e.target.value;
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="message"></div>
<input type="text" id="input">
<script src="script.js"></script>
</body>
</html>
以上代码通过数据劫持、依赖收集和视图更新的机制,实现了一个简单的MVVM框架的数据绑定功能。