Proxy
扩展(增强)对象一些功能,是一种设计模式,一种代理模式
语法:
const p=new Proxy(target,handler);
target: 被代理的对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。)
handler: 对代理对象做的什么操作
handler={
set(){},
get(){},
deleteProperty(){},
has(){},
apply(){},
...
}
作用: 比如vue拦截、预警、上报、扩展功能、统计、增强对象等等
- set()设置拦截
let obj = new Proxy({}, {
set(target, prop, value) {
console.log(target, prop, value);
if (prop == 'age') {
if (Number.isIntege(value)) {
throw new TypeError('年龄必须是整数');
}
if (value > 200) {
throw new RangeError('...');
}
}
target[prop] = value;
// 或者使用Reflectfan射
// return Reflect.set(...arguments);
}
})
obj.a = 123;
obj.name = 'strive';
- deleteProperty() 删除属性
let json={a:1,b:2};
let newObj=new Proxy(json,{
deleteProperty(target,property){
delete target[property]
}
})
delete newObj.a
注意:如果代码报错
[Vue warn]: Error in created hook: "TypeError: 'deleteProperty' on proxy: trap returned falsish for property 'a'"
TypeError: 'deleteProperty' on proxy: trap returned falsish for property 'a'
解决方法:
MDN上明确的说明了set方法应该返回一个布尔值。 返回true表示赋值成功。如果set方法返回false,并且分配发生在严格模式代码中,则会引发TypeError。
deleteProperty(target, property) {
if (prop in target){
delete target[prop]
console.log('property removed: ' + prop)
return true
}
else {
console.log('property not found: ' + prop)
return false
}
}
- has()
has(target,property){
return property in target;
}
- apply()
function fn(){
return `我是函数`;
}
let newFn=new Proxy(fn,{
apply(){
return '函数';
}
})
newFn(); // 函数
示例:
- 需求:访问之前操作
let obj={name:'strive'};
let newObj=new Proxy(obj,{
get(target,property){
console.log(target.property);
return target[property];
}
})
newObj.name
- 需求:实现访问一个对象身上的属性,默认不存在的时候给了undefined,希望如果不存在输出错误(警告)信息
let obj={name:'strive'};
let newObj=new Proxy(obj,{
get(target,property){
if(property in target){
return property[target];
}else{
console.warn() // 打印错误
throw new ReferenceError(`${property}属性上不存在此对象`)
}
}
})
newObj.name
newObj.age
- 类似react DOM
const DOM = new Proxy({}, {
get(target, property) {
console.log(target, property);
return function (attr = {}, ...children) {
const el = document.createElement(property);
// 添加属性
for (let child of children) {
if (typeof child == 'string') {
child = document.createTextNode(child);
}
el.appendChild(child)
}
console.log(el);
return el;
}
}
})
let oDiv = DOM.div({
id: 'div1',
class: 'aaa'
},
'我是div', 'hello', DOM.a({
href: ''
}, '访问地址'))
document.body.append(oDiv)
- new vue()
<div id="box">
{{msg}}
<div>{{name}}</div>
<input type="text" v-model="inputVal">{{inputVal}}
</div>
class vue extends EventTarget {
constructor(option) {
super();
this.option = option;
this._data = this.option.data;
this.el = document.querySelector(this.option.el);
this.observe(this._data);
this.complileNode(this.el);
}
// 监听数据
observe(data) {
let _this = this;
this._data = new Proxy(data, {
set(target, prop, newVal) {
let event = new CustomEvent(prop, {
detail: newVal
}); //创建自定义事件
_this.dispatchEvent(event); // 分发事件
console.log(newVal)
return Reflect.set(...arguments);
}
})
}
complileNode(el) {
let child = el.childNodes;
[...child].forEach(node => {
if (node.nodeType === 3) { // 文本节点
let text = node.textContent;
let reg = /\{\{\s*([^\s\{\}]+)\s*\}\}/;
if (reg.test(text)) { //匹配{{msg}}{{name}}并替换,赋值
let $1 = RegExp.$1;
console.log($1);
console.log(this._data[$1])
this._data[$1] && (node.textContent = text.replace(reg, this._data[$1]));
// 监听事件
this.addEventListener($1, e => {
node.textContent = text.replace(reg, e.detail);
})
}
} else if (node.nodeType === 1) { // 元素节点
let attributes = node.attributes;
if (attributes.hasOwnProperty('v-model')) {
let keyName = attributes["v-model"].nodeValue;
node.value = this._data[keyName];
node.addEventListener('input', e => {
this._data[keyName] = node.value;
})
}
console.log(attributes);
this.complileNode(node);
}
})
}
}
let vm = new vue({
el: '#box',
data: {
name: 'lili',
msg: '测试数据',
inputVal: "hello word"
}
})