摘要:假設我們正在編寫一個渲染廣東省地圖的頁面,目前從第三方資源里獲得了廣東省的所有城市以及它們所對應的并且成功地渲染到頁面中此處我們遍歷老數據把它們添加到空對象中然后返回這個對象使用適配器模式可以解決參數類型有些許不一致造成的問題。
策略模式
定義一系列的算法,把它們一個個封裝起來,并且使它們可以相互替換。目的是將算法的使用與算法的實現分離開來。
一個基于策略模式的程序至少由兩部分組成:
一組策略類,策略類封裝了具體的算法,并且負責具體的計算過程。
環境類Context, Context接受客戶的請求,隨后把請求委托給某一個策略類。
validator = { validate: function (value, type) { switch (type) { case "isNonEmpty ": { return true; // NonEmpty 驗證結果 } case "isNumber ": { return true; // Number 驗證結果 break; } case "isAlphaNum ": { return true; // AlphaNum 驗證結果 } default: { return true; } } } }; // 測試 alert(validator.validate("123", "isNonEmpty"));
var validator = { // 所有可以的驗證規則處理類存放的地方,后面會多帶帶定義 types: {}, // 驗證類型所對應的錯誤消息 messages: [], // 當然需要使用的驗證類型 config: {}, // 暴露的公開驗證方法 // 傳入的參數是 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]; // 根據key查詢是否有存在的驗證規則 checker = this.types[type]; // 獲取驗證規則的驗證類 if (!type) { continue; // 如果驗證規則不存在,則不處理 } if (!checker) { // 如果驗證規則類不存在,拋出異常 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: "傳入的值不能為空" }; // 驗證給定的值是否是數字 validator.types.isNumber = { validate: function (value) { return !isNaN(value); }, instructions: "傳入的值只能是合法的數字,例如:1, 3.14 or 2010" }; // 驗證給定的值是否只是字母或數字 validator.types.isAlphaNum = { validate: function (value) { return !/[^a-z0-9]/i.test(value); }, instructions: "傳入的值只能保護字母和數字,不能包含特殊字符" }; 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中,使得它們易于切換,易于理解,易于擴展。同時,算法還可以復用在其他地方,從而避免許多重復的復制粘貼工作。
代理模式代理模式為一個對象提供一種代理以控制對這個對象的訪問。
虛擬代理是我們最常用的代理模式,它把一些開銷很大的對象,延遲到真正需要用到這個對象的時候才去創建
虛擬代理實現圖片預加載
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請求
我們可以通過一個代理函數來收集一段時間之內的請求,最后把請求合并到一起發送給服務器
var proxySynData = (function(){ var cache = [], //緩存我們需要同步的內容 timer; //定時器 return function(ID){ if(!timer){ //定時器不存在就創建 timer = setTimeout(function(){ synData(cache.join()); //同步合并后的數據 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); } }; }
緩存代理
緩存代理就很好理解了,可以緩存一些開銷很大的運算結果;如果你第二次執行函數的時候,傳遞了同樣的參數,那么就直接使用緩存的結果,如果運算量很大,這可是不小的優化
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 = {}; //緩存運算結果的緩存對象 return function(){ var args = Array.prototype.join.call(arguments);//把參數用逗號組成一個字符串作為“鍵” if(cache.hasOwnProperty(args)){//等價 args in cache console.log("使用緩存結果"); return cache[args];//直接使用緩存對象的“值” } console.log("計算結果"); return cache[args] = add.apply(this,arguments);//使用本體函數計算結果并加入緩存 } })(); 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觀察者模式
觀察者模式又叫發布-訂閱模式,它定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴它的對象都將得到通知。
定義一個事件對象,它有以下功能
監聽事件(訂閱公眾號)
觸發事件(公眾號發布)
移除事件(取訂公眾號)
// 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的分發,改變store里面的state
(2) subscribe(listener): 注冊listener,store里面state發生改變后,執行該listener。返回unsubscrib()方法,用于注銷當前listener。
(3) getState(): 讀取store里面的state
(4) replaceReducer(): 替換reducer,改變state修改的邏輯
所以store內部維護listener數組,用于存儲所有通過store.subscribe注冊的listener;當store tree更新后,依次執行數組中的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、根據action查詢reducer中變更state的方法,更新store tree
2、變更store tree后,依次執行listener中所有響應函數
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; } //循環遍歷,執行listener,通知數據改變 var listeners = currentListeners = nextListeners; for (var i = 0; i < listeners.length; i++) { var listener = listeners[i]; listener(); } return action; }在redux中,我們用connect()方法將react中的UI組件與redux的狀態、事件關聯起來。
var Connect = function (_Component) { _inherits(Connect, _Component); /* * 構造函數中,構造一個訂閱對象,屬性有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; } ··· // 調用store.subscribe(listener)注冊監聽方法,對store的變化進行訂閱,當store變化的時候,更新渲染view Connect.prototype.componentDidMount = function componentDidMount() { if (!shouldHandleStateChanges) return; this.subscription.trySubscribe(); // //實際調用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)); //調用的是Subscription.js中方法,向store內部注冊一個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);
在不改變對象自身的基礎上,在程序運行期間給對象動態地添加一些額外職責
// 給window綁定onload事件 window.onload = function() { alert(1) } var _onload = window.onload || function(){} //維護中間變量,但是如果裝飾鏈太長,或需要的裝飾函數太多,這些中間變量的數量就會越來越多 window.onload = function() { _onload() alert(2) } //可能會存在this被劫持問題 var getId = document.getElementById; //全局函數,this指向window document.getElementById = function(ID){ console.log(1); return getId(ID); } document.getElementById("demo"); //this預期指向document //需要手動把document當做上下文this傳入getIdAOP裝飾函數
AOP(Aspect Oriented Programming)面向切面編程
把一些與核心業務邏輯無關的功能抽離出來
再通過“動態織入”方式摻入業務邏輯模塊
// 前置裝飾 Function.prototype.before = function(beforeFunc){ var that = this; //保存原函數的引用 return function(){ //返回了了包含原函數和新函數的代理函數 beforeFunc.apply(this, arguments); // 執行新函數 return that.apply(this, arguments); //執行原函數并返回原函數的執行結果 } } document.getElementById = document.getElementById.before(function() { alet(1) })
//定義一個組件 class Home extends Component { //.... } //以往從狀態樹取出對應的數據,通過props傳給組件使用通過react-redux自帶的connect()方法 export default connect(state => ({todos: state.todos}))(Home); //使用裝飾器的話就變成這樣,好像沒那么復雜 @connect(state => ({ todos: state.todos })) class Home extends React.Component { //.... }適配器模式
適配器模式是作為兩個不兼容的接口之間的橋梁,它結合了兩個獨立接口的功能。
假設我們正在編寫一個渲染廣東省地圖的頁面,目前從第三方資源里獲得了廣東省的所有城市以及它們所對應的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; //此處我們遍歷老數據把它們添加到空對象中然后返回這個對象 } return function () { return address; } }; render(addressAdapter(getGuangdongCity));
使用適配器模式可以解決參數類型有些許不一致造成的問題。
redux為了和react適配,所有有 mapStateToProps()這個函數來把state轉為Props外部狀態,這樣就可以從外部又回到組件內了
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/94866.html
摘要:動態生成隨機下單頁面的為了避免用戶直接訪問下單需要將動態化,用隨機數作為參數,只能秒殺開始的時候才生成。該文件不被緩存的做法隨機數。淺談秒殺系統架構設計如何只允許,第一個提交的單進入訂單系統。未超過秒殺商品總數,提交到子訂單系統。 秒殺是電子商務網站常見的一種營銷手段。 原則 不要整個系統宕機。 即使系統故障,也不要將錯誤數據展示出來。 盡量保持公平公正。 實現效果 秒殺開始前,...
摘要:作用域鏈保證對環境中定義的變量和函數的有序訪問。通俗來說,執行環境和作用域就是變量或函數有效執行所在的一個環境。總結要想搞清作用域,首先要搞清預解析,然后判斷作用域范圍,先判斷本層環境有無聲明及賦值,如果有聲明,則判斷調用前是否賦值。 這幾天看了一下JS高級程序設計里的介紹作用域的章節,也參考了網上的資料,現在結合著自己的理解,給大家分享一下我自己對JS作用域的理解。 作用域及執行環境...
摘要:最后再說一下程序方面,因為小編是一名制作,所以程序方面的配合了解到的也就是平常項目中經常遇到的。最后做一下總結,在一個項目的進行中,設計制作程序三者之間是密不可分的,而各個部分之間需要相互配合,才能將項目做的更好。 以下是我在平常工作中所遇到的,文筆不是很好,求大神輕噴。在項目進行的過程中,主要分為設計--制作--程序這三個步驟,首先先從設計頁面開始,包括頁面上面的諸多效果在內。而制作...
摘要:最后再說一下程序方面,因為小編是一名制作,所以程序方面的配合了解到的也就是平常項目中經常遇到的。最后做一下總結,在一個項目的進行中,設計制作程序三者之間是密不可分的,而各個部分之間需要相互配合,才能將項目做的更好。 以下是我在平常工作中所遇到的,文筆不是很好,求大神輕噴。在項目進行的過程中,主要分為設計--制作--程序這三個步驟,首先先從設計頁面開始,包括頁面上面的諸多效果在內。而制作...
摘要:還有一個問題,就是不能在創建子類性時,像父類型的構造函數傳遞參數。組合繼承將原型鏈和借用構造函數組合到一起,發揮兩者之長的一張繼承模式,下面來看個例子。組合繼承最大的問題是無論在什么情況下,都會調用兩次父類型構造函數。 繼承 繼承是面向對象語言中特別重要的概念,js的繼承主要是靠原型鏈實現的。 原型鏈!!! 看到我給標題打了三個嘆號嗎,這里真的很重要!這里真的很重要!這里真的很重要!j...
閱讀 4723·2021-11-15 11:39
閱讀 2691·2021-11-11 16:55
閱讀 2200·2021-10-25 09:44
閱讀 3504·2021-09-22 16:02
閱讀 2433·2019-08-30 15:55
閱讀 3122·2019-08-30 13:46
閱讀 2656·2019-08-30 13:15
閱讀 1944·2019-08-30 11:12