精华内容
下载资源
问答
  • 数据劫持

    2021-02-13 18:01:58
    数据劫持 也叫属性拦截器,vue.js是通过它实现双向绑定的 数据发生变化时,获取到数据, 1 在input框中输入内容时,绑定的对象数据也会发生变化. 2 修改对象中的数据,input框也可以发生变化. <!DOCTYPE html> <...

    数据劫持

    也叫属性拦截器,vue.js是通过它实现双向绑定的

    数据发生变化时,获取到数据,

    1 在input框中输入内容时,绑定的对象数据也会发生变化.

    2 修改对象中的数据,input框也可以发生变化.

    <!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>
      <input type="text" oninput="changeData(this)" name="" id="">
      <div> </div>
      <script>
        let Person = { name: '你好' }
        let temp = null;
        function changeData(obj) {
          // 获取输入框内容,赋给person对象
          Person.name = obj.value;
    
          Object.defineProperty(Person, 'name', {
            get: function () {
              return temp;
            },
            set: function (val) {  // 获取到正在修改的值
              temp = val
              console.log('数据在修改');
            }
          })
          edit(Person.name);
        }
    
        function edit(val) {
          document.querySelector('div').innerHTML = val;
        }
    
      </script>
    </body>
    
    </html>
    
    展开全文
  • 1、什么是数据劫持? 首先写一个简单vue 例子: 数据劫持 {{message}} var vm = new Vue({ el:#app, data(){ return { message:测试数据 } } }) 将数据挂载到视图上很简单,使用{{}}...
  • Vue 数据绑定之数据劫持 vue 的数据绑定 = 数据劫持 + 发布者-订阅者模式 数据劫持会使用到一个 Object.defineProperty() 来劫持 data 中的各个属性的 getter 、setter 如果不了解 Object.defineProperty() 请查看...

    Vue 数据绑定之数据劫持

    vue 的数据绑定 = 数据劫持 + 发布者-订阅者模式

    数据劫持会使用到一个 Object.defineProperty() 来劫持 data 中的各个属性的 gettersetter

    如果不了解 Object.defineProperty() 请查看 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

    let zhaosir = new Zhaosir({
        el: '#app',
        data: {
            a: {
                a: '是a'
            },
            b: "是b",
            msg: 'hello world',
        }
    })
    
    function Zhaosir(options = {}){     // 生成的一个实例对象
        this.$options = options;		// 将传递过来的对象绑定到this.$options上
        let data = this._data = this.$options.data;
        observe(data);
        for (let key in data) {
            Object.defineProperty(this,key,{
                enumerable: true,
                configurable: true,
                get(){
                    return this._data[key];
                },
                set(newVal){
                    this._data[key] = newVal;
                }
            })
        }
        new Compile(this.$options.el,this);
    }
    function Observe(data){                     // 生成一个getter、setter 函数
        for (let key in data) {
            let val = data[key];
            observe(val);
            Object.defineProperty(data,key,{
                enumerable: true,
                configurable: true,
                get(){ 
                    return val;
                },
                set(newVal) {
                    if (val === newVal) return;
                    val = newVal;
                    observe(val);
                }
            });
        }
    }
    function Compile(el,vm) {			// 将数据渲染到页面上
        vm.$el = document.querySelector(el);
        let fragment = document.createDocumentFragment();
        while (child = vm.$el.firstChild) {
            fragment.appendChild(child);
        }
        replace(fragment)
        function replace(fragment){
            Array.from(fragment.childNodes).forEach(function(node){
                let text = node.textContent;
                let reg = /\{\{(.*)\}\}/;
                if (node.nodeType === 3 && reg.test(text)){
                    let arr = RegExp.$1.split('.');
                    let val = vm;
                    arr.forEach(function(k){
                        val = val[k]
                    })
                    node.textContent = text.replace(/\{\{(.*)\}\}/,val);
                }
                if (node.childNodes) {
                    replace(node);
                }
            })
        }
    
        vm.$el.appendChild(fragment);
    }
    
    function observe (data) {				 // 实现数据劫持
        if (typeof data !== 'object') return;
        return new Observe(data);
    }
    

    参考资料:

    • JavaScript 高级程序编程(红宝书)

    • MDN 文档

    展开全文
  • 数据劫持|数据代理

    千次阅读 2019-05-28 09:38:30
    数据劫持: 指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。 比较典型的是 Object.defineProperty() 和 ES2015 中新增的 Proxy 对象。数据劫持最著名的应用当属...

    数据劫持:

    指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。
    比较典型的是 Object.defineProperty() 和 ES2016 中新增的 Proxy 对象。数据劫持最著名的应用当属双向绑定,这也是一个已经被讨论烂了的面试必考题。例如 Vue 2.x 使用的是 Object.defineProperty()(Vue 在 3.x 版本之后改用 Proxy 进行实现)。


    一、Object.defineProperty

    Vue 的双向绑定已经升级为前端面试的必考题,原理我就不再重复了,网上一大片。简单来说就是利用 Object.defineProperty(),并且把内部解耦为 Observer, Dep, 并使用 Watcher 相连。
    Object.defineProperty() 的问题主要有三个:

    1、不能监听数组的变化

    let arr = [1,2,3]
    let obj = {}
    Object.defineProperty(obj, 'arr', {
      get () {
        console.log('get arr')
        return arr
      },
      set (newVal) {
        console.log('set', newVal)
        arr = newVal
      }
    })
    obj.arr.push(4) // 只会打印 get arr, 不会打印 set
    obj.arr = [1,2,3,4] // 这个能正常 set
    

    数组的以下几个方法不会触发 set:

    • push
    • pop
    • shift
    • unshift
    • splice
    • sort
    • reverse

    Vue 把这些方法定义为变异方法 (mutation method),指的是会修改原来数组的方法。与之对应则是非变异方法 (non-mutating method),例如 filter, concat, slice 等,它们都不会修改原始数组,而会返回一个新的数组。Vue 官网有相关文档讲述这个问题。

    2、必须遍历对象的每个属性
    使用 Object.defineProperty() 多数要配合 Object.keys() 和遍历,于是多了一层嵌套。如:

    Object.keys(obj).forEach(key => {
      Object.defineProperty(obj, key, {
        // ...
      })
    })
    

    3、必须深层遍历嵌套的对象
    所谓的嵌套对象,是指类似

    let obj = {
      info: {
        name: 'eason'
      }
    }
    

    如果是这一类嵌套对象,那就必须逐层遍历,直到把每个对象的每个属性都调用 Object.defineProperty() 为止。 Vue 的源码中就能找到这样的逻辑 (叫做 walk 方法)。

    4、造成的问题
    在这里插入图片描述
    造成所谓的响应式数据,DOM无法进行更新。


    解决方案:

    Vue 无法检测到对象属性的添加删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。例如:

    var vm = new Vue({
      data:{
        a:1
      }
    })
    
    // `vm.a` 是响应式的
    
    vm.b = 2
    // `vm.b` 是非响应式的
    

    对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性。例如,对于:

    Vue.set(vm.someObject, 'b', 2)
    

    您还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名:

    this.$set(this.someObject,'b',2)
    

    有时你可能需要为已有对象赋值多个新属性,比如使用 Object.assign()_.extend()。但是,这样添加到对象上的新属性不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的属性一起创建一个新的对象。

    // 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
    this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
    

    也有一些数组相关的注意事项,之前已经在列表渲染中讲过。


    二、Proxy

    在数据劫持这个问题上,Proxy 可以被认为是 Object.defineProperty() 的升级版。外界对某个对象的访问,都必须经过这层拦截。因此它是针对 整个对象,而不是 对象的某个属性,所以也就不需要对 keys 进行遍历。这解决了上述 Object.defineProperty() 的第二个问题。

    let obj = {
      name: 'Eason',
      age: 30
    }
    let handler = {
      get (target, key, receiver) {
        console.log('get', key)
        return Reflect.get(target, key, receiver)
      },
      set (target, key, value, receiver) {
        console.log('set', key, value)
        return Reflect.set(target, key, value, receiver)
      }
    }
    let proxy = new Proxy(obj, handler)
    proxy.name = 'Zoe' // set name Zoe
    proxy.age = 18 // set age 18
    

    一道面试题

    什么样的 a 可以满足 (a === 1 && a === 2 && a === 3) === true 呢?(注意是 3 个 =,也就是严格相等)???

    Object.defineProperty(window, 'a', {
      get () {
        current++
        return current
      }
    })
    console.log(a === 1 && a === 2 && a === 3) // true
    
    展开全文
  • JS数据劫持 Java数据挟持 使用jdk的PropertyChangeSupport实现属性监听 使用Cglib实现属性监听 背景 java在使用JavaBean的时候,有时我们需要监听属性的变更。例如在访问bean的getter方法,或者调用bean的...

    目录

    背景

    JS数据劫持

    Java数据劫持

    使用jdk的工具类实现属性监听

    使用Cglib实现属性监听

    总结


    背景

    java在使用JavaBean的时候,有时我们需要监听属性的变更。例如在访问bean的getter方法,或者调用bean的setter方法时,进行拦截。在不对现有的所有代码进行入侵修改的前提下,有什么方法优雅解决这个问题呢?

    JS数据劫持

    JS的“数据劫持”提供了一种机制,允许程序对对象数据的访问与修改之前进行拦截。Vue能够在修改模型属性的时候,自动更新视图,使用的是便于JS提供的API,Object.defineProperty()。

    例如下面的代码:

    var player = {
      level  : 1,
      name   : "Tom"
    }
    
    Object.keys(player).forEach(function(key){
        Object.defineProperty(player,key,{
           get:function(){
              console.log('访问变量:'+key);
          },
          set:function(){
              console.log('修改变量:'+key);
          }
      })
    });
    
    player.name = "Lucy"
    player.level
    
    // 修改变量:name
    // 访问变量:level

    那么,Java是否也能实现这种机制呢?答案是肯定的。

    Java数据劫持

    对于一个简单的JavaBean,如下

    public class Player {
    
        private String name;
    
        private int level;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getLevel() {
            return level;
        }
    
        public void setLevel(int level) {
            this.level = level;
        }
    }

    我们怎么监听该对象的所有属性变更呢?jdk提供了一个工具,PropertyChangeSupport类,可以实现这个效果。其实其原理也很简单,就是事件驱动。

    使用jdk的工具类实现属性监听

    import java.beans.PropertyChangeSupport;
    
    public class PropertyListener {
    
        public static void main(String[] args) {
            Player player = new Player();
            PropertyChangeSupport support = new PropertyChangeSupport(player);
            // 为player添加属性变更监听器
            support.addPropertyChangeListener(listener ->
                    System.out.println(String.format("对象 [%s]属性发生变更,从%s变为%s",
                            listener.getPropertyName(), listener.getOldValue(), listener.getNewValue()))
            );
    
            player.setName("Tom");
            // 手动抛出事件
            support.firePropertyChange("name", null, player.getName());
            // 程序输出
            // 对象 [name]属性发生变更,从null变为Tom
        }
    }

    虽然以上的代码确实实现了属性监听,但不难发现,代码非常麻烦,特别是需要手动调用 support.firePropertyChange()这个方法。想象一下,这意味着我们需要对所有的setter()方法进行修改,这是灾难性的。有没有优雅的方式呢?

    熟悉SpringAop原理的同学,很快就想到了面向切面的方法拦截。没错,这就是我们的解决方案。我们可以通过代码织入,在不修改现有javabean的同时,达到目的。

    说到AOP,不得不说下java的动态代理。动态代理说到底也就是设计模式中的“代理模式”。该模式要求被代理的对象实现了某一个接口,然后我们生成的动态代理也实现了该接口。这也意味着,我们只能拦截接口的方法,对于非接口方法则黔驴技穷。

    使用Cglib实现属性监听

    Cglib是一个强大的代码生成类库,允许在运行起拓展java类与实现接口。不同于jdk的动态代理,Cglib动态代理是利用Asm动态生成被代理类的一个子类,然后对父类的所有非final方法进行重写,从而达到方法拦截。

    基于此,我们可以把support.firePropertyChange()这个动作写在方法拦截的统一入口,代码如下:

    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    import org.springframework.util.ReflectionUtils;
    
    import java.beans.PropertyChangeSupport;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    public class PropertyListenerInterceptor implements MethodInterceptor {
    
        private PropertyChangeSupport support;
    
        public void binding(Object target) {
            support = new PropertyChangeSupport(target);
            support.addPropertyChangeListener(evt ->
                            System.out.println(String.format("对象 [%s]属性发生变更,从%s变为%s",
                                    evt.getPropertyName(), evt.getOldValue(), evt.getNewValue()))
                    );
        }
    
        @Override
        public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            if (method.getName().startsWith("set")) {
                String fieldName = method.getName().replace("set", "");
                fieldName = fieldName.substring(0,1).toLowerCase() + fieldName.substring(1);
                Field field = ReflectionUtils.findField(target.getClass(), fieldName);
                field.setAccessible(true);
                Object oldValue = field.get(target);
                Object ret = methodProxy.invokeSuper(target, args);
                Object newValue = field.get(target);
                support.firePropertyChange(fieldName, oldValue, newValue);
                return ret;
            } else if (method.getName().startsWith("get")) {
                String fieldName = method.getName().replace("get", "");
                fieldName = fieldName.substring(0,1).toLowerCase() + fieldName.substring(1);
                System.out.println(String.format("访问对象 [%s]属性", fieldName));
            }
            return methodProxy.invokeSuper(target, args);
        }
    
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Player.class);
            PropertyListenerInterceptor interceptor = new PropertyListenerInterceptor();
            enhancer.setCallback(interceptor);
            Player player = (Player) enhancer.create();
            interceptor.binding(player);
            player.setLevel(10);
            player.setName("Kitty");
            player.getName();
    //        程序输出:
    //        对象 [level]属性发生变更,从0变为10
    //        对象 [name]属性发生变更,从null变为Kitty
    //        访问对象 [name]属性
        }
    }

    总结

    Java实现数据劫持,只能通过拦截setter/getter方法,如果使用反射或者绕过javabean方法直接对属性进行赋值,则无法拦截。

    JS支持针对属性进行拦截,并且提供了原生且优雅的API。

    希望后续Java能提供针对真正的属性监听API。

    展开全文
  • MVVM 双向数据绑定 数据劫持+发布者订阅模式(不兼容低版本 Object.defineProperty) vue不能新增不存在的属性没有get和set方法,不会监控数据的变化 深度响应 因为每次赋予一个新对象增加prototypey <!DOCTYPE ...
  • 数据劫持代理

    2021-04-09 20:23:56
    数据劫持代理 底层现实过程: 通过利用利用Object.defineProperty()监视数据的的变化 先枚举获得数据,调用Object.defineProperty(目标对象,添加的属性,value说明对象) 外面修改属性,在被set方法监听中修改添加...
  • vue 数据劫持

    2018-11-16 21:26:00
    Vue 数据劫持  额,因为最近都在使用 Vue ,所以也想了解一下它里面的东西到底是如何实现的,这里就先来说一个比较简单的 —— Vue 配置对象里面的 data 是如何实现数据劫持的,也就是说,为什么通过实例对象就...
  • 数据劫持 发布订阅模式 本篇文章主要介绍 Vue 实现数据劫持的思路,下一篇则会介绍发布订阅模式的设计。 二、针对 Object 类型的劫持 对于 Object 类型,主要劫持其属性的读取与设置操作。在 JavaScript 中对象的...
  • 数据代理和数据劫持 数据代理 有如下数据: a:{ b:{ name:'张三'; } } a.b.name // 张三 上述代码在访问name属性时需要a.b.name这样访问,那么如果能够直接使用a.name这样访问,就说对象a代理了a.b的任务,实现...
  • Vue中数据劫持

    2020-11-18 18:19:32
    Vue中的数据劫持 Vue中响应式系统的核心: { 数据劫持: 使用Object.defineProperty(this.属性名,{属性值的配置项} ); 依赖收集: 因为上一步数据劫持后, 在解析模板时,替换模板中的变量就需要读取变量的数据,此时...
  • 深入浅出 Vue 系列 -- 数据劫持实现原理
  • 主要介绍了3分钟了解vue数据劫持的原理实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • vue中最核心的一个点就是响应式数据,数据发生变化,视图也随之变化,实现响应式的一个重要的知识点就是数据劫持,对数据的取值与赋值进行拦截操作,并附加一些其他的操作 一.es5的Object.defineProperty 这个api是...
  • vue2.0数据劫持原理

    2021-06-07 20:20:46
    vue2.0数据劫持原理 vue2.0数据劫持 数据劫持的意义? 一个数据操作,我们希望在他做赋值的过程当中,我们还可以给他增加一些事情,比如说像这个视图上的改变,我们希望的是当你数据变化的这个过程当中,那我们就...
  • 主要介绍了vue的数据劫持以及操作数组的坑,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 数据劫持方法 最近空余时间复习了下JavaScript;做为学习总结 ! 1、对象内自带的set和get方法数据劫持、 //首先我们先定义一个对象 let obj = { $name: "张三", get name(){ // 获取 name 属性时触发 //...
  • Vue 数据劫持双向绑定

    2020-03-29 22:21:13
    vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。 数据劫持是双向绑定各种方案...
  • ES5-数据劫持

    2019-02-16 17:51:40
    VUE双向数据绑定核心功能由...1)Observer:监测数据变化进行相应回调(数据劫持); 实现一个简单的数据劫持,作为Object.defineProperty的练习。从而引出Proxy&amp;Reflect &lt;!DOCTYPE html&gt; ...
  • vue中的数据劫持

    2020-08-17 16:23:17
    vue中的数据劫持 在浏览一篇博文的时候,看到里面提到了vue中数据劫持的概念,之前只是知道有这个东西,知道这个东西是vue的核心之一,是实现数据双向绑定的重要原理,但并未深入研究,那么今天就借这篇文章学习整理...
  • Vue 核心之数据劫持

    2020-11-28 15:02:13
    Vue框架核心之数据劫持 前瞻 当前前端界空前繁荣,各种框架横空出世,包括各类mvvm框架横行霸道,比如Angular、Regular、Vue、React等等,它们最大的优点就是可以实现数据绑定,再也不需要手动进行DOM操作了,它们...
  • 详解JSON劫持技术 了解JSON劫持技术的不多,包括json劫持,JSONP劫持,以及如何防御JSON劫持和JSONP劫持
  • proxy 数据代理、数据劫持 1.自动循环了 2.对象添加数据可以劫持 defineProperty数据劫持 1.对象添加数据不能劫持; 2.数组操作不能劫持;(vue不能触发视图更新特性;$set) ...
  • 双向数据绑定、数据劫持 vue3.0以前用的Object.defineProperty()来实现响应式数据的 优点 监控对象 对于数组,重写数组里的方法 缺点: 需要分别对对象和数组(数组的变异方法)进行监控 如果新增了对象属性或...
  • 域名劫持和数据劫持

    2016-02-02 21:41:00
    在当前的移动互联网环境下,流量劫持主要分为两种方式:域名劫持和数据劫持,放任流量劫持会导致扰乱市场秩序、损害用户利益以及传播、等低俗甚至严重违法信息的恶果。 据业内人士透露,流量劫持人人喊打,但...
  • 数据响应式: 是指当数据改变后,会通知到使用该数据的代码 数据劫持: 需要在 设置 / 获取 数据对象属性的同时,做些其他的事情 mvvm: 常见的编程规范

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 45,867
精华内容 18,346
关键字:

数据劫持