1. What are structural patterns
Structural patterns are mainly used to deal with combinations of classes and objects, corresponding to mind maps:
2. Appearance Mode:Facade Pattern
Secondary encapsulation of an interface hides its complexity and simplifies its use. The appearance mode contains the following roles:
Facade
: Appearance roleSubSystem
: Subsystem role
Use time
When we split the system into subsystems, we reduce code complexity. The best practice when programming is to minimize communication and dependencies between subsystems. A good way to achieve this is to introduce a facade object that provides a single, unified interface to the subsystem.
1. Listen for events across browsers
To ensure that the code handling events runs consistently across most browsers, you need to keep an eye on the bubbling phase.
When making cross-browser sites, you’ve inadvertently used appearance mode:
var addMyEvent = function( el,ev,fn ){
if(el.addeventListener){// If a dom2-level method exists, the event type, event handler function, and a third argument are used and passed infalseEl.addeventlistener (ev,fn,false );
}else if(el.attachevent){// For compatibility with IE8 and earlier browsers, note that the event type must be added"on"The prefix el. AttachEvent ("on" + ev, fn );
}else{
el["on"+ ev] = fn; // All other methods are invalid, default is DOM0 level method, use square bracket syntax to specify attribute name as event handler}};Copy the code
2. jQuery $(document).ready(..)
We are all familiar with $(document).ready(..) . In the source, this is actually a bindReady() provided by the called method:
Loading events use two methods: window.onload () and $(document).ready().
bindReady: function() {...if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", jQuery.ready, false );
// If IE event model is used
} else if ( document.attachEvent ) {
document.attachEvent( "onreadystatechange", DOMContentLoaded );
// A fallback to window.onload, that will always work
window.attachEvent( "onload", jQuery.ready );
Copy the code
The Facade Facade pattern is heavily applied to the jQuery library to make it easier to use. For example, we use jQuery methods like $(el).css() or $(el).animate().
This frees us from having to manually call many internal methods in the jQuery kernel to implement certain behaviors, and also avoids having to manually interact with the DOM API.
Similarly, d3.js
3. Adapter mode:Adapter Pattern
- Tradition: Incompatible interfaces for two or more classes
JS
: Additional adaptation of two or more code libraries, front and back end data, etc.
Usage time When adapters are typically used:
- You need to integrate new components and work with existing components in your application.
- Refactoring, which parts of the program are rewritten with the improved interface, but the old code still needs the original interface.
1. jQuery.fn.css()
Normalized display
// Cross browser opacity:
// opacity: 0.9; Chrome 4+, FF2+, Saf3.1+, Opera 9+, IE9, iOS 3.2+, Android 2.1+
// filter: alpha(opacity=90); IE6-IE8
// Setting opacity
$( ".container" ).css( { opacity: .5 } );
// Getting opacity
var currentOpacity = $( ".container" ).css('opacity');
Copy the code
The internal implementation is:
get: function( elem, computed ) {
return ropacity.test( (
computed && elem.currentStyle ?
elem.currentStyle.filter : elem.style.filter) || "")? ( parseFloat( RegExp.The $1) / 100) +"" :
computed ? "1" : "";
},
set: function( elem, value ) {
var style = elem.style,
currentStyle = elem.currentStyle,
opacity = jQuery.isNumeric( value ) ?
"alpha(opacity=" + value * 100 + ")" : "",
filter = currentStyle && currentStyle.filter || style.filter || ""; style.zoom = 1; // If the opacity is set to 1, other filters are removed //exist - attempt to remove filter attribute# 6652
if ( value >= 1 && jQuery.trim( filter.replace( ralpha, ""= = =))"" ) {
style.removeAttribute( "filter" );
if( currentStyle && ! currentStyle.filter ) {return;
}
}
// otherwise, set new filter values
style.filter = ralpha.test( filter ) ?
filter.replace( ralpha, opacity ) :
filter + ""+ opacity; }};Copy the code
2. Vue
In thecomputed
Yck – Tips of The Front-end Interview
In Vue, we actually use the adapter pattern a lot.
For example, if a parent component passes a timestamp property to a child component, and the component internally needs to convert the timestamp to a normal date display, typically using computed data to do this, the adapter pattern is used.
4. Agent Mode:Proxy Pattern
Provide a proxy for other objects to control access to that object.
The pre-processing done before this method is called (the unified process code is put into the proxy for processing). Call this method and do post-processing.
For example: the star’s agent, the rental agent and so on are agents
What is the point of using the proxy pattern?
-
“Single responsibility principle” : In object-oriented design, different responsibilities are encouraged to be distributed into fine-grained objects. Proxy derives functions on the basis of the original object without affecting the original object, which conforms to the design concept of loose coupling and high cohesion
-
Follow the “open-closed principle” : proxies can be removed from the program at any time without changing other parts of the code. In a real world scenario, proxies may no longer be needed for a variety of reasons as the version iterations, so proxy objects can be easily replaced with calls to the original object.
Features:
- Solve the coupling degree between systems and system resource overhead
- Proxy objects protect the proxied objects from external influences
- In JS, its execution is often browser-dependent
- The event broker uses the proxy pattern.
Classification:
- Remote proxy (
Remote Proxy
) : Provides a local proxy object for an object in a different address space - Virtual proxy (
Virtual Proxy
) : If you need to create an object with high resource consumption, create a relatively small object to represent it. The real object will only be created when necessary. - Protection agent (
Protect Proxy
) : Controls access to an object and can give different levels of access to different users. - Buffer agent (
Cache Proxy
) : Provides temporary storage for the results of a target operation so that multiple clients can share the results. - Intelligent reference proxy (
Smart Reference Proxy
) : Provides some additional operations when an object is referenced, such as counting the number of times the object is called.
Disadvantages: :
-
Because of the addition of proxy objects between the client and the real subject, some types of proxy patterns can slow down the processing of requests, such as secure proxies.
-
Implementing the proxy pattern requires additional work, and some, such as remote proxies, are more complex.
The most used front-end is virtual proxy, protection proxy, buffer proxy
1. ES6
In theProxy
The Proxy constructor provided in ES6 makes it easy to use the Proxy pattern:
// target: represents the object to be proxied. Handler: sets the behavior of the proxied object.let proxy = new Proxy(target, handler);
Copy the code
2. Image preloading
Most web sites now have an image preloading mechanism, which uses a Daisy diagram (a revolving GIF) to indicate that an image is being loaded before the actual image is loaded.
const img = new Image();
img.src = '/some/big/size/image.jpg';
document.body.appendChild(img);
Copy the code
Create the virtual image node virtualImg and construct the create proxy function:
Const createImgProxy = (img, loadingImg, realImg) => {let hasLoaded = false;
const virtualImg = new Image();
virtualImg.src = realImg;
virtualImg.onload = () => {
Reflect.set(img, 'src', realImg);
hasLoaded = true;
}
return new Proxy(img, {
get(obj, prop) {
if (prop === 'src' && !hasLoaded) {
return loadingImg;
}
returnobj[prop]; }});Copy the code
The final call is to replace the original image node with a proxy image:
const img = new Image();
const imgProxy = createImgProxy(img, '/loading.gif'.'/some/big/size/img.jpg');
document.body.appendChild(imgProxy);
Copy the code
3. Paging data: caching proxy
For example, when the front and back ends are separated, when the back end requests paging data, it needs to request the back end data again every time the page number changes. We can cache the page and the corresponding result. When the same page is requested, we no longer request the back end interface but fetch the data from the cache.
const getFib = (number) => {
if (number <= 2) {
return 1;
} else {
return getFib(number - 1) + getFib(number - 2);
}
}
const getCacheProxy = (fn, cache = new Map()) => {
return new Proxy(fn, {
apply(target, context, args) {
const argsString = args.join(' ');
if(cache.has(argsString)) {// If there is a cache, return the cached data console.log(' output${args}Cache result:${cache.get(argsString)}`);
returncache.get(argsString); } const result = fn(... args); cache.set(argsString, result);returnresult; } }) } const getFibProxy = getCacheProxy(getFib); getFibProxy(40); // 102334155getFibProxy(40); // Output 40 cache result: 102334155Copy the code
4. Event broker
The event broker uses the proxy pattern.
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
let ul = document.querySelector('#ul')
ul.addEventListener('click', (event) => {
console.log(event.target);
})
</script>
Copy the code
By binding an event to the parent node, the parent node acts as a proxy to get the actual clicked node.
5. Decorator mode:Decorator Pattern
Decorators are similar to the concept of higher-order functions. The decorator takes the base form as an argument, adds processing to it and returns it. Advantages:
- The advantage is that the core responsibilities of a class (function) are separated from the decorative functions.
Question:
- Deco chains overlay function scopes and can also cause performance problems if they are too long.
In JavaScript:
- The decorator pattern provides a more flexible alternative to inheritance.
- Decorators are used to wrap objects of the same interface and to add new functionality in the form of overloaded methods. This pattern can be used to add its own behavior in front or after the decorator to achieve a specific purpose.
The core is to cache the last function
1. Simple examples
Here’s a simple example:
var xiaoming = function () {
this.run = function () {
return 'running'
},
this.eat = function () {
return: 'eat'}} // Xiao Ming can run and eat. // There is a decor class for Xiao Mingfunction (xiaoming) {
this.run = function () {
return xiaoming.run + 'soon'
}
this.eat = function () {
return xiaoming.eat + 'a lot'}}Copy the code
Through a decoration class, the realization of xiaoming class decoration.
2. TypeScript
Function modifiers:@
Or to put it in plain English: @: “I’m surrounded below.”
For example, in the following code, two functions, which are not called, will have output:
test(f){
console.log("before ...");
f()
console.log("after ...");
}
@test
func(){
console.log("func was called");
}
Copy the code
Run directly, output result:
before ...
func was called
after ...
Copy the code
3. React
Decorator mode in
In React, decorator mode is everywhere:
import React, { Component } from 'react';
import {connect} from 'react-redux';
class App extends Component {
render() {
//...
}
}
// const mapStateToProps
// const actionCreators
export default connect(mapStateToProps,actionCreators)(App);
Copy the code
The last step in Ant Design to create a form is actually a decorator pattern
class CustomizedForm extends React.Component {}
CustomizedForm = Form.create({})(CustomizedForm);
Copy the code
6. Bridge mode:Bridge Pattern
Abstraction
(Abstract class)RefinedAbstraction
(Extending abstract classes)Implementor
(Implementation class interface)ConcreteImplementor
(Concrete implementation class)
The application writes to a defined database API, such as ODBC, but after that API, you find that the implementation of each driver is completely different for each database vendor (SQL Server, MySQL, Oracle, etc.).
- Common in driver development, in
JavaScript
It’s very rare. - Some cross-platform software designs sometimes use bridging patterns
1. Website theme replacement
In large sites, different modules may have different themes, as well as day/night or user-chosen themes.
Creating multiple copies of each page for each topic is obviously unreasonable, and bridge mode is the better choice:
javascript-design-patterns-for-human
class About{
constructor(theme) {
this.theme = theme
}
getContent() {
return "About page in " + this.theme.getColor()
}
}
class Careers{
constructor(theme) {
this.theme = theme
}
getContent() {
return "Careers page in " + this.theme.getColor()
}
}
Copy the code
And different themes:
class DarkTheme{
getColor() {
return 'Dark Black'
}
}
class LightTheme{
getColor() {
return 'Off white'
}
}
class AquaTheme{
getColor() {
return 'Light blue'}}Copy the code
Generate themes:
const darkTheme = new DarkTheme()
const about = new About(darkTheme)
const careers = new Careers(darkTheme)
console.log(about.getContent() )// "About page in Dark Black"
console.log(careers.getContent() )// "Careers page in Dark Black"
Copy the code
7. Combination mode:Composite Pattern
- Also known as the partial-whole pattern, objects are grouped into a tree structure to represent a partial-whole hierarchy.
- Make the use of single objects and combined objects consistent. (Refer to cards and forms)
This pattern contains the following roles:
Component
– Declares the interface of the objects in the composition and implements the default behavior (based onComposite
)Leaf
– Represents the original object in compositionComposite
– inComponent
Interface to implement operations related to children, and storeThe Leaf (primitive)
Object.
1. File directory structure in the operating system
A computer file structure is an example of a composite pattern.
You can invoke composite objects higher up the structure tree, and messages will travel down this hierarchy.
2. Perform batch operationsDOM
Javascript Design patterns theory and Practice: Composite patterns
The DOM structure of HTML documents is a natural tree structure. The most basic elements become a DOM tree, which eventually forms a DOM document.
We commonly use the jQuery class library, in which the application of composite pattern is more frequent, for example, often have the following code implementation:
$(".test").addClass("noTest").removeClass("test");
Copy the code
No matter the $(" test ".)
Whether it’s one element, or multiple elements, is ultimately unifiedaddClass
andremoveClass
Interface to call.
Let’s briefly simulate the implementation of addClass:
var addClass = function (eles, className) {
if (eles instanceof NodeList) {
for (var i = 0, length = eles.length; i < length; i++) {
eles[i].nodeType === 1 && (eles[i].className += (' ' + className + ' ')); }}else if (eles instanceof Node) {
eles.nodeType === 1 && (eles.className += (' ' + className + ' '));
}
else {
throw "eles is not a html node";
}
}
addClass(document.getElementById("div3"), "test");
addClass(document.querySelectorAll(".div"), "test");
Copy the code
For NodeList or Node, client calls use the addClass interface in the same way. This is the basic idea of the composite pattern, so that the use of part and whole is consistent.
8. Enjoy Yuan mode:Flyweight Pattern
Flyweight mode is a mode used for performance optimization. “fly” stands for “fly”, meaning flyweight.
- It is used to reduce the number of objects created to reduce memory usage and improve performance
- Use sharing techniques to efficiently support a large number of fine-grained objects
The core of the share pattern is the use of sharing technology to effectively support a large number of fine-grained objects.
The share pattern is useful if the system is overloaded with memory due to the creation of a large number of similar objects. In JavaScript, browsers, especially mobile browsers, don’t allocate a lot of memory, so how to save memory becomes a very meaningful thing.
The enjoy mode has the following roles:
- Client: A class used to call a meta-factory to get intrinsic data, usually an object needed by an application,
- Metadata factory: Classes used to maintain metadata metadata
- Metaclasses: Classes that hold intrinsic data
1. Simple examples
In the example below, we create a “Book” class to handle specific books, and then a “BookFactory” class to control how these Book objects are created.
For better memory performance, objects are reused if the same object is instantiated twice.
class Book {
constructor(title, isbn, author, ratings) {
this.title = title;
this.isbn = isbn;
this.author = author;
this.ratings = ratings;
}
getAverageReview() {
let averageReview = (this.ratings.reduce((a,b) => a+b)) / this.ratings.length
return averageReview;
}
}
class BookFactory {
constructor() {
this._books = [];
}
createBook(title, isbn, author, ratings) {
let book = this.getBookBy(isbn);
if(book) {// Reuse objectsreturn book;
} else {
const newBook = new Book(title, isbn, author, ratings);
this._books.push(newBook);
return newBook;
}
}
getBookBy(attr) {
returnthis._books.find(book => book.attr === attr); }}Copy the code
2. Online form idea realization
Open the Google online form and extract and print its node elements.
You can see that even if you scroll to thousands of rows, they’re just sharing two views.
Use is to enjoy the yuan mode, to prevent infinite scrolling caused by stuck.
The following is a simulation implementation:
<section id="app">
<table id="table"></table>
<div class="controls">
<input type="range" name="scroll" id="scroll" value="0">
</div>
</section>
Copy the code
Style:
#app {
position: relative;
padding: 30px 0 30px 10px;
#table {padding: 20px; border-radius: 10px; min-width: 450px; The transition: 0.5 s background; Background: RGBA (73, 224, 56, 0.1); &. Low-range {background: Rgba (73, 224, 56, 0.47); Td {border-bottom: 1px solid rgba(73, 224, 56, 0.9)}} &. Mid-range {background: rgba(224, 196, 56, 0.47); Td {border-bottom: 1px solid rgba(224, 196, 56, 0.9)}} &. High-range {background: rgba(224, 56, 56, 0.47); Td {border-bottom: 1px solid rgba(224, 56, 56, 0.9)}} &. Ultra-high-range {background: rgba(224, 56, 56, 0.9); td { border-bottom: 1px solid black } } td { border-bottom: 1px solid black; padding: 10px; font-weight: bold; } } .controls { padding-top: 20px;#scroll {width: 450px; box-sizing: border-box; }}}Copy the code
Logical implementation, please eat with comments:
// Generate Cell instance const makeRowCells = data => data.map(value => new Cell(value)); // define const scrollViewport = 10; // Current table view size const tableSize = 2000; / / the number of rowsletscrollIndex = 0; // Initial scroll indexletDATA = []; // Initial data setwhile (DATA.length < scrollViewport) {
const unit = DATA.length * 10;
DATA.push('12345678'.split(' ').map(() => unit)); Constructor () {this.content = content;} / / constructor() {this.content = content; } // Update column updateContent(content) {this.content = content; this.cell.innerText = content; } // render columnsrender() {
const cell = document.createElement('td');
this.cell = cell;
cell.innerText = this.content;
returncell; Constructor (constructor(cellItems) {this.cellitems = cellItems; } // Update row updateRowData(newData) {this.cellItems.foreach ((item, idx) => {item.updatecontent (newData[idx]); }); } // Render linesrender() {
const row = document.createElement('tr');
this.cellItems.forEach(item => row.appendChild(item.render()));
returnrow; }} /** * constructor(selector) {this.$table= document.querySelector(selector); } // Add row addRows(rows) {this.rows = rows; this.rows.forEach(row => this.$table.appendChild(row.render())); ForEach ((row, idx) => row.updaterOwData (data[idx])); }} const table = new table ('#table'); Const scrollControl = document.querySelector('#scroll'); Table. AddRows (data. map(dataItem => New Row(makeRowCells(dataItem)))); Const onScrollChange = event => {// Prepare new DATA for the view idx) => item.map(cell => parseInt(event.target.value, 10)*10 + idx*10)); Table.updatetabledata (DATA); // Add a color difference style scrollIndex = event.target.value;if (event.target.value >= 0) {
table.$table.classList = 'low-range';
}
if(event.target.value > tableSize * 0.4) {table.$table.classList = 'mid-range';
}
if(event.target.value > tableSize * 0.7) {table.$table.classList = 'high-range';
}
if(event.target.value > tableSize * 0.9) {table.$table.classList = 'ultra-high-range'; }}; / / set the minimum and maximum range scrollControl. The scroll bar setAttribute ('min', 0);
scrollControl.setAttribute('max', tableSize); / / add the scroll event scrollControl. AddEventListener ('input', onScrollChange); Const event = {target: {value: 0}}; onScrollChange(event);Copy the code
9. Conclusion and reference
At this point, the structural design pattern is done, and the Meta-pattern is worth a blog post of its own.
Refer to the article
- JavaScript design patterns in detail
- Javascript design pattern theory and Practice: The Meta pattern
- Easy patterns: Flyweight
- Composite design pattern
- Javascript Design patterns theory and Practice: Composite patterns
- Yck – Tips of The Front-end Interview
❤️ Read three things
If you find this article inspiring, I’d like to invite you to do me three small favors:
- Like, so that more people can see this content (collection does not like, is a rogue -_-)
- Follow the public account “front end persuader” to share original knowledge from time to time.
- Look at other articles as well
- Design patterns that you inadvertently use (I) – Creative patterns
- “King of data Visualization library” d3. js fast start to Vue application
- “True ® Full Stack Road” Back-end guide to Web front-end development
- “Vue Practice” 5 minutes for a Vue CLI plug-in
- “Vue practices” arm your front-end projects
- “Intermediate and advanced front-end interview” JavaScript handwritten code unbeatable secrets
- The answer to the Vue question “Learn from source” that no interviewer knows
- “Learn from the source” Vue source JS operations
- The “Vue Practice” project upgrades vuE-CLI3 to correct posture
- Why do you never understand JavaScript scope chains?
Public account background reply “Design mode” to receive the author carefully homemade mind map.