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 role
  • SubSystem: 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. VueIn 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:

  1. Remote proxy (Remote Proxy) : Provides a local proxy object for an object in a different address space
  2. 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.
  3. Protection agent (Protect Proxy) : Controls access to an object and can give different levels of access to different users.
  4. Buffer agent (Cache Proxy) : Provides temporary storage for the results of a target operation so that multiple clients can share the results.
  5. 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: :

  1. 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.

  2. 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. ES6In 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. TypeScriptFunction 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. ReactDecorator 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, inJavaScriptIt’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:

  1. Component– Declares the interface of the objects in the composition and implements the default behavior (based onComposite)
  2. Leaf– Represents the original object in composition
  3. Composite– inComponentInterface 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 unifiedaddClassandremoveClassInterface 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:

  1. Like, so that more people can see this content (collection does not like, is a rogue -_-)
  2. Follow the public account “front end persuader” to share original knowledge from time to time.
  3. 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.