Цель: понять, как Object.defineProperty реализует перехват данных.
Время чтения: 3 минуты
Общий принцип таков:
- Определите функцию прослушивателя для мониторинга каждого свойства объекта.
- Установите методы получения и установки для каждого отслеживаемого свойства с помощью Object.defineProperty.
- следить за объектом
- Обработка объектов, встроенных в объекты
- работать с объектами массива
1. Сначала определите объект
let obj = {
name: 'jw'
}
2. Определите функцию слушателя
/**
* 判断监听的是否是对象
* 如果是对象,就遍历,并且对每个属性进行定义get 和 set
*/
function observer(obj) {
if(typeof obj === 'object') {
for (let key in obj) {
// defineReactive 方法设置get和set,见第三步
defineReactive(obj, key, obj[key]);
}
}
}
3. Определите функцию, которая обрабатывает каждое свойство
function defineReactive(obj, key, value) {
Object.defineProperty(obj, key, {
get() {
return value;
},
set(val) {
console.log('数据更新了')
value = val;
}
})
}
ok Первая версия была реализована здесь. попытайся
observer(obj);
obj.name = 'haha'
控制台输出:
//数据更新了
Вышеупомянутое было реализовано для установки свойств obj, это отслеживается, и некоторый код может быть выполнен. Но что, если в объекты встроены объекты? Например:
let obj = {
name: 'jw',
age: {
old: 60
}
}
выполнить следующий код
observer(obj);
obj.age.old = '50'
控制台输出: 空
4. Итеративно обработать отслеживаемый объект
// 修改defineReactive , 添加一行代码
function defineReactive(obj, key, value) {
// 如果对象的属性也是一个对象。迭代处理
observer(value);
Object.defineProperty(obj, key, {
//....
})
}
Снова выполните следующий код:
observer(obj);
obj.age.old = '50'
控制台输出:
//数据更新了
К сожалению, Object.defineProperty не работает, если объект представляет собой массив, например:
obj.skill = [1, 2, 3];
obj.skill.push(4);
控制台输出:
//空
На самом деле, не только push, но и slice, shift, unshift... все это не имеет никакого эффекта.
5. Переопределение методов массивов
let arr = ['push', 'slice', 'shift', 'unshift'];
arr.forEach(method=> {
let oldPush = Array.prototype[method];
Array.prototype[method] = function(value) {
console.log('数据更新了')
oldPush.call(this, value)
}
})
Снова выполните следующий код:
obj.skill = [1, 2, 3];
obj.skill.push(4);
控制台输出:
//数据更新了
Однако операция длины массива по-прежнему недействительна. Вот почему массив можно изменить только с помощью методов Vue.
Резюме: Object.defineProperty решает только проблему запуска уведомлений после изменения состояния.Кого следует уведомлять? Кого волнует, что эти свойства изменились? Скажу тебе позже.
Спасибо за прочтение!
Меня зовут Хай Миньюэ, я первокурсник начальной школы.
Полный код ниже
let obj = {
name: 'jw',
age: {
old: '60'
}
}
// vue 数据劫持 Observer.defineProperty
function observer(obj) {
if(typeof obj === 'object') {
for (let key in obj) {
defineReactive(obj, key, obj[key]);
}
}
}
function defineReactive(obj, key, value) {
observer(value);
Object.defineProperty(obj, key, {
get() {
return value;
},
set(val) {
console.log('数据更新了')
value = val;
}
})
}
observer(obj);
// obj.age.old = '50'
// Object.defineProperty 对 数组无效
let arr = ['push', 'slice', 'shift', 'unshift'];
arr.forEach(method=> {
let oldPush = Array.prototype[method];
Array.prototype[method] = function(value) {
console.log('数据更新了')
oldPush.call(this, value)
}
})
obj.skill = [1, 2, 3];
obj.skill.push(4);