Exploring ES6 composition: A guide to defining custom getters

After writing some code, I'm facing an issue with creating an object using composition instead of object inheritance. While variables are working perfectly as expected, when it comes to methods (and possibly getters), the values returned remain the same even after changing the variables.

The provided code captures the scenario well. How can I make it function the way I intend?

const CELLSIZE = 16;

const defaultObject = (state) => ({
    posx: state.posx,
    posy: state.posy,
    firstx: () => {
        return state.posx * CELLSIZE //???
    } 
})

const wall = (posx, posy)  => {
    let setup = {
        //Additional non-inherited variables will be included here
    }
    let state = {
        posx,
        posy,
    }
    return Object.assign(
        {},
        defaultObject(state),
        setup
    )
}

const x1 = wall(2, 5)

console.log(x1.firstx()) // Returns 32

x1.posx = 1

console.log(x1.firstx()) // Still returns 32, hoping for a return of 16

Answer №1

When utilizing the method, it retrieves data from the object provided as state:

const defaultObject = (state) => ({
    posx: state.posx,
    posy: state.posy,
    firstx: () => {
        return state.posx * CELLSIZE // <== Take note that you're reading from `state`
    } 
})

However, it is essential to read data from the newly created object instead of the original one. Yet, due to the complexity involved in copying elements without inheritance but through functions like Object.assign, arrow functions cannot be used for the firstx function. Instead, a standard function should be utilized while ensuring that this points to the appropriate location when called:

const defaultObject = (state) => ({
    posx: state.posx,
    posy: state.posy,
    firstx: function() {
        return this.posx * CELLSIZE; // <== Observe the change in reference to `this`
    } 
})

For demonstration purposes, see the live example below:

const CELLSIZE = 16;

const defaultObject = (state) => ({
    posx: state.posx,
    posy: state.posy,
    firstx: function() {
        return this.posx * CELLSIZE; // <== Note reading from `this`
    } 
})

const wall = (posx, posy)  => {
    let setup = {
        //Later there will be some not-inherited variables 
    }
    let state = {
        posx,
        posy,
    }
    return Object.assign(
        {},
        defaultObject(state),
        setup
    )
}

const x1 = wall(2, 5)

console.log(x1.firstx()) // Returns 32

x1.posx = 1

console.log(x1.firstx()) // Returns 16

The choice between using function notation or method notation for defining firstx doesn't have much impact unless super is employed within firstx.


If your preference is for a getter, an alternative approach could involve creating a getter like so:

const defaultObject = (state) => ({
    posx: state.posx,
    posy: state.posy,
    get firstx() {
        return this.posx * CELLSIZE; // <== Notice the shift towards reading from `this`
    } 
})

However, when making use of Object.assign, the value rather than the property descriptor linked to the getter is assigned to the new object. If needed, the property descriptor can be copied over after the assignment process as indicated by the '***' comments in the code snippet above.

In cases where flexibility in how wall manipulates objects is preferred over direct augmentation, altering the approach recommended by Oriol in this solution might present a simpler solution.

Answer №2

defaultObject is limited to accessing only the state. It creates an object with a method called firstx, which utilizes data from the state.

However, in the wall function, you obtain that object, duplicate its properties, and discard it.

When you make modifications to the property at the end, these changes are not visible to defaultObject because the original state remains unchanged.

It would be better for wall to return the same object as returned by defaultObject (without duplicating it), and for the methods of defaultObject to fetch properties directly from the object it returns.

const CELLSIZE = 16;

const defaultObject = (state) => {
  let obj = {
    posx: state.posx,
    posy: state.posy,
    firstx: () => {
      return obj.posx * CELLSIZE
    }
  };
  return obj;
};

const wall = (posx, posy)  => {
  let setup = {};
  let state = {posx, posy};
  return Object.assign(
    defaultObject(state),
    setup
  );
}

const x1 = wall(2, 5)
console.log(x1.firstx()) // Returns 32

x1.posx = 1
console.log(x1.firstx()) // Returns 16

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Transitioning from the older version of meteor 1.8.3 to the latest version 2.6

I'm working with an older project that is currently using version 1.8.3 of a software that supports MongoDB 5 in Meteor 2.6. I have reviewed the changelog and migration guide and now I have a question: Should I upgrade directly from 1.8.3 to 2.6, or i ...

Guide to adding customized CSS and JavaScript to a specific CMS page on Magento

I'm trying to incorporate a lightbox for video playback on a specific page of my CMS. I've placed my CSS file in JS/MY THEME/JQUERY/PLUGIN/VENOBOX/CSS/myfile.css and the JS files in JS/MY THEME/jquery/plugins/venobox/js/myfile.js, but it doesn&ap ...

