Javascript is incorrectly parsing variable literally - javascript

I have a function that is used to edit the css of a particular element. The element is selected through JQuery, and stored values for css retrieved from a 2D array. However, when I am setting styles in the code, it takes prop literally as "prop", and not as its string value. In the debugger I see that prop does indeed have a valid string value, but this is not coming through when setting styles. I understand this is likely a syntax issue.
function foo(selector) {
var element = $(selector);
for (var prop in stylesOld[selector]) { //stylesOld is a 2D array using key / value pairs
if (stylesOld[selector].hasOwnProperty(prop)) {
// here lies the problem
var styles = { prop : stylesOld[selector][prop]};
element.css(styles);
}
}
}
I have tried using prop.toString() however this returns an error. Thanks in advance.

The Javascript object literal syntax only supports literal keys. If you want variable keys, you have to assign the values individually instead:
var styles = {};
styles[prop] = stylesOld[selector][prop];

Related

Why are arrays / braces used in object literal definitions to allow dynamic computing of keys in Javascript / ES2015?

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)

Why do an object key without [] will not be the accepted parameter?

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"

How to get the property name as a string?

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: []
}

assigning value in javascript to sub-object field using array notation

I have an intention to set a field value of an object like this
$scope[nameOfField]=value;
which works if nameOfField is just field name.
However, if I define in $scope object "subObject":
$scope.subObject={};
var nameOfField='subObject.someSubField';
$scope[nameOfField]=12345;
this does not work. Apparently I can not address directly sub-object fields like this. I however do need to use nameOfField approach with sub-object fields, and appreciate hints how to make it work. I can not predict if subObject will be featured in nameOfField - it can be both name field and subObject.someSubField.
EDIT: Difference with the question Accessing nested JavaScript objects with string key is that I not only need to access value of object but modify it.
Well your current code would result into
$scope['subObject.someSubField']=12345;
which would be a syntax error. Correct would be
$scope[nameOfField1][nameOfField2]=12345;
So you need a function to archieve this. Here an example (obviously this has to be extended to support more then just the 2 levels):
var scope = {};
function setValue(scopeString, val){
var match = /(\w+)\.(\w+)/.exec(scopeString);
if(!scope[match[1]]) //create if needed
scope[match[1]] = {};
scope[match[1]][match[2]] = val;
}
function getValue(scopeString){
var match = /(\w+)\.(\w+)/.exec(scopeString);
return scope[match[1]][match[2]];
}
setValue('lvl1.lvl2', 1);
console.log(getValue('lvl1.lvl2'));

Passing an array as parameter in JavaScript

I have an array, and I want to pass it as a parameter in a function such as:
function something(arrayP){
for(var i = 0; i < arrayP.length; i++){
alert(arrayP[i].value);
}
}
I'm getting that arrayP[0] is undefined, which might be true as inside the function I never wrote what kind of array arrayP is. So,
Is is possible to pass arrays as parameters?
If so, which are the requirements inside the function?
Just remove the .value, like this:
function(arrayP){
for(var i = 0; i < arrayP.length; i++){
alert(arrayP[i]); //no .value here
}
}
Sure you can pass an array, but to get the element at that position, use only arrayName[index], the .value would be getting the value property off an object at that position in the array - which for things like strings, numbers, etc doesn't exist. For example, "myString".value would also be undefined.
JavaScript is a dynamically typed language. This means that you never need to declare the type of a function argument (or any other variable). So, your code will work as long as arrayP is an array and contains elements with a value property.
It is possible to pass arrays to functions, and there are no special requirements for dealing with them. Are you sure that the array you are passing to to your function actually has an element at [0]?

Categories