let per = {
name:"小明",
age:"20",
sex:"男",
hobby:{
ball:"basketball"
},
course:[{
name:"高数"
},{
name:"大学英语"
}]
}
//数组常用操作
let ARRAY_METHOD = [
'push',
'pop',
'shift',
'unshift',
'reverse',
'sort',
'splice'
];
//修改数组原型链结构,调用数组方法的时候,增加响应式处理
//获取数组的所有原型方法
let array_methods = Object.create(Array.prototype);
ARRAY_METHOD.forEach(method=>{
//重新定义方法
array_methods[method] = function(){
//将方法传进来的数据进行响应式化
for(let i = 0; i < arguments.length; i++){
reactify(arguments[i]);
}
let res = Array.prototype[method].apply(this,arguments);
return res;
}
})
/*
Object.defineProperty(obj, prop, descriptor)
三个参数:
obj:要定义属性的对象
prop:要定义或修改的属性的名称或 Symbol
descriptor:要定义或修改的属性描述符
configurable:true 该属性的对象能够改变,也能够被删除
enumerable:true 是否可以出现在对象的枚举属性中,即能否for...in.
value 该属性对应的值
writable:true 属性能否做赋值运算,为true时不能和get、set同时存在
get 属性的 getter 函数,当访问该属性时,会调用此函数
set 属性的setter函数,当修改属性时,会调用此函数
*/
function defineReactive(target,key,value,enumerable){
//数据由参数传入,参数在函数内部起到一个局部作用域的作用,避免变量全局污染,闭包
//如果是非数组的引用类型
if(typeof value === "object" && value != null){
//引用类型递归处理
reactify(value);
}
Object.defineProperty(target,key,{
configurable:true,
enumerable:!!enumerable,//是否可枚举
//响应式即表示在get、set上做一些别的操作
get(){//获取值的时候触发
console.log(`读取当前对象的${key}`)
return value;
},
set(newVal){//设置值的时候触发
console.log(`设置当前对象的${key}`)
if(typeof newVal === "object" && newVal!=null){
reactify(newVal);
}
value = newVal;
}
})
}
//将对象转化为响应式对象
function reactify(obj){
//直接对obj进行判断
//判断这个obj不是引用类型,是不是数组
//如果是引用类型则需要递归深层次的将所有数据转化为响应式的
//如果是数组,循环数组,递归将其内的所有元素进行响应式化
if(Array.isArray(obj)){
obj.__proto__ = array_methods;
for (var i = 0; i < obj.length; i++) {
reactify(obj[i])
}
}else{
let keys = Object.keys(obj);
for(let i = 0; i < keys.length; i++){
let key = keys[i];
let value = obj[key];
defineReactive(obj,key,value,true);
}
}
}
reactify(per);
结果展示: