1、 Proxy 作用
Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。
2、defineProperty 默认值问题
let proxy = new Proxy(target, handler);
- target 是用Proxy包装的被代理对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
- handler 是一个对象,其声明了代理target 的一些操作,其属性是当执行一个操作时定义代理的行为的函数。
3、handler 对象的方法
1、get 方法用来处理获取数据时的劫持行为;
2、set 方法用来处理设置数据时的劫持行为;
3、has 方法用来处理在判断是否有该属性时的劫持行为 ,return true 存在该属性,false 不存在该属性
4、apply 方法用来代理函数的执行,要求 target 必须是一个函数,在函数执行的时候做一个拦截
5、construct 方法用于拦截 new 操作符.
6、defineProperty 方法用于拦截 defineProperty 操作 return Object.defineProperty
7、deleteProperty 用于拦截对象属性的删除操作
8、getOwnPropertyDescriptor 方法用于拦截 getOwnPropertyDescriptor 操作
getOwnPropertyDescriptor 必须返回一个 object 或 undefined
Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符
9、getPrototypeOf 用于拦截对象调用 getPrototypeOf 方法
Object.getPrototypeOf 查找对象的原型方法
10、setPrototypeOf 方法主要用来拦截 Object.setPrototypeOf().
Object.setPrototypeOf 设置对象的原型方法
11、isExtensible 用于拦截对象的isExtensible方法
12、preventExtensions 用于拦截 Object.preventExtensions
13、ownKeys 会拦截一下操作:
Object.keys()
let data = {
name: "mt",
age: 18,
price: 5000
};
let proxyData = new Proxy(data,{
get(target,key){
// 获取的时候拦截
//console.log(arg);
if(key == "price"){
return target[key]*.9;
}
return target[key];
},
set(target,key,newVal){
// 设置的时候拦截
// if(key == "price"){
// if(newVal > target[key]){
// throw "败家媳妇给家里余钱吧";
// }
// }
// if(key == "price"){
// if(isNaN(newVal)){
// throw "对不起请给现金"
// }
// }
target[key] = newVal;
},
has(target,key){
//判断某个值存不存
//console.log(target,key);
if(key == "gf"){
return true;
}
return (key in target);
}
});
console.log(proxyData);
// console.log(proxyData.price);
// console.log(proxyData.age);
/*
Proxy.get 在对数据进行获取操作的时候,进行拦截
*/
// proxyData.price = 600;
// proxyData.price = 700;
//console.log("gf" in proxyData);
function fn(){
console.log(this,111,arguments);
}
// fn.apply(1);
fn = new Proxy(fn,{
apply(target,thisArg,...arg){
//console.log(target,thisArg,...arg);
//throw "该函数是一个类,不能直接调用";
//console.log(thisArg);
if(typeof thisArg !== "object"){
throw "该函数只支持事件和对象的方法调用,请勿直接调用";
}
target.apply(thisArg,arg);
//target(arg);
}
});
// apply 在函数执行的时候,进行一个拦截, (通过 new 调用这个函数,不会触发 apply 的代理)
//document.onclick = fn;
fn.call(document,"a","b","c");
// new fn;
//fn();
function Person(name,age){
//console.log(this,111,arguments);
this.name = name;
this.age = age;
}
Person = new Proxy(Person,{
apply(){
throw "Person是一个类,请勿直接调用";
},
construct(target,arg){
return new target(...arg); // construct 一定要返回一个对象
}
});
//console.log(Person);
//Person.call(document,"a","b","c");
let p = new Person("mt",18);
let data = {
name: "mt",
age: 18,
price: 5000
};
let proxyData = new Proxy(data,{
get(target,key){
if(key == "price"){
return target[key]*.9;
}
return target[key];
},
set(target,key,newVal){
target[key] = newVal;
},
has(target,key){
if(key == "gf"){
return true;
}
return (key in target);
},
defineProperty(target,key,descriptor){
// 当调用了 Object.defineProperty 时执行
//console.log(target,key,descriptor);
return Object.defineProperty(target,key,descriptor);
}
});
Object.defineProperty(proxyData,"child",{
configurable: true,
enumerable: true,
get(){
return "不要关心人家家事"
},
set(val){
console.log("这不是我的孩子",val);
}
});
//proxyData.child = 10;
console.log(proxyData.child);
基于proxy的数据响应式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
{
{
message }}
<div>
<p>姓名:{
{
name }},年龄: {
{
age}}</p>
<p v-html="htmlData"></p>
<input type="text" v-model="modelData" />
<p>{
{
modelData}}</p>
</div>
就是一段纯文本
</div>
<script>
class Event {
events = {
} // 事件池记录所有的相关事件及处理函数
on(eventName,fn){
if(!this.events[eventName]){
this.events[eventName] = [];
}
this.events[eventName].push(fn);
}
off(eventName,fn){
// 删除一个事件处理 eventName 事件名称 fn 对应的处理函数
if(!this.events[eventName]){
return ;
}
this.events[eventName] = this.events[eventName].filter(item=>item!=fn);
}
/*
dispatch 负责把触发到的事件给执行了
*/
dispatch(eventName){
if(!this.events[eventName]){
return ;
}
this.events[eventName].forEach(item => {
item.call(this);
});
}
}
class KVue extends Event {
constructor(option){
super();
this.$option = option;
let el = document.querySelector(option.el);
this.compileNode(el);
this.observe(option.data);
}
// 给数据添加数据劫持
observe(data){
let _this = this;
this.$option.data = new Proxy(data,{
get(target,key){
return target[key];
},
set(target,key,newVal){
target[key] = newVal;
_this.dispatch(key);
return true;
}
});
}
// // 完成数据劫持,在数据修改时去触发视图的变化
// dataProxy(data,key,value){
// let _this = this;
// Object.defineProperty(data,key,{
// configurable: true,
// enumerable: true,
// set(newVal){
// value = newVal;
// _this.dispatch(key);
// //console.log("数据已经修改了该触发视图的修改了",key);
// },
// get(){
// return value;
// }
// });
// }
// 根据当前元素的结构,将我们的数据编译进去
compileNode(el){
let child = el.childNodes; // 找到元素下的所有节点
child.forEach(node => {
if(node.nodeType == 1){
// 如果该节点是元素节点
let attrs = node.attributes;
[...attrs].forEach(attr=>{
let attrName = attr.name;
if(attrName.indexOf("v-") == 0){
let attrVal = attr.value;
//console.log(attrName,attrVal);
if(attrName === "v-html" ){
// 这是一个v-html指令,我们应该用数据替换该元素的内容
node.innerHTML = this.$option.data[attrVal];
this.on(attrVal,()=>{
// console.log(attrVal,"进行了修改");
node.innerHTML = this.$option.data[attrVal];
})
} else if(attrName == "v-model"){
// 这是一个双向绑定指令
node.value = this.$option.data[attrVal];
this.on(attrVal,()=>{
// console.log(attrVal,"进行了修改");
console.log(1);
node.value = this.$option.data[attrVal];
})
// 监听视图发生了变化,同步修改我们的数据
node.addEventListener("input",({
target})=>{
this.$option.data[attrVal] = target.value;
});
}
}
});
if(node.childNodes.length > 0){
// 如果该元素还有子元素继续想要查找
this.compileNode(node);
}
} else if(node.nodeType == 3){
// 如果该节点是文本节点
// console.log(node);
//console.dir(node);
let startContent = node.textContent;
let reg = /\{\{\s*(\S+)\s*\}\}/g;
//console.log(reg.test(startContent),startContent);
if(reg.test(startContent)){
node.textContent = startContent.replace(reg,(...arg)=>{
//console.log(arg[1]);
this.on(arg[1],()=>{
node.textContent = startContent.replace(reg,(...arg)=>{
return this.$option.data[arg[1]];
});
})
return this.$option.data[arg[1]];
});
}
}
});
}
}
let kvue = new KVue({
el: "#app",
data: {
message: "Hello KKB",
modelData: "呵呵",
name: "kkb",
age: 8,
htmlData: "<strong>圣诞节要陪我一起过吗</strong>"
}
});
/*
数据响应式:
监听数据发生变化,如果数据发生了变化,就同步视图进行改变
实现原理:
1. 编译模板,找出需要插入数据的位置,把数据插入进去
2. 利用数据劫持,监听数据发生改变,如果数据发生改变,则找到对应的插入数据的位置,修改视图
数据和视图双向绑定:
1. 利用数据响应式监听数据发生改变,然后同步视图
2. 利用 change 或 input 等事件监听视图发生改变,然后修改数据
!!! 最起码面试之前,一定找出来,在回顾一遍
*/
</script>
</body>
</html>