The final stubbornness of the cut figure: the design pattern of teaching but not meeting-structural

The final stubbornness of the cut figure: the design pattern of teaching but not meeting-structural

1. What is a structural model

The structural mode is mainly used to deal with the combination of classes and objects, corresponding to the mind map:

2. Appearance pattern: Facade Pattern

To hide the complexity of the interface secondary encapsulation and simplify its use. The appearance mode includes the following roles:

  • Facade: Appearance character
  • SubSystem: Subsystem role

When to use

When we divide the system into multiple subsystems, we reduce the code complexity. The best practice when programming is to minimize the communication and dependencies between subsystems. A good way to achieve this goal is to introduce an facadeobject that provides a single and unified interface to the subsystem.

1. Cross-browser monitoring of events

To ensure that the code that handles events runs consistently in most browsers, you need to pay attention to the bubbling phase.

When making a cross-browser website, you have inadvertently used the appearance mode :

var addMyEvent = function( el,ev,fn ){
  if( el.addEventListener ){//There is a DOM2 level method, then use and pass in the event type, event handler function and the third parameter false (indicating the bubbling phase)
        el.addEventListener( ev,fn, false );
  }else if(el.attachEvent){//To be compatible with IE8 and earlier browsers, note that the event type must be prefixed with "on"
        el.attachEvent( "on" + ev, fn );
       el["on" + ev] = fn;//Other methods are invalid. The DOM0 level method is used by default. Use square bracket syntax to specify the attribute name as the event handler

2. jQuery $(document).ready(..)

We are all familiar $(document).ready(..). In the source code, this is actually provided by a called method bindReady():

Load events share 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 );

FacadeAppearance widely used model jQuerydatabase and let it easier to use. For example, we use jQueryof $(el).css()or $(el).animate()other methods.

So that we do not have to manually jQuerycall a lot of internal kernel methods in order to achieve certain behavior, while avoiding the manual and DOM APIinteraction.

There are similarD3.js

3. Adapter Pattern: Adapter Pattern

  • Tradition: adapt to the incompatibility of two or more types of interfaces
  • JS: It can be additionally adapted to two or more code bases, front and back data, etc.

When to use the adapter is usually used:

  • Need to integrate new components and work with existing components in the application.
  • Refactoring, which parts of the program are rewritten with an improved interface, but the old code still needs the original interface.

1. jQuery.fn.css() canonical 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');

The internal implementation is:

get: function( elem, computed) {
  return ropacity.test( (
        computed && elem.currentStyle? 
            elem.currentStyle.filter: || "")?
    (parseFloat( RegExp.$1)/100) + "":
    computed? "1": "";

set: function( elem, value) {
  var 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, the 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) {

 //otherwise, set new filter values
  style.filter = ralpha.test( filter)?
    filter.replace( ralpha, opacity):
    filter + "" + opacity;

2. Computed in Vue

yck-"The Way of Front-end Interview"

In the Vuemiddle, in fact, we often use the adapter mode.

Such parent component subassembly to pass a timestamp attribute, the internal components need to stamp date display to normal, generally used computedto do the conversion of this matter, the process is used to pattern the adapter.

4. Proxy Pattern: Proxy Pattern

Provide a proxy for other objects in order to control access to this object.

The method of accessing a certain class (object) can be controlled in detail, and the pre-processing (the unified process code is placed in the agent for processing) before the method is called. Post-processing is done after calling this method.

For example: celebrity agents, renting agents, etc. are all agents

What is the significance of using the proxy mode?

  • "Single Responsibility Principle": In object-oriented design, different responsibilities are encouraged to be distributed to 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": the agent can be removed from the program at any time without modifying other parts of the code. In the actual scenario, as the version is iterated, there may be many reasons that the agent is no longer needed, then you can It is easy to replace the proxy object with the call of the original object.


  • Solve the coupling between systems and the high cost of system resources
  • The proxy object can be used to protect the proxy object, so that its scalability is not affected by the outside world
  • In js, its execution often depends on the browser
  • The event agent uses the agent mode.


  1. Remote proxy ( Remote Proxy): Provide a local proxy object for an object located in a different address space
  2. Virtual agent ( Virtual Proxy): If you need to create an object that consumes a lot of resources, first create an object that consumes a relatively small amount to represent it. The real object will only be created when needed.
  3. Protection agent ( Protect Proxy): Controls access to an object, and can provide different users with different levels of usage rights.
  4. Buffer proxy ( Cache Proxy): Provide temporary storage space for the results of a certain target operation, so that multiple clients can share these results.
  5. Intelligent Reference Agent ( Smart Reference Proxy): When an object is referenced, it provides some additional operations, such as recording the number of times the object is called.


  1. Due to the addition of proxy objects between the client and the real subject, some types of proxy modes may slow down the processing speed of the request, such as a protection proxy.
  2. Implementation of the proxy mode requires additional work, and the implementation process of some proxy modes is more complicated, such as remote proxy.

The front is the most used Alerts , protection agents , buffering agents

1. Proxy in ES6

ES6The provided Proxyconstructor allows us to easily use the proxy mode:

//target: represents the object to be proxied, handler: is used to set the behavior of the object to be proxied.
let proxy = new Proxy(target, handler);

2. Picture preloading

At present, general websites will have a picture preloading mechanism, that is, before the real picture is loaded, a chrysanthemum picture (circle gif picture) is used to indicate that the picture is being loaded.

const img = new Image();
img.src ='/some/big/size/image.jpg';

Create a virtual picture node virtualImgand construct a proxy function:

//Image lazy loading: virtual proxy
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;
      return obj[prop];

Finally, replace the original image node with a proxy image to call:

const img = new Image();
const imgProxy = createImgProxy(img,'/loading.gif','/some/big/size/img.jpg');

3. Paging data: caching proxy

For example, when the front-end and the back-end are separated, when requesting paged data from the back-end, the back-end data needs to be re-requested every time the page number changes. We can cache the page and the corresponding result. When the same page is requested, no more Instead, request the back-end interface to fetch 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, directly return the cached data console.log(`Output the cache result of $(args): $(cache.get(argsString))`);

        return cache.get(argsString);
      const result = fn(...args);
      cache.set(argsString, result);

      return result;
const getFibProxy = getCacheProxy(getFib);
getFibProxy(40);//102334155 getFibProxy(40);//Output the cached result of 40: 102334155

4. Event Agent

The event agent uses the agent mode.

<ul id="ul">
    let ul = document.querySelector('#ul')
    ul.addEventListener('click', (event) => {

By binding an event to the parent node, let the parent node act as an agent to get the actual clicked node.

5. Decorator Pattern: Decorator Pattern

On the basis of not changing the original object, by packaging and expanding it (adding attributes or methods), the original object can meet the more complex needs of users

Decorators are similar to the concept of higher-order functions. The decorator takes the basic form as a parameter, adds processing to it and returns it. advantage:

  • The advantage is that it separates the core responsibilities of the class (function) from the decoration function.


  • The decoration chain superimposes the function scope, if it is too long, it will also cause performance problems.

In JavaScript:

  • The decorator pattern provides a more flexible alternative than inheritance.
  • The decorator is used to package objects with the same interface, and is used to add new functions in the form of overloaded methods. This mode can add its own behavior before or after the decorator to achieve a specific purpose.

The core is to cache the last function

1. Simple example

Give a simple example:

var xiaoming = function () { = function () {
  }, = function () {
//Xiao Ming can run or eat
//Below is a decoration class to decorate Xiao Ming
var decor = function (xiaoming) { = function () {
    return +'soon'
  } = function () {
    return +'many'

Through a decoration class, the decoration of Xiaoming class is realized.

2. TypeScript function modifier: @

"@" is not so much a modified function as it is a reference and call to the modified function.

Or describe it in vernacular: @: "The following is surrounded by me."

For example, in the following piece of code, there will be output results if two functions are not called:

    console.log("before ...");
        console.log("after ...");

    console.log("func was called");

Run directly and output the result:

before ...
func was called
after ...

3. Decorator pattern in React

In React, the decorator pattern can be seen 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);

Ant DesignThe last step of creating the form in is actually considered as decorator mode

class CustomizedForm extends React.Component {}

CustomizedForm = Form.create({})(CustomizedForm);

6. Bridge pattern: Bridge Pattern

The bridge mode decouples the implementation layer from the abstract sub-layer, so that the two parts can be changed independently. The model includes the following roles:

  • Abstraction(Abstract class)
  • RefinedAbstraction(Extend the abstract class)
  • Implementor(Implementation class interface)
  • ConcreteImplementor(Specific implementation class)

Commonly used in applications (clients) and database drivers (services):

The application writes a defined database API, for example ODBC, but after this API, you will find that the implementation of each driver SQL Server,MySQL,Oracleis completely different for each database vendor ( etc.).

  • It is mostly JavaScriptseen in driver development, but rarely seen in China.
  • The cross-platform design of some software sometimes also applies the bridge mode

1. Site theme replacement

In large websites, different modules may have different themes, and there are also day/night or user-selected themes.

At this time, it is obviously unreasonable to create multiple copies of each page for each topic, and the bridge mode is a better choice:


Different modules:

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()

And different topics:

class DarkTheme{
    getColor() {
        return'Dark Black'
class LightTheme{
    getColor() {
        return'Off white'
class AquaTheme{
    getColor() {
        return'Light blue'

Generate theme:

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"

7. Composite Pattern: Composite Pattern

  • Also known as the part-whole model, the objects are combined into a tree structure to represent the hierarchical structure of "partial whole".
  • So that users have consistency in the use of single objects and combined objects. (Refer to the composition of cards and forms)

The model includes the following roles:

  1. Component-Declare the interface of the objects in the composition and implement the default behavior (based on Composite)
  2. Leaf -Represents the original object in the composition
  3. CompositeComponent-Implement sub-related operations in the interface and store Leaf(primitive)objects.

1. File directory structure in the operating system

The computer file structure is an example of the combined model.

If you delete a folder, you will also delete all the contents of the folder, right? This is essentially the principle of combined mode operation. you

You can call higher-level composite objects on the structure tree, and messages will be transmitted down this hierarchical structure.

2. Batch manipulation of DOM

Javascript design pattern theory and actual combat: combination mode

HTMLThe DOMstructure of the document is a natural tree structure, the most basic elements are drunk into a DOM tree, and finally form a DOMdocument, which is very suitable for applicable combination mode.

jQueryAmong our commonly used class libraries, the application of combination mode is more frequent. For example, the following codes are often implemented:


Regardless of whether $ (“.test”)is an element or multiple elements, it is ultimately called through a unified addClassand removeClassinterface.

We simply simulate addClassthe realization:

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");

For NodeListor in other Nodewords, client calls all use addClassthis interface in the same way . This is the most basic idea of ​​the combined mode, which makes the use of part and the whole consistent.

8. Flyweight Pattern: Flyweight Pattern

Flyweight ( flyweight) mode is a mode used for performance optimization. flyHere , " " means fly, which means flyweight.

  • Mainly used to reduce the number of objects created to reduce memory usage and improve performance
  • Use sharing technology to effectively support a large number of fine-grained objects

The core of the flyweight model is to use sharing technology to effectively support a large number of fine-grained objects.

Flyweight mode is very useful if the memory usage is too high due to the creation of a large number of similar objects in the system. Among them JavaScript, the memory allocated by browsers, especially mobile browsers, is not too much, and how to save memory has become a very meaningful thing.

Flyweight mode has the following roles:

  • Client: The class used to call the Flyweight Factory to obtain internal data, usually an object required by the application,
  • Flyweight Factory: Class used to maintain Flyweight data
  • Flyweight class: a class that maintains internal data

1. Simple example

In the following example, we create a "Book" class to deal with specific books, and then create a " BookFactory" class to control how to create these Book objects.

In order to obtain better memory performance, if the same object is instantiated twice, these objects will be reused.

class Book {
  constructor(title, isbn, author, ratings) {
    this.title = title;
    this.isbn = isbn; = 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 object
      return book;
    } else {
      const newBook ​​= new Book(title, isbn, author, ratings);
      return newBook;

  getBookBy(attr) {
    return this._books.find(book => book.attr === attr);

2. Realization of online form ideas

Open the Google online form, extract and print its node elements.

You can see that even if you scroll to a thousand rows, they only share two views.

The Flyweight mode is used to prevent infinite scrolling from causing lag.

The following is the simulation implementation:

First is HTML

<section id="app">
  <table id="table"></table>
  <div class="controls">
    <input type="range" name="scroll" id="scroll" value="0">


#app {
  position: relative;
  padding: 30px 0 30px 10px;

  #table {
    padding: 20px;
    border-radius: 10px;
    min-width: 450px;
    transition: background 0.5s;
    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;

Logic realization, please eat with notes:

//Generate cell instance
const makeRowCells = data => => new Cell(value));

//define constant
const scrollViewport = 10;//current table view size
const tableSize = 2000;//number of rows
let scrollIndex = 0;//Initial scroll index

let DATA = [];//initial data set
while (DATA.length <scrollViewport) {
  const unit = DATA.length * 10;
  DATA.push('12345678'.split('').map(() => unit));

* Cell class-column
class Cell {
  constructor(content) {
    this.content = content;
 //update column
  updateContent(content) {
    this.content = content;
    this.cell.innerText = content;

 //Render column
  render() {
    const cell = document.createElement('td');
    this.cell = cell;
    cell.innerText = this.content;

    return cell;


* row class-row
class Row {
  constructor(cellItems) {
    this.cellItems = cellItems;
 //update row
  updateRowData(newData) {
    this.cellItems.forEach((item, idx) => {

 //render line
  render() {
    const row = document.createElement('tr');
    this.cellItems.forEach(item => row.appendChild(item.render()));

    return row;

* Forms
class Table {
  constructor(selector) {
    this.$table = document.querySelector(selector);
 //add row
  addRows(rows) {
    this.rows = rows;
    this.rows.forEach(row => this.$table.appendChild(row.render()));

 //Update table data
  updateTableData(data) {
    this.rows.forEach((row, idx) => row.updateRowData(data[idx]));

//instantiate a new table
const table = new Table('#table');
//Match the DOM of the scroll bar
const scrollControl = document.querySelector('#scroll');
//Add a cell row under the table
table.addRows( => new Row(makeRowCells(dataItem))));

const onScrollChange = event => {
 //Prepare new data for the view
  DATA =, idx) => => parseInt(, 10)*10 + idx*10));
 //Update the data of the current table
 //Add color difference style
  scrollIndex =;
  if ( >= 0) {
    table.$table.classList ='low-range';
  if (> tableSize * 0.4) {
    table.$table.classList ='mid-range';
  if (> tableSize * 0.7) {
    table.$table.classList ='high-range';
  if (> tableSize * 0.9) {
    table.$table.classList ='ultra-high-range';
//Set the minimum and maximum range of the scroll bar
scrollControl.setAttribute('min', 0);
scrollControl.setAttribute('max', tableSize);
//Add scroll event
scrollControl.addEventListener('input', onScrollChange);

//Initialization event
const event = {target: {value: 0}};

9. Conclusion and references

At this point, the structural design pattern has been covered (water), and the Flyweight model is worthy of writing a separate blog.

Reference article

  • JavaScript design patterns in detail
  • Javascript design pattern theory and actual combat: Flyweight model
  • Easy patterns: Flyweight
  • Composite design pattern
  • Javascript design pattern theory and actual combat: combination mode
  • yck-"The Way of Front-end Interview"

❤️ After watching three things

If you think this content is quite inspiring for you, I would like to invite you to do three small favors for me:

  1. Like it, so that more people can see this content (If you don’t like it, it’s a hooligan-_-)
  2. Pay attention to the public account "Front-end adviser" and share original knowledge from time to time.
  3. Also look at other articles
  • Those design patterns you inadvertently use (1)-creational patterns
  • "The King of Data Visualization Library" D3.js gets started with Vue application quickly
  • "True® Full Stack Road" Back-end guide for web front-end development
  • "Vue Practice" A Vue CLI plug-in in 5 minutes
  • "Vue practice" arm your front-end project
  • "Intermediate and advanced front-end interview" JavaScript handwritten code invincible cheats
  • ``Learn from the source code'' Vue question answers that interviewers don't know
  • "Learn from the source code" JS Sao operation in Vue source code
  • The correct posture of the "Vue practice" project to upgrade vue-cli3
  • Why can't you understand the JavaScript scope chain?
Reference: The final stubbornness of the cut figure: the design pattern of teaching but not knowing-structural-cloud + community-Tencent