DEV Community

Cover image for JavaScript: Linked Objects
Bola Adebesin
Bola Adebesin

Posted on

JavaScript: Linked Objects

Recently, I've been working to understand JavaScript better with the goal of evolving how I write code, communicate ideas, and build stuff.

I've often heard something to the effect of, "JavaScript has classes, but it is not a class-based language". Along with the implication that, the use of classes "bends" the language rather than accepting it for what it is.

This felt like information that could be true, but that I didn't understand because...well JavaScript has classes. And it was my first language. I didn't come up learning Java or even complete a traditional computer science program at a four year institution.

Eventually, I was exposed to more information about JavaScript's Object.prototype. This great, ancestral object sits at the top of the prototype chain passing on properties and characteristics to other objects. Things started to become a little clearer, but also still kind of confusing. Prototypal inheritance sounded cool, but it felt like a distinction without a difference.

Then, I watched this workshop by Kyle Simpson the author of the You Don't Know JavaScript series. He demonstrated a way of linking objects together without using the new keyword or the syntactic sugar that is class.

I was intrigued. I wanted to try it. I also wanted to apply more of what I've been learning. So I wrote some logic for generating different types of tasks using the factory pattern and linked objects.

var TaskType = {
    TODO: "To Do", 
    IN_PROGRESS: "In Progress", 
    COMPLETED: "Completed"
}

var Task = {
    logTaskInfo(){
        console.log(`Title: ${this.title} | Details: ${this.details} | Status: ${this.status}`)
    }
}

function TaskFactory(){
    return createTask; 

    function createTask(type, title, details){
        var isInvalidType = !Object.values(TaskType).includes(type); 
        if(isInvalidType){
            throw new Error('Invalid Task Type')
        }
        return Object.assign(Object.create(Task), {
            title, 
            details, 
            status: type
        })
    }
}

var factory = TaskFactory(); 

var task1 = factory(TaskType.TODO, "Wash Dishes", "Load the dishwasher and scrub large pots and pans."); 
var task2 = factory(TaskType.IN_PROGRESS, "Write Code", "Apply newly acquired concepts like IFFE, Factory and OLOO"); 
var task3 = factory(TaskType.COMPLETED, "Walk Dog", "Take Soleil out for her morning walk."); 

task1.logTaskInfo(); 
task2.logTaskInfo(); 
task3.logTaskInfo(); 
Enter fullscreen mode Exit fullscreen mode

From TC39, Object.assign ( target, ...sources ) copies the values of all of the enumerable own properties from one or more source objects to a target object. And Object.create ( O, Properties ) creates a new object with a specified prototype.

So, with these lines of code:

Object.assign(Object.create(Task), {
            title, 
            details, 
            status: type
        })
Enter fullscreen mode Exit fullscreen mode

Object.Create(Task) creates a brand new object and links it to the existing Task object. And that brand new object is assigned a title, details, and a status.

Now, when task1 invokes logTaskDetails it works because logTaskDetails exists on the object that task1 is linked to.

Okay, "pretty cool", I thought. You could make the argument that this approach is more explicit than using something like the new keyword, but what's the big deal? But then things got really interesting.

This style becomes the foundation for behavior delegation or "dynamic composition". Where objects with different concerns are linked together and can share methods. It's less top-down and more peer-to-peer.

Here is an example Simpson presents with an AuthController and a LoginFormController:

var AuthController = {
    authenticate(){
        server.authenticate(
            [this.username, this.password], this.handleResponse.bind(this)
        )
    },
    handleResponse(resp){
        if(!resp.OK){
            this.displayError(resp.msg); 
        }
    }
}

var LoginFormController = Object.assign(
    Object.create(AuthController), 
    {
        onSubmit(){
            this.username = this.$username.val(); 
            this.password = $this.$password.val();
            this.authenticate() 
        }, 
        displayError(msg){
            alert(msg)
        }
    }
)
Enter fullscreen mode Exit fullscreen mode

I thought this was a really cool example, although I had to read through the code a few times to understand what was happening.

Here, the same way that task1 was linked to the Task object, LoginFormController is linked to AuthController. When an imaginary login form is submitted, LoginFormController's onSubmit method is called. Inside onSubmit the authenticate() method is called.

LoginFormController doesn't have an authenticate method, but the object it is linked to (AuthController) does. So, the method runs with this still referring to LoginFormController. Inside authenticate, this.username and this.password are still referring to values on LoginFormController and are passed to the server along with the callback handleResponse. Now, handleResponse is bound to LoginFormController with .bind as it is passed to the server. So whenever it is called, it will also be referring to LoginFormController even though that isn't where it was defined. When handleResponse is invoked, if the response was not okay, then it will call the display method. display has a this that references LoginController because that was the this handleResponse was bound to.

So, in summary, onSubmit which is defined in LoginController calls authenticate a method defined in AuthController. authenticate passes a callback, handleResponse, which is defined in Authcontroller, but bound to LoginController. And handleResponse calls display which is defined in LoginController.

This logic requires a strong understanding of this (no pun intended). And even though I think it's pretty cool, given this task, I would not have thought to structure the code this way. It's a very different way of thinking than I'm used to.

A classic case of, "the spirit is willing, but the flesh is weak". I'm going to keep working at it though.

In the meantime, please let me know what you think. Is this a style you have used or are interested in using? How do you think the code fares as far as readability?

Image by Alltechbuzz_net from Pixabay

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)

nextjs tutorial video

Youtube Tutorial Series ๐Ÿ“บ

So you built a Next.js app, but you need a clear view of the entire operation flow to be able to identify performance bottlenecks before you launch. But how do you get started? Get the essentials on tracing for Next.js from @nikolovlazar in this video series ๐Ÿ‘€

Watch the Youtube series

๐Ÿ‘‹ Kindness is contagious

Please leave a โค๏ธ or a friendly comment on this post if you found it helpful!

Okay