I'm still learning so I might be wording this wronf (which might be why I haven't had any luck in finding an answer by searching thus far) or I might be doing this wrong all together.
Basically I'm trying to create a function that, when called, will create an object (which is made up of other objects). If I create this Characters object outside of the function, I can access it as I'd expect to but when done inside the function, I can't seem to access it. I'm testing this in the console built into Chrome.
Here's my code.
function q1(ans) { //Are you male or female?
"use strict";
//store all above objects into new object
if (ans === "male") {
var Characters = {
spike: spike,
jet: jet,
vicious: vicious
};
}
if (ans === "female") {
//store all above objects into new object
var Characters = {
faye: faye,
julia: julia,
ed: ed
};
}
return Characters;
}
(The objects spike, jet, vicious (etc) are defined elsewhere and can be accessed without any issue.)
If I call my function q1("male"); the console seems to log that the Characters object is created but if I then try to access it by just typing Characters I get an error (ReferenceError: Characters is not defined). If I were to create this Characters object outside of a function and then try to access it as mentioned above, this works fine. This leads me to believe I'm not returning it properly (or I'm doing something else wrong entirely). What am I missing here?
Thanks in advance for any assistance.
this is due to scope. this means that character is only live inside the function so you can't access it outside of the function. what you have to do is set a new variable equal to the return value of the function when u call it. That way you can reference the returned variable outside the function and use it later on.
var male = q1("male")
Since you have already defined characters outside the scope of q1 you will need to reassign the Characters variable by returning Characters from the method.
So reassign the Characters and it should fix the problem.
One more very bad way is to not use var Characters, instead use Characters = ..., but bear in mind using global variable is not at all recommended and is a very bad practice.
Related
I have the following code that I am trying to use to register a callback on an array of buttons. But I cannot seem to understand how I can bind the strings that I would need in the callback. Any suggestions would be much appreciated!
for (var i = 0; i < this.car_types.length; ++i) {
this.select_car_buttons.push($("#button_select_car_" +
this.car_types[i].car_type));
this.select_car_buttons[this.select_car_buttons.length - 1]
.click(function() {
console.log(this.car_types[i].car_type);
}.bind(this));
}
Somehow the this object is the button itself and not the object under whose scope the function is called.
EDIT : It seems like the this object was indeed being passed in properly. The issue is that the variable i is not going out of scope and is being captured by reference not by value. How should I go about solving this problem?
Also there seem to lots of such issues with JavaScript as a language (at least things that can be classified as an issue considering the semantics employed by the traditional C family languages such as C and C++ to be correct), is there some article I can read that warns me against these types of issues?
ANOTHER EDIT : On trying making a closure with the value of i captured by value I tried the following code
this.select_car_buttons[this.select_car_buttons.length - 1]
.click((function(scoped_i) {
return function() {
console.log(this.car_types[scoped_i].car_type);
}.bind(this);
}(i)));
But I get the following error in Safari
TypeError: undefined is not an object (evaluating 'scoped_i')
EDIT : The same code works in Firefox and Chrome but not in Safari!
This is a scope issue. For modern browsers (that support ES6) you could just change var to let in your for loop and it would get fixed.
for (let i = 0; i < this.car_types.length; ++i)
Quoting the MDN docs
The let statement declares a block scope local variable, optionally initializing it to a value.
For more global support (non ES6 support) use an immediately invoked function to create extra scope for the variable (which you will pass as a parameter)
this.select_car_buttons[this.select_car_buttons.length - 1]
.click((function(scoped_i) { // IIF starts here, the new variable is called scoped_i for verbosity
return function() { // your original function code goes here
console.log(this.car_types[scoped_i].car_type); // use the newly scoped variable
}.bind(this);
}.bind(this)(i))); // end and execute the IIF while passing the i variable to it
Yes, this structure do make a lot of closures and make code very hard to read. Since you use jQuery, there are a much better way to solve this problem which saves the data in html:
html:
<button class="select-car" data-car-type="CarA">Select CarA</button>
<button class="select-car" data-car-type="CarB">Select CarB</button>
<!-- And a lot of buttons -->
js:
var selectCarOnClick = function() {
console.info($(this).data('car-type'));
};
$('button.select-car').click(selectCarOnClick);
Live exmaple: http://codepen.io/SCLeo/pen/VaQYjW
If you have a lot of other information to store and you want to use a object to store them instead of DOM, you can save car-name or car-id instead of car-type.
Here is the document about $.data: https://api.jquery.com/jquery.data/
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...
To create an IDE that would autocomplete all variables the user declares but would be oblivious to other variables such as Math.PI or even the module Math, the IDE would need to be able to identify all identifiers relating to variables declared by the user. What mechanism could be used to capture all such variables, assuming you already have access to the AST (Abstract Symbol Table) for the program?
I am using reflect.js (https://github.com/zaach/reflect.js) to generate the AST.
I think it's pretty much impossible
Here is why I think it's pretty much impossible without executing it:
Let us go through the unexplored parts, from easy to hard.
Easy to catch:
Function scope is missed here:
(function(x){
//x is now an object with an a property equal to 3
// for the scope of that IIFE.
x;
})({a:3});
Here is some fun dirty tricks for you all.:
Introducing... drum roll... Block Scoping!!
with({x:3}){
x;//x is now declared in the scope of that with and is equal to 3.
}
try{ throw 5}catch(x){
x // x is now declared in the scope of the try block and is equal to 5;
}
(people reading: I beg you to please not use these last two for actual scoping in code :))
Not easy:
Bracket notation:
var n = "lo";
a["h"+"e"+"l"+n] = "world"; // need to understand that a.hello is a property.
// not a part of the ast!
The really hard parts:
Let us not forget invoking the compiler These would not show up in the AST:
eval("var x=5"); // declares x as 5, just a string literal and a function call
new Function("window.x = 5")();// or global in node
In node.js this can also be done with the vm module. In the browser using document.write or script tag injection.
What else? Of course they can obfuscate all they want:
new Function(["w","i","n","dow.x"," = ","5"].join(""))(); // Good luck finding this!
new Function('new Function(["w","i","n","dow.x"," = ","5"].join(""))()')();// Getting dizzy already?
So what can be done?
Execute the code, once, in a closed, timed environment when you update the symbol table (just the relevant parts)
See what's the generated symbol table is from the execution
Boom, you got yourself a symbol table.
This is not reliable but it's probably as close as you get.
The only other alternative I can think of, which is what most IDEs are doing is to simply ignore anything that is not:
object.property = ... //property definition
var a = ... //scoped
b = ... //global, or error in strict mode
function fn(){ //function declaration
object["property"] //property with a _fixed_ literal in bracket notation.
And also, function parameters.
I have seen no IDE that has been able to deal with anything but these. Since they're the most common by far, I think it's perfectly reasonable to count those.
By adding them onto am object that already exists....ie
window.mynewvar = 5;
function mynewfunc() {
}
I prefer to declare one Javascript file for my all website. I am trying to decrease the usage of global variables. My examples at the below, in both case each object has a myName field.
I would like to know when they are initialized?
And In terms of memory and efficiency which one is more effective?
For variable a, is declaring a.myName the same as global "var myName = Rebecca" ?
var a = {
myName : 'Rebecca' ,
sayHello : function() {
console.log(this.myName);
}
};
var b = {
myName : function() {
return 'Rebecca';
},
sayHello : function() {
console.log(this.myName());
}
};
Thanks
I believe these will be initialized identically (i.e. when the code is reached). What's different is what's happening when they are initialized and where the load is placed when their data is actually required.
To me, it would depend a lot on what you were expecting to have in myName. If it were just a string, I'd avoid the function and go with choice a. On the other hand, if there were a great deal of logic involved and that logic might not need to be invoked (for example, if it only gets executed when a user clicks on a button or the application reaches a certain state), I'd go with choice b. As I understand it, the function does consume memory and won't get garbage collected (which is a minus), but it also won't consume CPU resources until it's actually needed (which can be a huge plus).
I'm not sure I understand the question, but I'd say it's not the same. If the only member of a is myName then the two are equivalent (both are occupying the global namespace. But if you have multiple properties, the savings become obvious. From your examples, I think it's clear you understand this, so again I may not understand the question.
They will be initialized when the statements are first encountered. In a, 'Rebecca' is initialized as the value for the myName key. In b, it's just data internal to the myName (anonymous) function. a will be slightly more efficient because it avoids a function call. I also find it more readable in this simple example.
I find the choice to put everything in a single file questionable. In some cases, you want a modular design. And since you're worried about efficiency (albeit perhaps prematurely), note that having one big file can actually hurt performance if pages include code they don't need.
1) They are initialized when the script is processed in the browser, unless you declare the objects in an event handler. In that case the object is created when the event script is executed.
2) In terms of efficiency, a will probably be more efficient. Note though that in the first case you use a.myName and in the second b.myName() to get the value of the property.
3) No. If you assign a value to a property of an object, you always have to get that value through the object. In this case either a.myName or a['myName'].
a doesn't make any sense, because you're logging a function reference. B is the way to go, since you're actually invoking the method, using ().
i am trying not to repeat the selector and get to its children via a the same objects parentElment declared variable.
I tried:
testimonialsBelt={
parentElment:$(".testimonialsCntnr ul"),
childrenElem:this.parentElment.children().length
}
I also tried:
testimonialsBelt={
parentElment:$(".testimonialsCntnr ul"),
childrenElem:$("testimonialsBelt.parentElment").children().length
}
but i keep on getting a undefined when calling alert(testimonialsBelt.childrenElem).
is there anyway to get the jquery object with object literals?
What is the rule? when can i use this and when must i have the full path? (in this case testimonialsBelt.parentElment).
i am trying to have all these variables in one object called testimonialsBelt. i know i can do this with loose javaScript.
Thanks
In object literals, you can only use this to refer to the object that you're declaring inside of a function. Try the following:
var testimonialsBelt = {
parentElment: $(".testimonialsCntnr ul"),
childrenElem: function() {
return this.parentElment.children().length;
}
};
The difference in calling childrenElem is that instead of using alert(testimonialsBelt.childrenElem), you would instead have alert(testimonialsBelt.childrenElem()).
Otherwise, this refers to the current scope that you are in (typically window if you are declaring the object literal as a global).
Addressing your edit: I'm not sure what you mean by "loose javascript," but I assume you mean as simple as possible. In which case, you can try the following, although I'm not a big fan of the method. It's more verbose, but is easy to understand.
var testimonialsBelt = {
parentElment: $(".testimonialsCntnr ul")
};
testimonialsBelt.childrenElem = parentElment.children().length;
This gives you an object where childrenElem is static (it doesn't change) and avoids calling $(".testimonialsCntnr ul") twice. However, if you expect $(".testimonialsCntnr ul").children() to change, then you will need to use my first example.
In JavaScript (not ECMAScript) you can use this:
testimonialsBelt={
parentElment:#1=$(".testimonialsCntnr ul"),
childrenElem:#1#.children().length
}