半年多以前有一次面试,当时只是想要张回家的免费机票,顺便看看运气javascript
却不想那次面试获益颇丰,因此没事出去面试面试对我的的知识总结以及思惟的深化颇有帮助的哦css
深化固然不是一朝一夕的事情,好比当时面试官就问我什么是“表现与数据分离”,就这个问题我就前先后后学习了好久,也和不少同事讨论过,可是一直没有一个比较好的结果html
最近在作ipad相关的单页应用研究,被一个问题困住了思惟,晚上看了两集布袋戏,完了在纸上画着画着,忽然对半年多以前的一道面试题颇有点思路前端
因而,今天晚上,抽时间记录之,此文只是我的看法,不必定正确,有误请指正,时间紧时间晚,行文不清晰勿怪java
首先,当时我简历是抄的,里面有一句“对表现与数据分离有必定理解”,而后面试官就针对这点开问了node
PS:尼玛,那是抄的,我哪里知道他是干神马的......jquery
为了帮助我展开思惟,面试官出了一道题:面试
他有一个国家列表,如今要将国家列表放到A中,而后B能够由A选择,也能够有总列表选择
可是B中添加后,如果A中没有要动态为A增长这项。
问题出来了,我如今一看就知道面试官想问观察者相关的知识点,当时没有理解到啦......app
如今来看,说这道题与表现与数据分离有关是没有问题的,却不必定能让人很好的产生联想,因此这个问题的答案,仍是须要继续挖掘dom
针对这个问题其实有几个实现方案,就算是初级入门的朋友也是能作的,可是要好好的回答这个题咱们须要看到后面隐藏的意图
好比,如今是操做B时候须要A与之联动,若是如今出了一个C或者D呢???
http://sandbox.runjs.cn/show/tsszo0hg
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <title> 5 </title> 6 <style type="text/css"> 7 .country { margin: 20px; padding: 30px; border: 1px solid black;} .item 8 { margin: 10px; padding: 10px; border: 1px solid black; } .list { margin: 9 10px; padding: 10px; border: 1px solid black; min-height: 50px;} #pop { 10 border: 1px solid black; padding: 0; position: absolute; display: none; 11 background-color: White; } #pop li { list-style: none; padding: 10px; border-bottom: 12 1px solid black; } #pop li:hover { background-color: Gray; } 13 </style> 14 15 <script id="jquery_183" type="text/javascript" class="library" src="/js/sandbox/jquery/jquery-1.8.3.min.js"></script> 16 </head> 17 <body> 18 <div class="country"> 19 <h2> 20 所有国家 21 </h2> 22 <div id="all" class="list"> 23 </div> 24 </div> 25 <div class="country"> 26 <h2> 27 A国家 28 </h2> 29 <div id="a" class="list"> 30 </div> 31 </div> 32 <div class="country"> 33 <h2> 34 B国家 35 </h2> 36 <div id="b" class="list"> 37 </div> 38 </div> 39 <ul id="pop"> 40 <li attr="a"> 41 移动到A 42 </li> 43 <li attr="b"> 44 移动到B 45 </li> 46 <li attr="remove"> 47 删除 48 </li> 49 </ul> 50 51 <script type="text/javascript"> 52 var array = []; 53 var slice = array.slice; 54 var Pop = function() { 55 this.el = $('#pop'); 56 var scope = this; 57 this.el.on('click', 58 function(e) { 59 var item = $(e.target).attr('attr'); 60 scope.click(item); 61 scope.el.hide(); 62 }) 63 }; 64 Pop.prototype = { 65 show: function(e, callback) { 66 this.el.css('top', e.pageY); 67 this.el.css('left', e.pageX); 68 this.el.show(); 69 this.click = callback; 70 } 71 }; 72 var pop = new Pop(); 73 var CountryList = function(opts) { 74 this.list = opts.list || []; 75 this._events = {}; 76 this.root = opts.el || $('body'); 77 if (opts.events) { 78 for (var k in opts.events) this.on(k, opts.events[k]); 79 } 80 this.render(); 81 this.bindEvent(); 82 this.exec('onLoad') 83 }; 84 CountryList.prototype = { 85 render: function() { 86 var item; 87 for (var i = 0, 88 len = this.list.length; i < len; i++) { 89 item = $('<span class="item" id="' + i + '">' + this.list[i] + '</span>'); 90 this.root.append(item); 91 } 92 }, 93 bindEvent: function() { 94 }, 95 add: function(name) { 96 var item = $('<span class="item" id="' + this.list.length + '">' + name + '</span>'); 97 this.list.push(name); 98 this.root.append(item); 99 this.exec('onAdd', name, item); 100 }, 101 remove: function(name) { 102 var index = this.list.indexOf(name); 103 if (index != -1) { 104 this.list.splice(index, 1); 105 var item = this.root.find('#' + index); 106 item.remove(); 107 } 108 this.exec('onRemove', name, item); 109 }, 110 on: function(type, fn) { 111 if (!this._events[type]) { 112 this._events[type] = []; 113 } 114 this._events[type].push(fn); 115 }, 116 exec: function(type) { 117 if (!this._events[type]) { 118 return; 119 } 120 var i = 0, 121 l = this._events[type].length; 122 if (!l) { 123 return; 124 } 125 var args = slice.call(arguments, 1); 126 for (; i < l; i++) { 127 this._events[type][i].apply(this, args); 128 } 129 }, 130 exist: function (name) { 131 return this.list.indexOf(name) != -1; 132 } 133 }; 134 135 136 var all = new CountryList({ 137 list: ['中国', '美国', '英国', '法国', '朝鲜', '日本'], 138 el: $('#all'), 139 events: { 140 onLoad: function () { 141 this.root.on('click', function(e) { 142 var el = $(e.target); 143 if(el.attr('class') != 'item') return; 144 pop.show(e, function(item) { 145 if(item == 'a') { 146 a.add(el.html()) 147 } else if(item == 'b') { 148 b.add(el.html()) 149 } 150 }); 151 }); 152 } 153 154 155 } 156 }); 157 var a = new CountryList({ 158 el: $('#a'), 159 events: { 160 onLoad: function () { 161 this.root.on('click', function(e) { 162 var el = $(e.target); 163 if(el.attr('class') != 'item') return; 164 pop.show(e, function(item) { 165 if(item == 'b') { 166 b.add(el.html()) 167 } 168 }); 169 }); 170 } 171 } 172 }); 173 var b = new CountryList({ 174 el: $('#b'), 175 events: { 176 onLoad: function () { 177 this.root.on('click', function(e) { 178 var el = $(e.target); 179 if(el.attr('class') != 'item') return; 180 pop.show(e, function(item) { 181 if(item == 'remove') { 182 b.remove(el.html()) 183 } 184 }); 185 }); 186 }, 187 onAdd: function(name) { 188 if(a.exist(name) == false) a.add(name) 189 }, 190 onRemove: function(name) { 191 console.log('remove'); 192 } 193 } 194 }); 195 </script> 196 </body> 197 </html>
1 var CountryList = function(opts) { 2 this.list = opts.list || []; 3 this._events = {}; 4 this.root = opts.el || $('body'); 5 if (opts.events) { 6 for (var k in opts.events) this.on(k, opts.events[k]); 7 } 8 this.render(); 9 this.bindEvent(); 10 this.exec('onLoad') 11 }; 12 CountryList.prototype = { 13 render: function() { 14 var item; 15 for (var i = 0, 16 len = this.list.length; i < len; i++) { 17 item = $('<span class="item" id="' + i + '">' + this.list[i] + '</span>'); 18 this.root.append(item); 19 } 20 }, 21 bindEvent: function() { 22 }, 23 add: function(name) { 24 var item = $('<span class="item" id="' + this.list.length + '">' + name + '</span>'); 25 this.list.push(name); 26 this.root.append(item); 27 this.exec('onAdd', name, item); 28 }, 29 remove: function(name) { 30 var index = this.list.indexOf(name); 31 if (index != -1) { 32 this.list.splice(index, 1); 33 var item = this.root.find('#' + index); 34 item.remove(); 35 } 36 this.exec('onRemove', name, item); 37 }, 38 on: function(type, fn) { 39 if (!this._events[type]) { 40 this._events[type] = []; 41 } 42 this._events[type].push(fn); 43 }, 44 exec: function(type) { 45 if (!this._events[type]) { 46 return; 47 } 48 var i = 0, 49 l = this._events[type].length; 50 if (!l) { 51 return; 52 } 53 var args = slice.call(arguments, 1); 54 for (; i < l; i++) { 55 this._events[type][i].apply(this, args); 56 } 57 }, 58 exist: function (name) { 59 return this.list.indexOf(name) != -1; 60 } 61 };
由于自己比较晚了,加之今天工做量比较大,如今头脑有点浆糊,因此写的代码各位看看就好,没必要认真,这个代码有何意义,有与表现与数据分离有什么关系呢?
其实这个代码自己就是实现了一个简单的数据集合类,在其内部有自建的事件机制,分别在三个点能够注册事件:
① 加载时
② 增长一项时
③ 删除一项时
因而,我就在几个点插入了本身的逻辑,咱们特别来讲一下b,咱们为b注册了onload事件,而且onadd事件,在增长一项时会看看b中是否有,没有就增长了
这样作的话,咱们就能够只关注b的数据了,而没必要关心数据是拖过来,仍是飞过来,仍是爬过来了,咱们这里的表现是什么呢?
我认为,表现就是增长数据的方式,这个题来讲,是增长b项目的方式,咱们题目中要求是“拖”,我嫌立刻变成了“选”,这个增长数据的“表现”是多种多样的,
这里说是表现,我看行为更贴切,表现容易往模板上挂钩
好比说:
能够看到,咱们在console环境下,b中新增一项也会往a国家新增,这个也是一种“表现”
PS:不知道我这个样子理解表现是否正确
表现各类各样,可是各类各样的表现并不该该影响数据的维护,好比b国家竟然增长了一项all国家没有的数据,难道all国家列表中不该该增长吗?
反过来讲,咱们a列表,b列表数据都最终来源于all,若是all的国家都被删除了那么ab国家的数据也是不该该存在了,而如今是ab,实际状况可能a-z
而且,数据消失的方法五花八门......
若是咱们不是在数据上注册了事件的话,咱们可能遇到如下状况:
① 拖的时候咱们须要作相关逻辑准备
② 选的时候咱们须要作相关逻辑
③ 增长b国家项目的方法变成使用select或者其余什么方法了,这个时候咱们可能又要单独写逻辑了
因此咱们如今要将各类“表现”给分离出来,只要操做了“数据”,好比增删,我就会执行不变的逻辑,数据处理的逻辑不以表现而变化
以上即是我对变现与数据分离的理解,理解如果有误差,请各位指出
这种模式的编码可能用于哪些地方呢?
主要用于一对多的变化,特别是多个dom共享一个data,这个时候data变了,咱们不一样的dom也须要获得变化
好比,我如今ipad项目上有一个城市列表数据(在localstarage中),左右两边同时会有针对该数据的显示,这个时候咱们就须要对该data作一次封装
而且在其改变时候注册事件,让哥哥dom(view)注册事件,在data改变时候注册了事件的dom就会获得变化,好比左边致使数据变化
数据一旦发生变化,会触发相关事件,而后右边dom的数据就变了,这样作很是好,经过数据联系左右两边,由于左右两边可能根本没法通信
却由于数据联系起来了
其实关于这个的场景大同小异,另一个常见的场景不那么明显,好比咱们如今有一个商品,咱们选择的数量不一样,咱们须要显示的折扣点不一样,
一样咱们的价钱固然会发生变化,这个变化看上去只是小区域的,可是总会有些莫名其妙的dom在页面的各个地方显示的相关的数据,一个不当心就会漏了
而后会有bug,这个状况下,就能够为个数相关的建立相关事件,当个数变化时候,其折扣相关的dom,价格相关的dom,或者其余会有一个联动关系
固然实际的业务可能比较复杂,是否应该这么干,值得这么干还得看状况
昨晚大概这个时候,我忽然想到了半年多以前的这个问题,今天下班后继续对他进行了学习,时间不够学习程度有深有浅
如果文章有任何问题,请您留意,针对这一问题后面可能还会有更深一步的学习,接下来咱们的重心仍是nodejs,只不过最近时间蛮紧的。。。。。。
亲爱的道友们,我其实在咱们团队只是中等水平,咱们上海携程无线是一个优秀的团队,
若是你如今正在找工做,请加入咱们吧!!!
在咱们团队,你能够肆无忌惮的黑本身的老大,你会体会到和谐的氛围,固然妹子多多的!
要求:靠谱前端,吃苦耐劳,待遇刚刚的!!!
须要的朋友能够私信我
顺便推广道友的jquery技术交流群:239147101