Using xpath to select and interact with all buttons on a webpage

When implementing my code on a webpage containing rows of records with drop-downs that display when clicked, I noticed that for some reason, it only initiates a click action on the first row of the page. How can I modify my code to ensure that every reco ...

Employ the parameter within the main object

I currently have this code snippet in my function: add(messenger) { switch (messenger) { case 'skype': this.messengers = _.assign(this.messengers, {skype: ''}) break case 'telegram': this.messenge ...

Dynamic AJAX Dependent Dropdown Menu

Can you help me create a dynamic input form? I need assistance in creating an input form with a dynamic dropdown list, similar to the screenshot provided below: https://i.stack.imgur.com/rFSqV.png What is my current script setup? The script I have is d ...

What is the best way to run multiple tasks in Apify?

I am looking to extract Instagram post comments using the Instagram Comment Scraper. However, Instagram has a comment limit of 24, which means only 24 comments can be scraped in one run. To overcome this limitation, I need to execute multiple runs. For i ...

Does a Function Component become an object instance of itself when rendered with a tag?

Suppose I declare a constant variable num = 1 inside a function component. Would it be possible for me to reference this variable in the return statement as this.num? Is it permissible for function components to contain static variables of that nature? ...

When utilizing xml-to-json, make sure that the content of "Inner XML/XHTML" is placed within a json string value field

I am currently utilizing Saxon Home Edition for the purpose of converting XML to JSON: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" enco ...

Array of arrays implemented in JavaScript

I'm working with a JavaScript array that contains string arrays: array1, array2, array3. I need to break this array down and access the individual arrays. What is the best way to achieve this? ...

Connect with Match on JSON using JQ and join forces to achieve your

I am trying to merge data from two output files that have repeating entries with specific structures. Here is an example of the content: file1.json { "value": [ { "description": "foo bar", "id&qu ...

Can you explain the functionality of this Observable in the context of this Angular 2 example?

I'm not too familiar with JavaScript/TypeScript and I have a question about how this code snippet works: onGet() { this.serverService.getServers() .subscribe( (servers: any[]) => this.servers = servers, // an array of anythin ...

Vue.js Ready event is not firing

In my Vue function, I have two methods. The first method, postStatus, is used to save a post when the user clicks on the save button. The second method, getPosts, is used to retrieve all previous posts from the database for that user. Below is the Vue.js ...

What steps are involved in extracting post data from the javascript DOM using php?

Having an issue where my JavaScript sends data to PHP, but the parsing in PHP keeps failing. The data is received by PHP and displayed in a text area, however, it needs proper formatting before being parsed. Can anyone advise on how to correctly format the ...

MongoDB table collections (table names in other databases)

After setting up my express server to connect to mongodb, I encountered an issue despite everything working fine initially. I created a collection in my mongodb called projects (plural form). In my project.model.js file, I defined the model as follows: c ...

Tips for triggering a sound only when transitioning from a true to false value for the first time

I have data for individuals that includes a dynamically changing boolean value. This value can be true or false, and it updates automatically. The webpage fetches the data every 5 seconds and displays it. If the value for any person is false, a sound is p ...

The data retrieved in JSON format appears to be corrupted and

I've been experimenting with an API that retrieves data from my database in JSON format. In phpMyAdmin, the sentences appear perfect when I use utf8_general to input a CSV file. For example: A line that includes 'r and special characters etc ...

Surprising outcomes encountered while utilizing jq for selecting objects

When I include the body in the output list, some incorrect names are displayed. I was expecting it to only show the names for the NFL subreddit in both examples. Is this a feature or a bug? How can I ensure that only the tuples for the subreddit nfl are ou ...

Strategies for redirecting search queries when adding a new path

Issue I am facing a challenge with pushing a new path to the URI while maintaining existing search queries. For example: Current URL: https://example.com/foo?bar=123&foobar=123 When I use history.push('newPath'), I end up with https://exa ...

Mastering the ins and outs of ngStorage in AngularJS

Imagine a web page featuring a form that allows users to input multiple entries before submitting. As each entry is added, a summary of the data entered is displayed in a table at the top. Once all entries are completed, the user can then proceed to submit ...

Having trouble retrieving data when updating a user in ExpressJS

Trying to update an experience array in the User model with new data, but facing issues with saving the data in the exec function. As a result, unable to push the new data to the array on the frontend. Here is the current code snippet: router.post('/ ...