半年多以前有一次面試,當時只是想要張回家的免費機票,順便看看運氣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