I'm trying to write an object explorer that shows the user the properties and values of an object. My idea is that the will be able to drill down to see objects within objects, click on hyperlinks to examine the methods' source code and otherwise see the properties values.
Here's what I've got so far (jsFiddle).
Q: Help! I don't know what I'm doing! One thing I noticed is that I'm apparently not getting any properties where hasOwnProperty is true. Another thing is I need the function to be recursive. Another is: I think I remember reading somewhere of a JavaScript function that can get the text of a JavaScript function.
So, to answer your question: "What's the question?" I guess I can say "Can you help me write this generic JavaScript object explorer as possibly a jQuery plugin?"
Your question is quite general. To your two specific points:
In order to make your function recursive, I believe you can simply copy this block of code from your 'bodyLog' function to the place you have commented your recursion. You will want to add one to 'level' as you pass it down to your recursive call.
// $(obj).each(function(index,Element) { // EDIT shouldn't need this iteration, you're inside the loop
result += showObjectsMethodsAndValues(Element,level+1,true);
result += showObjectsMethodsAndValues(Element,level+1,false);
//});
The method that gets the string body of a function is simply calling '.toString()' on the method object. So if you do:
var myFun = function(a, b) {
return a+b;
};
alert(myFun.toString());
You will see the body of the function printed out. For built-in objects you will probably see "[native code]" as the body of the function, rather than real javascript code.
The reason you aren't seeing any hasOwnProperty properties is that I doubt the jQuery object has any. All of it's properties likely live in it's prototype object. Try this:
var myObj = {foo: 1, bar: 2};
myObj.prototype = {baz: 3};
Then call your function on myObj. Foo and bar should come back with hasOwnProperty true, baz will return false. I believe all of jQuery's properties live in it's prototype object, so they will all return false.
http://www.netgrow.com.au/files/javascript_dump.cfm is a good candidate.
Related
Is it possible to make an object callable by implementing either call or apply on it, or in some other way? E.g.:
var obj = {};
obj.call = function (context, arg1, arg2, ...) {
...
};
...
obj (a, b);
No, but you can add properties onto a function, e.g.
function foo(){}
foo.myProperty = "whatever";
EDIT: to "make" an object callable, you'll still have to do the above, but it might look something like:
// Augments func with object's properties
function makeCallable(object, func){
for(var prop in object){
if(object.hasOwnProperty(prop)){
func[prop] = object[prop];
}
}
}
And then you'd just use the "func" function instead of the object. Really all this method does is copy properties between two objects, but...it might help you.
ES6 has better solution for this now. If you create your objects in a different way (using class, extending 'Function' type), you can have a callable instance of it.
See also: How to extend Function with ES6 classes?
Following the same line of #Max, but using ES6 extensions to Object to pass all properties and prototype of an object obj to the callable func.
Object.assign(func, obj);
Object.setPrototypeOf(func, Object.getPrototypeOf(obj));
Others have provided the current answer ("no") and some workarounds. As far as first-class support in the future, I suggested this very thing to the es-discuss mailing list. The idea did not get very far that time around, but perhaps some additional interest would help get the idea moving again.
https://esdiscuss.org/topic/proposal-default-object-method
"CALLABLE OBJECTS"
I haven't seen mention of this type of answer yet.. but this is how I do "callable" objects:
<< PSEUDO CODE >>
{...objectWithFunctionsInside}[keyString](optionalParams)
short example defining first, simplest and preferred method if I just want a "callable object," in my definition:
let obj = {
o:()=>{return("oranges")},
b:()=>{return("bananas")},
s:"something random here, doesn't have to be functions"
}
obj["o"]()
short example of nameless object being run within a function's return, with parameters (note parameters works in the first example too):
function autoRunMyObject(choice,param){
return{
o:(p)=>{return(p+"oranges")},
b:(p)=>{return(p+"bananas")},
}[choice](param)
}
autoRunMyObject("b","orange you glad I didn't say ")
and that's pretty much it
You could even get weirder with it and do nameless functions that auto-run themselves and produce an output right away... for no reason, lol.
... hit F12 and copy this code into your browser console and press enter, you'll get an output right away with the full string:
((autoparam="o")=>{return{
o:(p)=>p+"oranges",
b:(p)=>p+"bananas",
}[autoparam]("I guess this time it's ")})()
You could even pass in the string of "b" in the final parenthesis for a different output from the default "o".
Also, each of my examples (minus the pseudo code first example) are easily copy/paste-able into the browser console for quick testing -- it's a nice place to experiment with JS.
In summary -- this is how I like to do "callable objects"
It's much better than
SWITCH(){CASE:BREAK;};
statements and
IF{}ELSE IF(){}ELSE IF(){};
chains.
Suppose I've a Set as a lookup table.
const myset = new Set(["key1", "key2", "key3", "keepMe"]);
I wanted to filter another array of some other keys (say mykeys) which are in myset.
const mykeys = ["ignoreMe", "keepMe", "ignoreMeToo", "key2"];
Question:
Why do I have to use
const filtered = mykeys.filter(k => myset.has(k))
instead of
const filtered = mykeys.filter(myset.has)
// TypeError: Method Set.prototype.has called on incompatible receiver undefined
i.e., why do I've to create an anonymous lambda function in filter? keys.has has same signature (argument - element, return boolean). A friend told me it's related to this.
Whereas mykeys.map(console.log) works without error (although not being of much use).
I came across this article at MDN and I still don't get why "'myset' is not captured as this". I understand the workaround but not the "why". Can anyone explain it with some details and references in a human friendly way?
Update: Thank you all for the responses. Maybe I wasn't clear about what I'm asking. I do understand the workarounds.
#charlietfl understood. Here's his comment, the thing I was looking for:
Because filter() has no implicit this where as set.has needs to have proper this context. Calling it inside anonymous function and manually adding argument makes the call self contained.
You could use thisArg of Array#filter with the set and the prototype of has as callback.
This pattern does not require a binding of an instance of Set to the prototype, because
If a thisArg parameter is provided to filter, it will be used as the callback's this value. Otherwise, the value undefined will be used as its this value. The this value ultimately observable by callback is determined according to the usual rules for determining the this seen by a function.
const
myset = new Set(["key1", "key2", "key3", "keepMe"]),
mykeys = ["ignoreMe", "keepMe", "ignoreMeToo", "key2"],
filtered = mykeys.filter(Set.prototype.has, myset);
console.log(filtered);
This is a fundamental design decision dating back to the first definition of the JavaScript language.
Consider an object
var myObjet = {
someValue: 0,
someFunction: function() {
return this.someValue;
}
};
Now, when you write
var myValue = myObject.someValue;
You get exactly what you have put in it, as if you had written
var myValue = 0;
Similarly, when you write
var myFunction = myObject.someValue;
You get exactly what you have put in it, as if you had written
var myFunction = (function() {
return this.someValue;
});
...except now, you are not in an object anymore. So this doesn't mean anything. Indeed, if you try
console.log(myFunction());
you will see
undefined
exactly as if you had written
console.log(this.someValue);
outside of any object.
So, what is this? Well, JavaScript decides it as follows:
If you write myObject.myFunction(), then when executing the myFunction() part, this is myObject.
If you just write myFunction(), then this is the current global object, which is generally window (not always, there are many special cases).
A number of functions can inject a this in another function (e.g. call, apply, map, ...)
Now, why does it do this? The answer is that this is necessary for prototypes. Indeed, if you now define
var myDerivedObject = Object.create(myObject);
myDerivedObjet.someValue = 42;
you now have an object based on myObject, but with a different property
someValue
console.log(myObject.someFunction()); // Shows 0
console.log(myDerivedObject.someFunction()); // Shows 42
That's because myObject.someFunction() uses myObject for this, while myDerivedObject.someFunction() uses myDerivedObject for this.
If this had been captured during the definition of someFunction, we would have obtained 0 in both lines, but this would also have made prototypes much less useful.
Node.js querystring.parse() method returns what looks like to be an object, but one without a constructor. According to https://nodejs.org/api/querystring.html :
"... The object returned by the querystring.parse() method does not prototypically inherit from the JavaScript Object. This means that typical Object methods such as obj.toString(), obj.hasOwnProperty(), and others are not defined and will not work."
This easily causes bugs because typically you would assume that every Object understands some basic methods like toString() and that it has a "constructor" which more or less tells us its "type".
What's the best way to handle these rather incapable Objects? I tried:
let myOb = new Object(dumbObject);
But that produces a result which does not have the toString() -method either and does not have the property 'constructor'.
What's the best way to turn these dumb objects into ordinarily behaving ones? And, why would anybody want to create such objects in the first place?
I think (from the top of my head)
let newObject = JSON.parse(JSON.stringify(dumbObject))
should work.
If you want a more generic way to call toString() on an object, you can call it from Object.prototype using .call().
var s = Object.prototype.toString.call(smartObject);
But why? It's just going to give you "[object Object]" on any plain object. Why is it important to get that string?
var p = {};
var s = Object.create(null);
console.log(p.toString());
console.log(Object.prototype.toString.call(s));
typically you would assume
No, you wouldn't. If you make such assumptions, document them in your interface.
What's the best way to turn these dumb objects into ordinarily behaving ones?
Use Object.assign with an ordinary object as the target. Alternatively, you can also change the prototype by using Object.setPrototypeOf, but that's not recommended.
Or just create the properties like .toString or .constructor that you need. See this example.
And, why would anybody want to create such objects in the first place?
Because you need this safety when using objects instead of Maps. See this example.
Explicitly set the prototype of your dumb object with Object#setPrototypeOf
var dumbObject = Object.create(null);
Object.setPrototypeOf(dumbObject, Object.prototype);
dumbObject.toString() //runs fine
Thanks for all the answers, one of which contained a link to another question which really was my question as well, even if I didn't know that at first. That other question is: How to create a JS object with the default prototype from an object without a prototype?
From it I found the answer which I think is the simplest solution so far: Use Object.assign(). Like this:
let orphan = require('querystring').parse("{}") ;
// console.log ( "Got value: " + orphan ) ;
// Above causes the error:
// -> TypeError: Cannot convert object to primitive value
let oa = (x) => Object.assign({}, x);
console.log ("Got value: " + oa (orphan) ) ;
Note the issue is not particularly about "querystring" but with objects which have no prototype in general. Yes we should probably call these poor objects "orphans" instead of "dumb". But I think "dumb" is still quite good term as well. An object which has no prototype has very few (or no?) methods so it can answer very few if any questions we would like to ask it.
This doesn't work:
var s = '^foo';
console.log(['boot', 'foot'].some(s.match));
Uncaught TypeError: String.prototype.match called on null or undefined
But this does:
var s = '^foo';
console.log(['boot', 'foot'].some(function(i) { return i.match(s) }));
Why is this? I imagine somehow the String.prototype.match function is too "primitive" or something, but why exactly? Since I'm not using ES2015, the second version seems quite verbose. Is there an alternative?
EDIT
When I wrote the above, I actually got it backwards compared to my actual need, which was matching one string against a number of regexes. But thanks to the great answers and comments below, I get it: [/^foo/, /^boo/].some(''.match, 'boot').
Note: The value of this is determined by how the function is called! (exception: bound and arrow functions)
If you pass s.match to .some, then the function will be called with this set to the global object (e.g. window) not the string it "belongs" to.
I.e. it would be equivalent to this:
String.prototype.match.call(window, 'foo')
This cannot work because this has to refer to a string object.
You could solve this by binding the function to a specific this value:
['boot', 'foot'].some(s.match.bind(s));
Learn more about this:
MDN - this
You Don't Know JS: this or That?
How to access the correct `this` context inside a callback?
A function value in Javascript does not bring its object along with it. The value of s.match is a plain function value, with no knowledge that you happened to find it attached to s. In fact, no matter what String you access it through, it's always the same function value:
"foo".match === "bar".match
//= true
When you call a function through an object, Javascript sets this to that object for the duration of the function call. But as soon as anything comes between retrieving the function value and calling it, any object association is lost.
You can create a function that does remember a specific this value using bind, as in #Felix King's answer. someFunction.bind(someObject) has approximately the same meaning as function(arg1, arg2,...) { return someObject.someFunction(arg1, arg2,...); }, but it automatically handles the number of parameters properly.
I have a method, that perfectly works in Firefox, with which I can determine the name of an instance of a particular javascript object (please don't ask why I need it...).
Fr example:
var temp = new String("hello!");
var theName = getVarName(temp); //returns "temp"
This method uses "window.hasOwnProperty()" which doen't work in Internet Explorer: any suggestions?
If for whatever reason you do need to use window, use:
Object.prototype.hasOwnProperty.call(obj, p)
I have a method, that perfectly works in Firefox, with which I can determine the name of an instance of a particular javascript object
I don't think you do, because that's not possible in JavaScript. JS is a call-by-value language; when you write:
var temp= 'hello';
getVarName(temp);
that's exactly the same as saying:
getVarName('hello');
At which point the reference to ‘temp’ as a variable is lost. What I'm guessing your getVarName function does is basically this:
function getVarName(value) {
for (var name in window) {
if (window[name]===value)
return name;
}
}
This will work on IE and other browsers without Object.hasOwnProperty(); it will simply return the name of any global variable that matches the argument. The hasOwnProperty() call can be added to this function to refine it a little by only allowing direct properties of window (which act as global variables, including the ones you set explicitly), and not of any of its prototypes. I'm guessing that's what your version of the function is doing, but in practice it has very little effect since almost nothing inherits into ‘window’ by prototype.
You're confusing things a little bit by boxing your 'hello' in an explicit String object (which is very unusual and rarely a good idea), which makes it possible to have two different 'hello' objects that are disinguishable using the === identity comparator, so this will work:
var a= new String('hello!');
var b= new String('hello!');
getVarName(a); // 'a'
getVarName(b); // 'b' - distinguishable object from a
But that still doesn't stop you from doing:
var a= new String('hello!');
var b= a;
getVarName(a); // 'a' or 'b', depending on implementation
getVarName(b); // the same 'a' or 'b' as in the previous call
So, whilst you can fairly harmlessly lose the hasOwnProperty() call as above, what you're doing can't really work properly and you should probably look at a better way of achieving whatever it is you're up to.
Do not use global variables. Always put them in namespaces so that you don't run into such problems.
If I understood correctly, you want the global variable name when given the value under any scope. For example, the following should work too
var temp = "hello";
function foo(x) {
alert(getVarName(x)); //returns "temp" as well.
}
foo(temp);
This is sort of reverse hash lookup on Window object which is not only expensive, since you need to iterate over all the properties of window object each time, but also unreliable (two variables can have a same value).