For example; the function alert or writeln; how could I find which interface these functions come from programmatically within JavaScript?
Yes, like this:
if (typeof(yourFunction) !== "undefined") {
// do something, like call the function
}
You can easily check to see that a function is defined with typeof:
if (typeof(maybeFunction) === "function") {
// do something
}
On the other hand, it is not easy in general to know where a function is defined. Different browsers host their core functional implementations in different places, and furthermore it is incredibly easy to copy references to functions:
var myAlert = alert; // Now myAlert is a function,
// but where will you find a function myAlert() declaration? Nowhere...
So I think the proper answer to your question is, it's not possible (in general). You can use a debugger to find it on the fly, or a good text editor or grep tool to find it offline, but you won't be able to find it programatically.
If you want to "list an objects functions", you can do:
function listOwnMethods(obj) {
var ownMethods = [];
for (var p in obj) {
if (obj.hasOwnProperty(p) && typeof obj[p] == 'function') {
ownMethods.push(p);
}
}
return ownMethods;
}
However, this will not list the non–enumerable properties. If you want to also get enumerable inherited methods, remove the hasOwnProperty test.
Some versions of JavaScript also have getters and setters, so properties may behave like functions even though their Type is not "function". Finally, host objects can return anything they like when tested with typeof, so you may not be able to determine all (or even any) of a host object's methods that way.
Related
I'd like to test if a function is a constructor (meant to be called with new), or just a regular function that should be called literally.
This very well may not be possible, I could not find anything on the matter, but wanted to see if it is possible simply for the sport of it.
Here is the basic check that I have so far:
function isConstructor(func) {
// Ensure it is a function...
if (typeof func !== 'function') {
return false;
}
// Check for at least one custom property attached to the prototype
for (var prop in func.prototype) {
if (func.prototype.hasOwnProperty(prop)) {
return true;
}
}
// No properties were found, so must not be a constructor.
return false;
}
Is this a decent approximation?
Is there anything else I can check for?
What drawbacks/false positives exist for this approach?
NOTE: This is a question of curiosity, not requirement. Please don't say "that's a bad unit test" or "is it really worth it?". This is simply a fun exercise to see what is possible (albeit bad/unreasonable/not-to-be-used).
It's not possible because any function in Javascript can be a constructor. You may call any function prefaced with new to have its this point to a new object, just like you may .call any function to have its this point to anything you want.
For example I have class:
function Test() {
}
Test.prototype = {
'setTest' : function(test) {
this.test = test;
}
}
var test = new Test();
Test.setTest('test');
I want to save object test in database.
How to serialize object test to string? (methods, variables, etc)
Simple with json
JSON.stringify( test );
In this case, for the question you're asking, there really isn't a way to do what you want. The problem with your request lies in "serializing everything attached to the object, including functions".
Serialization normally only occurs for data, because executables are usually machine bound, in that they are compiled for a given machine, based on certain characteristics. Now, it's reasonable to say that javascript functions just require a javascript interpreter, because javascript is write-once, run-everywhere. But when people write serializers, because all serializers tend to work the same, we write them for data only. In this case, the industry standard is JSON, which is an object-data only serializer.
There are three solutions that avail themselves to you at this point:
Write your own serialier/deserializer that encapsulates functions. This can be tricky, because not all javascript engines will give you access to the source.
Write your own re-loading mechanism that generates a specific new initialized type on each restore, and save the typename as one of the properties on serialization. That way the initialization of each variable gives you the methods, and then merging with the data gives you the complete object.
Store each function as a string and eval it on the fly as you need it. This is incredibly tricky, and is quite prone to errors. I can think of no case where this becomes helpful, because it's quite fragile. However, it is an option, and cannot be overlooked.
I know that 3 is a sub-answer for 1, so you can consider it that there are only two useful answers.
I know that superficially this works on Chrome and IE9, so it should work everywhere the majority of users are likely to use it:
var abc = function(thing) { return thing; }
abc.toString();
// gives "function(thing) { return thing; }" on the command line
So you can certainly serialize the methods as strings in place of the actual method, but you're going to need to create a duplicate object so you can capture every element on the source object (I think, rather than replacing them in place).
Hopefully this helps you think about the problem some more, and maybe to realize you don't need to serialize the methods (nobody ever does that I know of, not reasonably).
The best way to do this is to write your own serialize method which creates a JSON object with attributes, based on your getters. Normally you define a getter per attribute. So it should work for most cases (so you don't have to define a serialize method for each class).
function serialize(obj) {
var serialized = {};
for(var prop in obj) {
if (obj.hasOwnProperty(prop) && typeof obj[prop] == 'function') {
if (/^get.*/.test(prop)) {
var value = obj[prop]();
var name = prop.replace('get', '');
if (typeof value === 'object') {
serialized[name] = this.serialize(value);
continue;
}
serialized[name] = value;
}
}
}
return serialized;
};
To reset your attribute values back to the class you have two options:
Create a function in your class which creates a valid object instance based on the serialized JSON.
Create a unserialize method and map the JSON with your class using the setters.
Example:
function unserialize(obj, emptyClass) {
// Check emptyClass for setters and map the data from obj to it.
return 'class instance';
}
Typically, you'd do this with JSON, which is widely supported across browsers/languages/libraries/etc. The only hangup is that JSON does not support functions – but do you really need to serialize those?
I've had to support functionality similar to this before. I ended up saving the name of the function as a string and serializing it as JSON. Then when I come back to the client, I execute the function using a helper like the one posted in this question.
If anyone has a better way to solve this problem, I'd want to see it!
I recently had to find a solution for this problem. I'm sure it can be improved upon.
First I created a module for instantiating the "serialisable" object.
function MyObj(serialised){
this.val = "";
if(serialised){
var unserialised = JSON.parse(serialised);
for (var i in unserialised) {
this[i] = unserialised[i];
}
}
}
MyObj.prototype.myMethod = function () { return this.val;};
module.exports = MyObj;
you of course have to consider error handling and other validations.
I'm having to use hasOwnProperty a lot in my code and it is annoyingly long and camel-cased to type. I wanted to be able to just say myObj.has('x'), but when I tried to make an alias for hOP in the Object.prototype 'has' now gets enumerated in for..in loops. What is the best way to get what I want? I mean I could just make a global function that works like has(obj, prop) but I like the dot format better and I would like to know what tricks javascript might have up it's sleeve, so I am looking for suggestions.
Update: this seems pretty hacky but is String.prototype.in = function(obj){return obj.hasOwnProperty(this)} OK? With that I can then say if ( 'x'.in(myObj) ) {... Unfortunately it adds another layer of function call rather than just aliasing hasOwnProperty, but I like the syntax.
You can only prevent enumeration in ES5 compatible browsers, using Object.defineProperty():
Object.defineProperty(myObj, "has", { value: Object.prototype.hasOwnProperty });
defineProperty() defaults to setting non-enumerable properties. A better ES3 approach would be to just alias the function and don't stick it on Object.prototype:
var has = function (ob, prop) {
return Object.prototype.hasOwnProperty.call(ob, prop);
}
I don't see anything wrong with your own String.prototype.in approach either, except maybe potential naming collisions in the future, but that's your call. Calling it String.prototype.on would remove ambiguity with the in operator.
This should do the trick
Object.prototype.has = function(x) {
return this.hasOwnProperty(x)
}
Take care when using SomeNativeThing.prototype and use
if ((typeof Object.prototype.has) !== 'function') {
...
}
to ensure your not overriding anything
as #pomeh states, using Object.prototype.has = Object.prototype.hasOwnProperty is way better
Surprisingly, this Apple page has Element.prototype equal to undefined, so I cannot use this awesome snippet of code.
Are there any reason for doing this?
Apple is using the Coherent JS framework which has this block of code:
// Trick picked up from Prototype to get around IE8's fixed Element & Event
(function() {
var element = this.Element;
this.Element = {};
Object.extend(this.Element, element || {});
}).call(window);
window.Element is originally a function, but it's being replaced and extended with a regular object. Only functions have .prototype properties.
Workaround:
The prototype chain for any HTML element seems to be:
Specific element type (HTMLBodyElement, HTMLDivElement, etc...)
HTMLElement
Element
Node
Object
You should be able to attach your referenced code to the prototype to any of the bold objects in the chain and get the style for an html element. I would not recommend this in production code as modifying objects like that is generally considered harmful, but if you are just trying to export a style from another website, it should work well enough.
Object.prototype.exportStyles = (function () { //Works if you use it on an element, the code will protect you from yourself if you try to use it on regular objects.
HTMLElement.prototype.exportStyles = (function () { //Safer because it is farther down the inheritance line, affecting fewer objects.
//Avoiding name collisions and other surprises.
In addition to what Dennis explained well, the easiest solution to avoid changing built-in objects (which people seem to love to do over and over, as Apple did on their site and Luc1245 did in the post you've mentioned).
A non-intrusive alternative is to run something like:
function exportStyles = ( function ( /* what Luc1245 posted */;
exportStyles.apply( /* this */ theElement, /* args */ []);
It seems that they have overwritten the default value of Element and assigned it the value of an object instance, which by default doesn't have the prototype property. Try the following in the console:
var a = {};
console.log(typeof a.prototype === 'undefined');
function abc() {}
Element = abc;
var b = new Element();
console.log(typeof b.prototype === 'undefined');
There isn't an universal reason to override built-in functions, so I'd guess it's probably because they thought it would make the most sense semantically (as it seems the Element object is used for DOM manipulation) and they don't have the possibility of conflicting with external libraries, which is why it's usually discouraged.
Question
Is there a clean and robust way to handle calls to a plugin so that the caller can always expect to have a JavaScript array returned to them, regardless of whether the plugin is a Firefox add-on or an ActiveX control? I've already got the actual plugin calls wrapped in functions, like:
function getDevice(deviceKey) {
return plugin.getDevice(deviceKey);
}
I can change it to something like:
function getDevice(deviceKey) {
return normalizeArray(plugin.getDevice(deviceKey));
}
function normalizeArray(array) {
return typeof(array) == 'unknown' ? array.toArray() : array;
}
but I still need to remember to actually call normalizeArray from the wrapper functions, and the normalize implementation seems like it could be lacking in robustness and/or making some suspect assumptions.
Is there a better way to handle this situation?
Background
I'm writing some JavaScript to interact with a plugin through JavaScript. The plugin is available as an add-on in FF and as an ActiveX in IE. There are a number of methods available in the plugin that return arrays. In FF, calling:
typeof(retVal);
on the object returns 'object'. I can do things like:
retVal.length;
retval[0];
and they work as expected. When I make those same method calls in IE, calling:
typeof(retVal);
returns 'unknown' and calls like:
retVal.length;
retval[0];
are undefined.
I've done some debugging and discovered that what the ActiveX is really returning is an array of variants. This object IS recognizable by JScript, and a JavaScript version of the array can be obtained by caling retVal.toArray().
I would make the array normalisation part of the wrapper but keep it as a separate function so:
function getDevice(deviceKey) {
return normalizeArray( plugin.getDevice(deviceKey) );
}
You're right, the normalizeArray function does seem a bit brittle. You might instead want to do an instanceof test, and even wrap it in try..catch as IE is known to do strange things when ActiveX objects are tested this way, so:
function normalizeArray(obj) {
try {
if (obj instanceof Array) {
return obj;
}
} catch(d) {}
// Convert obj to array - see if toArray property is truthy
if (obj.toArray) {
// Is this syntax correct?
// If it is, call it
return obj.toArray();
} else {
// Not Array, no toArray(), what next?
}
}
However I'm not sure if the object returned by your Firefox plugin is a javascript Array or if it's created in the same scope as the normalizeArray function (i.e. it might be created from a different Array constructor) so the instanceof test will fail even if it is an Array. An alternative test is:
Object.prototype.toString.call(obj) == '[object Array]'
which I think should definitely be wrapped in try..catch for IE if there is any chance obj is an ActiveX object.
Incidentally, typeof is an operator, there is no need to use the grouping operator ().