考核内容: es6 数据代理(数据劫持)的使用方法
题发散度: ★★★
试题难度: ★★★
解题思路:
前端界空前繁荣,各种框架横空出世,包括各类mvvm框架横行霸道,比如Angular、Regular、Vue、React等等,
它们最大的优点就是可以实现数据绑定,再也不需要手动进行DOM操作了,它们实现的原理也基本上是脏检查或数据劫持。
最近接触了一些面试者,当我问起“如何实现数据双向绑定”时,会脱口而出“数据劫持”,然后呢?然后就没有然后了 ;“数据劫持”是基础,但远不是想听到的答案;
数据代理(也可叫数据劫持)
指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。
首先要理解问题:数据双向绑定 是一种模式,web语境下一般指数据从dom到JS对象之间的自动同步。DOM 与 JS 被隔离在两个不同的运行时上,互相之间需要通过命令式的 DOM接口 沟通:DOM 需要正确触发事件,将信息传输给JS程序;而JS也需要在状态变更后,有意识地调用适当的接口,改变DOM内容。
同一个拦截器函数,可以设置拦截多个操作。
Object.defineProperty(obj, prop, desc)的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性
obj 需要定义属性的当前对象
prop 当前需要定义的属性名
desc 属性描述符
html:
<div id="box"></div>
<input type="text" id="username" oninput="setusername()" value="shuke">
js:
//defineProperty 拦截
let username=document.getElementById("username"),
box=document.getElementById("box"),
obj = {}
Object.defineProperty(obj, 'nickname', {
get () {
console.log('数据被调用了')
return username.value
},
set (newVal) {
console.log('数据发生了改变', newVal)
box.innerHTML=newVal
}
})
box.innerHTML=obj.nickname
function setusername() {
obj.nickname=username.value
}
效果 :
存取描述符 --是由一对 getter、setter 函数功能来描述的属性
get:一个给属性提供getter的方法,如果没有getter则为undefined。该方法返回值被用作属性值。默认为undefined。
set:一个给属性提供setter的方法,如果没有setter则为undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认值为undefined。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
html:
<div id="box"></div>
<input type="text" id="username" oninput="setusername()">
js:
//Proxy 代理
let username = document.getElementById("username"),
box = document.getElementById("box"),
obj = new Proxy({}, {
get: function (target, propKey, receiver) {
console.log(`数据被调用了 ${propKey}!`);
return Reflect.get(target, propKey, receiver);
},
set: function (target, propKey, value, receiver) {
console.log(`数据发生了改变 ${propKey}!`,value);
box.innerHTML=value
return Reflect.set(target, propKey, value, receiver);
}
});
obj.name=""
box.innerHTML=obj.name
function setusername() {
obj.name=username.value
}
效果:
参考代码:
答案:
B, 同一个拦截器函数,只可以设置拦截一个操作。
下面是 Proxy 支持的拦截操作一览,一共 13 种。