前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >有关于双向绑定功能在ES6中实现的数据代理(数据劫持)

有关于双向绑定功能在ES6中实现的数据代理(数据劫持)

作者头像
舒克
发布2021-09-24 16:02:03
9110
发布2021-09-24 16:02:03
举报

考核内容: 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:

代码语言:javascript
复制
<div id="box"></div>
<input type="text" id="username" oninput="setusername()" value="shuke">

js:

代码语言:javascript
复制
//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:

代码语言:javascript
复制
<div id="box"></div>
<input type="text" id="username" oninput="setusername()">

js:

代码语言:javascript
复制
//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
    }

效果:

参考代码:

答案

代码语言:javascript
复制
B, 同一个拦截器函数,只可以设置拦截一个操作。

下面是 Proxy 支持的拦截操作一览,一共 13 种。

  1. get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。
  2. set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
  3. has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
  4. deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
  5. ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  6. getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  7. defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
  8. preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
  9. getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
  10. isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
  11. setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  12. apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
  13. construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-09-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 迈向前端工程师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档