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>