I've looked everywhere, and it seems like the kind of thing that should be simple, so forgive me if this IS simple or violates best practices or I just missed it.
I want to be able to convert an object reference into a string. Not the object being referred to, but the reference itself.
Let's say I have a method nested in an object, for example:
a.b.c.d()
where a is the object and d is the method, how do I retrieve the string:
"a.b.c.d" ?
To be a little more specific, I want to pass the object reference as an argument to a function and be able to convert it to a string representing the key path:
function convertPath ( a.b.c.d ) {
...
}
could return the string "a.b.c.d". (I'd perform some other operations with it instead of just returning it, but I'm keeping this simple.)
Methods like "toString", etc. return the actual function being referenced as a string.
I've seen plenty of references to do the opposite (pass a string and convert it to an object reference), which would create other complications for me, but not this.
Any help would be appreciated. Thanks!
Related
I am studying prototyping in Javascript, and for an experiment was trying to strip some basic objects of their information as much as possible. However, I ran into an issue in the following code segment:
let x = [];
Object.setPrototypeOf(x, null);
console.log(Object.prototype.toString.call(x));
>>> [object Array]
My confusion here: after removing the prototype, how does Object.prototype.toString still know that x is of Array type? Where is this information coming from, if not any of the properties? Running Object.getOwnPropertyNames(x) and Object.getOwnPropertySymbols(x), I do not see any properties left on x which contain any path to the string Array. Where is this information stored?
And if this is one of those "internal slots" (although I'm not sure which it would be), is there no way to modify it? Can I at least access it directly?
Update:
Yousaf has noted that the property String.toStringTag is normally accessed by Object.prototype.toString to provide the string which goes in place of "Array" in the above example. Indeed, setting x[Symbol.toStringTag] = 'my_string' allows us to change the .toString() value. However, I think it still does not solve the mystery, as x[Symbol.toStringTag] is undefined before we set it, not containing the desired "Array" string.
When .toString() is called on any object, it creates a string of the form [object x] where x is the name of the constructor in-case of built-in constructors like Date and Array.
When .toString() is called, there are several steps taken and one of those steps is checking if the value of this inside .toString() is an array. If its an array, tag is set to the string 'Array'.
From the Spec - 19.1.3.6 Object.prototype.toString ( ):
If isArray is true, let builtinTag be "Array".
Before ES6, when .toString() was called on the instances of user defined constructors, [object Object] was logged. ES6 provided a way to override the tag using a well-known Symbol Symbol.toStringTag.
The documentation on mozilla.org states: "Every object has a toString() method that is automatically called when the object is to be represented as a text value or when an object is referred to in a manner in which a string is expected. By default, the toString() method is inherited by every object descended from Object. If this method is not overridden in a custom object, toString() returns "[object type]", ..."
Sounds logical: A toString() method will be called. So I defined an own "class" and defined a toString() method:
function DataField(name, dataType) {
// ....
}
DataField.prototype.toString = function() {
return this.type.name + " " + this.name;
}
I instantiate an object like this:
var dataField = new DataField("myint", DataType.INTEGER);
I try to output the object respectively the string representation of the object in NodeJS:
console.log(dataField);
console.log("" + dataField);
console.log(dataField.toString());
The first output differs from the other ones. The first one will give some kind of JSON.stringify() representation of the object. toString() is not called automatically as I'd expect it to be. The second and third output will result in exactly what I'd expect:
INTEGER myint
So I'm a bit confused. That means that toString() will not automatically be called though the explanation of mozilla.org suggests. Is this a regular behaviour in NodeJS or did I missinterpret the explanation on mozilla.org or am I missing something else here?
You might think: Well, then let's just call toString() explicitely if necessary and everything is fine. But there is some inconvenience with that. If I create an array of objects and use console.log() to ouput that array, no toString() will be called. Which means: The output generated is useless. This would mean that if I store objects with explicite implementations of toString() within an array in a container object I will have to implement toString() of the container object in a very specific and incovenient way: I must write code to explicitely create a string representations of any array of objects contained in the container object myself. Of course I could do that, but I would intuitively assume a toString() method to be called automatically therefor eliminating such need for explicite implementations.
Regarding NodeJS it seems that finding information is not that easy on this specific problem. So if you know about this topic any help is appreciated. The question is: What is the most elegant way to create a (human readable) string representation of a data model (= a data container object containing lists of other data objects)?
In Node.js, console.log calls util.format, which does not call toString().
When an object is passed into a function, the object is passed by value (although the value for an object is a reference). I am passing very large objects into my functions, but only accessing a handful of properties (6-12) from within the function. Memorywise, would it be better to only pass the properties instead of the whole object? Or would this actually create more of a memory issue if my properties are strings?
Given the two functions below, what happens memorywise? In f1, does javascript/v8 create a new object foo in memory or is it really just a pointer? Does it remain just a pointer if I access a string property or does V8 then make a copy of that string for use within the function. In f2, I assume that a completely new copy of the string foo is made for the function. Is this a correct assumption?
function f1(x) {
var y = x.foo;
}
function f2(foo) {
var y = foo;
}
var obj = {foo: "test"};
f1(obj);
f2(obj.foo);
"Memorywise, would it be better to only pass the properties instead of the whole object? Or would this actually create more of a memory issue if my properties are strings? "
That would require more memory. Doesn't really matter if they're strings, since strings are generally implemented as Reference Types as well, but for each argument, there will be a copy of the value instead of just a single copy of the Object reference.
The object reference is very light weight, so you just as just copy that instead of all individual members of the object.
Also, this will be required if you rely on mutations of the object within the function.
In your examples, there's only one property being passed (a string). If your code doesn't rely on mutations of the object itself, then there'll be no significant difference between the two.
In the example that passes the object, the only copy is the object reference. It isn't a pointer, but it is very light weight, and nothing to be concerned about.
In the example that passes the string, it would seem as though it makes a copy of the entire string, but since stings are immutable in JavaScript, implementations generally implement them as reference types as well.
Therefore it's as efficient to pass a single character string as it is to pass a 10,000 character string.
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.
I need to make a Javascript object that would behave as an associative array, but with some functions that are called before getting and setting properties.
For example, the task may be like this: we should make an object, that would contain a squared value of a key, like this:
obj.two should be equal to 4,
obj.four should be equal to 16,
obj['twenty one'] should be equal to 441.
This is an example. Actually I need to make setting operation overridden too. The getting and setting operations would go to the database, and they not necceserily would take strings as keys, but any types of objects, from which it would create a DB query.
How would I do that a) with as less thirdparty libraries as possible and b) to make it work on as much platforms as possible?
I am new to JS, I've found that JS has no associative arrays, relying on the ability to define objects on the fly with arbitrary properties. I googled and had an idea to use or even override lookupgetter (and setter), where define a new getter/setter on the fly, but I coundn't find if the interpreter would use this method every time it encounters new key. Anyway, it looks like I wouldn't be able to use anything except strings or maybe numbers as keys.
In Java, I would just implement java.util.Map.
Please help me, how would I do the same in Javascript?
edit
I think I will get what I want if I manage to override [[Get]] and [[Put]] methods mentioned here http://interglacial.com/javascript_spec/a-8.html#a-8.6.2.1
For your example, doesn't this do what you want:
var myObj = {};
myObj["two"] = 4;
myObj["four"] = 16;
myObj["twenty one"] = 441;
alert(myObj["four"]); // says 16
Or are you trying to say that the object should magically calculate the squares for you?
JavaScript object keys are strings. If you try to use a number as a key JavaScript basically converts it to a string first.
Having said that, you can use objects as keys if you define a meaningful toString method on them. But of course meaningful is something that happens on a case by case basis and only you will know what needs to be done for your case.
You can also define objects that maintain their own internal data structures which you access via object methods. I think explaining that is beyond the scope of this post. Google "javascript module pattern" for some pointers to get you started.
See http://ejohn.org/blog/javascript-getters-and-setters/
Also this particular answer: Javascript getters and setters for dummies?
edit
According to Does JavaScript have the equivalent of Python's __getattribute__? and Is there an equivalent of the __noSuchMethod__ feature for properties, or a way to implement it in JS? there is no nice way of accomplishing exactly what the OP wants. Getters and setters are not useful because you must know the name of what you're looking for in advance.
My recommendation would thus be to do something like:
var database = {}
database.cache = {}
database.get = function(key) {
// INSERT CUSTOM LOGIC to recognize "forty-two"
if (!(key in database.data))
database.cache[key] = fetch_data_from_database();
return database.cache[key];
}
database.put = function(key, value) {
database.cache[key] = value;
send_data_to_database(key, value);
}
I decided that the most correct way to implement this is to use Harmony:Proxies. It isn't working on all platforms, but it lets implement this in the most seamless way; and it may be supported in more platforms in the future.
This page contains an example that I used as a template to do what I want:
http://wiki.ecmascript.org/doku.php?id=harmony:proxies