第 74 题: 使用 JavaScript Proxy 实现简单的数据绑定

发布于 2022-10-17 18:29:05 字数 162 浏览 142 评论 15

Proxy 这个词的原意是代理,用在这里表示由它来 代理 某些操作,可以译为 代理器,可以理解成,在目标对象之前架设一层 拦截,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

薄荷梦 2022-05-04 13:56:12
    <input type="text">
    <p>hello world</p>

    <script>
        let input = document.getElementById('input');
        let text = document.getElementById('text');

        let obj = {};

        let proxy = new Proxy(obj, { 
            get(target, property){
                return Reflect.get(...arguments);
            },
            
            set(target, property, value){ 
                target[property] = value; 
                text.innerText = target[property]; 
                return Reflect.set(...arguments); 
            }
            
        })

        proxy.val = text.innerText;
        input.value = proxy.val;

        input.addEventListener('input', function(){
            proxy.val = this.value;
        })
    </script>

情深已缘浅 2022-05-04 13:56:04
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <span></span>
    <br>
    <input type="text" oninput="input(event)">
    <script>
        let spanEl = document.querySelector('span')
        let inputEl = document.querySelector('input')
        let data = {
            val: null
        }
        let proxy = new Proxy(data, {
            get(target, p, receiver) {
                return Reflect.get(target, p, receiver)
            },
            set(target, p, val, receiver) {
                spanEl.innerText = val
                inputEl.value = val
                return Reflect.set(target, p, val, receiver)
            }
        })

        function input(e) {
            proxy.val = e.target.value
        }

    </script>
</body>

</html>
放飞的风筝 2022-05-04 13:55:58
  <input type="text" oninput="handleChange()">
  <div></div>
  <script>

    let inp = document.getElementById('inp')
    let app = document.getElementById('app')
    
    let obj = {
      defaultValue: 'hello world'
    }
    let proxy = new Proxy(obj, {
      get: function(obj, key) {
        console.log('get')
        return obj[key]
      },
      set(obj, key, value) {
        obj.defaultValue = value
        notify()
      }
    })

    app.innerHTML = proxy.defaultValue
    inp.value = proxy.defaultValue
    
    function notify() { 
      app.innerHTML = proxy.defaultValue
    }

    function handleChange() {
      proxy.defaultValue = inp.value
    }
  </script>
°懵少女 2022-05-04 13:55:58
<body>
  hello,world
  <input type="text">
  <p></p>
</body>
<script>
  const model = document.getElementById("model")
  const word = document.getElementById("word")
  var obj= {};

  const newObj = new Proxy(obj, {
      get: function(target, key, receiver) {
        console.log(`getting ${key}!`);
        return Reflect.get(target, key, receiver);
      },
      set: function(target, key, value, receiver) {
        console.log('setting',target, key, value, receiver);
        if (key === "text") {
          model.value = value;
          word.innerHTML = value;
        }
        return Reflect.set(target, key, value, receiver);
      }
    });

  model.addEventListener("keyup",function(e){
    newObj.text = e.target.value
  })
</script>

model.value = value; 这一行代码是不是不需要写

灯角i 2022-05-04 13:55:52
<body>
  <div>
    <input type="text" v-model="title">
    <input type="text" v-model="title">
    <div v-bind="title"> </div>
  </div>

  <script>
    'user strict'
    function View() {
      // 设置代理拦截
      let proxy = new Proxy({}, {
        get(obj, property) { },
        set(obj, property, value) {
          document.querySelectorAll(`[v-model='${property}'],[v-bind='${property}']`)
            .forEach(el => el.innerHTML = el.value = value)
        }
      })

      // 初始化 绑定元素
      this.run = function () {
        const elems = document.querySelectorAll("[v-model]");
        elems.forEach(el => {
          el.addEventListener('keyup', event => {
            proxy[event.target.getAttribute('v-model')] = event.target.value;
          })
        })
      }

    }

    let view = new View();
    view.run();
  </script>

</body>
笔芯 2022-05-04 13:55:16

监听dom的value变化 去更新 obj
obj的数据发生变化 去更新 dom


