defineProperty 是 object 原型上的一个方法
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
Object.defineProperty()(obj, prop, descriptor)
参数:obj 需要定义属性的当前对象,即要被监听的对象
prop 当前监听对象的属性名,
descriptor:对这个对象的配置,比如属性是否可读 是否可枚举,我们这次会用到它中的 get(),set()方法
let data = {
name: "zhangxiaozhuo",
age: 18,
};
let value = data.name;
Object.defineProperty(data, "name", {
get() {
// get()方法为获取,当我们读取data对象中的属性时就会执行get()方法
console.log("读");
return value;
//return 的值是什么,那么我们获取的值就是什么
},
set(newVal) {
//参数newVal:被重新赋予的值
//set()方法为设置方法,当我们改变data对象中的属性时就会执行get()方法
console.log("写");
value = newVal;
},
});
data.name = "zhangshuai";
console.log(data.name);
// 写
// 读
// zhangshuai
我们可以把它放在一个函数里
let data = {
name: "zhangxiaozhuo",
age: 18,
};
function defineReactive(data, key, value) {
Object.defineProperty(data, key, {
get() {
// get()方法为获取,当我们读取data对象中的属性时就会执行get()方法
console.log("读");
return value;
//return 的值是什么,那么我们获取的值就是什么
},
set(newVal) {
//参数newVal:被重新赋予的值
//set()方法为设置方法,当我们改变data对象中的属性时就会执行get()方法
console.log("写");
value = newVal;
render(); //
},
});
}
//data数据会有多个属性 所以我们需要遍历它
for (const key in data) {
defineReactive(data, key, data[key]);
}
//当我们执行更改data的属性时 函数就会被渲染
function render() {
console.log("函数渲染了");
}
data.name = 1234;
当 data 对象里面中还有对象时,我们可以进行递归操作,观察 value 是否为对象,如果为对象时,那么会继续递归执行
let data = {
name: "zhangxiaozhuo",
age: 18,
love: {
he: "hah",
},
};
function defineReactive(data, key, value) {
observe(value);
//当data对象里面中还有对象时,我们可以进行递归操作,观察value是否为对象,如果为对象时,那么会继续递归执行
Object.defineProperty(data, key, {
get() {
// get()方法为获取,当我们读取data对象中的属性时就会执行get()方法
console.log("读");
return value;
//return 的值是什么,那么我们获取的值就是什么
},
set(newVal) {
//参数newVal:被重新赋予的值
//set()方法为设置方法,当我们改变data对象中的属性时就会执行get()方法
console.log("写");
if (value === newVal) {
//如果我们设置的是属性和原来相同,那么不会让render()函数渲染
return;
}
value = newVal;
render(); //
},
});
}
//data数据会有多个属性 所以我们需要遍历它
function observe(data) {
if (typeof data === "object") {
for (const key in data) {
defineReactive(data, key, data[key]);
}
}
}
//当我们执行更改data的属性时 函数就会被渲染
function render() {
console.log("函数渲染了");
}
observe(data);
// data.name = "zhangxiaozhuo"
data.love.he = "124";
// console.log(data.love.he);
//我们不能给对象增加和修改值
data.arr[0]=100;
//在 vue、中不会观察数组变化 执行 data.arr[100] =100 监听不到 因为数组也是一个对象,这是是增加行为,那么就不会监听 这就是为什么 vue 不能根据索引增加数组的
在我们写项目时会有大量的数据存在数组中,如果根据索引遍历数组中的每一项,会比较浪费性能,所以 vue 中不会用 defineReactive 观察数组的变化
但是它会利用数组的变异方法实现页面渲染,改写了数组方法
在源码中改写了数组方法,让它的方法在数组里面可以执行 render 函数,在执行 render 函数之前会执行他的原型的功能 也就是执行原型链数组的方法
let data = {
name: "zhangxiaozhuo",
age: 18,
love: {
he: "hah",
},
arr: [1, 2],
};
function defineReactive(data, key, value) {
observe(value);
Object.defineProperty(data, key, {
get() {
// get()方法为获取,当我们读取data对象中的属性时就会执行get()方法
console.log("读");
return value;
//return 的值是什么,那么我们获取的值就是什么
},
set(newVal) {
//参数newVal:被重新赋予的值
//set()方法为设置方法,当我们改变data对象中的属性时就会执行get()方法
console.log("写");
if (value === newVal) {
//如果我们设置的是属性和原来相同,那么不会让render()函数渲染
return;
}
value = newVal;
render(); //
},
});
}
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
["push", "pop", "shift", "unshift", "sort", "splice", "reverse"].forEach(
(method) => {
arrayMethods[method] = function () {
arrayProto[method].call(this, ...arguments);
render();
};
}
);
function observe(data) {
if (typeof data === "Array") {
data.__proto_ = arrayMethods;
return;
}
if (typeof data === "object") {
for (const key in data) {
defineReactive(data, key, data[key]);
}
}
}
//当我们执行更改data的属性时 函数就会被渲染
function render() {
console.log("函数渲染了");
}
observe(data);
总结代码
const data = {
name: 'shanshan',
age: 18,
shan: {
name: 'shanshan',
age: 18,
obj: {}
},
arr: [1, 2, 3]
}
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift', 'unshift' ,'sort', 'splice', 'reverse'].forEach(method => {
arrayMethods[method] = function () {
arrayProto[method].call(this, ...arguments);
render();
}
})
function defineReactive (data, key, value) {
observer(value);
Object.defineProperty(data, key, {
get () {
return value;
},
set (newVal) {
if(value === newVal) {
return;
}
value = newVal;
render();
}
})
}
function observer (data) {
if(Array.isArray(data)) {
data.__proto__ = arrayMethods;
return;
}
if(typeof data === 'object') {
for(let key in data) {
defineReactive(data, key, data[key])
}
}
}
function render () {
console.log('页面渲染啦');
}
function $set (data, key, value) {
if(Array.isArray(data)) {
data.splice(key, 1, value);
return value;
}
defineReactive(data, key, value);
render();
return value;
}
function $delete(data, key) {
if(Array.isArray(data)) {
data.splice(key, 1);
return;
}
delete data[key];
render();
}
observer(data);
利用Object.defineProperty实现响应式的劣势
天生就需要进行递归
监听不到数组不存在的索引的改变
监听不到数组长度的改变
监听不到对象的增删