0

I have an object of colors as strings - a subset of those colors contains another object called gradients which is an object of functions. Within those functions - how do I reference the other colors that are siblings of gradient?

  • test0 is { ...cyan400,...}
  • test1 is {}
  • this within the function is undefined

Can we get an explanation for the reason behind the values of this's in each case?


{
    ...
    cyan400: "#a6fcff",
    cyan200: "#d9feff",

    test0: this,

    gradients: {

        test1: this,

        g1: (angle = "0deg") => oneLineTrim`
            linear-gradient(
                ${angle},
                ${this.magenta600} 0%,
                ${this.purple800} 50%,
                ${this.indigo800} 100%
            )
        `,
        ...
    }
}
jchi2241
  • 2,032
  • 1
  • 25
  • 49
  • The top-level object is always referenced with `this` if `this` is not called inside a non-arrow function. You have to bind colors object to some variable and then reference it by the variable name. – DedaDev Jul 21 '19 at 02:21
  • @Deda - In this case, it appears the OP wants a reference to the object that contains the property `cyan400` which is not something you can get to with `this`. – jfriend00 Jul 21 '19 at 03:02

1 Answers1

2

In short, when you have objects nested inside of others, there is no such a thing as a "parent" object in Javascript. The language won't help you get a reference to the parent. You will have to either reference the top level object by name directly yourself or manually put a reference to the top level object in the lower level object as a property.

When I define your object and assign it to a variable data and run your code and look at the values of data.test0 and data.gradients.test1 and value of this inside of the data.gradients.g1() function, they all have exactly the same value for me which is the lexical value of this in the scope where the object was defined. That's how it should work. this refers to the lexical scope where the objects were declared, it does not refer to the object itself. Inside a non-fat arrow method (method declared with a regular function), this will be determined by how the method was called.

Some people get confused on this because when you call a regular method (not defined with a fat-arrow), the value of this inside that method will often be the object that the method is part of. This is because of how that value of this is set when a method is called. You can see the six ways the value of this is set in a regular method call in this answer: The six ways the value of this is controlled or determined.

There is no mechanism in the Javascript language to reference a "parent" object automatically. Objects in Javascript are not actually "contained" within one another. When you do a nested definition like this, the inner objects are created on their own and garbage collected on their own and the outer object just contains a reference to them. It's quite possible that other variables elsewhere in your program can contain the same reference to those nested objects, included inside other object's properties. So, there is no actual "parent" object in Javascript.

So, let's look at some runnable code, related to what you posted:

this.greeting = "hello";

let data = {
    cyan400: "#a6fcff",
    cyan200: "#d9feff",
    test0: this,
    gradients: {
        test1: this,
        g1: (angle = "0deg") => {
            console.log(this);
        }
    }
}

console.log(data.test0);
console.log(data.gradients.test1);
data.gradients.g1();

When I run this in node.js these last three statements all show the exact same output:

{ greeting: "hello" }

Because all three values of this are the same, which is as expected.

If I then, add a regular method to the gradients object and call it:

this.greeting = "hello";

let data = {
    cyan400: "#a6fcff",
    cyan200: "#d9feff",
    test0: this,
    gradients: {
        test1: this,
        g1: (angle = "0deg") => {
            console.log(this);
        },
        g2: function() {
            console.log(this);
        }
    }
}

data.gradients.g2();

Then, the value of this in g2 will be the gradients object. That's because it was called with obj.method() syntax and it was declared as a regular function (not a fat-arrow function) so the value of this for obj.method() is set to obj which in this case is the gradients object.


If you're trying to reference properties in a parent object, there is no automatic way to reference that in Javascript using this or any other mechanism. You will have to either place a reference to the top level object inside of gradients like this:

let data = {
    cyan400: "#a6fcff",
    cyan200: "#d9feff",
    test0: this,
    gradients: {
        test1: this,
        g1: (angle = "0deg") => {
            // use property manually set on this object
            // to get to the object that has the colors
            console.log(this.colors.cyan400);
        }
    }
}

// set reference to parent object that contains the colors
// can't be done inside the static declaration, has to be done afterwards
data.gradients.colors = data;

Or, reference the top level name of the object itself and get to the colors that way:

let data = {
    cyan400: "#a6fcff",
    cyan200: "#d9feff",
    test0: this,
    gradients: {
        test1: this,
        g1: (angle = "0deg") => {
            // reference top level object directly
            console.log(data.cyan400);
        }
    }
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979