数据双向绑定_VUE2.x实现双向数据绑定的核心API

VUE2.x实现双向数据绑定的核心API

作者: 郭政鸿 2020/8/1

dafcab1642cfec8adf890e7a82f323f0.png

前言: 前端有三大框架Vue、React、Angular, 本文知识由Vue引出, Vue的一个十分重要的特性就是双向数据绑定, 在Vue的1.x 和 2.x版本中, 双向数据绑定依赖的是Object.defineProperty API, 此API中双向数据绑定最主要就是依赖get和set

下面我们就用原生javascript来动手实现一个简版的双向数据绑定demo

<input type="text" id="ih" />
<h1 id="vh">h1>

<script>let $ = el => document.querySelector(el);// 定义响应式数据const data = {value: "hello",
  };// 事件驱动数据更新
  $('#ih').addEventListener("input", e => {
    data.value = e.target.value;
  });// 模拟模板数据渲染const setValue = () => {
    $('#ih').value = data.value;
    $('#vh').innerText = data.value;
  };// 初始化模板数据function init(value, value2) {
    setValue();
  }// 定义响应式数据函数function defineReactiveValue(data) {for (const key in data) {let _value = data[key];Object.defineProperty(data, key, {get() {if (this._key === undefined) {this._key = _value;
          }return this._key;
        },set(newValue) {this._key = newValue;// 更新模板数据
          setValue();
        }
      })
    }
  }// 使data数据响应式
  defineReactiveValue(data);// 初始化模板数据
  init();script>

知道了双向数据绑定的基本原理我们在来看看get和set的定义方法吧.

顺便说下, 目前处于RC版本的vue实现双向数据绑定采用的是ES6的Proxy API而不再是ES5的Object.defineProperty

1) 字面量声明时定义

// 死循环代码
let obj = {
  num: 665,
  get num() {return this.num + 1},
  set num(v) {this.num = v + 1}
}
// 解决办法
let obj = {
 _num: 665,
  get num() {return this._num + 1},
  set num(v) {this._num = v + 1}
}

2) Object.create创建时定义

let obj = Object.create(Object.prototype, {
  num: {
    set(v) {
      this._num = v + 10;
    },
    get() {
      this._num || (this._num = 0);
      return this._num + 1;
    }
  }
})

3) Object.defineProperty/Reflect.defineProperty

// 使用数据描述符
let obj = {};
Reflect.defineProperty(obj, "num", {
  writable: false,
  enumrable: false,
  configurable: false,
  value: 999
});
// 直接字面量定义的对象: writable, enumrable, configurable值都是true

// 使用存储描述符
let obj = {};
Reflect.defineProperty(obj, "num", {
  writable: false,
  configurable: false,
  get() {
    return this.value + 1;
  },
  set(v) {
    this.value += 1;
  }
})

4) Object.defineProperties

let obj = {};
Object.defineProperties(obj, {
  num: {
    writable: true,
    enumrable: true,
    configurable: true,
    value: 0
  },
  val: {
    configurable: true,
    get() {
      return this.val;
    },
    set(v) {
      this.val = v;
    }
  }
})
// 若想冻结属性用Object.freeze(即使configurable, writable为true也不可修改)
(Object.freeze || Object)(obj);
// 若想保护prototype
// (Object.freeze || Object)(Fn.prototype);

5) Object.prototype.__defineGetter__/__defineSetter__

let obj = {x: 123}
Object.prototype.__defineGetter__("getX", function() {
  return this.x;
});
Object.prototype.__defineSetter__("setX", function(v) {
  this.x = v;
});

6) ES6的Proxy实现

let obj = new Proxy({}, {
  get(target, key, receiver) {
    console.log("get: ");
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    console.log("set: ");
   Reflect.set(target, key, value, receiver);
 }
});

7) 总结

查看对象的属性描述符

使用Object.getOwnPropertyDescriptor(obj, prop)方法

defineProperty&defineProperties的属性描述符

  • 数据描述符

    • configurable: 默认值false, 设置为true该属性描述符才能修改,value也能delete
    • enumerable: 默认值false,设置为true对象才能被for in遍历
    • writable: 默认值false,设置为truevalue才能被赋值运算符改变
    • value: 默认值undefined
  • 存储描述符

    function Obj() {
     this.xx = "666";
    }
    // 实例化对象
    let obj = new Obj();
    // 默认writable, enumerable, configurable的值全为false, value为undefined
    Object.defineProperty(obj, "yy", {
     writable: false,
     enumerable: false,
     configurable: false,
     value: "999"
    })
    // 查看配置选项
    console.log(Object.getOwnPropertyDescriptor(obj, "yy"));

    /* configurable默认值false  */
    // 设置为false, 不能delete, 不能重新定义属性, 如:
    Object.defineProperty(obj, "yy", {}); // 报错

    delete obj.yy; // 忽略操作, 无报错
    /* configurable默认值false  */

    /* writable默认值为false */
    // 设置为false, 不能通过通过赋值符号赋值
    obj.yy = "123"; // 忽略操作, 无报错
    /* writable默认值为false */

    /* enumerable默认值为false */
    // 设置为false, 不能通过for in遍历
    for (const key in obj) {
     console.log(key); // 只输出xx, 无yy
    }
    /* enumerable默认值为false */

    属性描述符可同时设置的键值

    configurableenumerablevaluewritablegetset
    数据描述符YesYesYesYesNoNo
    存取描述符YesYesNoNoYesYes

    如果一个描述符不具有 valuewritablegetset 中的任意一个键,那么它将被认为是一个数据描述符。如果一个描述符同时拥有 valuewritablegetset 键,则会产生一个异常。

    也就是有get或者set就不能有writablevalue

    • get: 默认值undefined
    • set: 默认值undefined接收唯一参数作为新值

参考链接:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值