普通简易版本:

    名字:<input type="text"><br/>
    你的名字: <p></p>


    <script type="text/javascript">
      var obj = {
        name: ''
      }
      Object.defineProperty(obj, 'name', {
        set: function(value) {
          document.getElementById('name').value = value
          document.getElementById('pName').innerHTML = value
        }
      })
      document.getElementById('name').addEventListener('input', function(e){
        obj.name = e.target.value
      })

    </script>

proxy版本 好像没啥特殊的:

    名字:<input type="text"><br/>
    你的名字: <p></p>


    
    <script type="text/javascript">
      // Proxy
      var obj = {
        name: ''
      }
      var proxyObj = new Proxy(obj, {
        get: function(target, key, receiver) {
          return Reflect.get(target, key, receiver)
        },
        set: function(target, key, value, receiver) {
          if (key === 'name') {
            document.getElementById('name').value = value
            document.getElementById('pName').innerHTML = value
          }
          return Reflect.set(target, key, value, receiver)
        }
      })

      document.getElementById('name').addEventListener('input', function(e){
        proxyObj.name = e.target.value
      })
    </script>
归途 2022-05-04 13:55:06
<body>
  His Name:<span></span>
</body>
<script>
//定义需要监控的_data对象
  var _data = { name: "zhangsan", age: 18 };

//监控_data
  var data = new Proxy(_data, {
    set(obj, key, value) {
      obj[key] = value;
     //当有值的时候刷新显示区域
      render();
    },
    get(obj, key) {
      return obj[key];
    }
  })

  function render() {
    document.getElementById("username").innerHTML = data.name;
  }
  render();
</script>

可打开f12修改 data.name 查看改变情况

滥情哥ㄟ 2022-05-04 13:50:06
<input type="text" >
<p></p>
<script>
  const input = document.getElementById('input');
  const p = document.getElementById('p');
  const obj = {};
  const newObj = new Proxy(obj, {
    get: function(target, key, receiver) {
      console.log(`getting ${key}!`);
      return Reflect.get(target, key, receiver);
    },
    set: function(target, key, value, receiver) {
      console.log(target, key, value, receiver);
      if (key === 'text') {
        input.value = value;
        p.innerHTML = value;
      }
      return Reflect.set(target, key, value, receiver);
    },
  });

  input.addEventListener('keyup', function(e) {
    newObj.text = e.target.value;
  });
</script>
逆流。 2022-05-04 13:49:42
<b></b>
<button onclick="increase()">+</button>
<button onclick="decrease()">-</button>
const data = { count: 0 };
const proxy = new Proxy(data, {
  get(target, property) {
    return target[property];
  },
  set(target, property, value) {
    target[property] = value;
    render(value);
  }
});

render(proxy.count);

function render(value) {
  document.getElementById('count').innerHTML = value;
}

function increase() {
  proxy.count += 1;
}

function decrease() {
  proxy.count -= 1; 
}

set 方法必须返回 true 或者 false 你这样写是有问题的 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/set

水水月牙 2022-05-04 13:30:20

利用Proxy实现一个简化版的MVVM
参照vue的响应式设计模式,将数据劫持部分的Obejct.defineProperty替换为Proxy即可,其他部分,如compile(编译器没有实现,用写好的html模拟已完成编译),watcher,dep,事件监听等基本保持不变,简单实现代码如下:

<!-- html部分 -->
<div></div>
<input type="text" name=""/>
// js部分
class Watcher{
	constructor(cb){
		this.cb = cb;
	}
	update(){
		this.cb()
	}
}
class Dep{
	constructor(){
		this.subs = [];
	}
	publish(){
		this.subs.forEach((item)=>{
			item.update && item.update();
		})
	}
}
class MVVM{
	constructor(data){
		let that = this;
		this.dep = new Dep();
		this.data = new Proxy(data,{
			get(obj, key, prox){
				that.dep.target && that.dep.subs.push(that.dep.target);
				return obj[key]
			},
			set(obj, key, value, prox){
				obj[key] = value;
				that.dep.publish();
				return true;
			}
		})
		this.compile();
	}
	compile(){
		
		let divWatcher = new Watcher(()=>{
			this.compileUtils().div();
		})
		this.dep.target = divWatcher;
		this.compileUtils().div();
		this.dep.target = null;
		
		let inputWatcher = new Watcher(()=>{
			this.compileUtils().input();
		})
		this.dep.target = inputWatcher;
		this.compileUtils().input();
		this.compileUtils().addListener();
		this.dep.target = null;
	}
	compileUtils(){
		let that = this;
		return {
			div(){
				document.getElementById('foo').innerHTML = that.data.foo;
			},
			input(){
				document.getElementById('bar').value = that.data.bar;
			},
			addListener(){
				document.getElementById('bar').addEventListener('input', function(){
					that.data.bar = this.value;
				})
			}
		}
	}
}
let mvvm = new MVVM({foo: 'foo233', bar: 'bar233'})

