• Vue2双向绑定原理


    一、vue2中通过Object.defineProperty()进行数据劫持(事件监听数据变化)实现双向数据绑定

    Object.defineProperty() :

    定义:

    Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。(自己的理解:通过该方法可以为对象新增属性或者修改已有属性,可以监听到属性的变化)

    在Vue2中我们可以利用该方法对数据对象(data)使用Object.defineProperty()进行监听data里面属性的变化进行双向数据绑定

    二、Object.defineProperty() 的用法可以参考mdn官方文档,这里不进行说明,文档如下:

    Object.defineProperty() - JavaScript | MDN

    三、利用Object.defineProperty() 封装一个函数用于对象新增属性并且能监听该属性的变化

    1. function objAddProperty(obj, key,value){
    2. // 返回处理完毕的对象
    3. return Object.defineProperty(obj,key, {
    4. set(val) {
    5. //属性被赋值或者被修改时该函数自动执行,函数只有一个形参,
    6. //形参值为该属性被修改后的最新值
    7. console.log('obj.a被修改成...'+val)
    8. value = val
    9. },
    10. get(){
    11. //属性被访问时该函数自动执行
    12. //属性的值即为该函数的返回值
    13. console.log('obj.a被访问...')
    14. return value
    15. }
    16. })
    17. }
    18. //声明一个对象
    19. let obj={}
    20. // 为对象新增属性并进行监听该属性的变化
    21. obj= objAddProperty(obj, 'a',1)
    22. console.log(obj);
    23. obj.a=666

    三、利用Object.defineProperty() 封装一个函数用于修改对象已有属性并且能监听该属性的变化,也可以实现不进行属性的修改,实现属性的劫持(Vue2中的数据劫持,劫持对象属性的变化)

     1、劫持对象中的某个属性

    1. function observeKey(obj, key) {
    2. let value = obj[key];
    3. Object.defineProperty(obj, key, {
    4. set(val) {
    5. //属性被赋值或者被修改时该函数自动执行,函数只有一个形参,
    6. //形参值为该属性被修改后的最新值
    7. console.log('属性被修改成...'+val)
    8. value = val
    9. },
    10. get(){
    11. //属性被访问时该函数自动执行
    12. //属性的值即为该函数的返回值
    13. console.log('属性被访问...')
    14. return value
    15. }
    16. });
    17. }
    18. let obj = { a: 1 };
    19. observeKey(obj, "a");
    20. // 读取a,触发get函数
    21. console.log(obj.a);
    22. // 设置a,触发set函数
    23. obj.a = 2;

    2、劫持对象中的所有属性

    其实就是遍历对象的属性进行监听

    1. function observeObj(obj){
    2. //循环对象的属性进行监听
    3. for(let key in obj){
    4. observeKey(obj,key)
    5. }
    6. }
    7. function observeKey(obj, key) {
    8. let value = obj[key];
    9. Object.defineProperty(obj, key, {
    10. set(val) {
    11. //属性被赋值或者被修改时该函数自动执行,函数只有一个形参,
    12. //形参值为该属性被修改后的最新值
    13. console.log('属性被修改成...'+val)
    14. value = val
    15. },
    16. get(){
    17. //属性被访问时该函数自动执行
    18. //属性的值即为该函数的返回值
    19. console.log('属性被访问...')
    20. return value
    21. }
    22. });
    23. }
    24. let obj = { a: 1,b:2 };
    25. observeObj(obj)
    26. // 读取a,触发get函数
    27. console.log(obj.a);
    28. // 设置a,触发set函数
    29. obj.a = 2;
    30. // 读取b,触发get函数
    31. console.log(obj.b);
    32. // 设置a,触发set函数
    33. obj.b = 3;

    注意:上面的有个缺陷,就是当属性值也是对象的时候,不能劫持属性值,如{a:1,c:{b:1}}

    解决方案:判断对象的属性值数据类型,判断是否需要进行递归

    1. function observeObj(obj){
    2. //循环对象的属性进行监听
    3. for(let key in obj){
    4. if(obj[key] instanceof Object){
    5. observeObj(obj[key])
    6. }
    7. else{
    8. observeKey(obj,key)
    9. }
    10. }
    11. }
    12. function observeKey(obj, key) {
    13. let value = obj[key];
    14. Object.defineProperty(obj, key, {
    15. set(val) {
    16. //属性被赋值或者被修改时该函数自动执行,函数只有一个形参,
    17. //形参值为该属性被修改后的最新值
    18. console.log('属性被修改成...'+val)
    19. value = val
    20. },
    21. get(){
    22. //属性被访问时该函数自动执行
    23. //属性的值即为该函数的返回值
    24. console.log('属性被访问...')
    25. return value
    26. }
    27. });
    28. }
    29. let obj = { a: 1,c:{b:2} };
    30. observeObj(obj)
    31. // 读取a,触发get函数
    32. console.log(obj.a);
    33. // 设置a,触发set函数
    34. obj.a = 2;
    35. // 读取b,触发get函数
    36. console.log(obj.c.b);
    37. // 设置a,触发set函数
    38. obj.c.b = 3;

     注意,observeObj这个函数,不能劫持对象的新增属性,只能劫持对象已有的属性。

    四、Object.defineProperty()的缺陷

    1、深度监听需要一次性递归(性能消耗大)

    2、无法监听对象新增和删除的属性

    五、简单模拟双向数据绑定

    注意:不懂this._name的移步使用Object.definePropety报错 Maximum call stack size exceeded_不见浅诗~的博客-CSDN博客

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
    6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    7. <title>Document</title>
    8. </head>
    9. <body>
    10. <div class="app">
    11. <input type="text">
    12. <p><span></span></p>
    13. <p><button>点击修改data数据模型的name值为马云</button></p>
    14. </div>
    15. <script>
    16. const button=document.querySelector("button")
    17. const span=document.querySelector("span")
    18. const input =document.querySelector('input')
    19. const data={
    20. name:'hsq'
    21. }
    22. input.value=data.name
    23. span.innerText=`data数据模型的值为${data.name}`
    24. Object.defineProperty(data,'name',{
    25. get(){
    26. return this._name
    27. },
    28. set(newValue){
    29. input.value=newValue
    30. this._name=newValue
    31. span.innerText=`data数据模型的值为${newValue}`
    32. console.log('data数据模型name属性值',newValue);
    33. }
    34. })
    35. input.addEventListener("input",function(e){
    36. console.log('输入框的值',e.target.value);
    37. data.name=e.target.value
    38. })
    39. button.addEventListener("click",function(){
    40. data.name='马云'
    41. })
    42. </script>
    43. </body>
    44. </html>

  • 相关阅读:
    大型网站架构
    [python] 基于Dataset库操作数据库
    软件测试面试(三)
    挖掘文本的奇妙力量:传统与深度方法探索匹配之道
    java将list转为逗号隔开字符串,将逗号连接的字符串转成字符数组,​将逗号分隔的字符串转换为List​(Java逗号分隔-字符串与数组相互转换)
    Apache的日志分割
    03使用Spring基于XML的方式注册第一个组件
    前端学习<三>CSS进阶——04-如何让一个元素水平垂直居中?
    alova.js快速入门教程
    18 SpringMVC实战
  • 原文地址:https://blog.csdn.net/h18377528386/article/details/127515469