這裏是主要的業務邏輯,主要是針對模塊化作筆記記錄。html
這裏不關注業務邏輯代碼,只關注實現和應用 es6開發
因此分紅了四個模塊+2個特殊模塊:前端
app/js/lottery/calculate.js
計算模塊-負責彩票投注數和獎金數運算的app/js/lottery/timer.js
倒計時模塊app/js/lottery/base.js
基礎模塊-跟彩票自己相關的基礎信息的模塊app/js/lottery/interface.js
接口模塊-負責跟彩票中心(至關於後臺)交互的模塊app/js/index.js
入口模塊-特殊模塊1,負責處理整個應用的入口管理app/js/lottery.js
整合模塊-特殊模塊2,負責整合被分散的模塊class Calculate { } // 使用 es6的語法導出這個 class export default Calculate
/** * [computeCount 計算注數] * @param {number} active [當前選中的號碼的個數] * @param {string} play_name [當前的玩法標識,如R2,即任二] * @return {number} [注數] */ computeCount(active, play_name) { let count = 0; // 使用 es6的 map 結構 const exist = this.play_list.has(play_name); //判斷玩法列表裏面是否有這樣的玩法,set形式 // 使用 es6的填充數組功能 fill const arr = new Array(active).fill('0'); //生成長度爲active的數組,並填充爲0 if (exist && play_name.at(0) === 'r') { // 調用靜態方法combine count = Calculate.combine(arr, play_name.split('')[1]).length; } return count; }
這是裏面combine方法,這是一個靜態方法,這是 es6纔有的,須要 static 關鍵字便可。jquery
/** * [combine 組合運算 C(m,n)] */ static combine(arr, size) { // 省略 })(arr, size, []) return allResult }
class Timer { /** * 倒計時方法 * @param number end 截止時間 * @param function update 每次更新時間時的回調函數 * @param function handle 倒計時結束時的回調函數 * @return */ countdown(end, update, handle) { const now = new Date().getTime(); const self = this; if (now - end > 0) { handle.call(self); } else { // 剩餘時間 let last_time = end - now; // 常量,用來處理毫秒轉天,時,分,秒 const px_d = 1000 * 60 * 60 * 24; const px_h = 1000 * 60 * 60; const px_m = 1000 * 60; const px_s = 1000; // 剩餘時間轉換爲天,時,分,秒 let d = Math.floor(last_time / px_d); // 須要減去天的毫秒數 let h = Math.floor((last_time - d * px_d) / px_h); // 須要減去天和小時的毫秒數 let m = Math.floor((last_time - d * px_d - h * px_h) / px_m); // 須要減去天和小時和分鐘的毫秒數 let s = Math.floor((last_time - d * px_d - h * px_h - m * px_m) / px_s); let r = []; if (d > 0) { r.push(`<em>${d}</em>天`); } // 判斷數組長度主要是爲了防止數據錯亂,例如只有時,沒有分,秒的狀況 if (r.length || (h > 0)) { r.push(`<em>${h}</em>時`); } if (r.length || m > 0) { r.push(`<em>${m}</em>分`); } if (r.length || s > 0) { r.push(`<em>${s}</em>秒`); } // self.last_time = r.join(''); // 執行更新回調函數,使用的是計算以後的倒計時時間 update.call(self, r.join('')); // 間隔每秒執行一次,從新執行倒計時程序 setTimeout(function () { self.countdown(end, update, handle); }, 1000); } } } export default Timer
import $ from 'jquery';
這裏的初始化號碼由於須要補0,因此要使用 es7才提供的 padStart,須要藉助babel-polyfill
來實現,由於他的方便易用性,因此會被大量地被你們使用。git
/** * [initNumber 初始化號碼] * @return {[type]} [description] */ initNumber(){ for(let i=1;i<12;i++){ this.number.add((''+i).padStart(2,'0')) } }
omit 的數據是在整合模塊裏面被定義爲一個 map 結構的數據的,下面有說。
這裏主要關注 map 結構的應用:程序員
for...of
的方式,使用omit 的 entries()
獲取到全部值,而後遍歷/** * [setOmit 設置遺漏數據] * @param {[type]} omit [description] */ setOmit(omit){ let self=this; // map 結構處理數據 self.omit.clear(); for(let [index,item] of omit.entries()){ //omit是個map結構 self.omit.set(index,item) } $(self.omit_el).each(function(index,item){ $(item).text(self.omit.get(index)) }); }
這裏須要注意2個地方:es6
+
的方式,很是直觀而且簡單。self.getTotal()
直接調用當前實例的方法,這個其實很大程度上,將項目 class 化,而後經過實例這個 class,而後很方便的使用這個實例的全部方法。/** * [addCodeItem 添加單次號碼] * @param {[type]} code [description] * @param {[type]} type [description] * @param {[type]} typeName [description] * @param {[type]} count [description] */ addCodeItem(code,type,typeName,count){ let self=this; // es6的字符串模板使用 const tpl=` <li codes="${type}|${code}" bonus="${count*2}" count="${count}"> <div class="code"> <b>${typeName}${count>1?'複式':'單式'}</b> <b class="em">${code}</b> [${count}注,<em class="code-list-money">${count*2}</em>元] </div> </li> `; $(self.cart_el).append(tpl); self.getTotal(); //獲取總金額 }
導入了 jquery 模塊來使用,導入的目的是由於這個接口模塊裏面使用了其餘模塊的函數,例如self.setOmit(res.data);
,由於這個函數裏面涉及了 jquery 的相關使用,因此若是在這裏須要使用的話,就要引入 jquerygithub
使用了 es6的 promise進行異步操做,promise能夠代替 es5的無限回調問題。ajax
import $ from 'jquery'; class Interface{ /** * 先獲取遺漏數據,而後進行前端顯示,須要promise * @param string issue json數據 * @return promise */ getOmit(issue){ // 保存 this 指向 let self=this; // es6的 promise return new Promise((resolve,reject)=>{ $.ajax({ url:'/get/omit', data:{ issue:issue }, dataType:'json', success:function(res){ self.setOmit(res.data); resolve.call(self,res) }, error:function(err){ reject.call(err); } }) }); } // 省略
導入模塊json
import 'babel-polyfill'; import Base from './lottery/base.js'; import Timer from './lottery/timer.js'; import Calculate from './lottery/calculate.js'; import Interface from './lottery/interface.js'; import $ from 'jquery';
深度拷貝數組
Reflect.ownKeys
能夠拿到原對象的全部屬性。參考Reflect const copyProperties = function (target, source) { for (let key of Reflect.ownKeys(source)) { //拿到源對象上的全部屬性 if (key !== 'constructor' && key !== 'prototype' && key !== 'name') { //過濾 let desc = Object.getOwnPropertyDescriptor(source, key); // 獲取指定對象的自身屬性描述符 Object.defineProperty(target, key, desc); } } }
備註:
關於Object.defineProperty
和Object.getOwnPropertyDescriptor
關於深拷貝(深複製)和淺拷貝(淺複製)
多重繼承能夠根據各個不一樣的功能模塊分不一樣程序員獨立開發,最後合併起來,並且功能模塊耦合度比較小,出現BUG也能很快定義到功能模塊,修改其中某一個對其餘沒有影響。
js 設計的時候是定位爲簡單的腳本語言,因此沒有考慮 class 和繼承的問題,直至如今也依然木有考慮繼承的問題,都是 js 的開發者本身開發出來使用的,全部就有了這種相似多重繼承的多重繼承,本質上是將一個對象的屬性拷貝到另外一個對象上面去,其實就是對象的融合。
...mixins
,利用 rest 語法獲取函數的多餘參數簡化了寫法。使用以前提到的深度拷貝函數進行多重繼承
const mix = function (...mixins) { class Mix { } //聲明一個空的類 for (let mixin of mixins) { copyProperties(Mix, mixin); //深度拷貝 copyProperties(Mix.prototype, mixin.prototype); //也拷貝原型 } return Mix }
將Lottery進行多重繼承,繼承來自Base, Calculate, Interface, Timer的 class。
super()
,但須要注意順序,必須先使用super()
。class Lottery extends mix(Base, Calculate, Interface, Timer) { constructor(name = 'syy', cname = '11選5', issue = '**', state = '**') { super(); this.name = name; this.cname = cname; this.issue = issue; this.state = state; this.el = ''; this.omit = new Map(); this.open_code = new Set(); //開獎號碼 this.open_code_list = new Set(); //開獎記錄 this.play_list = new Map(); this.number = new Set(); //獎號 this.issue_el = '#curr_issue'; this.countdown_el = '#countdown'; //倒計時的選擇器 this.state_el = '.state_el'; //狀態的選擇器 this.cart_el = '.codelist'; //購物車的選擇器 this.omit_el = ''; //遺漏 this.cur_play = 'r5'; //當前的默認玩法 // 這個方法是在其餘類中已經被實現了,這裏只須要直接調用便可 this.initPlayList(); this.initNumber(); this.updateState(); //更新狀態 this.initEvent(); }
這裏經過異步獲取到後臺的數據,而後根據獲得的數據結果進行調用倒計時函數等操做。這是一個比較完整的異步調用函數處理寫法方式。
/** * [updateState 狀態更新] * @return {[type]} [description] */ updateState() { let self = this; this.getState().then(function (res) { // getState()是接口裏的方法 self.issue = res.issue; //拿到期號 self.end_time = res.end_time; //拿到截止時間 self.state = res.state; //拿到狀態 $(self.issue_el).text(res.issue); //更新期號 self.countdown(res.end_time, function (time) { //倒計時 $(self.countdown_el).html(time) }, function () { //從新獲取 setTimeout(function () { self.updateState(); self.getOmit(self.issue).then(function (res) { }); self.getOpenCode(self.issue).then(function (res) { }) }, 500); }) }) }
這個入口就將邏輯分離出來了,若是須要處理多個入口,能夠很方便的在這裏根據不一樣餓實例導入不一樣的模塊,而且統一管理。
import Lottery from './lottery'; //引入彩票的入口文件 // 實例化Lottery實例 const syy=new Lottery();