Reading Notes-javascript design patterns-interfaces, encapsulation, and chain calls
There are three main reasons for using the design pattern in javascript: maintainability: The design pattern helps reduce the coupling between modules. This makes it easy to refactor code and use different modules, and also makes it easy for programmers to cooperate in large projects. Communication: The Design Pattern provides a set of general terms for processing different types of objects. Programmers can briefly describe how their systems work. Performance: some performance optimization modes can greatly improve the execution efficiency of programs, such as the metadata mode and proxy mode. Abuse of the design mode also has some consequences: complexity: code becomes complicated and difficult for beginners to understand performance: Most design modes will reduce the Code Performance implementation more or less, and reasonable use is the difficulty. One suggestion is: Try to use the most suitable one, but do not sacrifice performance excessively. Interface: an interface is used to describe the methods of the object. Although it indicates the semantics of these methods, it is not implemented. Simulate interface 1 in Javascript: Use comments to describe the interface to copy the code/* interface Composite {function add (child); function remove (child); function getChild (index );} interface FormItem {function save ();} */var CompositeForm = function (id, method, action) {} CompositeForm. prototype. add = function () {} CompositeForm. prototype. remove = function () {} CompositeForm. prototype. getChild = function () {} CompositeForm. prototype. save = function (){} Copy the code. The system does not check whether the correct method is implemented, nor throws errors. It relies entirely on self-consciousness. 2. Use the property check imitation interface: copy the code/* interface Composite {function add (child); function remove (child); function getChild (index );} interface FormItem {function save ();} */var CompositeForm = function (id, method, action) {this. implementsInterfaces = ['composite ', 'formitem'];} function addForm (formInstance) {if (! Implements (formInstance, 'composite ', 'formitem') {throw new Error ("Object does not implement a required interface. ") ;}} function implements (object) {for (var I = 1; I <arguments. length; I ++) {var interfaceName = argument [I]; var interfaceFound = false; for (var j = 0; j <object. implementsInterfaces. length; j ++) {if (object. implementsInterfaces [j] = interfaceName) {interfaceFound = true; break ;}// if (! InterfaceFound) {return false ;}// all the interfaces return true;} are found. Copy the code here. CompositeForm declares that it has implemented the 'composite 'and 'formitem' interfaces, the method is to add the names of these two interfaces to the array of an object and explicitly declare the interfaces supported by them. Any function that requires its parameters to belong to a specific type can check this attribute and throw an exception when the implementation method cannot be found. Iii. Duck-style discriminative imitation interface it regards the method set implemented by the object as the only criterion for judging whether it is a class. The viewpoint behind this method is very simple: if the object has all methods with the same name as the method defined by the interface, it can be considered as implementing this interface. Interface Class Definition copy code/** define Interface method */var Interface = function (name, methods) {if (arguments. length! = 2) {throw new Error ("Interface constructor called with" + arguments. length + "arguments, but expected exactly 2. ") ;}; this. name = name; this. methods = []; for (var I = 0, len = methods. length-1; I <len; I ++) {if (typeof methods [I]! = 'String') {throw new Error ("Interface constructor expects method names to be" + "passed in as a string. ");} this. methods. push (methods [I]) ;};};/** extends the static verification method for the Interface */Interface. ensureImplements = function (obj) {if (arguments. length <2) {throw new Error ("Function Interface. ensureImplements called with "+ arguments. length + "arguments, but expected at least 2. ") ;}; for (var I = 0, len = method S. length-1; I <len; I ++) {var interface = arguments [I]; if (interface. constructor! = Interface) {throw new Error ("Function Interface. ensureImplements expects arguments "+" two and above to be instances of Interface. ") ;}; for (var j = 0, methodsLen = interface. methods. length; j <methodsLen; j ++) {var method = interface. methods [j]; if (! Object [method] | typeof object [method]! = 'Function') {throw new Error ("function Interface. ensureImplements: object "+" does not implement the "+ interface. name + "interface. method "+ method +" was not found. ") ;};};}}copy the code to inherit the chain inheritance implementation copy the code function extend (subClass, superClass) {var F = function () {}; F. prototype = superClass. prototype; subClass. prototype = new F (); subClass. peototype. constructor = subClass; subClass. superclass = sup ErClass. prototype; if (superClass. prototype. constructor = Object. prototype. constructor) {superClass. prototype. constructor = superClass;} copy the code superclass to weaken the coupling between the subclass and the super class. At the same time, ensure that the constructor of the superclass is correctly set. The implementation of prototype inheritance is actually copy inheritance. The implementation method is as follows. Function clone (object) {function F () {}; F. prototype = object; return new F ;}actually returns an empty object with the given object as the native object. Implementation of the metadata class. There is a reusable method that does not require strict inheritance. If you want to use a function in multiple classes, you can expand the method to share the function with these classes. The practice is as follows: first, create a class that contains various general methods, and then use it to expand other classes. This class that contains general methods is called the Meta class. Implementation of the meta-classes: copy the code function augment (receivingClass, givingClass) {// if has the third arg if (arguments. length [2]) {for (var I = 2, len = arguments. length; I <len; I ++) {receivingClass. prototype [arguments [I] = givingClass. prototype [arguments [I];} else {for (methodName in givingClass. prototype) {if (! ReceivingClass. prototype [methodName]) {receivingClass. prototype [methodName] = givingClass. prototype [methodName] ;}}} copying code encapsulation is to hide the data representation and implementation details inside the object. To access encapsulated objects, you can only use the defined operation method. In javascript, we have no keywords. We can only use the closure concept to create private attributes and methods. There are three basic modes for javascript to create objects. The portal is open and uses underscores to indicate private methods and attributes, and closures to create real private members. Copy the code var Book = function (newIsbn, newTitle, newAuthor) {// Private Attribute var isbn, title, anthor; function checkIsbn (isbn ){...}; // privileged method, which can access the private variable this. getIsbn = function () {return isbn;}; this. setIsbn = function () {if (! CheckIsbn (newIsbn) throw new Error ('book: Invalid ISBN. '); isbn = newIsbn;}; this. getTitle = function () {return newTitle;}; this. setTitle = function (newTitle) {title = newTiyle | '';}; this. getAuthor = function () {return author ;}; this. setAuthor = function (newAuthor) {author = newAuthor | '';}; this. setIsbn (newIsbn); this. setTitle (newTitle); this. setAuthor (newAuthor);} // methods that do not need to directly access private attributes can be stored in prototy Pe statement. Book. prototype = {display: function () {...} copy the more advanced object creation code. Copy the code var Book = (function () {// Private Static Property var numOfBooks = 0; // Private Static Method function checkIsbn (isbn ){...}; return function (newIsbn, newTitle, newAuthor) {// Private Attribute var isbn, title, anthor; // privileged method, which can access the private variable this. getIsbn = function () {return isbn;}; this. setIsbn = function () {if (! CheckIsbn (newIsbn) throw new Error ('book: Invalid ISBN. '); isbn = newIsbn;}; this. getTitle = function () {return newTitle;}; this. setTitle = function (newTitle) {title = newTiyle | '';}; this. getAuthor = function () {return author ;}; this. setAuthor = function (newAuthor) {author = newAuthor | '';}; numOfBooks ++; if (numOfBooks> 50) throw new Err ('book: only 50 instances of Book can be created. '); thi S. setIsbn (newIsbn); this. setTitle (newTitle); this. setAuthor (newAuthor) ;}}) (); // methods that do not need to directly access private attributes can be declared in prototype. Book. prototype = {display: function (){...}} copy the Code to make it simple: first, create a closure with (), execute it immediately, and return another function. Second, the return function is assigned to Book to become a constructor. Three times, the inner function called by the Book is instantiated. The outer function is used to create a closure that can store static private members. Four times, checkIsbn is designed as a static method. Therefore, this static method is not generated for every instance in the Book. This method acts on the constructor class instead of the instance. Five times, because these static methods are included in the closure, other methods in the closure can be accessed, and only one portion of the memory exists. Six times, these method declarations are not privileged Methods outside the returned constructor, so they cannot access private properties defined in the constructor. Seven times to determine whether a private method should be designed as a static method, an empirical rule is to see if it wants to access any instance data. If not, so it is more efficient to design it as a static method.