I'm constructing what needs to be a name of var on the fly using
var myNameToConstruct = 'prefix_' + t; // where t is a value passed as string from a function
Using this name I need to initiate a method of an object earlier defined as
var prefix_someName = $("#ele").getObjInstance(); // where getObjInstance is a method used by this particular object.
prefix_someName from my second line of code is an object in itself but the name I'm constructing is a string so even though it looks the same, it is not the same thing (what I gather from this code blowing up)
Is there a way to turn prefix_someName(string) into prefix_someName(object) on the go so that its recognized properly and the method can be called on it or do I have to rewrite code on a deeper level to make this happen?
If your objects are defined globally:
window[myNameToConstruct].methodToCall();
Related
In this Udacity video on game development, the instructor mentions that Javascript allows us to create an object by giving a handle to its definition. Then, it says that to allow this "an overloaded object definition will update a hash table with a pointer to its class definition".
I quite know what a hash table, a pointer, an overloaded method, and the factory pattern are, but I cannot make sense of this mysterious statement, or of the rest of the explanation.
"Hash table" is just a fancier way of saying "ordinary Javascript object". What the instructor means by "handle to its definition" is just another way of saying "the function that acts as a constructor for the class".
Ultimately, what he means by the statement you mentioned:
each overloaded entity definition will update a hash table with a pointer to its class definition
is the following:
All "subclasses" of Entity will register their constructor function in a single shared hashmap/object using the key which is the type value.
This allows you to get the constructor function (in other words, the function to call new on, which will return an instance of that entity) for any type by accessing gGameEngine.factory[type].
This is nice, because whenever a programmer adds a new type of entity, so long as they remember to add a new entry to that gGameEngine.factory object with the correct key, then that object will contain everything you need to construct any type of supported object.
So the code that iterates over the JSON structure generated by the level editor can create an instance of any type the same way, using something like:
var typeConstructor = gGameEngine.factory(tileSpec.type),
instance;
if (typeConstructor) {
instance = new(typeConstructor)(tileSpec /* or whatever params */);
}
This is similar to the code visible at around the 1 minute mark of the video you linked to.
Make sense now?
I think all he's saying is that you can map references to functions / objects / variables inside another object. He's using one type of property accessor syntax, but I think he's overcomplicating things by using language like 'handle' and 'hash table'.
var someClass = function () {
// stuff
}
var containingObject = {};
containingObject["someClass"] = someClass;
// same thing as
containingObject.someClass = someClass;
Then you can instantiate the class by calling the containingObject property.
var classInstance = new containingObject["someClass"]()
// or
var classInstance = new containingObject.someClass()
I am trying to accomplish something like in : eval() with variables from an object in the scope
The correct answers suggests using the "with" keyword, but I can't find any examples of someone actually using "with". Can someone explain how to pass multiple variables using "with" into an "eval" expression like in the link above ?
i wouldn't recommend using with or eval except as a learning exercise because either one slows code down, and using them both at once is especially bad and frowned upon by the larger js community.
but it does work:
function evalWithVariables(code) {
var scopes=[].slice.call(arguments,1), // an array of all object passed as variables
A=[], // and array of formal parameter names for our temp function
block=scopes.map(function(a,i){ // loop through the passed scope objects
var k="_"+i; // make a formal parameter name with the current position
A[i]=k; // add the formal parameter to the array of formal params
return "with("+k+"){" // return a string that call with on the new formal parameter
}).join(""), // turn all the with statements into one block to sit in front of _code_
bonus=/\breturn/.test(code) ? "": "return "; // if no return, prepend one in
// make a new function with the formal parameter list, the bonus, the orig code, and some with closers
// then apply the dynamic function to the passed data and return the result
return Function(A, block+bonus+code+Array(scopes.length+1).join("}")).apply(this, scopes);
}
evalWithVariables("a+b", {a:7}, {b:5}); // 12
evalWithVariables("(a+b*c) +' :: '+ title ", {a:7}, {b:5}, {c:10}, document);
// == "57 :: javascript - How to pass multiple variables into an "eval" expression using "with"? - Stack Overflow"
edited to use any number of scope sources, watch out for property name conflicts.
again, i don't normally use with, but this was kinda fun...
I'm trying to use the unobtrusive date picker in an old liferay project (3.6) which I believe is running prototype.js.
I have a call like this:
datePickerController.createDatePicker({formElements:{"elementId":"%d/%m/%Y"}});
made to this:
createDatePicker: function(options) { addDatePicker(options); },
I've not been able to use a variable in place of a hard-coded elementId. I've tried array indexing, dot indexing, string variable, etc. but can't get it to work.
It looks to me like the called function only wants a generally unspecified object, yet if I do one of the above (array, dot, etc.) the browser complains about the bracket (array indexed), the dot (dot indexing), parens or anything other than the expected format.
The underlying called module (addDatePicker) expects formElements so I can't change that.
I don't understand how the browser knows enough to complain about the format of the function's parameter...obviously I'm seriously lacking here!
Pointers greatly appreciated.
e.g.
obj[tag] = 'elementId';
datePickerController.createDatePicker({formElements:{obj[tag]:"%d/%m/%Y"}});
// SCRIPT1003: Expected ':'
You can't put a variable key in an object literal - the syntax requires that the keys be constant values.
You'll need to create the object and fill it, and then pass it:
var obj = {};
var tag = 'elementId';
obj[tag] = "%d/%m/%Y";
// you now have obj = { elementId: "%d/%m/%Y" }
...createDatePicker({ formElements: obj });
Say I have some javascript in yum.js that defines an object like this
/* yum.js */
var Yum = {
MY_ID: "yum#yum.yum",
MAX_PIES: 100,
pies_eaten: [],
pie_string: "blueberry",
eat_pie: function(pietype) {
alert("mmmm, " + pietype);
}
}
I have an HTML file yumtest.html where I want to include yum.js and call some functions. The HTML looks like
<!-- yumtest.html -->
<html>
<head>
<script type="text/javascript" src="yum.js"></script>
<script type="text/javascript">
function run_tests() {
alert("The Yum class is type '" + typeof Yum + "'.");
alert("pie_string is '" + Yum.pie_string + "'.");
Yum.eat_pie("apple");
}
</script>
</head>
<body>
<input type="button" value="Click to run tests" onclick="run_tests()">
</body>
</html>
This works fine: I can click the button and the alert tells me "pie_string is 'blueberry'". This is good.
However, say I want to initialize pie_string to some value returned by a function on another object. I change yum.js so pie_string is created with
pie_string: OtherObj.get_best_pie(),
//... in another file, OtherObj is defined with
get_best_pie: function() {
return "blueberry";
},
When I click the button to run the same code, this doesn't work. yumtest.html tells me "The Yum class is type 'undefined'", and calls to Yum.x properties or methods don't work. Code execution stops.
So I'm wondering:
1) Why does initializing the variable as a function's return value prevent me from including the file in an HTML page?
(my current guess is that the function is unable to execute and return when the file is being parsed and added to the HTML - see Extra Info below)
2) Is there a way to get an error message about the inclusion failing, or is silent failure normal/expected from JavaScript?
Extra Information
The reason I ask is because I see this happening in a Firefox extension. Several variables are being initialized this way using
varname : Components.classes["#mozilla.org/extension-name-string;1"]
.getService(Components.interfaces.nsIPrefBranchInternal),
to load various user preferences. This works fine and the extension runs normally, so presumably this is valid Javascript code. Or at least - valid in some situations? Perhaps the difference between running as actual Javascript and being parsed and included in HTML prevents the function from running and returning a value?
Workaround
I suppose one workaround would be to redefine Yum as a class, and initialize variables in the constructor.
For those who can't or don't want to refactor their code into a class, good news. You can keep the variable defined in-place but still include that file in HTML by defining the variable as a function and calculating the return value inside. For example, we could do
pie_string: function() {
// do some calculations here;
// whatever we would have done in get_best_pie()
return "blueberry";
},
This means you have to change all of you calls of Yum.pie_string to Yum.pie_string() (or perhaps you want to rename the function 'get_pie_string()' so it's clear...). But the javascript file can then be included inside HTML and used normally.
phew!
Say I have some javascript in yum.js that defines a class like this
You don't have a class, in the sense of a template from which instances can be created, you have an object. JavaScript doesn't have classes in the sense that, say, Java does, though you can use JS functions as object constructors if called with the new keyword.
1) Why does initializing the variable as a function's return value prevent me from including the file in an HTML page?
This has nothing to do with whether you are including the JS as an external file - even if you included it in a script block in your html file it still wouldn't work.
The problem is that you are not initialising variables (plural), you are creating a single variable Yum that is being initialised from an object literal. What you are thinking of as "variables" are really "properties" of that object. JS object literal syntax does not allow you to set properties that reference other properties of the same object - if you think about it the object doesn't exist until after that line of code runs and the entire object literal has been evaluated, and of course you can't refer to properties of something that doesn't exist yet.
What you can do is initialise most of the object properties via the object literal and then add your pie_string property afterwards:
var Yum = {
MY_ID: "yum#yum.yum",
MAX_PIES: 100,
pies_eaten: [],
get_best_pie: function() {
return "blueberry";
},
eat_pie: function(pietype) {
alert("mmmm, " + pietype);
}
}
Yum.pie_string = Yum.get_best_pie();
Or define the get_best_pie() function independent of the object literal and then set the pie_string property from it:
function get_best_pie() {
return "blueberry";
}
var Yum = {
pie_string : get_best_pie(),
eat_pie: function(pietype) {
alert("mmmm, " + pietype);
},
// etc
}
2) Is there a way to get an error message about the inclusion failing, or is silent failure normal/expected from JavaScript?
The inclusion of the file is succeeding, but then you are getting a JavaScript error which you are already seeing (the one you quoted).
Regarding your "extra information" section, the code you quote is, again, not creating a variable - with the : syntax it must be an extract from the middle of another object literal, creating a property. If so, it is perfectly valid for it to be set to the return from a function defined as a property of a different object that already exists - I think you'll find that's what's happening.
because there is never a function called get_best_pie for you to call
It is a member of an object, notably an object that doesn't exist yet, so even if you called it by the correct name (Yum.get_best_pie()) it still wouldn't work. you need to define the function outside the object using
function get_best_pie(){
//...
}
in the file somewhere
At object creation time you are trying to assign one of the properties to the return value of another property (a function) declared in the same object!
In javascript, objects have no scope, only functions. Thus, the reference to get_best_pie() in the Yum object's definition will scope to the window as in window.get_best_pie, which does not exist. Because Yum does not exist yet, the function you want to call (whose return value you want to store in pie_string) does not exist yet (until after the object is created).
The reason the libraries you're looking at work is because they apparently are not referring to the container object that is being created. They reference the Components object which must already have been instantiated (or, if not, then the code snippet you gave exists inside a function which is not invoked at object creation time).
Javascript has no explicit way to create getters and setters for properties (so far, though ideas on this are in the works). This means that properties must be simple scalars or objects that are dereferenced without parentheses, or, you must use functions instead and dereference the value using parentheses to invoke the function (rather than refer to it).
You might consider something like the following. It is close to what you want and lets you continue to use property notation rather than switching to a function:
var Yum = {
MY_ID: "yum#yum.yum",
MAX_PIES: 100,
pies_eaten: [],
get_best_pie: function() {
return "blueberry";
},
eat_pie: function(pietype) {
alert("mmmm, " + pietype);
},
init: function() {
Yum.pie_string = get_best_pie();
// other initialization here.
}
};
Yum.init();
They key to why including yum.js fails is that the object we're calling for get_best_pie() is not just any object - it's an 'xpconnect wrapped' object, and so calling get_best_pie() requires extra permissions. If OtherObj was just a regular object that style of initialization would work, and we could include the HTML file. But the browser fails to access OtherObj when trying to include yum.js. Presumably this leaves us with only a partial object definition for Yum, which is why it remains undefined and can't be used.
The reason this fails for the Firefox extension is because Components.classes is an 'xpconnect wrapped' object, and the html page doesn't have permission to access it.
To include yum.js in an HTML file we need to have the proper permissions when initializing pie_string. One way to do that (as mentioned by #ErikE and #nnnnnn) is to initialize the variable inside a function - this allows the caller to ask for permission before initializing, and yum.js can then be parsed and included normally in yumtest.html.
var Yum = {
//... rest of object definition goes here
init : function() {
//important: assigning to 'pie_string' will create a new local variable;
//use this.pie_string instead to reference the property of this class.
this.pie_string = Components.classes["#mozilla.org/extension-string;1"]
.getService(Components.interfaces.nsIPrefBranchInternal);
}
};
Improper Solutions
Here are two sqlutions that don't work. Does anybody know why?
Trying to ask for permission inside a script before including the file
<script type="text/javascript">
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
</script>
<script type="text/javascript" src="yum.js"></script>
Trying to ask for permission and then including the file dynamically
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var script = document.createElement("script");
script.type="text/javascript";
script.src="yum.js";
document.getElementsByTagName("head")[0].appendChild(script);
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.