概述
具有代表性的数据模型
- 对象嵌套对象
- Array类型的数据模型,且数组内置方法触发后会改变代理数据
data: {
name: "小明",
age: 10,
address: "家庭住址",
info: {
IDCard: "身份证号码",
school: "学校名字",
obj: {
name: "sddd"
}
},
list: [{ a: 1, b: 2 }]
}
数据代理实现——代理单个数据
function proxyKey(key, proxyObj, originObject) {
Object.defineProperty(proxyObj, key, {
configurable: true,
get: function () {
return originObject[key];
},
set: function (val) {
return (originObject[key] = val);
},
});
return proxyObj;
}
const prpxyObj = {};
let data = {
a: 20,
b: 30
}
window.test = proxyKey("a", prpxyObj, data)
代理数组方法
- 目的:修改数组对象的原型链,改变数组内置方法的执行体,如push方法、pop方法
- Object.defineProperty()中的value属性:代理属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等),即你可控制返回结果。
const arrProxyFunc = new Array();
function proxyArrayFunctionCall(obj, proxyFunction) {
Object.defineProperty(obj, proxyFunction, {
enumerable: true,
configurable: true,
value: function (...args) {
const arrProxyFunc = arrayProtoType[proxyFunction];
const result = arrProxyFunc.apply(this, args);
return result;
},
});
}
function proxyArray(array) {
const obj = {
eleType: "Array",
pop() {},
push(){}
};
proxyArrayFunctionCall(obj, "pop", );
proxyArrayFunctionCall(obj, "push");
array.__proto__ = obj;
return array;
}
const arr = [];
window.arr = arr;
window.test = proxyArray(arr);
- 可以看到,无论是arr的原型链被我们修改了,这样就实现了对数组内置方法的代理
- 我们在实现数据代理的时候会用到这个点

实现Vue框架中的数据代理
- 实现对象嵌套对象的数据代理:
proxyObject()方法中可以看到代码片段
- 实现数组类型的数据代理:
proxyArray()
- 命名空间:
getNameSpace(),该方法是为了保证在对象嵌套对象的情况下,数据源不会取错。如代理obj对象下的test,代理num这个key时,它在原数据的取值应是obj.test.num。所以,当实现其代理时命名空间就是字符串obj.test
let obj = {
a: 10,
test: {
num: 20
}
}
- Object.getOwnPropertyNames(): 返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
- 我们在实现代理数组方法时用到了
Object.getOwnPropertyNames,映射数组原型链上所有的内置方法。
const arrayProtoType = new Array();
function getNameSpace(nameSpace = null, key = null) {
if (nameSpace && !key) {
return nameSpace;
} else if (key && !nameSpace) {
return key;
} else {
return `${nameSpace}.${key}`;
}
}
function getObjectKey(obj) {
return Object.keys(obj);
}
function proxyKey(key, proxyObj, originObject) {
Object.defineProperty(proxyObj, key, {
configurable: true,
get: function () {
return originObject[key];
},
set: function (val) {
return (originObject[key] = val);
},
});
return proxyObj;
}
function proxyObject(vm, option, nameSpace) {
let proxyObj = {};
const proxyAllKey = getObjectKey(option);
for (let i = 0; i < proxyAllKey.length; i++) {
const key = proxyAllKey[i];
const val = option[key];
proxyKey(key, proxyObj, option);
if (val instanceof Object || val instanceof Array) {
option[key] = constructorProxy(vm, val, getNameSpace(nameSpace, key));
}
}
return proxyObj;
}
function proxyArrayFunctionCall(vm, obj, proxyFunction, nameSpace) {
Object.defineProperty(obj, proxyFunction, {
enumerable: true,
configurable: true,
value: function (...args) {
const arrProxyFunc = arrayProtoType[proxyFunction];
const result = arrProxyFunc.apply(this, args);
return result;
},
});
}
function proxyArray(vm, array, nameSpace) {
const arrayProtoTypeAllKey = Object.getOwnPropertyNames(
arrayProtoType.__proto__
);
const obj = {
eleType: "Array",
...arrayProtoTypeAllKey,
};
arrayProtoTypeAllKey.forEach((functionName) => {
proxyArrayFunctionCall(vm, obj, functionName, nameSpace);
});
array.__proto__ = obj;
return array;
}
function constructorProxy(vm, options, nameSpace = "") {
let proxy = {};
if (options instanceof Array) {
proxy = new Array(options.length);
for (let i = 0; i < options.length; i++) {
if (options[i] instanceof Object) {
proxy[i] = proxyObject(vm, options[i], nameSpace);
} else {
proxy[i] = proxyKey(options[i], {}, options[i]);
}
}
proxy = proxyArray(vm, options, nameSpace);
} else if (options instanceof Object) {
proxy = proxyObject(vm, options, nameSpace);
} else {
throw new Error("Error");
}
return proxy;
}
export default constructorProxy;