JavaScript发布订阅模式与defineProperty模拟实现Vue数据双向绑定

什么是发布-订阅模式?

发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。在JavaScript开发中,我们一般用事件模型来替代传统的发布-订阅模式。[1]
DOM节点绑定事件函数是最常用的发布-订阅模式

//订阅点击事件
document.body.addEventListener( 'click', function(){
  alert(2)
}, false )

document.body.click();  //模拟用户点击

//取消订阅点击事件
document.body.removeEventListener( 'click' )

Object.defineProperty

我们平时能轻松创建、修改对象,都是基于以下特性↓
JavaScript的对象有两种内部属性类型:数据属性访问器属性

数据属性承载(payload)对象指定属性的值([[Value]])以及控制其是否可被修改([[Writable]])或者是否可被删除([[Configurable]])和是否可被for-in循环返回到[[enumerable]],是对内的属性。
访问器属性,对外的属性。在读取属性时,会调用getter函数,这个函数负责返回有效的值;在修改属性值时,会调用setter函数承载传入的新值并决定如何处理数据。这两个函数都是可以被重写的。
而以上这些功能必须使用Object.defineProperty()方法实现

Tips: vue.js就是利用Object.defineProperty()的访问器思想来更新视图的。
IE8是第一个实现Object.defineProperty()的浏览器版本。然而这个版本实现存在诸多限制:只能在DOM对象上使用这个方法,而且只能创建访问器属性[2]。由于实现不彻底被不建议开发者使用,但在IE9得到改善,所以vue.js只能兼容到IE9及以上版本。

get and set在ES6的表现

ES6中的新语法class类也很好地保留了 类似访问器属性

class Person {
        constructor(name, age){
            this.name = name;
            this.age = age;
            this.innerTitle = "";

        }
        get title(){
            return this.innerTitle;
        }
        set title(value){
            this.innerTitle = value;
        }

        sayName(){
            alert(this.name);
        }
        getOlder(years){
            this.age += years;
        }
    }
    var p = new Person('Niko',24)
    p.title = 'asd'
    console.log(p.title)    //asd

基于设计模式和Object.defineProperty简单实现vue双向绑定模型

基本思路:

  • 利用Object.defineProperty监听对象赋值动作,
  • 遍历所有节点,
  • 使用观察者模式对拥有‘v-model’属性的DOM节点订阅上述事件
  • 对拥有‘v-bind’属性的DOM节点进行发布事件
  • 针对表单标签仅监听addEventListener('input',function(e){...})事件
图片资源来自[3]

HTML

<div id="app">
    <textarea cols="30" rows="10" v-model="asd"></textarea>
    <h1 v-bind="asd"></h1>
    <h1 v-bind="qwe"></h1>
</div>

观察者对象

   const _observer = {
        //存储消息列表
        clientList:{},
        //监听消息
        listen:function(key,fn){
            if(!this.clientList[key]){
                this.clientList[key] = []
            }
            this.clientList[key].push(fn)
        },
        //发布消息
        trigger:function(key,data){
            let fns = this.clientList[key];
            if(!fns || fns.length === 0) {
                return false;
            }
            for(let i=0,fn;fn = fns[i++];){
                fn.call(this,data);
            }
        }
    };

具体实现

    // 遍历app所有节点
    let nodes = document.querySelector('#app').children;
    // v-model节点对象
    let vModelList = Object.create(null);
    for(let i=0,node;node=nodes[i++];) {
        //存储v-model nodes
        if (node.hasAttribute('v-model')) {
            let key = node.getAttribute('v-model');
            //添加表单监听事件
            node.addEventListener('input', function (e) {
                vModelList[key] = e.target.value
            });
            /**
             * 核心 Object.defineProperty
             * **/
            //监听vModelList对象指定key值变化
            Object.defineProperty(vModelList, key, {
                enumerable: true,
                configurable: true,
                set: function (newVal) {
                    // 发布
                    _observer.trigger(key, newVal)
                }
            });
        }

        //存储v-bind nodes
        if (node.hasAttribute('v-bind')) {
            let key = node.getAttribute('v-bind');
            // 订阅
            _observer.listen(key, function (newVal) {
                node.innerHTML = newVal //这里只用innerHTML简单实现,未使用模版引擎
            })
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容