Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(invalid) How to use the "this" keyword in JSON

Tags:

javascript

How to use the "this" keyword in the JavaScript object notation (JSON) in the example below?

{
    firstName: "Foo",
    lastName: "Bar",
    fullName: function () {
        return this.firstName + " " + this.lastName;
    }
}

The code above neither work with the this keyword nor without it. Why is that? The only working solution is to assign the JSON object to a variable and to use the variable in the code.

Is there something similar to the "this" keyword that can be used instead of it?

Any suggestions are welcome.

edit: As stated in the comments below, it's not JSON but "javascript object literal". Thank you for that hint. Nevertheless, my question still remains valid (for the javascript object literal).

invalid: The code above works as expected, thus fullName() returns "Foo Bar".

like image 258
kalamar Avatar asked Oct 29 '13 13:10

kalamar


People also ask

What are invalid characters in JSON?

Unicode codepoints U+D800 to U+DFFF must be avoided: they are invalid in Unicode because they are reserved for UTF-16 surrogate pairs. Some JSON encoders/decoders will replace them with U+FFFD.

What is an invalid JSON value?

It means that the editor failed to get a response to the server or the response wasn't in a valid JSON format. Basically, if the editor can't communicate with the server, it will show this error message instead. To fix the problem, you essentially need to fix whatever is getting in the way of the communication.

What does '@' mean in JSON?

It has no special meaning to JSON. This particular data uses it as a key string.

What is a valid JSON key?

JSON Keys must be Valid Strings According to JSON.org, a string is a sequence of zero or more Unicode characters, wrapped in double quotes, using backslash escapes. Any valid string can be used as a JSON key. These keys must be enclosed in the double-quotes ( " ).


1 Answers

If I understand it, this actually may not be an invalid question (just a poorly asked one, with some mistakes), and is really potentially a little interesting. The JSON misnomer was an unfortunate distracting mistake, but really, he's asking how to dynamically have a property be equal to a dynamic result of other properties--i.e., why doesn't

x = {
  a: "hello",
  b: " ",
  c: "world",
  d: this.a + this.b + this.c
}

result in

x.d === "hello world"

?

Instead, what you find is that this refers to window there. This shows us that this is only something gotten from inside a function, and not an intrinsic relational reference we can rely on outside of that context.

This is clearly why the asker then used a function, to get access to this. However, I think the problem is then you get a function you have to call, where he actually wants text. (I might be extrapolating a bit there, not sure.)

One might be tempted to just use an IIFE, but that won't work either:

x = {
  a: "hello",
  b: " ",
  c: "world",
  d: (function() { this.a + this.b + this.c })()
}

x.d === NaN  // undefined + undefined + undefined

What happens is that when when we call an IIFE, we create a new scope, with a new this--also, it would only be run once, on object initialization, and wouldn't dynamically reflect those other value changes.

Because he said 'we have to assign the object to a variable and then use that', I assumed he had found a workaround wherein you just declare it twice, and on the second time it works if you're referring to properties on a global (so, x.a + x.b), and provided the wrong example.

All of this goes to show that lexical scoping overtakes dynamic scoping here. Another naive way one might consider trying to work around this would be like this:

x = {
  a: "hello",
  b: " ",
  c: "world",
  d: (function() { this.x.a + this.x.b + this.x.c })()
  // or maybe:
  e: (function() { this.x.a + this.x.b + this.x.c }).call(this)
}

(Explanation: since this refers to window and x is a global, and therefore property of window (i.e., window.x === x)... honestly we could also just omit this here, but it helps illustrate what's going on here so I'll leave it.)

But no, that function is called while x is being initialized, and so x isn't yet defined in lexical scope until after that function call. or e, 'this' is still window.

What we learn here is that there is no way to access sibling properties at evaluation time of the object within Javascript, either.

Neat!

As a result, to achieve the desired goal, two separate statements are needed:

x = {
  a: "hello",
  b: " ",
  c: "world"
}

x.d = x.a + x.b + x.c;

unless you desired the ability to have x.d dynamically respond with those values, instead of be the result of adding those values at this moment--then you'd just use the original code in the question, and have to evaluate it as a function.

... ...

As soon as I had finished writing the above, I remembered a javascript feature I've never used: getters and setters. Sure enough, they accomplish exactly what we're looking for here:

var hello = {
  a: "hello",
  b: "world",
  get test() {
    return this.a + this.b
  }
}
// undefined
hello.test
// "helloworld"
hello.a = 'stuff'
// "stuff"
hello.test
// "stuffworld"

Of course, this is computationally equivalent to calling hello.test() and just making test a function, as in the original code. This getter can't be serialized just like a function can't, as far as JSON goes (though we established that isn't the point here, I just want to be clear). In the end, the only thing we really gained was the ability to omit parenthesis--and this won't serialize over JSON, to be clear, if that really was really an intentional part of the question.

like image 83
Kyle Baker Avatar answered Oct 27 '22 20:10

Kyle Baker