第2章 this call apply
bind()方法建立一個新的函數,在bind()被調用時,這個新函數的this被bind的第一個參數指定,其他的參數將做爲新函數的參數供調用時使用。
bind()方法底層實現javascript
Function.prototype.bind = Function.prototype.bind || function () { var self = this var rest1 = Array.prototype.slice.call(arguments) var context = rest1.shift() // 獲取第一個參數,this的指向 return function () { var rest2 = Array.prototype.slice.call(arguments) // 獲取其他參數 return self.apply(context, rest1.concat(rest2)) // 將預設參數和其他參數一塊兒傳參 } } var food = { name: '漢堡', price: '5塊錢', getPrice: function (place,name) { console.log(place + this.price + name) } } var getPrice1 = food.getPrice.bind({ name: '雞腿', price: '7塊錢' }, '肯打雞 ') getPrice1('jesse')
bind()的另外一個最簡單的用法是使一個函數擁有預設的初始參數。只要將這些參數(若是有的話)做爲bind()的參數寫在this後面。當綁定函數被調用時,這些參數會被插入到目標函數的參數列表的開始位置,傳遞給綁定函數的參數會跟在它們後面html
第3章 閉包和高階函數
高階函數的應用
函數柯里化java
var curring = function(fn){ var args = []; return function(){ if(arguments.length === 0){ return fn.apply(this,args); }else{ [].push.apply(args,arguments); return arguments.callee; } } } var cost = (function(){ var money = 0; return function(){ for(var i = 0;,l = arguments.length;i < l;i++){ money += aruments[i]; } return money; } })() var cost = curring(cost); cost(100); console.log(cost());
uncurring()算法
Function.prototype.uncurring = fucntion(){ var self = this; return function(){ var obj = Array.prototype.shift.call(arguments); return self.apply(obj,arguments); } } var push = Array.prototype.push.uncurring(); (function(){ push(arguments,4); console.log(arguments); //[1,2,3,4] })(1,2,3);
函數節流,用於解決函數頻繁被調用而形成的性能問題編程
var throttle = function(fn,interval){ var _self = fn,timer,firstTime = true; return function(){ var args = arguments,_me = this; if(firstTime){ _self.apply(_me,args); return firstTime = false; } if(timer){//500毫秒以內再次觸發的縮放事件不處理 return false; } timer = setTimeout(function(){ clearTimeout(timer); timer = null; _self.apply(_me,args); },interval||500); }; }; window.onresize = throttle(function(){ console.log(1) },500);
惰性加載函數設計模式
var addEvent = function( elem, type, handler ){ if ( window.addEventListener ){ addEvent = function( elem, type, handler ){//重寫函數,避免頻繁調用嗅探函數 elem.addEventListener( type, handler, false ); } }else if ( window.attachEvent ){ addEvent = function( elem, type, handler ){ elem.attachEvent( 'on' + type, handler ); } } addEvent( elem, type, handler ); }; var div = document.getElementById( 'div1' ); addEvent( div, 'click', function(){ alert (1); }); addEvent( div, 'click', function(){ alert (2); });
第4章 單例模式
單例模式的核心是確保只有一個實例,並提供全局訪問。
該模式可用於定義單一彈窗緩存
var CreateDiv = function( html ){ this.html = html; this.init(); }; CreateDiv.prototype.init = function(){ var div = document.createElement( 'div' ); div.innerHTML = this.html; document.body.appendChild( div ); }; //將建立對象和保證單一對象分開 var ProxySingletonCreateDiv = (function(){ var instance; return function( html ){ if ( !instance ){ instance = new CreateDiv( html ); } return instance; } })(); var a = new ProxySingletonCreateDiv( 'sven1' ); var b = new ProxySingletonCreateDiv( 'sven2' ); alert ( a === b );
ES6寫法閉包
let instance class CreateDiv{ constructor(html){ if(instance){ return instance } this.html = html; this.init(); return instance = this } init(){ var div = document.createElement('div') div.innerHTML = this.html document.body.appendChild(div) } } var a = new CreateDiv( 'sven1' ); var b = new CreateDiv( 'sven2' ); console.log(a===b) // true
第5章 策略模式
策略模式指的是定義一系列的算法,把它們一個個封裝起來,而且使它們能夠相互替換
該模式經常使用於表單校驗app
var strategies = { "S": function( salary ){ return salary * 4; }, "A": function( salary ){ return salary * 3; }, "B": function( salary ){ return salary * 2; } }; var calculateBonus = function( level, salary ){ return strategies[ level ]( salary ); }; console.log( calculateBonus( 'S', 20000 ) ); // 輸出:80000 console.log( calculateBonus( 'A', 10000 ) ); // 輸出:30000
5.6.2 用策略模式重構表單校驗
/***********************策略對象**************************/ var strategies = { isNonEmpty: function( value, errorMsg ){ if ( value === '' ){ return errorMsg; } }, minLength: function( value, length, errorMsg ){ if ( value.length < length ){ return errorMsg; } }, isMobile: function( value, errorMsg ){ if ( !/(^1[3|5|8][0-9]{9}$)/.test( value ) ){ return errorMsg; } } }; /***********************Validator 類**************************/ var Validator = function(){ this.cache = []; }; Validator.prototype.add = function( dom, rules ){ var self = this; for ( var i = 0, rule; rule = rules[ i++ ]; ){ (function( rule ){ var strategyAry = rule.strategy.split( ':' ); var errorMsg = rule.errorMsg; self.cache.push(function(){ var strategy = strategyAry.shift(); strategyAry.unshift( dom.value ); strategyAry.push( errorMsg ); return strategies[ strategy ].apply( dom, strategyAry ); }); })( rule ) } }; Validator.prototype.start = function(){ for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){ var errorMsg = validatorFunc(); if ( errorMsg ){ return errorMsg; } } }; /***********************客戶調用代碼**************************/ var registerForm = document.getElementById( 'registerForm' ); var validataFunc = function(){ var validator = new Validator(); validator.add( registerForm.userName, [{ strategy: 'isNonEmpty', errorMsg: '用戶名不能爲空' }, { strategy: 'minLength:6', errorMsg: '用戶名長度不能小於10 位' }]); validator.add( registerForm.password, [{ strategy: 'minLength:6', errorMsg: '密碼長度不能小於6 位' }]); var errorMsg = validator.start(); return errorMsg; } registerForm.onsubmit = function(){ var errorMsg = validataFunc(); if ( errorMsg ){ alert ( errorMsg ); return false; } };
第6章 代理模式
虛擬代理在惰性加載中的應用
var miniConsole = (function(){ var cache = []; var handler = function( ev ){ if ( ev.keyCode === 113 ){ var script = document.createElement( 'script' ); script.onload = function(){ for ( var i = 0, fn; fn = cache[ i++ ]; ){ fn(); } }; script.src = 'miniConsole.js'; document.getElementsByTagName( 'head' )[0].appendChild( script ); document.body.removeEventListener( 'keydown', handler );// 只加載一次miniConsole.js } }; document.body.addEventListener( 'keydown', handler, false ); return { log: function(){ var args = arguments; cache.push( function(){ return miniConsole.log.apply( miniConsole, args ); }); } } })(); miniConsole.log( 11 ); // 開始打印log // miniConsole.js 代碼 miniConsole = { log: function(){ // 真正代碼略 console.log( Array.prototype.join.call( arguments ) ); }
緩存代理
var mult = function(){ console.log( '開始計算乘積' ); var a = 1; for ( var i = 0, l = arguments.length; i < l; i++ ){ a = a * arguments[i]; } return a; }; mult( 2, 3 ); // 輸出:6 mult( 2, 3, 4 ); // 輸出:24 //如今加入緩存代理函數: var proxyMult = (function(){ var cache = {}; return function(){ var args = Array.prototype.join.call( arguments, ',' ); if ( args in cache ){ return cache[ args ]; } return cache[ args ] = mult.apply( this, arguments ); } })(); proxyMult( 1, 2, 3, 4 ); // 輸出:24 proxyMult( 1, 2, 3, 4 ); // 輸出:24 咱們在經常在項目中遇到分頁的需求,同一頁的數據理論上只須要去後臺拉取一次,這些已經拉取到的數據在某個地方被緩存以後,下次再請求同一頁的時候,即可以直接使用以前的數據。
第7章 迭代器模式
迭代器模式是指提供一種方法順序訪問一個聚合對象中的各類元素,而又不須要暴露該對象的內部表示框架
var getActiveUploadObj = function(){ //... }; var getFlashUploadObj = function(){ //... }; var getFormUploadObj = function(){ //... }; var iteratorUploadObj = function(){ for(var i=0,fn;fn=arguments[i++];){ var uploadObj = fn(); if(uploadObj !== false){ return uploadObj; } } }; var uploadObj = iteratorUploadObj(getActiveUploadObj,getFlashUploadObj,getFormUploadObj);
第8章 發佈訂閱模式
發佈—訂閱模式又叫觀察者模式,它定義對象間的一種一對多的依賴關係,當一個對象的狀 態發生改變時,全部依賴於它的對象都將獲得通知。在 JavaScript 開發中,咱們通常用事件模型 來替代傳統的發佈—訂閱模式。
var Event = (function(){ var clientList = {}, listen, trigger, remove; listen = function( key, fn ){ if ( !clientList[ key ] ){ clientList[ key ] = []; } clientList[ key ].push( fn ); }; trigger = function(){ var key = Array.prototype.shift.call( arguments ), fns = clientList[ key ]; if ( !fns || fns.length === 0 ){ return false; } for( var i = 0, fn; fn = fns[ i++ ]; ){ fn.apply( this, arguments ); } }; remove = function( key, fn ){ var fns = clientList[ key ]; if ( !fns ){ return false; } if ( !fn ){ fns && ( fns.length = 0 ); }else{ for ( var l = fns.length - 1; l >=0; l-- ){ var _fn = fns[ l ]; if ( _fn === fn ){ fns.splice( l, 1 ); } } } }; return { listen: listen, trigger: trigger, remove: remove } })(); Event.listen( 'squareMeter88', function( price ){ // 小紅訂閱消息 console.log( '價格= ' + price ); // 輸出:'價格=2000000' }); Event.trigger( 'squareMeter88', 2000000 ); // 售樓處發佈消息
第9章 命令模式
在面向對象設計中,命令模式的接收者被當成 command 對象的屬性保存起來,同時約定執行命令的操做調用 command.execute 方法。在使用閉包的命令模式實現中,接收者被封閉在閉包產生的環境中,執行命令的操做能夠更加簡單,僅僅執行回調函數便可。不管接收者被保存爲對象的屬性,仍是被封閉在閉包產生的環境中,在未來執行命令的時候,接收者都能被順利訪問。用閉包實現的命令模式以下代碼所示:
var RefreshMenuBarCommand = function( receiver ){ return { execute: function(){ receiver.refresh(); } } }; var setCommand = function( button, command ){ button.onclick = function(){ command.execute(); } }; var refreshMenuBarCommand = RefreshMenuBarCommand( MenuBar ); setCommand( button1, refreshMenuBarCommand );
第10章 組合模式
10.7 組合模式的例子——掃描文件夾
var Folder = function( name ){ this.name = name; this.files = []; }; Folder.prototype.add = function( file ){ this.files.push( file ); }; Folder.prototype.scan = function(){ console.log( '開始掃描文件夾: ' + this.name ); for ( var i = 0, file, files = this.files; file = files[ i++ ]; ){ file.scan(); } }; /******************************* File ******************************/ var File = function( name ){ this.name = name; }; File.prototype.add = function(){ throw new Error( '文件下面不能再添加文件' ); }; File.prototype.scan = function(){ console.log( '開始掃描文件: ' + this.name ); }; var folder = new Folder( '學習資料' ); var folder1 = new Folder( 'JavaScript' ); var folder2 = new Folder ( 'jQuery' ); var file1 = new File( 'JavaScript 設計模式與開發實踐' ); var file2 = new File( '精通jQuery' ); var file3 = new File( '重構與模式' ) folder1.add( file1 ); folder2.add( file2 ); folder.add( folder1 ); folder.add( folder2 ); folder.add( file3 ); var folder3 = new Folder( 'Nodejs' ); var file4 = new File( '深刻淺出Node.js' ); folder3.add( file4 ); var file5 = new File( 'JavaScript 語言精髓與編程實踐' ); folder.add( folder3 ); folder.add( file5 ); folder.scan();
10.8 一些值得注意的地方
- 組合模式不是父子關係 組合對象把請求委託給它所包含的全部葉對象,它們可以合做的關鍵是擁有相同的接口。
- 對葉對象操做的一致性
- 雙向映射關係
第11章 模板方法模式
var Beverage = function(){}; Beverage.prototype.boilWater = function(){ console.log( '把水煮沸' ); }; Beverage.prototype.brew = function(){ throw new Error( '子類必須重寫brew 方法' ); }; Beverage.prototype.pourInCup = function(){ throw new Error( '子類必須重寫pourInCup 方法' ); }; Beverage.prototype.addCondiments = function(){ throw new Error( '子類必須重寫addCondiments 方法' ); }; Beverage.prototype.customerWantsCondiments = function(){ return true; // 默認須要調料 }; Beverage.prototype.init = function(){//封裝了子類的算法框架 this.boilWater(); this.brew(); this.pourInCup(); if ( this.customerWantsCondiments() ){ // 若是掛鉤返回true,則須要調料 this.addCondiments(); } }; var CoffeeWithHook = function(){}; CoffeeWithHook.prototype = new Beverage(); CoffeeWithHook.prototype.brew = function(){ console.log( '用沸水沖泡咖啡' ); }; CoffeeWithHook.prototype.pourInCup = function(){ console.log( '把咖啡倒進杯子' ); }; CoffeeWithHook.prototype.addCondiments = function(){ console.log( '加糖和牛奶' ); }; CoffeeWithHook.prototype.customerWantsCondiments = function(){ return window.confirm( '請問須要調料嗎?' ); }; var coffeeWithHook = new CoffeeWithHook(); coffeeWithHook.init();
模板方法模式是基於繼承的一種設計模式,父類封裝了子類的算法框架和方法的執行順序, 子類繼承父類以後,父類通知子類執行這些方法
第12章 享元模式
var Upload = function( uploadType){ this.uploadType = uploadType; }; Upload.prototype.delFile = function( id ){ uploadManager.setExternalState( id, this ); // (1) if ( this.fileSize < 3000 ){ return this.dom.parentNode.removeChild( this.dom ); } if ( window.confirm( '肯定要刪除該文件嗎? ' + this.fileName ) ){ return this.dom.parentNode.removeChild( this.dom ); } } var UploadFactory = (function(){ var createdFlyWeightObjs = {}; return { create: function( uploadType){ if ( createdFlyWeightObjs [ uploadType] ){ return createdFlyWeightObjs [ uploadType]; } return createdFlyWeightObjs [ uploadType] = new Upload( uploadType); } } })(); var uploadManager = (function(){ var uploadDatabase = {}; return { add: function( id, uploadType, fileName, fileSize ){ var flyWeightObj = UploadFactory.create( uploadType ); var dom = document.createElement( 'div' ); dom.innerHTML = '<span>文件名稱:'+ fileName +', 文件大小: '+ fileSize +'</span>' + '<button class="delFile">刪除</button>'; dom.querySelector( '.delFile' ).onclick = function(){ flyWeightObj.delFile( id ); } document.body.appendChild( dom ); uploadDatabase[ id ] = { fileName: fileName, fileSize: fileSize, dom: dom }; return flyWeightObj ; }, setExternalState: function( id, flyWeightObj ){ var uploadData = uploadDatabase[ id ]; for ( var i in uploadData ){ flyWeightObj[ i ] = uploadData[ i ]; } } } })(); var id = 0; window.startUpload = function( uploadType, files ){ for ( var i = 0, file; file = files[ i++ ]; ){ var uploadObj = uploadManager.add( ++id, uploadType, file.fileName, file.fileSize ); } }; startUpload( 'plugin', [ { fileName: '1.txt', fileSize: 1000 }, { fileName: '2.html', fileSize: 3000 }, { fileName: '3.txt', fileSize: 5000 } ]); startUpload( 'flash', [ { fileName: '4.txt', fileSize: 1000 }, { fileName: '5.html', fileSize: 3000 }, { fileName: '6.txt', fileSize: 5000 } ]);
享元模式帶來的好處很大程度上取決於如何使用以及什麼時候使用,通常來講,如下狀況發生時 即可以使用享元模式。
- 一個程序中使用了大量的類似對象。
- 因爲使用了大量對象,形成很大的內存開銷。
- 對象的大多數狀態均可以變爲外部狀態。
- 剝離出對象的外部狀態以後,能夠用相對較少的共享對象取代大量對象。
第13章 職責鏈模式
職責鏈模式的最大優勢就是解耦了請求發送者和 N 個接收者之間的複雜關 系,因爲不知道鏈中的哪一個節點能夠處理你發出的請求,因此你只需把請求傳遞給第一個節點即 可
var order500 = function( orderType, pay, stock ){ if ( orderType === 1 && pay === true ){ console.log( '500 元定金預購,獲得100 優惠券' ); }else{ return 'nextSuccessor'; // 我不知道下一個節點是誰,反正把請求日後面傳遞 } }; var order200 = function( orderType, pay, stock ){ if ( orderType === 2 && pay === true ){ console.log( '200 元定金預購,獲得50 優惠券' ); }else{ return 'nextSuccessor'; // 我不知道下一個節點是誰,反正把請求日後面傳遞 } }; var orderNormal = function( orderType, pay, stock ){ if ( stock > 0 ){ console.log( '普通購買,無優惠券' ); }else{ console.log( '手機庫存不足' ); } }; Chain.prototype.setNextSuccessor 指定在鏈中的下一個節點 Chain.prototype.passRequest 傳遞請求給某個節點 var Chain = function( fn ){ this.fn = fn; this.successor = null; }; Chain.prototype.setNextSuccessor = function( successor ){ return this.successor = successor; }; Chain.prototype.passRequest = function(){ var ret = this.fn.apply( this, arguments ); if ( ret === 'nextSuccessor' ){ return this.successor && this.successor.passRequest.apply( this.successor, arguments ); } return ret; }; var chainOrder500 = new Chain( order500 ); var chainOrder200 = new Chain( order200 ); var chainOrderNormal = new Chain( orderNormal ); chainOrder500.setNextSuccessor( chainOrder200 ); chainOrder200.setNextSuccessor( chainOrderNormal ); chainOrder500.passRequest( 1, true, 500 ); // 輸出:500 元定金預購,獲得100 優惠券 chainOrder500.passRequest( 2, true, 500 ); // 輸出:200 元定金預購,獲得50 優惠券 chainOrder500.passRequest( 3, true, 500 ); // 輸出:普通購買,無優惠券 chainOrder500.passRequest( 1, false, 0 ); // 輸出:手機庫存不足 Function.prototype.after = function( fn ){ var self = this; return function(){ var ret = self.apply( this, arguments ); if ( ret === 'nextSuccessor' ){ return fn.apply( this, arguments ); } return ret; } }; var order = order500yuan.after( order200yuan ).after( orderNormal ); order( 1, true, 500 ); // 輸出:500 元定金預購,獲得100 優惠券 order( 2, true, 500 ); // 輸出:200 元定金預購,獲得50 優惠券 order( 1, false, 500 ); // 輸出:普通購買,無優惠券
第14章 中介者模式
中介者模式的做用就是解除對象與對象之間的緊耦合關係。增長一箇中介者對象後,全部的相關對象都經過中介者對象來通訊,而不是互相引用,因此當一個對象發生改變時,只須要通知中介者對象便可。中介者使各對象之間耦合鬆散,並且能夠獨立地改變它們之間的交互。
var goods = { // 手機庫存 "red|32G": 3, "red|16G": 0, "blue|32G": 1, "blue|16G": 6 } var colorSelect = document.getElementById( 'colorSelect' ), memorySelect = document.getElementById( 'memorySelect' ), numberInput = document.getElementById( 'numberInput' ), colorInfo = document.getElementById( 'colorInfo' ), memoryInfo = document.getElementById( 'memoryInfo' ), numberInfo = document.getElementById( 'numberInfo' ), nextBtn = document.getElementById( 'nextBtn' ); var mediator = (function(){ return { changed: function( obj ){ var color = colorSelect.value, // 顏色 memory = memorySelect.value,// 內存 number = numberInput.value, // 數量 stock = goods[ color + '|' + memory ];// 顏色和內存對應的手機庫存數量 if ( obj === colorSelect ){ // 若是改變的是選擇顏色下拉框 colorInfo.innerHTML = color; }else if ( obj === memorySelect ){ memoryInfo.innerHTML = memory; }else if ( obj === numberInput ){ numberInfo.innerHTML = number; } if ( !color ){ nextBtn.disabled = true; nextBtn.innerHTML = '請選擇手機顏色'; return; } if ( !memory ){ nextBtn.disabled = true; nextBtn.innerHTML = '請選擇內存大小'; return; } if ( ( ( number - 0 ) | 0 ) !== number - 0 ){ // 輸入購買數量是否爲正整數 nextBtn.disabled = true; nextBtn.innerHTML = '請輸入正確的購買數量'; return; } nextBtn.disabled = false; nextBtn.innerHTML = '放入購物車'; } } })(); // 事件函數: colorSelect.onchange = function(){ mediator.changed( this ); }; memorySelect.onchange = function(){ mediator.changed( this ); }; numberInput.oninput = function(){ mediator.changed( this ); };
第15章 裝飾者模式
由於裝飾者對象和它所裝飾的對象擁有一致的接口,因此它們對使用該對象的客戶來講是透 明的,被裝飾的對象也並不須要瞭解它曾經被裝飾過,這種透明性使得咱們能夠遞歸地嵌套任意 多個裝飾者對象
var plane = { fire: function(){ console.log( '發射普通子彈' ); } } var missileDecorator = function(){ console.log( '發射導彈' ); } var atomDecorator = function(){ console.log( '發射原子彈' ); } var fire1 = plane.fire; plane.fire = function(){ fire1(); missileDecorator(); } var fire2 = plane.fire; plane.fire = function(){ fire2(); atomDecorator(); } plane.fire(); // 分別輸出: 發射普通子彈、發射導彈、發射原子彈
用AOP裝飾函數
Function.prototype.before = function( beforefn ){ var __self = this; // 保存原函數的引用 return function(){ // 返回包含了原函數和新函數的"代理"函數 beforefn.apply( this, arguments ); // 執行新函數,且保證 this 不被劫持,新函數接受的參數 // 也會被原封不動地傳入原函數,新函數在原函數以前執行 return __self.apply( this, arguments ); // 執行原函數並返回原函數的執行結果, 2 // 而且保證 this 不被劫持 } } Function.prototype.after = function( afterfn ){ var __self = this; return function(){ var ret = __self.apply( this, arguments ); afterfn.apply( this, arguments ); return ret; } }; document.getElementById = document.getElementById.before(function(){ alert (1); }); var button = document.getElementById( 'button' );
不喜歡這種污染原型的方式,那麼咱們能夠作一些變通
var before = function( fn, beforefn ){ return function(){ beforefn.apply( this, arguments ); return fn.apply( this, arguments ); } } var a = before( function(){alert (3)}, function(){alert (2)} ); a =before( a, function(){alert (1);} ); a();
第16章 狀態模式
狀態模式的關鍵是把事物的每種狀態都封裝成單獨的類,跟此種狀態有關的行爲都被封裝在這個類的內部
javascript版的狀態機
var Light = function(){ this.currState = FSM.off; // 設置當前狀態 this.button = null; }; Light.prototype.init = function(){ var button = document.createElement( 'button' ), self = this; button.innerHTML = '已關燈'; this.button = document.body.appendChild( button ); this.button.onclick = function(){ self.currState.buttonWasPressed.call( self ); // 把請求委託給FSM 狀態機 } }; var FSM = { off: { buttonWasPressed: function(){ console.log( '關燈' ); this.button.innerHTML = '下一次按我是開燈'; this.currState = FSM.on; } }, on: { buttonWasPressed: function(){ console.log( '開燈' ); this.button.innerHTML = '下一次按我是關燈'; this.currState = FSM.off; } } }; var light = new Light(); light.init();
第17章 適配器模式
適配器模式的做用是解決兩個軟件實體間的接口不兼容的問題。使用適配器模式以後,本來因爲接口不兼容而不能工做的兩個軟件實體能夠一塊兒工做。
var guangdongCity = { shenzhen: 11, guangzhou: 12, zhuhai: 13 }; var getGuangdongCity = function(){ var guangdongCity = [ { name: 'shenzhen', id: 11, }, { name: 'guangzhou', id: 12, } ]; return guangdongCity; }; var render = function( fn ){ console.log( '開始渲染廣東省地圖' ); document.write( JSON.stringify( fn() ) ); }; 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 ) );
- 適配器模式主要用來解決兩個已有接口之間不匹配的問題,它不考慮這些接口是怎樣實現的,也不考慮它們未來可能會如何演化。適配器模式不須要改變已有的接口,就可以使它們協同做用。
- 裝飾者模式和代理模式也不會改變原有對象的接口,但裝飾者模式的做用是爲了給對象增長功能。裝飾者模式經常造成一條長的裝飾鏈,而適配器模式一般只包裝一次。代理模式是爲了控制對對象的訪問,一般也只包裝一次。
- 外觀模式的做用卻是和適配器比較類似,有人把外觀模式當作一組對象的適配器,但外觀模式最顯著的特色是定義了一個新的接口。
第18章 單一職責原則
SRP 原則體現爲:一個對象(方法)只作一件事情
第19章 最少知識原則
最少知識原則要求咱們在設計程序時,應當儘可能減小對象之間的交互。若是兩個對象之間沒必要彼此直接通訊,那麼這兩個對象就不要發生直接的相互聯繫。常見的作法是引入一個第三者對象,來承擔這些對象之間的通訊做用。若是一些對象須要向另外一些對象發起請求,能夠經過第三 者對象來轉發這些請求。
第20章 開放封閉原則
當須要改變一個程序的功能或者給這個程序增長新功能的時候,可使用增長代碼的方式,可是不容許改動程序的源代碼
經過封裝變化的方式,能夠把系統中穩定不變的部分和容易變化的部分隔離開來。在系統的 演變過程當中,咱們只須要替換那些容易變化的部分
第22章 代碼重構
- 提煉函數
- 合併重複的條件判斷
- 把條件分支語句提煉成函數
- 合理使用循環
- 提早讓函數退出代替嵌套條件分支
- 傳遞對象參數代替過長的參數列表
- 儘可能減小參數數量
- 少用三目運算符
- 合理使用鏈式調用
- 分解大型類
- 用return退出多重循環