MST

星途 面试题库

面试题:JavaScript数据绑定与属性特性的关联

假设你正在使用JavaScript开发一个简单的MVVM框架,阐述如何利用对象的属性特性(如configurable、writable等)来实现数据绑定,使得数据的变化能够实时反映在视图上,同时视图的变化也能更新数据。请给出关键实现思路和相关代码示例。
20.1万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

关键实现思路

  1. 数据劫持:利用 Object.defineProperty() 方法,该方法可以定义或修改对象的属性特性。通过设置 configurablewritable 等特性,对数据对象的属性进行劫持,当属性值发生变化时触发相应的更新操作。
  2. 依赖收集:在数据读取时,收集依赖该数据的视图更新函数,这样当数据变化时,能够通知到所有依赖它的视图进行更新。
  3. 视图更新:当数据变化触发更新操作时,调用收集到的视图更新函数,实现视图的实时更新。

代码示例

// 定义一个简单的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框架的数据绑定功能。