你们好,我是苏日俪格,在不少次面试的时候, 面试官都会多多少少问到vue的双向数据绑定的原理是什么? 这个问题就很尴尬了, 我当初来如今的这家公司的时候初试和复试都遇到了这个问题, 不只如此, 以前面试的几家公司也都问了这个让我值得深思又避免不了尴尬的问题, 在这两天完成了本身的任务后, 静了下心来回想一下这个问题.html
在网上查找许多资料, 多是IT界的大佬们讲的太专业太深奥了, 也多是我真的真的是一个名副其实的理工科生, 对文字理解能力差吧, 可我仍是没懂, 后来让我这个历来不爱读书的孩子在书中找到了这个黄金屋, 偶然发现了有关绑定数据属性的问题, 下面我把个人理解跟你们分享一下,本文纯属我的理解,有哪里不对的地方请在评论区指出,你们一块儿学习共同进步。vue
所谓双向数据绑定, 无非就是数据层和视图层中的数据同步, 在写入数据时视图层实时的跟着更新, 以前在网上看到大佬们是这么描述的:git
实现mvvm的双向绑定,是采用数据劫持结合发布者-订阅者模式的方式,经过Object.defineProperty()来劫持各个属性的setter,getter,在数据变更时发布消息给订阅者,触发相应的监听回调。就必需要实现如下几点:
一、实现一个数据监听器Observer,可以对数据对象的全部属性进行监听,若有变更可拿到最新值并通知订阅者
二、实现一个指令解析器Compile,对每一个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
三、实现一个Watcher,做为链接Observer和Compile的桥梁,可以订阅并收到每一个属性变更的通知,执行指令绑定的相应回调函数,从而更新视图
四、mvvm入口函数,整合以上三者github![]()
一共分四步, 每一步都有大堆解释和一大堆的代码, 然而我真的只是一个名副其实的理工科生, 看到了文中的发布者-订阅者模式, 因而乎我去网上各类百度, 我的理解就是getter函数里面执行的任务就是watcher订阅者, 而setter函数执行的任务就是发布者; 相信不少人看过了这个也是只知其一;不知其二, 下面我来解释一波:面试
ECMAScript中有两种属性: 数据属性和访问器属性, 数据属性通常用于存储数据数值, 访问器属性对应的是set/get操做, 不能直接存储数据值, 每种属性下面又都含有四个特性.下面介绍一下:mvvm
1.[[Configurable]]: 表示可否经过delete将属性删除,可否把属性修改成访问器属性, 默认为false。当把属性Configurable设置为false后,该属性不能经过delete删除,而且也没法再将该属性的Configurable设置回true
2.[[Enumerable]]: 表示属性能否被枚举(便是否能够经过for in循环返回),默认false
3.[[Writable]]: 表示属性是否可写(便是否能够修改属性的值),默认false
4.[[Value]]: 该属性的数据值, 默认是undefined函数
1.[[Configurable]]: 表示可否经过delete将属性删除,可否把属性修改成数据属性, 默认为false。当把属性Configurable设置为false后,该属性不能经过delete删除,而且也没法再将该属性的Configurable设置回true
2.[[Enumerable]]: 表示属性能否被枚举(便是否能够经过for in循环返回),默认false
3.[[Get]]: 读取属性时调用的函数, 默认为undefined
4.[[Set]]: 写入属性时调用的函数, 默认是undefined学习
在修改属性的特性或者定义访问器属性的时候, 须要借助ECMAScript 5中的一个方法: Object.defineProperty(), 这个方法接收三个参数: 属性所在对象, 属性的名字, 描述符对象; 为对象定义多个属性的话, 就用函数的复数写法:Object.defineProperties();spa
那么经过这个ES5的方法就能够直接很简单粗暴的说明双向绑定的原理:双向绑定
<input type="text" id="inp" /> <div id="box"></div> <script> let obj = {}; let oInp = document.getElementById('inp'); let oBox = document.getElementById('box'); Object.defineProperty(obj, 'name', { configurable: true, enumerable: true, get: function() { console.log(111) return val; }, set: function(newVal) { oInp.value = newVal; oBox.innerHTML = newVal; } }); oInp.addEventListener('input', function(e) { obj.name = e.target.value; }); obj.name = '苏日俪格';
那么实现数据双向绑定的核心就是利用为每个属性都建立了订阅者的实例对象, 以便观察, getter函数里面返回一个value值,在setter函数中写入修改后的值并调用update方法更新视图的数据值, Configurable和Enumerable这两个特性描述符默认是true, 所以不用写
function defineReactive (obj, key, val) { var dep = new Dep(); //这是一个构造函数 其原型是为属性添加订阅者 Object.defineProperty(obj, key, { get: function() { if(Dep.target) { dep.addSub(Dep.target); //添加订阅者到Dep实例对象 } return val; // 返回监听到的value值 }, set: function (newVal) { if(newVal === val) return; val = newVal; // 写入新的value值 dep.notify(); // 做为发布者发出通知 而后dep会迭代调用各自的update方法来更新视图 } }); } function observe(obj, vm) { Object.keys(obj).forEach(function(key) { defineReactive(vm, key, obj[key]); }); }
若是想要仔细研究的能够参考一下他的博客或者http://www.cnblogs.com/canfoo/p/6891868.html
但愿你们阅读完本文能够有所收获,由于能力有限,掌握的知识也是不够全面,欢迎你们提出来一块儿分享!谢谢O(∩_∩)O~
欢迎访问个人GitHub,喜欢的能够star,项目随意fork,支持转载但要下标注,同时恭候:我的博客