通过mvvm.data.foo或者mvvm.data.bar可以操作数据,可以观察到view做出了改变;在输入框改变输入值,也可以通过mvvm.data观察到数据被触发改变

彩扇题诗 2022-05-04 11:48:45
        let person = {
            name:'jesse',
            age:25
        }
        let proxy = new Proxy(person,{
            get(target,prop){
                console.log('get')
                return target[prop]
            },
            set(obj,prop,value){
                if(value>=30){
                    throw new Error('invalid')
                }
                obj[prop] = value
            }
        })
        console.log(proxy.name) //get jesse
        proxy.age = 30   //Uncaught Error: invalid
呆° 2022-05-03 14:12:50
<b></b>
<button onclick="increase()">+</button>
<button onclick="decrease()">-</button>
const data = { count: 0 };
const proxy = new Proxy(data, {
  get(target, property) {
    return target[property];
  },
  set(target, property, value) {
    target[property] = value;
    render(value);
  }
});

render(proxy.count);

function render(value) {
  document.getElementById('count').innerHTML = value;
}

function increase() {
  proxy.count += 1;
}

function decrease() {
  proxy.count -= 1; 
}
_畞蕅 2022-05-03 10:31:28

Proxy实现一个简单的双向绑定的 todo list

<div>
    <input type="text">
    <div>
      TODO:
      <span></span>
    </div>
    <div>Add To Todo List</div>
    <ul></ul>
  </div>
const input = document.getElementById('input')
    const text = document.getElementById('text')
    const list = document.getElementById('list')
    const btn = document.getElementById('btn')

    let render

    const inputObj = new Proxy({}, {
      get (target, key, receiver) {
        return Reflect.get(target, key, receiver)
      },
      set (target, key, value, receiver) {
        if (key === 'text') {
          input.value = value
          text.innerHTML = value
        }
        return Reflect.set(target, key, value, receiver)
      }
    })

    class Render {
      constructor (arr) {
        this.arr = arr
      }
      init () {
        const fragment = document.createDocumentFragment()
        for (let i = 0; i < this.arr.length; i++) {
          const li = document.createElement('li')
          li.textContent = this.arr[i]
          fragment.appendChild(li)
        }
        list.appendChild(fragment)
      }
      addList (val) {
        const li = document.createElement('li')
        li.textContent = val
        list.appendChild(li)
      }
    }

    const todoList = new Proxy([], {
      get (target, key, receiver) {
        return Reflect.get(target, key, receiver)
      },
      set (target, key, value, receiver) {
        if (key !== 'length') {
          render.addList(value)
        }
        return Reflect.set(target, key, value, receiver)
      }
    })

    window.onload = () => {
      render = new Render([])
      render.init()
    }

    input.addEventListener('keyup', e => {
      inputObj.text = e.target.value
    })

    btn.addEventListener('click', () => {
      todoList.push(inputObj.text)
      inputObj.text = ''
    })
傾城如夢未必闌珊 2022-05-02 23:51:59
<body>
  hello,world
  <input type="text">
  <p></p>
</body>
<script>
  const model = document.getElementById("model")
  const word = document.getElementById("word")
  var obj= {};

  const newObj = new Proxy(obj, {
      get: function(target, key, receiver) {
        console.log(`getting ${key}!`);
        return Reflect.get(target, key, receiver);
      },
      set: function(target, key, value, receiver) {
        console.log('setting',target, key, value, receiver);
        if (key === "text") {
          model.value = value;
          word.innerHTML = value;
        }
        return Reflect.set(target, key, value, receiver);
      }
    });

  model.addEventListener("keyup",function(e){
    newObj.text = e.target.value
  })
</script>
~没有更多了~

关于作者

梦幻的心爱

暂无简介

0 文章
0 评论
25 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文