OOP refactoring technique I'm teaching myself Object Oriented Programming in JavaScript and I'm looking over this small P5 code of CodingTrain's No.78, which deals with flying particles in the canvas, as a material.
The full code is below:
// Daniel Shiffman
// http://codingtra.in

// Simple Particle System
// https://youtu.be/UcdigVaIYAk

const particles = [];

function setup() {
  createCanvas(600, 400);
}

function draw() {
  background(0);
  for (let i = 0; i &lt; 5; i++) {
    let p = new Particle();
    particles.push(p);
  }
  for (let i = particles.length - 1; i &gt;= 0; i--) {
    particles[i].update();
    particles[i].show();
    if (particles[i].finished()) {
      // remove this particle
      particles.splice(i, 1);
    }
  }
}

class Particle {

  constructor() {
    this.x = 300;
    this.y = 380;
    this.vx = random(-1, 1);
    this.vy = random(-5, -1);
    this.alpha = 255;
  }

  finished() {
    return this.alpha &lt; 0;
  }

  update() {
    this.x += this.vx;
    this.y += this.vy;
    this.alpha -= 5;
  }

  show() {
    noStroke();
    //stroke(255);
    fill(255, this.alpha);
    ellipse(this.x, this.y, 16);
  }

}

Wanting to train my OOP skill, I'm trying to refactor this code into more sophisticated one in the OOP point of view. So I refactored it by adding Particles_Manipulation class and moving the process written in draw function into the Particles_Manipulation class as action method.
The code is below:
// Daniel Shiffman
// http://codingtra.in

// Simple Particle System
// https://youtu.be/UcdigVaIYAk

class Particle {

  constructor() {
    this.x = 300;
    this.y = 380;
    this.vx = random(-1, 1);
    this.vy = random(-5, -1);
    this.alpha = 255;
  }

  finished() {
    return this.alpha &lt; 0;
  }

  update() {
    this.x += this.vx;
    this.y += this.vy;
    this.alpha -= 5;
  }

  show() {
    noStroke();
    //stroke(255);
    fill(255, this.alpha);
    ellipse(this.x, this.y, 16);
  }

}

class Particles_Manipulation{
  constructor(){
    this.particles = [];
  }
  
  push_particles(_n){
    for (let i = 0; i &lt; _n; i++) {
      let p = new Particle();
      this.particles.push(p);
    }
  }
  
  action(){
    for (let i = this.particles.length - 1; i &gt;= 0; i--) {
      this.particles[i].update();
      this.particles[i].show();
      if (this.particles[i].finished()) {
        // remove this particle
        this.particles.splice(i, 1);
      }
    }
  }
}

const my_Particles_Manipulation = new Particles_Manipulation();

function setup() {
  createCanvas(600, 400);
}

function draw() {
  background(0);
  my_Particles_Manipulation.push_particles(5);
  my_Particles_Manipulation.action();
}


