I have multiple Hashmap in my javavscript code and I'm trying to dynamically load the relevant map according to the name passed to the function.
The problem is when I pass the value as string value it actually tries to get the keys of the string rather than the object which it refers to.
This jsfiddle properly illustrates my problem.
Line 13 gives the expected output whereas Line 14 creates keys out of the string name.
Its basically the difference between:
Object.keys(PROP_ONE)
and
Object.keys("PROP_ONE")
While the first is an identifier resolved to an object, the second one is just a string. not more. You may access it using bracket notation due to the fact that its part of window:
Object.keys(window["PROP_ONE"])
Disclaimer:
All in all, dynamic keys should just be used if really neccessary. They make your code slower and more buggy.
Related
Since JavaScript has no straight concept of a "set", the way I always create an object that acts as a set is to create an associative array where the keys are the elements of the set and the values are true, e.g.
function ToSet ( arr )
{
// return set of
var S = {};
arr.forEach(function (elem) { S[elem] = true; });
return S;
}
Since the trues are dummy data, is there a better value to use? Maybe one that only takes up 1 byte?
Since JavaScript has no straight concept of a "set"...
Javascript does have a Set object as of ES6. You can read about it here on MDN. And, there are multiple polyfills to use something similar in pre-ES6 environments.
If trying to simulate a Set using a plain Javascript object, the key must be a string so you have to find a unique representation of your object that can be used as a string key. For a number, that's the string representation of the number, for a boolean, that's "true" or "false". For an object, you have to create a unique string that represents the object and if the object is presented again you can recreate the same string. There are multiple possibilities for how to do that in pre-ES6. An ES6 set can hold an object directly without making a string representation of it.
Here's a related answer on using a Set-like object in pre-ES6: Mimicking sets in JavaScript?
And, a derivative of that that aims more for the ES6 Set interface here: https://github.com/jfriend00/ES6-Set.
In this ES6-like interface, there's a function called getKey() here that illustrates a strategy for making a unique string key for many types of ES5 variables. You can see the details there.
As for what to set the data to in the Set, the best bet is to set it to the actual value of the data in the set because the key will be forced to be a string so you need to store the actual value somewhere. So, if the value isn't a string itself, then the actual value can be the value in the object.
Since the trues are dummy data, is there a better value to use? Maybe
one that only takes up 1 byte?
As for data size, exactly how Javascript variables are stored is an implementation detail that is not forced by a specification so it can vary from one implementation to another. Because a variable's type has to be part of the value, every variable has to have some sort of universal part of its value. You're not likely to find a value that is smaller than a simple boolean since a type that only has two possible values should be as small as possible already.
But, if you want to be able to tell the difference between a 3 and a "3" stored in your set (e.g. you want them stored as separate items in the set), you will have to add a type modifier to the key so they are stored with separate keys and if you want to be able to get all the values from the set you will need to be able to reconstruct the original value. One simple way to get back to the original value is to store it as the value of the property on the object rather than just true.
I have module that returns an object to a callback with the structure of the following pseudocode:
module.exports=function(){
global.foo=function(){
var callbacks=Array.prototype.slice.call(arguments,1);
var conf=arguments[0];
return global[callbacks.shift()].apply(null,[conf,callbacks]);
}
global.bar=function(){
var callbacks=Array.prototype.slice.call(arguments,1);
var conf=arguments[0];
return global[callbacks.shift()].apply(null,[conf,callbacks]);
}
// This one is where i ran into trouble
global.foobar=function(){
var callbacks=Array.prototype.slice.call(arguments,1);
var conf=arguments[0];
// right here
if(callbacks.length===[].length){
return global[callbacks.shift()].apply(null,[conf,callbacks]);
}
}
var conf={'pseudo':'object'};
return global['foo'](conf,'foo','bar','foobar');
}
Everything works fine until foobar, and what happens is that when I get down to checking if there are anymore callbacks - because if their are, I want to call them - callbacks.length is 1 at this point. This didn't make sense to me, and I discovered that at that point callbacks actually equaled [[]]. I don't know why this is getting returned, so I guess I have two questions. Can anyone see why callbacks is equal to [[]]?
What I discovered along the way is that when using Strings called as functions in the global namespace - as in var bar='foo'; global[bar]() calls global.foo() - multiple brackets are ignored. So for example, global[[[[[[['foo']]]]]]] === global['foo']. Also weirdly enough (at least to me), the following:
// With
global.bar='foo';
global.foo=function(){return true;}
//the following
global[global[bar]]()
// throws a TypeError: undefined is not a function
None of that makes sense to me.
I recognize that this question ends up asking three questions, and is a bit disorganized, but frankly I'm a bit confused, and I'm not really sure how I want to ask what I want to know, so I just said everything.
My main questions is regarding the multiple brackets, and why that works.
Thanks
My main questions is regarding the multiple brackets, and why that
works.
The multiple brackets works only because it's trying to do a string conversion to get a property name. So, no matter how many nested arrays you have, it ends up calling .toString() on each array and since the inner array only has a single item that has a string in it, multiple .toString() calls just end up resolving to the inner string.
Here's a demo of the same concept in a browser:
window.foo = "hi"
document.write(window[[[[["foo"]]]]]);
For some further explanation:
["foo"].toString() === "foo";
So, then:
[["foo"].toString()].toString() === "foo"
But, if the outer .toString() is there, it is already driving things to a string so you can remove the inner .toString() and thus you get:
[["foo"]].toString() === "foo"
And, you can then nest it as many levels as you want as long as something on the outer level is driving it to a string.
And, since property names are always strings, when you do:
global[[[[[[['foo']]]]]]]
You're ultimately asking for a property name that can be looked up on the global object and since the property name is a string, that calls .toString() on the outer array. When the outer array goes to convert it's only item to a string, it asks that one item to convert itself to a string so this:
global[[[[[[['foo']]]]]]]
turns into this:
global[[[[[[['foo'].toString()].toString()].toString()].toString()].toString()].toString()]
Which hopefully explains why you end up with nothing more than this in the end:
global["foo"]
FYI, if you look at the ES5/ES6 spec for Array.prototype.toString(), it ends up calling array.join() which for a single element array just ends up doing a .toString() on that single element which is how it causes all the nested arrays to just call .toString() on themselves. The outer array calls .toString() on the first nested array which called .toString() on it's single item which is the next nested array and so on until it finally gets to the inner string which is returned back from all the .toString() calls. And, it matters not how many arrays deep it is nested.
Spec reference for Array.prototype.toString(): http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.tostring
Spec reference for Array.prototype.join() which is called by Array.prototype.toString(): http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.join
In your other scenario here:
// With
global.bar='foo';
global.foo=function(){return true;}
//the following
global[global[bar]]()
// throws a TypeError: undefined is not a function
None of that makes sense to me.
Here's what is going on in global[global[bar]]() one piece at a time:
bar resolves to the string 'foo'.
So, global[bar] resolves to global['foo'] which is your function.
But, then you try to do another global reference on it like this global[global[bar]], then you are essentially trying to do:
global[yourfunction]
or
global[global.foo]
That will try to convert yourfunction to a string and look up that property on the global object. That will be undefined. So, you will then try to do undefined() which is a TypeError because undefined is not a function.
What will work in this case is just:
global[bar]()
I'm a beginner and just successfully trouble-shoot my code. I'm glad that I found it, however it took me a long time. I'm hoping to learn why it happened.
Here's the buggy original code. Assume that the variable [nextAlpha] has already been assigned a string value:
nextAlpha.toUpperCase();
Through some creative testing I was able to determine it was the line causing issues. I thought perhaps it's not actually updating the value of variable [nextAlpha]. I tried this instead, and it worked:
nextAlpha = nextAlpha.toUpperCase();
I've left the rest of my code out, but assume that [var = nextAlpha] has already been declared at the top of my script, which I think means "globally." With that information, I thought it was enough to simply call the method on the variable. Why doesn't this "update" the string to upper case like it does when I go the extra step to (re)assign it to the original [nextAlpha] string?
toUpperCase returns the converted string as a new object - it does not perform the conversion on nextAlpha.
From the Mozilla reference:
The toUpperCase method returns the value of the string converted to uppercase. toUpperCase does not affect the value of the string itself.
reference
In JavaScript, Strings are immutable:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures
Unlike in languages like C, JavaScript strings are immutable. This means that once a string is created, it is not possible to modify it. However, it is still possible to create another string based on an operation on the original string
toUpperCase() is a function (so return a value) not a property (affect the variable itself)
I want to know how is the string length of a string calculated in js.
Is is a function call or a class data member.
I want to know what happens when we execute the following code :
a = 'this is a string';
console.log(a.length); // what actually happens at this point?
Also if a do this :
a += ' added something';
console.log(a.length); // at what point is the new length calculated
//and/or updated for the object 'a';
And at last, do I need to store the string length in a temp variable while using a loop over the string or can I directly use the following (which one is faster/processor efficient) :
for(var i=0;i<a.length;i++){
// doing anything here
}
Summing up my question, I want to know the processing behind String.length and which practice is better while looping over strings?
A string is immutable in JavaScript.
a += "somestring" doesn't change the length of a string but makes a new string.
This means there is no "new length", but the length is just part of the definition of the string (more precisely it is stored in the same structure in implementations).
Regarding
for(i=0;i<a.length;i++){ // did you forget the 'var' keyword ?
a not so uncommon practice (if you don't change a) was to optimize it as
for (var i=0, l=a.length; i<l; i++)
in order to avoid the reading of the length but if you compare the performances with modern engines, you'll see this doesn't make the code any faster now.
What you must remember : querying the length of a string is fast because there is no computation. What's a little less fast is building strings (for example with concatenation).
Strings are a primitive type. At least that's what the documentation says. But we can access the length of the string as if we are accessing the property of an object(with the dot notation). Which indicates it's an object, Right?
Turns out, whenever we make a call from the string primitive to some property using the dot notation (for example, say length), the Js engine will take this primitive string and wrap it into an equivalent wrapper object, which is a String object. And then, the .length on that String object returns the length.
Interesting thing to note here is, that when we do something like this, our string still stays the same primitive string during all of this. And a temporary object is created to make our string operation work. Once the required property is fetched, this temporary object is deleted from the memory.
Hope this gives some high level understanding.
I'm answering your first question.
I'm also curious about this puzzle so I did some search myself, ended up finding -
Based on String documentation from Mozilla:
String literals (denoted by double or single quotes) and strings
returned from String calls in a non-constructor context (i.e., without
using the new keyword) are primitive strings. JavaScript automatically
converts primitives to String objects, so that it's possible to use
String object methods for primitive strings. In contexts where a
method is to be invoked on a primitive string or a property lookup
occurs, JavaScript will automatically wrap the string primitive and
call the method or perform the property lookup.
So as I understand, when you use somestring.length, the primitive string will first be wrapped as a String object, and then since the object has its property length, so it's just a internal method call to access and return.
I am new to DOJO and trying to figure out the difference between these two uses of the seemingly two things.
dndController: new dijit.tree.dndSource("dijit.tree.dndSource",{copyOnly:true})
and
dndController: "dijit.tree.dndSource"
The second one works, but when I use the first one, it gives me an error when loading my tree. It says type node is undefined. The reason I want to use the first one though is because I want to set copyOnly to true.
Any answers appreciated it.
That parameter expects a constructor function instead of the object you passed. Perhaps the following would work:
dndController: function(arg, params){
return new dijit.tree.dndSource(
arg, // don't mess up with the first parameter
dojo.mixin({}, params, {copyOnly:true}))
//create a copy of the params object, but set copyOnly to true
}
Some explanation:
I actually don't know anything about drag-and-drop on trees. All I did was look at the Tree source code (its at dijit/Tree.js or something like that) to find out where dndController is used. From that point I could find out that is was supposed to be a function that can receive these two parameters (or a string representing the path to such a function...). The actual dijit.tree.dndSource function that is used I just copied from your question statement, hoping it would work.
The dojo.mixin function mixes in all the objects in its 2nd, 3rd, ... arguments into the first argument. By using a new, empty, object as the "receiving" object we have a neat way to make a shallow copy of params, settting copyOnly without modifying the original params object.