在上一篇文章里介绍了使用 jQuery 的事件来实现数据视图双向绑定,并且提到 Object.defineProperty。这篇文章专门详解如何用 Object.defineProperty 方法同样实现数据视图双向绑定的功能。
Object.defineProperty 是用来控制 Javascript 对象属性读取行为的方法,用法很直白:
1 2 3 4 5 6 7 8 9 10 11 | Object.defineProperty(obj, 'key', { get: () => { console.log('obj->key is got and always return 1'); return 1; }, set: (val) => { console.log('obj->key is set and nothing happend'); } }); |
其中第二个参数是对象的属性名,第三个参数是行为的配置。除了可以对属性值获取以及复制行为进行控制以外,还可以配置是否可写,是否可被遍历等,具体的配置参数在 MDN 相关章节解释得很清楚,我这就不多说了,这里我能用上的也只有 get 和 set 而已。
利用 Object.defineProperty 做双向绑定的具体思路:
- 利用 get,将页面上的某个地方显示的数据,绑定到对象上
1234Object.defineProperty(product, 'price', {get: () => +document.getElementById('某个 price 的 id').innerText}); - 利用 set,当对象的属性发生改变时,更新到视图上
123456Object.defineProperty(product, 'num', {set: (num) => {document.getElementById('某个商品数量的 id').innerHTML = num;}});
完整的代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Demo</title> </head> <body> <ul id="cart"> <li class="product"> 单价 $<span class="price">1</span> <button class="incre">+</button> <input type="number" class="number" value="1"> <button class="decre" disabled>-</button> 单品总价:$<span class="total-price">1</span> </li> <li class="product"> 单价 $<span class="price">3</span> <button class="incre">+</button> <input type="number" class="number" value="2"> <button class="decre">-</button> 单品总价:$<span class="total-price">6</span> </li> </ul> <p>总数量:<span id="total-num">3</span></p> <p>总价:$<span id="total-price">7</span></p> <script> // 定义购物车数据模型 var cart = { setProducts: (products) => { cart.products = products; cart.check(); }, check: () => { var num = 0; var price = 0; cart.products.forEach((product) => { num += product.num; price += product.getTotalPrice(); }); cart.num = num; cart.price = price; }, }; class Product { getTotalPrice() { return this.num * this.price; } setNum(num) { this.num = Math.min(Math.max(num, 1), 10); cart.check(); } canIncre() { return this.num < 10; // 假设库存都为 10 } canDecre() { return this.num > 1; } incre() { this.setNum(this.num + 1); } decre() { this.setNum(this.num - 1); } } (() => { var $products = document.querySelectorAll('.product'); var products = []; for (var i = 0; i < $products.length; ++i) { let $product = $products[i]; let product = new Product(); Object.defineProperty(product, 'price', { value: +$product.querySelector('.price').innerText }); Object.defineProperty(product, 'num', { get: () => +$product.querySelector('.number').value, set: (num) => { $product.querySelector('.number').value = num; $product.querySelector('.total-price').innerHTML = product.getTotalPrice(); $product.querySelector('.incre').disabled = !product.canIncre(); $product.querySelector('.decre').disabled = !product.canDecre(); } }); $product.querySelector('.incre').addEventListener('click', () => { product.incre(); }); $product.querySelector('.decre').addEventListener('click', () => { product.decre(); }); $product.querySelector('.number').addEventListener('change', (e) => { product.setNum(e.target.value); }); products.push(product); } Object.defineProperty(cart, 'num', { set: (num) => { document.getElementById('total-num').innerHTML = num; } }); Object.defineProperty(cart, 'price', { set: (price) => { document.getElementById('total-price').innerHTML = price; } }); cart.setProducts(products); })(); </script> </body> </html> |
数据视图双向绑定(纯 JS 利用 Object.defineProperty 实现) by Chris Yue is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

文章不错,我要帮站长分担建站费!
天使投赏人