I'm trying to understand the proper ways to keep options object properties the same on the global scope while changing them locally inside other functions.
What I'm trying to accomplish is this: I have one function that all the others use that accepts specific parameter names. So, I need each of the initial functions that use this shared function to be able to redefine the options object property value and then execute the shared function using the shared parameter name but with its unique property value.
The example below demonstrates my issue. In the example, all of the functions (as shown via console.log) produce the correct value except for the final function. Part of the problem is that cat() and dog() are redefining the value of options.x globally instead of locally. I was hoping that I could use let to change the parameters locally and then pass it to mouse(), but using let options.x = 3; let x = options.x; produces an error.
I've heard that I can return/redefine the global scoped options.x value after the function runs to set it back, but I would prefer to never change the global parameter values in the global scope if possible. That way I don't run the risk of functions accidentally using each other's uniquely defined parameters.
var options = {x: 10}
console.log(options.x + ' original options object');
cat(options);
function cat() {
options.x = 3;
x = options.x;
console.log(x + ' cat = 3'); // this should equal 3
mouse(x);
}
dog(options);
function dog() {
options.x = 7;
x = options.x;
console.log(x + ' dog = 7'); // this should equal 7
mouse(x);
}
// SHARED FUNCTION
function mouse() {
console.log(x + ' mouse = cat() and/or dog()'); // I want this to equal 3 for cat() and 7 for dog() just as it is with x defined inside cat() and dog().
}
console.log(options.x + ' final global scope check = 10'); // I want this to remain 10, not 3. But the changes in cat() are poluting the global definition of options.x.
The code below simply eliminates the options object and seems to work easily. Maybe I should simply avoid using the object at all to avoid these issues?
var param = 1;
console.log(param + ' param should = 1');
cat();
function cat() {
var param = 3;
console.log(param + ' param should = 3');
mouse(param);
}
dog();
function dog() {
var param = 7;
console.log(param + ' param should = 7');
mouse(param);
}
// SHARED FUNCTION
function mouse(param) {
console.log(param + ' should log as whatever value the other functions sent');
}
console.log(param + ' param global check');
The problem is that you are reassigning the object options every time, since the objects are reference types. Even if you would do something like this in your function: newOptions = options you would still reference your old options object and any changes on the "new" newOptions object would also change the options object.
However if you want to get the exact same object and change it without affecting the other one you must clone it. You can achieve this in JavaScript with Object.create() method. You can read more about it here.
So your entire code would then look like this:
var options = {x: 10}
console.log(options.x + ' original options object');
cat(options);
function cat(options) {
newOptions = Object.create(options);
newOptions.x = 3;
x = newOptions.x;
console.log(x + ' cat = 3'); // this should equal 3
mouse(x);
}
dog(options);
function dog(options) {
newOptions = Object.create(options);
newOptions.x = 7;
x = newOptions.x;
console.log(x + ' dog = 7'); // this should equal 7
mouse(x);
}
// SHARED FUNCTION
function mouse(x) {
console.log(x + ' mouse = cat() and/or dog()'); // I want this to equal 3 for cat() and 7 for dog() just as it is with x defined inside cat() and dog().
}
console.log(options.x + ' final global scope check = 10'); // I want this to remain 10, not 3. But the changes in cat() are poluting the global definition of options.x.
Related
I am wondering if this way is correct:
var userInput = confirm('roll die?');
var rollDie = function() {
while(userInput) {
var dieSide = Math.floor(Math.random() * 6);
document.write('you rolled a ' + (dieSide + 1));
userInput = false;
}
}
rollDie(userInput);
Or do I need to write var rollDie = function(userInput) {
Javascript works with scopes. You call the function 'rollDie' from a scope where 'userInput' is a variable. The function 'rollDie' has its own scope. In your example there's no variable 'userInput' in the scope of the function rollDie. There for javascript is looking for the variable in an outer scope and find the variable. So your program is working but the code is not good code.
because you call the function rollDie with the parameter 'userInput' you should add 'userInput' as param to the function rollDie. var rollDie = function(userInput) {} It is always better the give a function all the params the function needs to execute. This prefents problems with the 'this' scope in javascript when you call the function in an other context and make it easier to refactor your code.
twoStrars is quicker :)
You should understand the difference and then choose for yourself.
Basically, you have these two patterns:
x as global variable:
var x = 1;
var f = function() {
console.log('x in f:', x);
x = 2;
}
console.log('x before f:', x);
f();
console.log('x after f:', x);
and x as argument:
var x = 1;
var f = function(x) {
console.log('x in f:', x);
x = 2;
}
console.log('x before f:', x);
f(x);
console.log('x after f:', x);
There two main differences:
if f uses a global variable, it is going to modify the global variable, whereas if it works with an argument, it does not affect any variables visible outside, i.e. the first code writes x after f: 2, whereas the second writes x after f: 1
if f uses a global variable, then it becomes less convenient to pass it different values. With an argument, you don't even need a global variable, you can call f(1); f(2); f(3456);. With global vaiables, you would accomplish the same with var x=1; f(); x=2; f(); x=3456; f();.
Instead of going more into details, I'll give you a link: Why are global variables evil?
Anyway, there are cases when global variables are good! I would make a global variable for a value which is constant and used by multiple functions (var GRAVITY = 9.81; or var BASE_URL = "https://stackoverflow.com/";)
This line:
rollDie(userInput);
…means you're trying to pass a value into your rollDie() method. This isn't strictly necessary because you have this variable declared globally:
var userInput = confirm('roll die?');
So, you could pass nothing in if you wanted, but if you want to write much cleaner code it's preferable to avoid having these global variables around as much as you can. The way you've written it – passing in a value to your function – is much nicer, so it's better to write var rollDie = function(userInput) {.
So, I'm still reading Apress Pro Javascript Techniques and i'm having troubles with closures.
As John Resig states:
Closures allow you to reference variables that exist within the parent function.
However it does not provide the value of the variable at the time it is created; It provides the last value of the variable withing the parent function. The most common issue under which you'll see this occurr during a for loop. There is one variable being used as an interaor (e.g., i). Inside of the for loop, new functions are being created that utilize the closure to reference the iterator again. The rpoblem is tat the time the new closured functions are called, they will reference the last value of the iterator (i.e., the last position in an array), not the value taht you woul expect.
Then he presents, in listing 2-16 an example using anonymous functions to induce scope.
/**
* Listing 2-16. Example of Using Anonymous Functions to induce the
* Scope Needed to Create Multiple Closure-Using Functions
*/
// An element with an ID of main
var obj = document.getElementById("main");
// An array of items to bind to
var items = ["click", "keypress"];
for (var i = 0; i < items.length; i++) {
// Use a self executed anonymous function to induce scope
(function() {
// Remembre the value within this scope
var item = items[i];
// Bind a function to the element
obj["on" + item] = function() {
// item refers to a parent variable that has been successfully
// scoped within the context of this loop
alert("thanks for your " + item);
};
})();
}
This example works as expected, and the behavious of the main object is correct.
The in the following, it uses another time a self-executing function to induce scope, during an iteration.
The purpose of the function is to create an object, defining getters and setters for all its properties. In this case, the example does not work.
/**
* Listing 2-25. Example of Dynamicaaly Generated Methods That Are Created
* When a New Object is instantiated
*/
// Create a new user object that accepts an object of properties
function User(properties) {
// Iterate thorugh the properties of the object, and make sure
// that it's properly scoped (sas discussed previously)
var that = this;
for (var i in properties) {
(function() {
console.log("property: " + i);
// Create a nwe getter for the property
that["get" + i] = function() {
return properties[i];
};
// Create a new setter for the property
that["set" + i] = function(val) {
properties[i] = val;
};
})();
}
}
// Create a new user object instance and pass in an object of
// properties to seed it with
var user = new User({
name: "Bob",
age: 44
});
// Just note that the name property does not exists, as it's private within the
// properties object
alert(user.name == null);
// However, we're able to access its value using the new getnaem()
// method that was dynamically generated
console.log("name: " + user.getname()); // name: 44 :(
alert(user.getname() == "Bob");
// Finally, we can see that it's possible to set and gt the age using
// the newly generated functions
user.setage(22);
alert(user.getage() == 22);
Instead, after passing the i parameter as argument to the self-executing function,it works.
for (var i in properties) {
(function(prop) {
console.log("property: " + i);
// Create a nwe getter for the property
that["get" + prop] = function() {
return properties[prop];
};
// Create a new setter for the property
that["set" + prop] = function(val) {
properties[prop] = val;
};
})(i);
}
My question is:
Why in the first case (for loop), it is not necessary to pass the i parameter, while
in the second (for in) it is needed in order to work properly?
In the first example you are declaring a copy of the contents of the array element in a local variable:
var item = items[i];
As the inline comment says, here we're remembering the value within the closure scope.
In the 2nd example, instead of passing i as a parameter, you could also have done:
(function() {
var prop = i; // See here!
console.log("property: " + i);
// Create a nwe getter for the property
that["get" + prop] = function() {
return properties[prop];
};
// Create a new setter for the property
that["set" + prop] = function(val) {
properties[prop] = val;
};
})();
Which makes both examples more similar.
Similarly, you could also alter the first example to pass i as a parameter, rather than declaring it verbosely in a variable.
(function(item) {
// Bind a function to the element
obj["on" + items[item] = function() {
// item refers to a parent variable that has been successfully
// scoped within the context of this loop
alert("thanks for your " + items[item]);
};
})(i);
It's arbitrary as to whether you declare a local copy of the variable using a var statement, or pass it as a parameter into your self executing function.
Edit:
#Zecc bought up a good point in the comments, which I'd like to explain:
(function (i) {
// using `i` works as expected.
}(i));
Where as:
(function () {
var i = i;
// using `i` doesn't work... i = undefined.
}());
This is because the var variable = value statement is effectively:
(function () {
var i;
i = i;
}());
and the var keyword always assigns the variable(s) following it with the value undefined.
It's because you're referencing i within that.get and that.set in the second case, while in the first case, you're referencing item, which is invariant.
In the first example, the self-executing function has access to the current value that the reference i points to (since it is executed right away), it makes a copy of the current item with item=item[i] so that the inner function for the event handler, which will be called later, will reference the correct item.
If you wouldn't do that the inner function, the event handler, since it doesn't execute right away would reference to the i in the top functions' scope; since the for will be long finished executing when you get to click the thing, it would reference to the last value of i most probably.
By keeping the current item with item=item[i] in the self executing function you get in item the correct current item for each one of them and so the event-handler can have access to the right value, the last value placed in each of the local item vars.
Is there a way to change the root object in JavaScript?
For example, in browsers, the root object is "window". So
X = 5;
console.log(Y);
is the same as:
window.X = 5;
console.log(window.Y);
What I want to do now, is changing this root object, so when I do the following:
X = 6;
Reason why I need this:
In Node.js applications, every part of the program can access the global object. That's a big problem because every script that is executed by a Node.js webserver can add new variables to it. They will be there until the webserver is restarted. I want to avoid this by changing the global object.
Update
I've tested the following code and got a really interesting result.
What did you expect of the following code?
var X = {A:"a",B:"b"};
with(X){
A = 5;
C = 7;
}
for(a in X){
console.log(a+" is "+X[a]);
}
/*
Expected Console Output:
A is 5
B is b
C is 7
Real Console Output:
A is 5;
B is b;
*/
Is there a way to get output as I expected it?
Update
I've now tested the module system with the following code.
//program.js
var t = require("./module.js");
t.Test();
console.log(A);
//module.js
A = 5;
exports.Test = function(){
console.log("hello world!");
}
The output was:
hello world!
5
This tells me, that the variable "A" defined in module.js was added to the global object of program.js. The module does not solve my problem, either.
There is the with statement, but it is not recommended and forbidden in strict mode.
It is better to refer to the variable holding the object explicitly.
In response to updated question:
with will search up the scope chain until it finds an object with a matching property or gets to window. It is no good for defining new properties on an object.
var X = { A: 5, B: 8, C: 7};
with(X){
console.log(A, B, C);
}
If you're talking about variables, JavasScript has function scope.
X = 5; // global variable
console.log( window.X ); // 5
(function() {
var X = 6; // declare a local variable by using the "var" keyword
console.log( X ); // 6
})();
console.log( window.X ); // 5
Otherwise, you can create an Object, and add properties to it.
X = 5;
console.log( window.X ); // 5
var obj = {};
obj.X = 6;
console.log( obj.X ); // 6
console.log( window.X ); // 5
EDIT: Adding another possible solution that could be used.
You could invoke an anonymous function, but set the context of the function to your X object. Then this in the function will refer to X.
var X = {};
(function(){
this.A = 5;
this.B = 8;
this.C = 7;
}).call(X);
for(a in X){
console.log(a+" is "+X[a]);
}
The .call() method (as well as the .apply() method) allow you to explicitly set the thisArgof a calling context. The first argument you pass will be howthis` is defined in the context of the invocation.
Or just pass X in as an argument.
var X = {};
(function(X){
X.A = 5;
X.B = 8;
X.C = 7;
})(X);
for(a in X){
console.log(a+" is "+X[a]);
}
Though the simplest is to simply reference it (as I noted in my answer above).
var X = {};
X.A = 5;
X.B = 8;
X.C = 7;
for(a in X){
console.log(a+" is "+X[a]);
}
or use a module pattern:
/****** I'm guessing at the use of "global" here ********/
global.myNamespace = (function(global,undefined) {
// define the object to be returned
var X = {};
// define private local variables
var a_local = 'some value';
var another_local = 'some other value';
// define private functions
function myFunc() {
// do something with local variables
}
// give the return object public members
X.someProperty = 'some value';
X.anotherProperty = 'another value';
X.publicFunc = function() {
//do something with the local variables
// or public properties
};
X.anotherFunc = function() {
//do something with the local variables
// or public properties
};
// return the object
return X;
})(global);
console.log(myNamespace);
I found a way in EcmaScript 6 to adjust with(context) { ... }, so that any new variables we assign will go into the context object, not the global / window object.
Thanks to this article Metaprogramming in ES6: Part 3 - Proxies for teaching me about the ES6 Proxy feature.
In the proxy:
We override has to return true, so our context appears to have all properties, and when we set any variable it will go to the object.
We override get to get the property from our context, or if it isn't really there, we get the property from an up object (which defaults to the global window).
I know that with is frowned upon, but this technique enables to create mutable extensible modules where we can access members conveniently as foo rather than Module.foo, and I don't think it is unsafe or ambiguous.
function scope(x, up=window) {
return new Proxy(x, {
has: (o, k) => true,
get: (o, k) => k in o ? o[k] : up[k]
});
}
var Test = {};
with (scope(Test)) {
x = 1;
y = 2;
add_x = function(y) {
console.log(x + y);
}
}
Test.add_x(10);
with (scope(Test)) {
x = 3;
add_y = function(x) {
console.log(x + y);
}
}
Test.add_x(20);
Test.y = 5;
Test.add_y(30);
I'm a total Javascript newb, and I'm trying to wrap my head around OLN. What I'm encountering is that, when calling an object method from another method on the same object, the value of local value of 'this' in the called method is changing. Here's my code:
var generator = {
generateForLevelSkillAndCount : function(level, skill, count) {
var functionCall = this['generate_' + level + '_' + skill];
return functionCall(count);
},
generate_0_4 : function(count) {
return this.generate_generic_dots(count, 3);
},
generate_generic_dots : function(count, maxDots) {
/* do cool stuff and return it */
}
};
So, I call generator.generateForLevelSkillAndCount(0, 4, 20) and it works properly, calling generate_0_4(count). However, this is where it fails, with Chrome's Javascript console telling me "Uncaught TypeError: Object [object DOMWindow] has no method 'generate_generic_dots'."
I know enough to know that the problem is that the value of this in generate_0_4 is a DOMWindow object, rather than generator (which is what this is pointing to in generateForSkillLevelAndCount but I can't figure out why that would possibly be happening.
Update: I updated the example code per CMS's suggestion to get rid of eval, but the same error is being returned, so it's not just an eval bug.
In JavaScript, the context object (this) is set to the "global object" (window, in browsers) unless the method is accessed as an object property. Therefore:
var foo = { bar: function() { alert(this.baz); }, baz: 5 };
var bar = foo.bar;
var baz = 3;
foo.bar(); // alerts 5, from foo
foo["bar"](); // alerts 5, from foo
bar(); // alerts 3, from the global object
Note that all three function calls are to the exact same function!
So, in your code, you're assigning the desired method to functionCall and calling it directly, which causes the function to use window as its context object. There are two ways around this: access the method as an object property or use .call() or .apply():
function generateForLevelSkillAndCount1(level, skill, count) {
return this['generate_' + level + '_' + skill](count);
}
function generateForLevelSkillAndCount2(level, skill, count) {
var functionCall = this['generate_' + level + '_' + skill];
return functionCall.call(this, count);
}
First of all, I would encourage you to avoid eval where you don't need it, for example, in your fist function:
//...
generateForLevelSkillAndCount : function(level, skill, count) {
var functionCall = this['generate_' + level + '_' + skill];
return functionCall(count);
},
//...
You can use the bracket notation property accessor instead eval, it's unnecessary in this case.
Now, I guess you are trying your code on the Chrome's Console, and eval is failing because the console has a bug, when eval is invoked from a FunctionExpression (such as generateForLevelSkillAndCount), the evaluated code uses the Global context for its Variable Environment and Lexical Environment.
See this answer for more information on this bug.
Edit: After re-reading your code, the problem happens because you lose the base object reference when you assign the function to your functionCall variable, you can either:
Invoke the function directly, without using that variable:
//...
generateForLevelSkillAndCount : function(level, skill, count) {
this['generate_' + level + '_' + skill](count);
},
//...
Or still use your variable, but persist the this value:
//...
generateForLevelSkillAndCount : function(level, skill, count) {
var functionCall = this['generate_' + level + '_' + skill];
return functionCall.call(this, count);
},
//...
More info on this...
You can control the execution context of the method call by using call():
var generator = {
generateForLevelSkillAndCount : function(level, skill, count) {
return this['generate_' + level + '_' + skill].call(this, count);
},
generate_0_4 : function(count) {
return this.generate_generic_dots.call(this, count, 3);
},
generate_generic_dots : function(count, maxDots) {
/* do cool stuff and return it */
}
};
I'm as baffled as you are, but have you considered checking out what happens if you call generate_0_4 explicitly, instead of parsing it through eval()?
When you call generate_0_4 dynamically (using an implicit to_string()) it is returned to generateForLevelSkillAndCount as an ad-hoc function. Because it's in Window scope rather than Object scope, it can't reference this, and the internal call fails because this doesn't exist in that context.
Here's how to see what's happening:
generate_0_4 : function(count) {
throw(this);
return this.generate_generic_dots(count, 3);
},
With generator.generateForLevelSkillAndCount(0, 4, 1); you get [object Window] or [object DOMWindow].
With generator.generate_0_4(1); you get what you're expecting (and what works): [object Object] or #<an Object>.
This is a feature of Javascript: the value of this will depend on the object from which the function was called, not where it was defined. (Which makes sense when functions are first-class objects themselves.) this in most other contexts refers to the window object.
There are two common workarounds, using a wrapper function:
function bind(func, obj) {
return function() {
func.apply(obj, arguments);
}
}
or using a closure:
var self = this;
function generate_blah() {
// use self instead of this here
}
In your case, though, simply replacing
var functionCall = this['generate_' + level + '_' + skill];
return functionCall(count);
with
this['generate_' + level + '_' + skill](count);
would do the trick.
Here's some JavaScript:
linkElem.click(function () {
var data = linkElem.data();
alert(''+data.mls + ' ' + data.id);
});
It works.
linkElem is a local variable that I create in a loop inside a function. I assign some data to it with jQuery's .data(). If I did not call .click(), linkElem would be reassigned during the loop and then recycled after the function returns. However, I have created an anonymous function which references linkElem. So I am no longer sure what is going on.
My guess is that all of the anonymous functions and linkElems created during the loop are given UIDs of some kind and moved to persistent/global scope. Is this correct? Gratuitous detail would be much appreciated.
Yes, your description is pretty close. The local storage for a Javascript function call is just a block of memory allocated for local variables. If you "capture" that by creating another function inside a called function, then the storage is retained and the local variables carry on with their lives, unaware that the function that gave them birth might be long dead.
It's important to keep in mind that only functions create such storage — things like brace-enclosed loop bodies are not separate storage areas. Thus a common error is to declare a variable in a function and re-use it among several functions created in a loop. That's not inherently wrong, but the effect can be surprising:
function whatever() {
for (var i = 0; i < 3; ++i) {
setTimeout(function() { alert(i); }, 5000);
}
}
If you run that, you'll see three alerts that all say "3". Why? Because they all share the same "i" variable. You can avoid that by introducing another function layer:
function whatever() {
for (var i = 0; i < 3; ++i) {
setTimeout((function(private_i) { return function() { alert(private_i); }; })(i), 5000);
}
}
The "wrapper" function is just there to provide a local variable (the parameter "private_i") whereto the loop variable "i" can be copied.
However, I have created an anonymous function which references linkElem. So I am no longer sure what is going on.
It still gets reassigned, unless you are wrapping it in another level of scope (NB: another function).
Consider the following:
for (var j = 0;j < 10;j += 1) {
arrayOfLinks[j].onclick = function () {
alert(j);
};
}
In this case, all of those links would alert 10 when clicked, because j is outside of the scope and is being updated.
If you're creating linkElem in the same way, you are likely to only get the result of the last linkElem in the loop.
This is a better way:
linkElem.click(function () {
var data = $(this).data(); // no longer dependent on `linkElem` reference
alert(''+data.mls + ' ' + data.id);
});
Please refer to this How do JavaScript closures work?
This may help you understanding closures.
Whenever you see the function keyword within another function, the inner function has access to variables in the outer function.
function foo(x) {
var tmp = 3;
function bar(y) {
alert(x + y + (++tmp));
}
bar(10);
}
foo(2)
This will always alert 16, because bar can access the x which was defined as an argument to foo, and it can also access tmp from foo.
That is a closure. A function doesn't have to return in order to be called a closure. Simply accessing variables outside of your immediate lexical scope creates a closure.
function foo(x) {
var tmp = 3;
return function (y) {
alert(x + y + (++tmp));
}
}
var bar = foo(2); // bar is now a closure.
bar(10);
The above function will also alert 16, because bar can still refer to x and tmp, even though it is no longer directly inside the scope.
However, since tmp is still hanging around inside bar's closure, it is also being incremented. It will be incremented each time you call bar.
The simplest example of a closure is this:
var a = 10;
function test() {
console.log(a); // will output 10
console.log(b); // will output 6
}
var b = 6;
test();
When a Javascript function is invoked, a new execution context is created. Together with the function arguments and the parent object, this execution context also receives all the variables declared outside of it (in the above example, both 'a' and 'b').
It is possible to create more than one closure function, either by returning a list of them or by setting them to global variables. All of these will refer to the same x and the same tmp, they don't make their own copies.
[you]: Fascinating, tell me more!
Here the number x is a literal number. As with other literals in JavaScript, when foo is called, the number x is copied into foo as its argument x.
On the other hand, JavaScript always uses references when dealing with Objects. If say, you called foo with an Object, the closure it returns will reference that original Object!
function foo(x) {
var tmp = 3;
return function (y) {
alert(x + y + tmp);
x.memb = x.memb ? x.memb + 1 : 1;
alert(x.memb);
}
}
var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);
As expected, each call to bar(10) will increment x.memb. What might not be expected, is that x is simply referring to the same object as the age variable! After a couple of calls to bar, age.memb will be 2! This referencing is the basis for memory leaks with HTML objects.