使用JavaScript Function.prototype進行程式碼重構的一些例子
Example1 – how to enhance a big / complex function with your own logic
Suppose there is already a big function with huge logic, and you have the task to enhance it by appending your own logic to its end.
var bigFunction = function() { // big logic console.log("big logic"); }
Of course if the big function is owned by you, you could directly append the enhancement, like below:
var bigFunction = function() { // big logic console.log("big logic"); // append our own enhancement directly console.log("our own enhancement"); }
Another approach, if you would not like to touch the existing one, is to use a temporary variable to store the old bigFunction:
var _old = bigFunction; bigFunction = function() { if ( _old ) { _old(); } console.log("our own enhancement"); } bigFunction(); // After it is executed, console will print out
A Better Solution
We add a new function in Function.prototype:
Function.prototype.after = function( func ){ var _self = this; return function() { var ret = _self.apply( this, arguments ); if ( ret === false ) { return false; } func.apply( this, arguments); return ret; } }
This after function returns a new function, which will first call the original function, and then call the subsequent function passed in via variable “func” if the original function execution is successfully ( ret != false ).
Then just construct a new function using after function:
bigFunction = bigFunction.after( function() { console.log("our own logic"); });
Now when bigFunction() is executed, we get the same printout. With this approach, the temporary variable is avoided.
Example 2 – Write performance measurement code without polluting your productive code
Suppose in your application you have to create mass div node. You need to measure the creation performance. The most straightforward way is to get timestamp before and after creation. In order to get timestamp you cannot avoid to pollute your productive code with two “new Date()” ( or any other way for time measurement in JavaScript ) as below.
var append_doms = function() { var d = new Date(); // dirty code - nothing to do with application logic!!! for( var i = 0; i < 100000; i++) { var div = document.createElement( "div"); document.body.appendChild(div); } // dirty code - nothing to do with application logic!!! console.log(" time consumed: " + ( new Date() - d)); };
A Better Solution
Using the idea of the first example, we create another method before in Function.prototype, which has similar design as Function.prototype.after:
Function.prototype.before = function( func) { var _self = this; return function() { if ( func.apply( this, arguments ) === false ) { return false; } return _self.apply( this.arguments); } }
With this approach, our productive code is clean – not any time measurement code there.
var append_doms = function() { for( var i = 0; i < 100000; i++) { var div = document.createElement( "div"); document.body.appendChild(div); } };
And we wrap the original function with before and after function we defined in Function.prototype:
var log_time = function( func, log_name) { return func = ( function() { var d; return func.before( function(){ d = new Date(); }).after( function(){ console.log( log_name + ( new Date() - d)); }); })(); };
Now we get a new function log_time which is dedicatedly used for performance measurement. This new function actually consists of three parts:
(1) an anonymous function with body “d = new Date();”, chained by Function.prototype.before.
(2) the original append_doms
(3) an anonymous function with body “console.log( log_name + ( new Date() – d)); “, chained by Function.prototype.after.
We can elegantly call it via one line of code below to achieve the performance measurement.
log_time(append_doms, "consumed time: ")();
AOP in Java
Update on 2016-07-29: In Java it could be done elegantly via Spring framework. Please find more detail from this blog: [An example to explain why we need AOP – Aspect Oriented Programming](https://blogs.sap.com/?p=145576) .
Example 3 – Replace lots of IF-ELSE with Design Pattern “Chain of Responsibility”
Suppose I am responsible for developing a file upload function and the upload could be finished by various approach if each feature is supported by client side. The priority is listed below:
// Priority: ActiveX > HTML5 > Flash > Form(default)
If means for example, if ActiveX is supported by client’s browser, it should be used. The default is upload via form, if all previous tries have failed.
function isActiveXSupported(){ //... return false; } function isHTML5Supported(){ //... return true; } function isFlashSupported(){ //... return false; }
The codes above just simulate the situation that HTML5 upload should be used, since its preceding attempt, isActiveXSupported, returns false. In order to get the proper upload service, we have to code lots of tedious IF-ELSE evaluation:
var uploadAPI; if ( isActiveXSupported()) { // lots of initialization work uploadAPI = { "name": "ActiveX"}; } else if( isHTML5Supported()) { // lots of initialization work uploadAPI = { "name": "HTML5"}; } else if( isFlashSupported()) { // lots of initialization work uploadAPI = { "name": "Flash"}; } else { // lots of initialization work uploadAPI = { "name": "Form"}; } console.log(uploadAPI); // HTML5 service is got
A Better Solution
We do some minor change on Function.prototype.after:
Function.prototype.after = function( func ){ var _self = this; return function() { var ret = _self.apply( this, arguments ); if ( ret ) { return ret; } return func.apply( this, arguments); } }
Now if the execution of original function is successfully ( returns true ), we terminate the function chain, that is, we don’t pass the responsibility chain to its subsequent function passed via func.
With this approach, there is no more IF-ELSE evaluation. In fact, now we spread the evaluation into the dedicated initialization function of each API:
var getActiveX = function() { try { // lots of initialization work return { "name": "ActiveX"}; } catch (e) { // user broswer does not support ActiveX return null; } } var getHTML5 = function() { try { // lots of initialization work return { "name": "HTML5"}; } catch (e) { // user broswer does not support HTML5 return null; } } var getFlash = function() { try { // lots of initialization work return { "name": "Flash"}; } catch (e) { // user broswer does not support Flash return null; } } var getForm = function() { return { "name": "Form"}; } ```Now in order to get appropriate API, we just use single line:> var uploadAPI = getActiveX.after(getHTML5).after(getFlash).after(getForm)();This design idea is actually the so called “Chain of Responsibility”. Simply speaking, the function in the beginning of chain ( in my example, it is getActiveX ) will analyze whether it is able to resolve the task. If yes, the whole statement returns, task is over. Otherwise, it simply delegate the task to the next node in the chain.# Example 4 – eliminate lots of IF-ELSE in validity check via Strategy Design PatternFor example, before we assemble the request payload to send OData request via OData API, we must perform various validity check on the user input. If you have a page with lots of UI elements, usually it will lead to lots of IF-ELSEIF-ELSEIF validity check code spread in your application.```javascriptvar send = function() { var value = input.value; if( value.length === '' ) { return false; } else if( value.length > MAX_LENGTH) { return false; } ... // LOTS OF other rules to be checked else { // all check passes, now ready to call OData API } }
A Better Solution
Instead of directly coding all those validity checks in the application, we can first put the rules in a JavaScript object (so called “Strategy” in Design Pattern ):
var valid_rules = { not_empty: function( value ) { return value.length !== ''; }, max_length: function( value ) { return value.length <= MAX_LENGTH ; } }
With strategy ready, now we can simply write the validity check function in a generic way. It doesn’t know any detail about validity rules, but simply scan rules configured in strategy object one by one. If any check fails, the whole function will return false – check not pass.
var valid_check = function() { for( var i in valid_rules ) { if ( vali_rules[i].apply( this, arguments) === false ) { return false; } } }
Finally the send function could be reduced as:
var send = function( value ) { if ( valid_check( value ) === false ) { return; } // all check passes, now ready to call OData API }
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24475491/viewspace-2718376/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 重構你的javascript程式碼JavaScript
- JavaScript進階之路——認識和使用Promise,重構你的Js程式碼JavaScriptPromiseJS
- 編寫可測試的Javascript程式碼(2):從反模式進行重構JavaScript模式
- 一個利用正規表示式進行程式碼重構,去除冗餘程式碼的例子行程
- 實際業務中使用策略模式對程式碼進行重構模式
- 3個例子教你重構 Python 程式碼Python
- 6 個例子教你重構 Python 程式碼Python
- 使用acorn對JavaScript程式碼進行解析。JavaScript
- 程式碼重構與單元測試——對方法的引數進行重構(五)
- 重構:仔細檢視 改進程式碼
- 程式碼重構與單元測試——使用“以查詢取代臨時變數”再次對Statement()方法進行重構(七)變數
- 程式碼重構--大話重構
- 程式碼重構
- 使用javascript如何給url進行編碼JavaScript
- 重構遺留程式碼(6):進攻複雜的方法
- Javascript函數語言程式設計的一些例子[轉載]JavaScript函數程式設計
- 程式碼重構之法——方法重構分析
- 程式碼重構:類重構的 8 個小技巧
- .NET重構—單元測試的程式碼重構
- javascript對空格和換行進行編碼程式碼例項JavaScript
- “硬核”程式碼重構
- 重構 PHP 程式碼PHP
- PHP程式碼重構PHP
- 程式碼重構(四)
- 使用職責鏈模式來重構你的程式碼模式
- 程式碼重構:函式重構的 7 個小技巧函式
- 一些 JavaScript 中的程式碼小技巧JavaScript
- 使用重構件(Codemod)加速JavaScript開發和重構JavaScript
- 程式碼的壞味道和重構
- 重構出更加swifty的程式碼Swift
- 程式碼重構技巧(二)
- 談談程式碼重構
- 【讀程式碼重構有感】
- 重構:程式碼異味
- 程式碼重構與單元測試——繼續將相應的方法進行移動(八)
- 有關程式碼執行效率提升的小例子
- 使用結構化克隆在 JavaScript 中進行深度複製JavaScript
- 程式碼維護:改進程式碼的一些方法 (轉)