Explain Codes LogoExplain Codes Logo

How to "properly" create a custom object in JavaScript?

javascript
object-oriented-programming
es6-classes
prototypes
Alex KataevbyAlex Kataev·Oct 19, 2024
TLDR

Create a custom object in JavaScript utilizing the class syntax which integrates a constructor and methods:

class CustomObject { constructor(value) { this.property = value; // Going Class-y! } // Method tied to the prototype method() { return this.property; // Returns stuff - how property-ate! } } // Let's create an instance with 'new' const myObject = new CustomObject('example'); console.log(myObject.method()); // Outputs: 'example'

This example utilizes a constructor for initialization and capitalizes on prototypal inheritance for sharing methods among instances.

Intermediate: Exploring various ways of object construction

The ES6 class syntax and extends for inheritance

With ES6 classes, you can create objects in a structured, easy-to-read way with inheritance:

class ParentObject { constructor(name) { this.name = name; // Name is set, start the game! } greet() { return `Hello, my name is ${this.name}`; // Nice to meet you too! } } class ChildObject extends ParentObject { constructor(name, age) { super(name); // Look, I'm inheriting things! this.age = age; // Age ain't nothing but a number! } introduce() { return `${super.greet()}. I'm ${this.age} years old.`; // Reusing parents' stuff like a pro! } } const child = new ChildObject('Alice', 5); console.log(child.introduce()); // Output: 'Hello, my name is Alice. I'm 5 years old.'

Deploying prototypes to share attributes

Prototypes help sharing properties and methods across instances:

function Animal(sound) { this.sound = sound; // Got something to say? } Animal.prototype.speak = function() { return this.sound; // Hear me roar... or squeak 😅 }; const dog = new Animal('bark'); console.log(dog.speak()); // Output: 'bark'

Utilizing closures for data encapsulation

Closures can be used to protect or encapsulate data:

const createCounter = () => { let count = 0; // Shh! I'm hiding a secret number here. return { increment() { count += 1; // Caution: number inflating! }, getCount() { return count; // Yes, I'm a snitch 🙊 }, }; };

Defining static methods and properties

Static methods and properties belong to the constructor rather than the instances:

class Tool { static count = 0; // Cozy static space for a static count. constructor(name) { this.name = name; // Name it to claim it! Tool.count++; // Up goes the count. } } const hammer = new Tool('Hammer'); // Let's build this! 🛠️ console.log(Tool.count); // Output: 1

Advanced: Secrecy, paradigms, and potential pitfalls

Private fields: ES6 classes' best-kept secret

ES6 brings private fields to JavaScript classes:

class MyClass { #privateField; // Private property: no trespassing! constructor(value) { this.#privateField = value; // Locking the value in a secret vault } getPrivateField() { return this.#privateField; // Retrieving the secret } }

Mixing paradigms for robustness

Combining different object creation techniques can result in a powerful, flexible object model:

class MixinBuilder { constructor(superclass) { this.superclass = superclass; } with(...mixins) { return mixins.reduce((c, mixin) => mixin(c), this.superclass); // Yes, I'm mixin' it up! } }

Factories, pitfalls, and the art of composition

Factory functions: mass production of objects

Factory functions provide an alternative to the new keyword:

const createWidget = (type) => ({ type, render() { console.log(`Rendering a ${type} widget.`); // Hey, look what I can do! } });

Avoiding pitfalls of this

Misunderstanding this within methods can lead to bugs. Arrow functions can help:

class Button { constructor(label) { this.label = label; this.click = () => { console.log(`${this.label} button clicked`); // Click bait! }; } }

Choosing dynamic composition over static inheritance

Object composition provides a more flexible approach to combining behaviors:

const canSpeak = (state) => ({ speak: () => console.log(state.sound), // Let your inner voice out! }); const dog = Object.assign({}, canSpeak({sound: 'bark'})); dog.speak(); // Output: 'bark'