My simple example:
let a = { foo : 5}, b = { stuff : 7};
let func = function(obj, prop) { ++obj[prop]; }
func(a, 'foo');
func(b, 'stuff');
When I call func I set the property name as string literal directly. If I want to rename the property late (in my code source) then I have to change the string literal too. Is it possible to get the property name as a string in runtime instead of using the string value as a literal?
UPD (for clarity)
In my project, I have some classes each of them has a property which contains an array. But this property has the different name for each class. I have a logic for handling these arrays content. This logic is the same for each class. Right now I pass the property name as a string literal, but if I later rename these properties in my code source then I must to change and the string literals too. If I forget to do it I will have a problem. So I want to get rid of the use of string literals in this task.
I think what you're trying to do is a bad idea. If something behaves the same in different objects, call it the same name. But hey, there's an ES6 concept for that!
Symbols are used by JavaScript to support similar behaviour in different "classes". So let's do that here.
Let's create a Symbol:
const incrementableProp = Symbol("incrementableProp")
First, let's store the name of the property in your object that should have the behaviour:
const a = {
specialProp: [],
[incrementableProp]: "specialProp"
}
The Symbol itself will always be the same, so the increment function can find it reliably:
function incrementProp(obj) {
if(incrementableProp in obj)
obj[obj[incrementableProp]]++
else throw new TypeError("This object does not support an incremental property.")
}
Lastly, let's make sure you need to change the name only once by removing the Symbol definition in the object. We'll use a decorator for that:
function special(target, key, descriptor) {
target[incrementableProp] = key
return descriptor
}
So now you can do this:
const a = {
#special specialProp: []
}
Related
I came across this example in a MDN doc, for example:
class Search1 {
constructor(value) {
this.value = value;
}
[Symbol.search](string) {
return string.indexOf(this.value);
}
}
If I pull up node, and run just the line included as part of the object literal, it doesn't work:
> Symbol.search
Symbol(Symbol.search)
> [Symbol.search]
[ Symbol(Symbol.search) ]
> [Symbol.search]('somthing')
TypeError: [Symbol.search] is not a function
I think I've also seen this syntax in a few other places, like e.g. in the react docs:
handleChange(event) {
this.setState({ [event.target.id]: event.target.value });
}
Is this just a use of destructuring syntax? It doesn't seem like it.
brackets are used when you have variable as key and not a plain string.
const obj = {
"someId": 'abc',
};
const e = {
target: {
id: "someId"
}
};
console.log(obj[e.target.id]);
Apart from above mentioned, it is also used to access the numeric keys (Just like array) and when key is computed. See - https://javascript.info/object#square-brackets
Turns out, that's just part of the spec.
It looks a bit like array de-structuring, but it's not.
In the case of [event.target.id], you're assigning the value that event.target.id points to be a key in the object passed to setState(). If you tried to do this without the brackets ([]), it would not work, not how you expect anyway.
In the case of [Symbol.search](string), here you're using the Symbol.search symbol (see symbols) as a key which is dynamically evaluated immediately to its actual, unique value. The dynamic evaluation is allowed because this value becomes the key in an object literal definition. The value which the key points to is a function being defined here, which takes string as its first and only parameter, and operates on that. This is a hook for allowing an object to define how it behaves when used as a parameter, in this case to the .search() function. See here.
Thanks for #randomSoul's answer, for completing it I might say that braces also make you to have a string key with spaces like below:
const myOBJ = {
'my key': 'my assigned String Value'
}
Then you can call that key value pair with this braces syntax like:
console.log(myOBJ['my key'])
This is rarely used in JavaScript, but the main purpose of using braces for getting the value from object literal is for getting dynamically computed keys of object. Like that you have an object that each key is represented user id, and you based on that you want to decide to get the specific user id that you got from your url params or somewhere else then you would be able to get user data like below:
console.log(lastFiveUserData[myUserId].age)
The code is a general toggle handler for a component state. I cant seems to figure out why the first set of code create a new key name property while the second set of code use the accepted parameter.
controlToggle = (property) => {
this.setState({property: !this.state.property})
}
controlToggle = (property) => {
this.setState({[property]: !this.state.property})
}
You make use of [] while setting or getting a dynamic object key. If you do not provide the key within [] it will use the variable name as the key within the object which in your case is property
So for instance
controlToggle = (property) => {
this.setState({property: !this.state.property})
}
The above code will set the state with key as property
While the correct way is
controlToggle = (property) => {
this.setState({[property]: !this.state[property]})
}
I think that's because in JavaScript, for a key-value pair like {XXX: YYY}, it will automatically treat the first XXX as the key/property name of the value, so you have to add the [] brackets to "escape" that pattern to use the variable's value as the key instead of treating it as a string essentially.
More specifically, the stuff inside the [] brackets will be treated as normal JavaScript code and be "calculated"
Can anyone explain objects and properties to me in Javascript in layman's terms? The Javascript MDN documentation is confusing me.
I am trying to solve a problem in a Javascript tutorial about objects and properties The question is below and my attempt at the code, I think I followed the MDN as far as I can grasp it. Any help would be appreciated!
Question: Add the value of the property argument as a key on the object argument.
The value of the new property should be set to null.
Return object after adding the new property.
Input Example:
{}, 'hello'
{ x: 5 }, 'y'
Output Example:
{ hello: null }
{ x: 5, y: null }
note: the property name is NOT 'property'. The name is the value of the argument called property (a string).
My code:
//NOTE: the function addProperty(object, property) was already in the console and I have to write the solution inside of it.
function addProperty(object, property) {
// code here
let result = addProperty({x: 5}, 'y');
obj[property] = null;
return obj;
}
addProperty(x, 'y');
Layman's explanation:
An object is a collection of properties. You can give a name to the object to keep things organized. For example, lets create a person object.
var person = {};
The object has no properties right now. To further describe the person we can add properties to the object.
person.Name = 'Zim';
person.Age = 29;
person.Gender = 'Male';
person.Weight = 80;
Now this object has some properties to help describe it. A different way to write the same thing:
var person = { Name: 'Zim', Age: 29, Gender: 'Male', Weight: 80 };
If we had to create a program that displays a list of people, storing all of our information inside objects would help keep things organized.
Object properties are sometimes referred to as keys.
Adding properties to objects:
You can add a property to an object using brackets, just like you had in your addProperty function. If you just need it to add a property, set that property to null and return the result it would look something like this:
function addProperty(object, property) {
// code here
object[property] = null;
return object;
}
This would let us create a properies on our object from above by calling
addProperty(person, 'Occupation');
addProperty(person, 'Income');
addProperty(person, 'Height');
I think you're over-thinking it.
First the question:
Add the value of the property argument as a key on the object
argument. The value of the new property should be set to null. Return
object after adding the new property.
emphasis added
So, property will be the KEY (of a key/value pair), and the VALUE will be null of object, which we are also passing in as an argument.
One way to interrogate key/value pairs on a javascript object is through the square-brackets []. So, if you have a key/value pair: { foo: "bar" }, you can get "bar" by: object['foo']. You can also create new key/value pairs like this, so your function can look like:
function addProperty(object, property) {
object[property] = null;
return object;
}
var obj = {};
obj = addProperty(obj, "hello");
console.log(obj);
console.log(addProperty({x: 5}, 'y'));
What our function is doing is taking the object passed into it (as an argument), creating a new KEY with our property argument, and setting its VALUE to null, and simply returning the object.
*Side note -
Be careful, the code you have posted will create an endless recursive loop, as you keep calling the same function with no way to break out of it.
This particular example is simple:
var one={};
var two={x:5};
function addProperty(object property){
obj[property]=null;
return obj;
}
addProperty(one, 'hello');
addProperty(two, 'y');
Objects in javascript are really flexible, their properties can be added or removed, even when set from the very beginning.
If you take:
var x={};
x will be an object with nothing in it, but if instead of that you write:
var x={
inner:'b'
};
x will be an object with a property called inner which value is 'b', now, if we want to access that property we could do something like this:
var valueOfInner=x.inner;
or
var valueOfInner=x['inner'];
The same if we want to change the value of that property:
x.inner=8;
or
x['inner']=8;
Now, you'll notice that when we use x.['inner'], we could very well use instead:
var propertyName='inner';
x[propertyName]=8;
So you can access and modify a property of an object without actually knowing exactly which property you're manipulating.
Finally, if you're (willing or by accident) trying to set the value of a property that doesn't exists, the property will be automatically be created, for example:
x['blah']=456;
Will create the property blah even when it wasn't defined at first.
Edit: yes, you can define the object and define its properties later:
var x={};
//more code or something
x['y']=777;//now x has a y property with the value 777
a property is a key:
let obj = {
name: 'Jordan',
age: 15
}
obj.name is a property, same goes for obj.age.
function me(a, b) {
function he(c, d) {
if(!a[c]) {
}
}
}
Please someone explain 'if(!a[c])' for me.
why this square bracket is used here in [c] though it is a parameter. it is not an array obviously.
what does if(!a[c]) make sense? how two parameters are used like this?
There is nothing special about that code.
It is saying, in English, If the property c of a is falsey, then the condition is true.
In JavaScript, bracket notation can be used to access properties of an object or members of an array.
For example, someArray[5] will access the 6th member of the array, while someObject['someProp'] will access someProp of the object someObject.
The argument a is likely an object. The syntax:
if (!a[c])
Checks to see if the property in the variable c on the a object does not have a truthy value. This could be if it was null, false, undefined or any other falsey value.
The bracket notation can be used with property names. So, if you have an object like this:
var x = { name: "John"};
Then, you can access that property like in any of these ways:
x.name
x["name"];
// Or, if the property name is in a variable
var prop = "name";
x[prop]
Your example is using a version of the last of the above three options when the property name is in another Javascript variable.
In javascript, properties can be accessed dynamically using the square-bracket syntax.
Consider the following:
var person = {name:'Sarah'};
console.log(person.name); // logs 'Sarah';
Sometimes, you might want to dynamically access properties of an object, using a variable that holds the name of the property you want to access. The above example could also be written like this:
var person = {name:'Sarah'};
var prop = 'name';
console.log(person[prop]); // logs 'Sarah';
I have been working with a fair bit of JSON parsing and passing in Javascript within Node.js and browsers recently and bumped into this conundrum.
Any objects I created using a constructor, cannot be fully serialized fully via JSON.stringify, UNLESS I initialised all values within the constructor individually! This means my prototype becomes essentially useless in designing these classes.
Can someone shed some light on why the following doesn't serialize as I expect?
var ClassA = function () { this.initialisedValue = "You can see me!" };
ClassA.prototype = { initialisedValue : "You can't see me!", uninitialisedValue : "You can't see me!" };
var a = new ClassA();
var a_string = JSON.stringify(a);
What happens:
a_string == { "initialisedValue" : "You can see me!" }
I would expect:
a_string == { "initialisedValue" : "You can see me!", "uninitialisedValue" : "You can't see me!" }
Update (01-10-2019):
Finally noticed #ncardeli 's Answer, which does allow us to do something like the following to achieve my above requirement (in 2019!):
Replace
var a_string = JSON.stringify(a);
with
var a_string = JSON.stringify(a, Object.keys(ClassA.prototype));
Full code:
var ClassA = function () { this.initialisedValue = "You can see me!" };
ClassA.prototype = { initialisedValue : "You can't see me!", uninitialisedValue : "You can't see me!" };
var a = new ClassA();
var a_string = JSON.stringify(a, Object.keys(ClassA.prototype));
console.log(a_string)
Simply because this is the way JSON works. From the ES5 spec:
Let K be an internal List of Strings consisting of the names of all the own properties of value whose [[Enumerable]] attribute is true.
This makes sense, because there is no mechanism in the JSON specification for preserving information that would be required to parse a JSON string back into a JavaScript object if inherited properties were included. In your example, how would this parsed:
{ "initialisedValue" : "You can see me!", "uninitialisedValue" : "You can't see me!" }
There is no information to parse it into anything other than a flat object with 2 key-value pairs.
And if you think about it, JSON is not intended to map directly to JavaScript objects. Other languages must be able to parse JSON strings into simple structures of name-value pairs. If JSON strings contained all the information necessary to serialize complete JavaScript scope chains, other languages may be less capable of parsing that into something useful. In the words of Douglas Crockford on json.org:
These [hash tables and arrays] are universal data structures. Virtually all modern programming languages support them in one form or another. It makes sense that a data format that is interchangeable with programming languages also be based on these structures.
I'd like to add that, even though JSON.stringify will only stringify the object's own properties, as explained in the accepted answer, you can alter the behavior of the stringification process by specifying an array of String as the second parameter of JSON.stringify (called a replacer array).
If you specify an array of String with the whitelist of properties to stringify, the stringification algorithm will change its behavior and it will consider properties in the prototype chain.
From ES5 spec:
If PropertyList is not undefined, then
a. Let K be PropertyList.
Else
a. Let K be an internal List of Strings consisting of the names of
all the own properties of value whose [[Enumerable]] attribute is
true. The ordering of the Strings should be the same as that used by
the Object.keys standard built-in function.
If you know the name of the properties of the object to stringify beforehand, you can do something like this:
var a_string = JSON.stringify(a, ["initialisedValue", "uninitialisedValue"]);
// a_string == { "initialisedValue" : "You can see me!", "uninitialisedValue" : "You can't see me!" }
There is another possibility to still have properties from the prototype to be stringified.
As of the JSON spec (15.12.3 stringify http://es5.github.io/#x15.12.3):
[...]
2. If Type(value) is Object, then
a. Let toJSON be the result of calling the [[Get]] internal method of value with argument "toJSON".
b. If IsCallable(toJSON) is true
i. Let value be the result of calling the [[Call]] internal method of toJSON passing value as the this value and with an argument list consisting of key.
[...]
So yu can write your own JSON stringifier function. A general implementation could be:
class Cat {
constructor(age) {
this.age = age;
}
get callSound() {
// This does not work as this getter-property is not enumerable
return "Meow";
}
toJSON() {
const jsonObj = {}
const self = this; // If you can use arrow functions just use 'this'-keyword.
// Object.keys will list all 'enumerable' properties
// First we look at all own properties of 'this'
Object.keys(this).forEach(function(k) {
jsonObj[k] = self[k];
});
// Then we look at all own properties of this's 'prototype'
Object.keys(Object.getPrototypeOf(this)).forEach(function(k) {
jsonObj[k] = self[k];
});
return JSON.stringify(jsonObj);
}
}
Object.defineProperty(Cat.prototype, 'callSound2', {
// The 'enumerable: true' is important!
value: "MeowMeow", enumerable: true
});
let aCat = new Cat(4);
console.log(JSON.stringify(aCat));
// prints "{\"age\":4,\"callSound2\":\"MeowMeow\"}"
This works as long as the right properties (those you actually want to be JSON stringified) are enumerable and those you dont want to be stringified aren't. So you need to be careful which properties you make enumerable or become implicitly enumerable when you assign values to 'this'.
Another possibility is to assign each property you actually want to be stringifed manually one by one. Which might be less error prone.