摘要:假設(shè)我們正在編寫一個渲染廣東省地圖的頁面,目前從第三方資源里獲得了廣東省的所有城市以及它們所對應(yīng)的并且成功地渲染到頁面中此處我們遍歷老數(shù)據(jù)把它們添加到空對象中然后返回這個對象使用適配器模式可以解決參數(shù)類型有些許不一致造成的問題。
策略模式
定義一系列的算法,把它們一個個封裝起來,并且使它們可以相互替換。目的是將算法的使用與算法的實現(xiàn)分離開來。
一個基于策略模式的程序至少由兩部分組成:
一組策略類,策略類封裝了具體的算法,并且負責(zé)具體的計算過程。
環(huán)境類Context, Context接受客戶的請求,隨后把請求委托給某一個策略類。
validator = { validate: function (value, type) { switch (type) { case "isNonEmpty ": { return true; // NonEmpty 驗證結(jié)果 } case "isNumber ": { return true; // Number 驗證結(jié)果 break; } case "isAlphaNum ": { return true; // AlphaNum 驗證結(jié)果 } default: { return true; } } } }; // 測試 alert(validator.validate("123", "isNonEmpty"));
var validator = { // 所有可以的驗證規(guī)則處理類存放的地方,后面會多帶帶定義 types: {}, // 驗證類型所對應(yīng)的錯誤消息 messages: [], // 當然需要使用的驗證類型 config: {}, // 暴露的公開驗證方法 // 傳入的參數(shù)是 key => value對 validate: function (data) { var i, msg, type, checker, result_ok; // 清空所有的錯誤信息 this.messages = []; for (i in data) { if (data.hasOwnProperty(i)) { type = this.config[i]; // 根據(jù)key查詢是否有存在的驗證規(guī)則 checker = this.types[type]; // 獲取驗證規(guī)則的驗證類 if (!type) { continue; // 如果驗證規(guī)則不存在,則不處理 } if (!checker) { // 如果驗證規(guī)則類不存在,拋出異常 throw { name: "ValidationError", message: "No handler to validate type " + type }; } result_ok = checker.validate(data[i]); // 使用查到到的單個驗證類進行驗證 if (!result_ok) { msg = "Invalid value for *" + i + "*, " + checker.instructions; this.messages.push(msg); } } } return this.hasErrors(); }, // helper hasErrors: function () { return this.messages.length !== 0; } }; // 驗證給定的值是否不為空 validator.types.isNonEmpty = { validate: function (value) { return value !== ""; }, instructions: "傳入的值不能為空" }; // 驗證給定的值是否是數(shù)字 validator.types.isNumber = { validate: function (value) { return !isNaN(value); }, instructions: "傳入的值只能是合法的數(shù)字,例如:1, 3.14 or 2010" }; // 驗證給定的值是否只是字母或數(shù)字 validator.types.isAlphaNum = { validate: function (value) { return !/[^a-z0-9]/i.test(value); }, instructions: "傳入的值只能保護字母和數(shù)字,不能包含特殊字符" }; var data = { first_name: "Tom", last_name: "Xu", age: "unknown", username: "TomXu" }; //該對象的作用是檢查驗證類型是否存在 validator.config = { first_name: "isNonEmpty", age: "isNumber", username: "isAlphaNum" }; validator.validate(data); if (validator.hasErrors()) { console.log(validator.messages.join(" ")); }
通過策略模式,消除了大片的條件分支語句。將算法封裝在獨立的strategy中,使得它們易于切換,易于理解,易于擴展。同時,算法還可以復(fù)用在其他地方,從而避免許多重復(fù)的復(fù)制粘貼工作。
代理模式代理模式為一個對象提供一種代理以控制對這個對象的訪問。
虛擬代理是我們最常用的代理模式,它把一些開銷很大的對象,延遲到真正需要用到這個對象的時候才去創(chuàng)建
虛擬代理實現(xiàn)圖片預(yù)加載
var addImg = (function(){ var img = document.createElement("img"); document.body.appendChild(img); return { setSrc: function(src){ img.src = src; } } })(); var proxyAddImg = (function(){ var img = new Image(); img.onload = function(){ addImg.setSrc(this.src); } return { setSrc: function(src){ addImg.setSrc("loading.gif"); img.src = src; } } })(); proxyAddImg.setSrc("demo.png");
虛擬代理合并Http請求
我們可以通過一個代理函數(shù)來收集一段時間之內(nèi)的請求,最后把請求合并到一起發(fā)送給服務(wù)器
var proxySynData = (function(){ var cache = [], //緩存我們需要同步的內(nèi)容 timer; //定時器 return function(ID){ if(!timer){ //定時器不存在就創(chuàng)建 timer = setTimeout(function(){ synData(cache.join()); //同步合并后的數(shù)據(jù) cache.length = 0; //清空緩存 clearTimeout(timer); //清除定時器 timer = null; //方便垃圾回收 }, 2000); } cache.push(ID); //存入緩存 } })(); var list = document.getElementsByTagName("input"); for(var i = 0, item; item = list[i]; i++){ item.onclick = function(){ if(this.checked){ proxySynData(this.id); } }; }
緩存代理
緩存代理就很好理解了,可以緩存一些開銷很大的運算結(jié)果;如果你第二次執(zhí)行函數(shù)的時候,傳遞了同樣的參數(shù),那么就直接使用緩存的結(jié)果,如果運算量很大,這可是不小的優(yōu)化
var add = function(){ var sum = 0; for(var i = 0, l = arguments.length; i < l; i++){ sum += arguments[i]; } return sum; }; var proxyAdd = (function(){ var cache = {}; //緩存運算結(jié)果的緩存對象 return function(){ var args = Array.prototype.join.call(arguments);//把參數(shù)用逗號組成一個字符串作為“鍵” if(cache.hasOwnProperty(args)){//等價 args in cache console.log("使用緩存結(jié)果"); return cache[args];//直接使用緩存對象的“值” } console.log("計算結(jié)果"); return cache[args] = add.apply(this,arguments);//使用本體函數(shù)計算結(jié)果并加入緩存 } })(); console.log(proxyAdd(1,2,3,4,5)); //15 console.log(proxyAdd(1,2,3,4,5)); //15 console.log(proxyAdd(1,2,3,4,5)); //15觀察者模式
觀察者模式又叫發(fā)布-訂閱模式,它定義對象間的一種一對多的依賴關(guān)系,當一個對象的狀態(tài)發(fā)生改變時,所有依賴它的對象都將得到通知。
定義一個事件對象,它有以下功能
監(jiān)聽事件(訂閱公眾號)
觸發(fā)事件(公眾號發(fā)布)
移除事件(取訂公眾號)
// subscription.js var CLEARED = null; var nullListeners = { notify: function notify() {} }; function createListenerCollection() { // the current/next pattern is copied from redux"s createStore code. // TODO: refactor+expose that code to be reusable here? var current = []; var next = []; return { clear: function clear() { next = CLEARED; current = CLEARED; }, notify: function notify() { var listeners = current = next; for (var i = 0; i < listeners.length; i++) { listeners[i](); } }, get: function get() { return next; }, subscribe: function subscribe(listener) { var isSubscribed = true; if (next === current) next = current.slice(); next.push(listener); return function unsubscribe() { if (!isSubscribed || current === CLEARED) return; isSubscribed = false; if (next === current) next = current.slice(); next.splice(next.indexOf(listener), 1); }; } }; } var Subscription = function () { function Subscription(store, parentSub, onStateChange) { _classCallCheck(this, Subscription); this.store = store; this.parentSub = parentSub; this.onStateChange = onStateChange; this.unsubscribe = null; this.listeners = nullListeners; } Subscription.prototype.addNestedSub = function addNestedSub(listener) { this.trySubscribe(); return this.listeners.subscribe(listener); }; Subscription.prototype.notifyNestedSubs = function notifyNestedSubs() { this.listeners.notify(); }; Subscription.prototype.isSubscribed = function isSubscribed() { return Boolean(this.unsubscribe); }; Subscription.prototype.trySubscribe = function trySubscribe() { if (!this.unsubscribe) { this.unsubscribe = this.parentSub ? this.parentSub.addNestedSub(this.onStateChange) : this.store.subscribe(this.onStateChange); this.listeners = createListenerCollection(); } }; Subscription.prototype.tryUnsubscribe = function tryUnsubscribe() { if (this.unsubscribe) { this.unsubscribe(); this.unsubscribe = null; this.listeners.clear(); this.listeners = nullListeners; } }; return Subscription; }();
Redux采用了觀察者模式
createStore(rootReducer,initialState,applyMiddleware(thunkMiddleware))返回值:
(1) dispatch(action): 用于action的分發(fā),改變store里面的state
(2) subscribe(listener): 注冊listener,store里面state發(fā)生改變后,執(zhí)行該listener。返回unsubscrib()方法,用于注銷當前l(fā)istener。
(3) getState(): 讀取store里面的state
(4) replaceReducer(): 替換reducer,改變state修改的邏輯
所以store內(nèi)部維護listener數(shù)組,用于存儲所有通過store.subscribe注冊的listener;當store tree更新后,依次執(zhí)行數(shù)組中的listener
function subscribe(listener) { if (typeof listener !== "function") { throw new Error("Expected listener to be a function."); } var isSubscribed = true; ensureCanMutateNextListeners(); nextListeners.push(listener); 了 return function unsubscribe() { if (!isSubscribed) { return; } isSubscribed = false; ensureCanMutateNextListeners(); var index = nextListeners.indexOf(listener); nextListeners.splice(index, 1); }; }
dispatch方法主要完成兩件事:
1、根據(jù)action查詢reducer中變更state的方法,更新store tree
2、變更store tree后,依次執(zhí)行l(wèi)istener中所有響應(yīng)函數(shù)
function dispatch(action) { if (!(0, _isPlainObject2["default"])(action)) { throw new Error("Actions must be plain objects. " + "Use custom middleware for async actions."); } if (typeof action.type === "undefined") { throw new Error("Actions may not have an undefined "type" property. " + "Have you misspelled a constant?"); } if (isDispatching) { throw new Error("Reducers may not dispatch actions."); } try { isDispatching = true; currentState = currentReducer(currentState, action); } finally { isDispatching = false; } //循環(huán)遍歷,執(zhí)行l(wèi)istener,通知數(shù)據(jù)改變 var listeners = currentListeners = nextListeners; for (var i = 0; i < listeners.length; i++) { var listener = listeners[i]; listener(); } return action; }在redux中,我們用connect()方法將react中的UI組件與redux的狀態(tài)、事件關(guān)聯(lián)起來。
var Connect = function (_Component) { _inherits(Connect, _Component); /* * 構(gòu)造函數(shù)中,構(gòu)造一個訂閱對象,屬性有this.store,方法this.onStateChange.bind(this) */ function Connect(props, context) { _classCallCheck(this, Connect); var _this = _possibleConstructorReturn(this, _Component.call(this, props, context)); _this.version = version; _this.state = {}; _this.renderCount = 0; _this.store = props[storeKey] || context[storeKey]; _this.propsMode = Boolean(props[storeKey]); _this.setWrappedInstance = _this.setWrappedInstance.bind(_this); (0, _invariant2.default)(_this.store, "Could not find "" + storeKey + "" in either the context or props of " + (""" + displayName + "". Either wrap the root component in a裝飾者模式, ") + ("or explicitly pass "" + storeKey + "" as a prop to "" + displayName + "".")); _this.initSelector(); _this.initSubscription(); return _this; } ··· // 調(diào)用store.subscribe(listener)注冊監(jiān)聽方法,對store的變化進行訂閱,當store變化的時候,更新渲染view Connect.prototype.componentDidMount = function componentDidMount() { if (!shouldHandleStateChanges) return; this.subscription.trySubscribe(); // //實際調(diào)用this.store.subscribe(this.onStateChange); this.selector.run(this.props); if (this.selector.shouldComponentUpdate) this.forceUpdate(); }; ··· Connect.prototype.componentWillUnmount = function componentWillUnmount() { if (this.subscription) this.subscription.tryUnsubscribe(); // 取消訂閱 this.subscription = null; this.notifyNestedSubs = noop; this.store = null; this.selector.run = noop; this.selector.shouldComponentUpdate = false; }; ··· //初始化訂閱邏輯 Connect.prototype.initSubscription = function initSubscription() { if (!shouldHandleStateChanges) return; var parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey]; this.subscription = new _Subscription2.default(this.store, parentSub, this.onStateChange.bind(this)); //調(diào)用的是Subscription.js中方法,向store內(nèi)部注冊一個listener---this.onStateChange.bind(this) this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription); }; Connect.prototype.onStateChange = function onStateChange() { this.selector.run(this.props); if (!this.selector.shouldComponentUpdate) { this.notifyNestedSubs(); } else { this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate; this.setState(dummyState); } }; ··· return Connect; }(_react.Component);
在不改變對象自身的基礎(chǔ)上,在程序運行期間給對象動態(tài)地添加一些額外職責(zé)
// 給window綁定onload事件 window.onload = function() { alert(1) } var _onload = window.onload || function(){} //維護中間變量,但是如果裝飾鏈太長,或需要的裝飾函數(shù)太多,這些中間變量的數(shù)量就會越來越多 window.onload = function() { _onload() alert(2) } //可能會存在this被劫持問題 var getId = document.getElementById; //全局函數(shù),this指向window document.getElementById = function(ID){ console.log(1); return getId(ID); } document.getElementById("demo"); //this預(yù)期指向document //需要手動把document當做上下文this傳入getIdAOP裝飾函數(shù)
AOP(Aspect Oriented Programming)面向切面編程
把一些與核心業(yè)務(wù)邏輯無關(guān)的功能抽離出來
再通過“動態(tài)織入”方式摻入業(yè)務(wù)邏輯模塊
// 前置裝飾 Function.prototype.before = function(beforeFunc){ var that = this; //保存原函數(shù)的引用 return function(){ //返回了了包含原函數(shù)和新函數(shù)的代理函數(shù) beforeFunc.apply(this, arguments); // 執(zhí)行新函數(shù) return that.apply(this, arguments); //執(zhí)行原函數(shù)并返回原函數(shù)的執(zhí)行結(jié)果 } } document.getElementById = document.getElementById.before(function() { alet(1) })
//定義一個組件 class Home extends Component { //.... } //以往從狀態(tài)樹取出對應(yīng)的數(shù)據(jù),通過props傳給組件使用通過react-redux自帶的connect()方法 export default connect(state => ({todos: state.todos}))(Home); //使用裝飾器的話就變成這樣,好像沒那么復(fù)雜 @connect(state => ({ todos: state.todos })) class Home extends React.Component { //.... }適配器模式
適配器模式是作為兩個不兼容的接口之間的橋梁,它結(jié)合了兩個獨立接口的功能。
假設(shè)我們正在編寫一個渲染廣東省地圖的頁面,目前從第三方資源里獲得了廣東省的所有城市以及它們所對應(yīng)的ID,并且成功地渲染到頁面中:
var getGuangdongCity = function () { var GuangdongCity = [ { name:"shenzhen", id : "11" }, { name:"guangzhou", id:12 } ]; return GuangdongCity; }; var render = function (fn) { console.log("starting render Guangdong map"); document.write(JSON.stringify(fn())); }; /* var GuangdongCity = { // shenzhen:11, // guangzhou:12, // zhuhai:13 // }; */ var addressAdapter = function (oldAddressfn) { var address = {}, oldAddress = oldAddressfn(); for(var i = 0 , c; c = oldAddress[i++];){ address[c.name] = c.id; //此處我們遍歷老數(shù)據(jù)把它們添加到空對象中然后返回這個對象 } return function () { return address; } }; render(addressAdapter(getGuangdongCity));
使用適配器模式可以解決參數(shù)類型有些許不一致造成的問題。
redux為了和react適配,所有有 mapStateToProps()這個函數(shù)來把state轉(zhuǎn)為Props外部狀態(tài),這樣就可以從外部又回到組件內(nèi)了
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/94866.html
摘要:動態(tài)生成隨機下單頁面的為了避免用戶直接訪問下單需要將動態(tài)化,用隨機數(shù)作為參數(shù),只能秒殺開始的時候才生成。該文件不被緩存的做法隨機數(shù)。淺談秒殺系統(tǒng)架構(gòu)設(shè)計如何只允許,第一個提交的單進入訂單系統(tǒng)。未超過秒殺商品總數(shù),提交到子訂單系統(tǒng)。 秒殺是電子商務(wù)網(wǎng)站常見的一種營銷手段。 原則 不要整個系統(tǒng)宕機。 即使系統(tǒng)故障,也不要將錯誤數(shù)據(jù)展示出來。 盡量保持公平公正。 實現(xiàn)效果 秒殺開始前,...
摘要:作用域鏈保證對環(huán)境中定義的變量和函數(shù)的有序訪問。通俗來說,執(zhí)行環(huán)境和作用域就是變量或函數(shù)有效執(zhí)行所在的一個環(huán)境??偨Y(jié)要想搞清作用域,首先要搞清預(yù)解析,然后判斷作用域范圍,先判斷本層環(huán)境有無聲明及賦值,如果有聲明,則判斷調(diào)用前是否賦值。 這幾天看了一下JS高級程序設(shè)計里的介紹作用域的章節(jié),也參考了網(wǎng)上的資料,現(xiàn)在結(jié)合著自己的理解,給大家分享一下我自己對JS作用域的理解。 作用域及執(zhí)行環(huán)境...
摘要:最后再說一下程序方面,因為小編是一名制作,所以程序方面的配合了解到的也就是平常項目中經(jīng)常遇到的。最后做一下總結(jié),在一個項目的進行中,設(shè)計制作程序三者之間是密不可分的,而各個部分之間需要相互配合,才能將項目做的更好。 以下是我在平常工作中所遇到的,文筆不是很好,求大神輕噴。在項目進行的過程中,主要分為設(shè)計--制作--程序這三個步驟,首先先從設(shè)計頁面開始,包括頁面上面的諸多效果在內(nèi)。而制作...
摘要:最后再說一下程序方面,因為小編是一名制作,所以程序方面的配合了解到的也就是平常項目中經(jīng)常遇到的。最后做一下總結(jié),在一個項目的進行中,設(shè)計制作程序三者之間是密不可分的,而各個部分之間需要相互配合,才能將項目做的更好。 以下是我在平常工作中所遇到的,文筆不是很好,求大神輕噴。在項目進行的過程中,主要分為設(shè)計--制作--程序這三個步驟,首先先從設(shè)計頁面開始,包括頁面上面的諸多效果在內(nèi)。而制作...
摘要:還有一個問題,就是不能在創(chuàng)建子類性時,像父類型的構(gòu)造函數(shù)傳遞參數(shù)。組合繼承將原型鏈和借用構(gòu)造函數(shù)組合到一起,發(fā)揮兩者之長的一張繼承模式,下面來看個例子。組合繼承最大的問題是無論在什么情況下,都會調(diào)用兩次父類型構(gòu)造函數(shù)。 繼承 繼承是面向?qū)ο笳Z言中特別重要的概念,js的繼承主要是靠原型鏈實現(xiàn)的。 原型鏈?。。?看到我給標題打了三個嘆號嗎,這里真的很重要!這里真的很重要!這里真的很重要!j...
閱讀 4760·2021-11-15 11:39
閱讀 2701·2021-11-11 16:55
閱讀 2208·2021-10-25 09:44
閱讀 3512·2021-09-22 16:02
閱讀 2444·2019-08-30 15:55
閱讀 3132·2019-08-30 13:46
閱讀 2674·2019-08-30 13:15
閱讀 1959·2019-08-30 11:12