Could you evaluate my refactoring is nice or not?
Magic numbers
Avoid magic numbers. Magic numbers are just numbers in your code that represent some abstract data value. The problem is that they are spreed within the code. To make changes to these values requires searching the code and often the same values are repeated making the task even more difficult.
Defining the magic numbers as named constants makes making changes very easy. See rewrite
Using native API's
P5 is great but using it to create such a simple rendering task is not worth the need to load a huge chunk of code to do what the native API will do quicker (No need for the P5's overhead)
Naming
There are a lot of problems with your naming style.
JS naming convention
The naming convention for JavaScript is to use lowerCamelCase for all names with the exception of objects created with the new token which uses UpperCamelCase (Static objects and object factories can also use UpperCamelCase). For constants you can use UPPER_SNAKE_CASE
_underscoredNames
Use underscore to avoid possible naming classes in systems where you have no control of names.
Using underscore should only ever be a very last option and should never be used if there is no need. Eg the argument _n in push_particles(_n) { should be pushParticles(n)
Avoid redundancy
It is easy to add too much to a name. Keep names short and use as few words as possible. Names are never created in isolation and like a natural language requires context to have meaning.
Eg

Particles_Manipulation is overkill when Particles is all that is needed

my_Particles_Manipulation Never use my (unless its instructional code) This name can just be particles, or smoke

push_particles (don't repeat names) What does this function do? &quot;Adds particles&quot;. push becomes add. What does it add particles to? &quot;The Particles object&quot;, thus particlescan be inferred. The nameaddis all that is needed. If you were adding monkeys then maybeaddMonkeys`


Avoid repeated long references
Avoid doing the same long reference path and array indexing by store the object reference in a variable.
      this.particles[i].update();
      this.particles[i].show();
      if (this.particles[i].finished()) {
        // remove this particle
        this.particles.splice(i, 1);
      }

Becomes
      const p = this.particles[i];
      p.update();
      p.show();
      if (p.finished()) {        
          this.particles.splice(i, 1);
      }

Keep an eye on Logic
The render system reduces the particle alpha each step. You use the alpha value to check when to remove particles alpha &lt; 0. However you reduce the alpha, then render, then remove. That means than once going every frame you are trying to render 5 invisible particles. Remove befor rendering.
JS and Memory
Javascript is a managed language. Meaning that it manages memory for you.
This is great in most cases but it comes with a serious performance hit in applications like particle systems where many small independent objects are frequently being created and destroyed .
Object pool
To avoid the memory management overhead we use object pools.
An object pool is just an array of objects that are not being used. When an object is no longer needed we remove it from the main array and put it in the pool. When a new object is needed we check the pool, if there are any object in the pool we use it rather than create a new one.
This means that the particle object will need a function that resets it.
See rewrite
Note you can also pool the particles in the one array by using a bubble iterator to avoid the expensive @MDN.JS.OBJ.Array.slice@. Dead particle bubble to one end of the array. This is even faster but makes the inner update loop a lot more complex. You would only do this if you where using 10K+ particles.
JavaScript OO design
Many believe that using class is how to write good OO JavaScript. Sorry to say it is not and is in fact the worst way to write OO JavaScript. The class syntax does not provide a good encapsulation model, its only plus is easy inheritance, which is kind of redundant in lieu of JavaScript's lose polymorphism
In this case factory functions (a function that creates Object AKA Object factory) is best suited. Because we use an object pool when need not even bother with assigning a prototytpe for particle functions.
Some advantages of factory functions

This eliminates the need to use this and new.

Lets you define private properties without the need to #hack names

Lets you create objects before the first execution pass has completed (move the init and setups to the top where it belongs)


Some disadvantages of factory functions

Requires a good understanding of closure and Scope both of which are a difficult subjects to get a good understanding of.

Does not work well with object prototypes.


Rewrite
The rewrite uses the CanvasRenderingContext2D API to avoid the overhead of P5, Some replacement functions are at the bottom.
Note that when defining constants it is always a good idea to include unit types, ranges, and or a description  as a comment


animate();
;(() =&gt; {
    const CANVAS_WIDTH = 600;       // in CSS pixels
    const CANVAS_HEIGHT = 400;
    const PARTICLE_SPREAD = 1;      // max x speed in canvas pixels
    const PARTICLE_POWER = 3;       // max y speed in canvas  pixels
    const PARTICLE_RADIUS = 12;     // max y speed in canvas pixels
    const MAX_PARTICLES = 1000;     // Approx max particle count
    const PARTICLES_RATE = 3;       // new particle per frame
    const SMOKE_PARTICLES_PATH = new Path2D;  // circle used to render particle
    const PARTICLE_COLOR = "#FFF";  // any valid CSS color value
    const START_ALPHA = 1;          // particle start alpha max
    const START_ALPHA_MIN = 0.5;    // particle start alpha

    // Rate particle alpha decays to match MAX_PARTICLES. The life time in seconds 
    // can be calculates as  Math.ceil(1 / ALPHA_DECAY_SPEED) * 60
    const ALPHA_DECAY_SPEED = 1 / (MAX_PARTICLES / PARTICLES_RATE);    

    SMOKE_PARTICLES_PATH.arc(0, 0, PARTICLE_RADIUS, 0, Math.PI * 2);
    const canvas = createCanvas(CANVAS_WIDTH, CANVAS_HEIGHT);
    const smoke = new Particles(canvas);
    animate.draw = function() {
        canvas.ctx.setTransform(1, 0, 0, 1, 0, 0);
        canvas.ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
        smoke.add(PARTICLES_RATE);
        smoke.update();
    }

    function Particle() { 
        var x, y, vx, vy, alpha;
        const PARTICLE = {
            init() {
                x = CANVAS_WIDTH / 2;
                y = CANVAS_HEIGHT;
                vx = random(-PARTICLE_SPREAD, PARTICLE_SPREAD);
                vy = random(-PARTICLE_POWER, -PARTICLE_POWER);
                alpha = random(START_ALPHA_MIN, START_ALPHA);
                return PARTICLE;
            },
            update() {
                x += vx;
                y += vy;
                alpha -= ALPHA_DECAY_SPEED;
                return alpha &lt; 0;
            },
            draw(ctx) {
                ctx.globalAlpha = alpha;
                ctx.setTransform(1, 0, 0, 1, x, y);
                ctx.fill(SMOKE_PARTICLES_PATH);
            },
        };
        return Object.freeze(PARTICLE.init());
    }
    function Particles(canvas) {
        const particles = [];
        const pool = [];
        const ctx = canvas.ctx;
        return Object.freeze({
            add(n) {
                while (n-- &gt; 0) {
                   pool.length ?
                        particles.push(pool.pop().init()) :
                        particles.push(Particle());
                }
            },
            update() {
                var i = 0;
                ctx.fillStyle = PARTICLE_COLOR;
                while (i &lt; particles.length) {
                    const p = particles[i];
                    p.update() ? 
                        pool.push(particles.splice(i--, 1)[0]) : 
                        p.draw(ctx);
                    i++;
                }
            }
        });
    }
})();


// functions to replace P5
function createCanvas(width, height) {
    const canvas = Object.assign(document.createElement("canvas"), {width, height, className: "renderCanvas"});
    document.body.appendChild(canvas);
    canvas.ctx = canvas.getContext("2d");
    return canvas;
}
function random(min, max) { return Math.random() * (max - min) + min }

function animate() {
    animate.draw?.();
    requestAnimationFrame(animate);
}
.renderCanvas {
    background: black;
}