Sometimes we don’t want a class to be inherently large and contain many responsibilities at once. Then we can use the decorated pattern.
The decorator pattern can dynamically add some additional responsibilities to an object without affecting other objects derived from this class.
The decorated pattern embeds one object into another object, which is actually equivalent to this object being wrapped by another object, forming a packaging chain.
1. Add some additional functions to the function without changing the original function
1. Save the original quote
window.onload = function() { console.log(1); }; var _onload = window.onload || function() {}; window.onload = function() { _onload(); console.log(2); }
Question:
(1) Intermediate variables must be maintained
(2) You may encounter the problem of this being hijacked
In the example of window.onload, there is no such trouble because when calling the ordinary function _onload, this also points to window, just like when calling window.onload.
2. this was hijacked:
var _getElementById = document.getElementById; document.getElementById = function(id) { console.log(1); return _getElementById(id); } return _getElementById(id); // 报错“Uncaught TypeError: Illegal invocation”
Because _getElementById is a global function, when the global function is called, this points to the window, and this in document.getElementById is expected to point to the document.
3. Solve this being hijacked:
var _getElementById = document.getElementById; document.getElementById = function(id) { console.log(1); return _getElementById.call(document, id); }
2. Use AOP to decorate functions
/* 让新添加的函数在原函数之前执行(前置装饰)*/ Function.prototype.before = function(beforefn) { var _self = this; return function() { beforefn.apply(this, arguments); // 新函数接收的参数会被原封不动的传入原函数 return _self.apply(this, arguments); }; };
/* 让新添加的函数在原函数之后执行(后置装饰)*/ 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() { console.log(1); });
3. Avoid contaminating prototypes
var before = function(fn, beforefn) { return function() { beforefn.apply(this, arguments); return fn.apply(this, arguments); }; }; var after = function(fn, afterfn) { return function() { var ret = fn.apply(this, arguments); afterfn.apply(this, arguments); return ret; }; }; document.getElementById = before(document.getElementById, function(){ console.log(1); });
4. Example – plug-in form validation
Combined with the [Form Validation] in "Learning JavaScript Design Patterns - Strategy Pattern" , and applied ajax to submit data verification, the effect is great!
Modify the above before method
var before = function(fn, beforefn) { return function() { if(beforefn.apply(this, arguments) === false) { // beforefn返回false,直接return,不执行后面的原函数 return; } return fn.apply(this, arguments); }; };
/* 模拟数据验证*/ var validate = function() { if(username === "") { console.log("验证失败!"); return false; } return true; } /* 模拟ajax提交*/ var formSubmit = function() { console.log("提交!!!"); }
username = 1; formSubmit = before(formSubmit, validate); // 提交!!! formSubmit(); username = ""; formSubmit = before(formSubmit, validate); // 验证失败! formSubmit();
5. Decorator mode and proxy mode
Similar points: Both patterns describe how to provide a certain degree of indirect reference for an object. Their implementation parts retain a reference to another object and send requests to that object.
Difference:
(1) Proxy mode: When direct local access is inconvenient or does not meet requirements, provide a substitute for this ontology. Define key functionality locally, and the agent provides or denies access to it, or does some extra things before accessing the ontology. (It still does the same thing as the main body)
(2) Decorator mode: dynamically add behaviors to objects. (You cannot determine all the functions of the object at the beginning, and actually add new responsibilities and behaviors to the object)
I hope this article will be helpful to everyone learning JavaScript programming.