Steve Neill wrote:
Can anyone suggest how to create an arbitrary object at
runtime WITHOUT using the deprecated eval() function. The
eval() method works ok (see below), but is not ideal.
eval is deprecated as a method of objects, and as JScript never
implemented it as such it was never really viable to use that method
(though it still has not been removed from Mozilla/Gecko browsers). eval
is un-deprecated in the current ECMA 262 standard (3rd edition) and
should be available in all implementations (however, ECMAScript 'Compact
Profile' (ECMA 327) are allowed to implement eval such that it just
throws exceptions whenever it is called).
There are better reasons for not using eval, such as not really needing
to in this context.
function Client() { }
Client.prototype.fullname = "John Smith";
var s = "Client";
eval("var o = new " + s + "();");
On the other hand, the above eval use can be re-arranged so that all of
the string concatenation can be avoided:-
var o = new (eval(s))();
- allowing for the much more flexible use of arguments with the
constructor identified by the string value of - s -. it should also
execute faster that way, but not nearly as fast as it would if - eval -
was not used at all.
(looking at the production rules for the - new - operator, I suspect
that parenthesising the function call is required to turn the
CallExpression - eval(s) - into a PrimaryExpression. PrimaryExpressions
being allowed as operands for - new -, while CallExpressions are not)
alert(o.fullname);
Note: I want the type name of the object to be represented
as a string (in this case "Client" -- this is a
non-negotiable requirement).
Don't jump to this conclusion too quickly, if you can passa the value of
variable holding a string that is the identifier of a function
(constructor) around could you instead pass the value of a variable
holding a reference to that function about instead? It is quite viable
to do:-
function Client(){ ... }
function Other(){ ... }
var f1 = Client; // reference to the 'Client' function object
var f2 = Other; // reference to the 'Other' function object
function getNewObject( constrct ){
return new constrct(); //Indirect and anonymous use of
//the constructor passed as the
//parameter - constrct.
}
var o1 = getNewObject( f1 ); //Client instance
var o2 = getNewObject( f2 ); //Other instance
var o3 = new f1(); //another Client instance
var o4 = new f2(); //another Other instance
- or something more indirect. Passing references to functions about is
not necessarily different to passing strings about.
I also tried the following (which appears to fail):
function Client() { }
Client.prototype.fullname = "John Smith";
var s = "Client";
var o = new Object();
o.construct = s;
alert(o.fullname);
I wouldn't say it failed, it is clear that the outcome programmed in the
code is the alerting of an undefined value, and that is what it did
(successfully), didn't it?
eval() is handy but not future-proof, so any suggestions
would be welcome.
There probably is not future-proofing issue here. There is a code design
issue as any use of - eval - is almost certainly indicative of
ill-conceived design (as there are so very few circumstances where its
use is appropriate/required, especially given how dynamic javascript is
to start with).
In javascript all functions are objects and all functions may be
constructors (they can all be called as constructors but the ones
explicitly returning ECMAScript native Object types will not be
successful constructors, and most will not be useful constructors). As a
result you can reference functions that you want to use as constructors
in exactly the same way as you reference any objects.
As Martin has said, if the constructor function is defined in the global
scope it becomes a property of the global object (on a browser the
global object is the window object), and a bracket notation property
accessor syntax, with a reference to the global object as the
MemberExpression/CallExpression to the left of the brackets, can access
any properties of the global object based on the value of a string
variable (or expression resulting in a string).
In code executing in the global context, or within function bodies that
are not being called as methods of object, the - this - keyword is a
reference to the global object.
var o = new this[ s ]();
Other references to the global object are available on web browsers in
the form of the - self - and - window - properties of the global object.
var o = new window[ s ]();
var o = new self[ s ](); // I don't recommend ever
// using - self - in this context.
Other methods of getting a reference to the global object include:
Making your own global variable that reefers to the global object by
executing:-
var global = this;
- in the global context as the page loads. This allows any code to use
the global variable - global - to refer to the global variable. E.G.:
var o = new global[ s ]();
A global function could be created:-
function getGlobal(){
return this;
}
- and allow any code to call that function from any context to get a
reference to the global object. E.G.:-
var o = new getGlobal()[ s ]();
A reference to the global object may also be retrieved in any context
(including an object's method, where - this - would refer to that
object) through the inline execution of a function expression that
returns - this -, allowing such a value to be assigned to a variable
local to a method without any external dependencies. E.G.:-
AnyObject.prototype.exampleFnc = function(){
var localGlobalRef = (function(){return this;})();
...
var o = new localGlobalRef[ s ]();
}
- Or as a rather heavyweight one-off:-
var o = new ((function(){return this;})())[ s ]();
Richard.