JavaScript: Initializing inner variable after initializing object - javascript

Update: rewriting question because original question had false assumptions (I was running code in a console that had already initialized the variables I thought were undefined).
This makes sense:
var obj = { 'whichScope': a };
obj.whichScope; //"ReferenceError: a is not defined"
But then how come this next example doesn't throw an error? And if the second line is getting run before the first line, why doesn't obj.whichScope resolve to "After"?
var obj = { 'whichScope': a };
var a = "After";
obj.whichScope; //"undefined"
If "var a" in the previous example runs before obj is initialized, does any part of 'a = "After";' also run before obj is initialized?
var a = "Before";
var obj = { 'whichScope': a };
a = "After";
obj.whichScope; //"Before"
If whichScope refers to a function that returns 'a' then it does resolve to "After" in the last example.

That is called variable hoisting.
References:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/var#var_hoisting
http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting
http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/
Variables (declared with var) and functions are hoisted to the top of their scope.
So technically, your script is like this:
var a; // = undefined
var obj = { 'whichScope': a };
a = "After";
Although your first example doesn't do what you say. The value of obj.whichScope is undefined, as expected.
DEMO: http://jsfiddle.net/pRQwK/
As for your last statement, If whichScope refers to a function that returns 'a' then it does resolve to "After" in the second example. - that is because the value of a isn't captured (by a closure). When setting a property, its value is captured immediately (unrelated to a closure).

Related

Why is the value of this variable undefined

I am currently learning javascript and came across this example
var t = function()
{
this.name = "Jam";
no = "123";
}
console.log(t.no); //Undefined
var m = new t();
console.log(m.name);
Why is the first statement undefined ?
t is a function object. As any other object, the function may have properties assigned. So in order your code to work you shall assign "123" to no property of your function (line A):
var t = function()
{
this.name = "Jam";
}
t.no = "123"; // line A
console.log(t.no); // "123"
var m = new t();
console.log(m.name);
Why is the first statement undefined ?
Because t doesn't have a property no.
First of all, the code inside the function, namely
this.name = "Jam";
no = "123";
is only executed when the function is called. You are doing this with var m = new t();, which comes after console.log(t.no);.
Secondly, no = "123"; does not create a property on the function object. It will attempt to set the value of variable no. Since the variable doesn't exist in your example, that line will either create a global variable no, or throw in error if your code is in strict mode.
Consider the following example:
var no = 21;
function foo() {
no = 42;
}
console.log(no); // 21
foo();
console.log(no); // 42
Because t is a function, that would be executed by t();. no on the other hand is a global scooed variable that is reached without prefix from everywhere.
t is a function expression. You you can access a returned object of a function like t().no or you can create a new object by using the function as a constructor like this
myT = new t()
console.log(t.no);
But your no variable is just a global variable inside the function and it is not a part of what it returns nor it is not attached to the returning object of the constructor function.
Here is a very good tutorial which covers all these topics at depths.

Vanilla JS | Invokable object that also contains property

I was asked in some assignment to do the following:
fun4(): returns a object that can be invoked as a function. this object should also has a ‘k’ property with null value ( so fun4()() should do something)
The first part of the question is easy and understandable.
The second one is the issue for me.
How can I create an object in JS that can be invoked and accessed statically.
To simplify:
Can one create an object that behaves as follows:
> let o = CustomObject;
> o.k
< null
> o()
< //Some value returned here from the function
Thanks!
It looks very straightforward to me...
let CustomObject = function(){ return "hello"; }
CustomObject.k = null;
This will pass your Acceptance criteria
As for vanilla js, this is what you are looking for:
var fun4 = function () {
let val = function () { console.log("hello"); };
val.k = null;
return val;
}
fun4() // returns the function
fun4()() // logs 'hello'
fun4().k // returns null
Given one of your comments above, one thing to watch out for is your use of let and var. In this case, var could be used instead without any difference (when val falls out of scope when the function returns, the variable will be released for garbage collection). However, when you are running this within the console (and when not inside a function or other well-defined and isolated scope), variables created using let will get destroyed after every invocation -- in other words, whenever you press return. The way to demonstrate this is to compare these:
var test = function () {
let x = 1;
let x = 1; // this will cause an immediate syntax error when the script is parsed;
}
On the other hand:
> let x = 1;
< undefined
> let x = 1; // In the console, this will not raise any error/exception
// because the scope is cleared after the line above has
// executed and returned
< undefined
You can see a 'k' property as below.

Why is no ReferenceError being thrown if a variable is used before it’s declared?

I’m trying to wrap my head around the behavior of reference errors thrown in JavaScript.
In the following example, a ReferenceError is thrown at the second line, and execution breaks:
var obj = {};
obj.func1 = func2;
alert('Completed');
Whereas in this example, the code completes successfully, though obj.func1 remains undefined:
var obj = {};
obj.func1 = func2;
var func2 = function() {
alert('func2');
};
alert('Completed');
My assumption was that an error would be thrown at the second line just the same, and when that wasn’t the case, I’d have expected obj.func1 to properly reference func2, but I’ve been double blind-sided. So what exactly is going on here?
This is due to Javascript variable declaration "hoisting".
A variable declared with var is visible everywhere in the function, so there's no Reference Error. However, it doesn't actually receive its value until you execute the statement that initializes it. So your second example is equivalent to:
var func2;
var obj = {};
obj.func1 = func2;
func2 = function() {
alert('func2');
};
alert('Completed');
In this rewrite, you can see that the variable exists when you perform the assignment to obj.func1. But since it doesn't yet have a value, you assign undefined to obj.func1. Assigning to func2 later doesn't change that.
var is hoisted; the variable exists throughout the current scope. Thus, the second example is equivalent to:
var obj;
var func2;
obj = {};
obj.func1 = func2;
func2 = function() {
alert('func2');
}
alert('Completed');
Thus, when you do the assignment, The name func2 is known, but undefined. In the first example, it is unknown, which raises ReferenceError.
Your func2 variable is not visible. That's why obj.func1 remains undefined.
var obj = {};
var func2 = function() {
alert('func2');
return "Test";
};
obj.func1 = func2;
alert('Completed');

Retrieving values from Execution Contexts in JavaScript

var value = 10;
var outer_funct = function(){
var value = 20;
var inner_funct = function(){
var value = 30;
console.log(value); // logs 30
console.log(window["outer_funct"]["value"]); // What I would like to log here is the value 20.
console.log(window["value"]); // logs 10
};
inner_funct();
};
outer_funct();
I believe the reason the second log is returning undefined is because window["outer_funct"] refers to the function object, and the function object doesn't have a property "value" associated with it. Instead, what I would like to do is refer to the execution context when window["outer_funct"] is invoked. Is this possible to do within the execution context of inner_funct?
I believe the reason the second log is returning undefined is because window["outer_funct"] refers to the function object, and the function object doesn't have a property "value" associated with it.
Correct.
Instead, what I would like to do is refer to the execution context when window["outer_funct"] is invoked. Is this possible to do within the execution context of inner_funct?
No, not with you having shadowed value (declared it in inner_funct). You have no way of getting to it with that symbol having been overridden like that. You could, of course, grab it into another symbol:
var value = 10;
var outer_funct = function(){
var value = 20;
var outer_value = value;
var inner_funct = function(){
var value = 30;
console.log(value); // logs 30
console.log(outer_value); // logs 20
console.log(window.value); // logs 10
};
inner_funct();
};
outer_funct();
If you hadn't shadowed it, then you could refer to value in the containing context, e.g.:
var value1 = 10;
var outer_funct = function(){
var value2 = 20;
var inner_funct = function(){
var value3 = 30;
console.log(value3); // logs 30
console.log(value2); // logs 20
console.log(value1); // logs 10
};
inner_funct();
};
outer_funct();
It's worth noting that the only reason that your original code's window["value"] returned 10 (btw, you could also use window.value) is that the var value = 10; is at global scope. All variables declared with var become properties of the global object, which on browsers is referred to via window (technically, window is, itself, just a property on the global object that points back to the global object).
You cannot refer to value using window["outer_funct"] exactly for the reasons you mentioned. What you can do is something like this:
var value = 10;
var outer_funct = function(){
var context = {// you can put multiple values in here
value: 20;
}
var inner_funct = function(){
var value = 30;
console.log(value); // logs 30
console.log(context.value); //logs 20
console.log(window["value"]); // logs 10
};
inner_funct();
};
outer_funct();
Another way you can do it is if you haven't shadowed value inside inner_funct. If you don't have a variable named the same thing, you can log it and it will return 20. But since you created another variable named value inside inner_funct, that will shadow the value of value in outer_funct.
I would also question why you would need to have three variables named exactly the same, across three scopes.
Local variables are intended to be non-accessible, also because they can depend on the function execution (how could you access that variable if the function has never been executed?).
If you really need some trick, you can have a look at this:
var person = function () {
// Private
var name = "Robert";
return {
getName : function () {
return name;
},
setName : function (newName) {
name = newName;
}
};
}();
alert(person.name); // Undefined
alert(person.getName()); // "Robert"
person.setName("Robert Nyman");
alert(person.getName()); // "Robert Nyman"
and notice that the function must be executed before you can use accessible methods.
No, it is absolutely impossible to access non-global shadowed variables in JavaScript.
You cannot get the execution context of a function as well, it is an implementation-dependent internal value (specification type) - you are correct, your code was looking for properties on the function object.
Variables in the global scope could be accessed as properties of the global object (window in browser), but if you are shadowing a local variable your only choice is to rename your own variable that casts the shadow.
var value = 30; is a local variable in function outer_funct, it could not be accessed from outside of this function.
in your code, although winodw["outer_funct"]["value"] is written inside inner_funct but it acts as trying to access a local variable from out of outer_funct because by `window['outer_funct'] you are staying at the top level.
Variables don't become properties of the functions under which you define them. Excluding the window object (in the case where they are globally declared) there's no object off of which you can access a locally-defined variable. There are workarounds as suggested by the other answers, but they are still a testament to JavaScript's inability to perform such a task in the actual circumstance that you've shown us.

JS function to return function, shares data which should be private

I'm trying to create a function which returns another function. I want separate information when each of the inner function is run, but this isn't happening. I know that explanation is not great, so I've put together a small example.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return that;
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
This outputs 2, 2. What I would like is 2, 4 to be output. I know this isn't explained perfectly, so if it's not clear what I'm trying to achieve, can someone explain why the variable seems to be shared across the two functions?
Thanks
Like this ?
var testFn = function(testVal) {
var test = testVal
return {
getVal: function() {
return test
}
}
};
var ab = testFn (4)
var ac = testFn (2)
console.log(ab.getVal(),ac.getVal()) //4 //2
The problem in your code is this.getVal() / returning this
because 'this' refers to the global scope / Window
You are glubbering with the global namespace and overwriting Window.getVal() , the moment you are setting b = testFn (2)
This results in overwriting as method getVal too because they both refer to the global Object and always share the same method getVal
Therefore they share the same closure and are outputing 2
console.log("The same: " + (Window.a === Window.b)) // true
console.log("The same: " + (a === b)) // true
you can see that if you change it a little:
var testFn = function(testVal) {
var x = {}
return (function(testVal) {
var test = testVal;
x.getVal = function () {
return test;
}
return x
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());//4 2
it suddenly works because it results in 2 different Objects returned (btw you don't even need the outer closure)
console.log("The same: " + (a === b)) // false
Here are the JSbins First / Second
I hope you understand this, I'm not good in explaining things
If theres anything left unclear, post a comment and I'll try to update the answer
This question comes down to the context in which functions are invoked in JavaScript.
A function that is invoked within another function is executed in the context of the global scope.
In your example, where you have this code:
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
})(testVal);
}
The inner function is being called on the global scope, so this refers to the global object. In JavaScript a function executed within another function is done so with its scope set to the global scope, not the scope of the function it exists within. This tends to trip developers up a fair bit (or at least, it does me!).
For argument's sake, lets presume this is in a browser, so hence this refers to the window object. This is why you get 2 logged twice, because the second time this runs, this.getVal overwrites the getVal method that was defined when you ran var a = testFn(4);.
JavaScript scopes at function level, so every function has its own scope:
var x = 3;
function foo() {
var x = 2;
console.log(x);
};
console.log(x); //gives us 3
foo(); // logs 2
So what you want to do is run that inner function in the context of the testFn function, not in the global scope. You can run a function with a specific context using the call method. I also recorded a screencast on call and apply which discusses this in greater detail. The basic usage of call is:
function foo() {...}.call(this);
That executes foo in the context of this. So, the first step is to make sure your inner function is called in the right context, the context of the testFn method.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
}.call(this, testVal);
}
The first parameter to call is the context, and any arguments following that are passed to the function as parameters. So now the inner function is being called in the right scope, it wont add getVal to the global scope, which is a step in the right direction :)
Next though you also need to make sure that every time you call testFn, you do so in a new scope, so you're not overwriting this.getVal when you call testFn for the second time. You can do this using the new keyword. This SO post on the new keyword is well worth reading. When you do var foo = new testFn() you create and execute a new instance of testFN, hereby creating a new scope. This SO question is also relevant.
All you now need to do is change your declaration of a and b to:
var a = new testFn(4);
var b = new testFn(2);
And now console.log(b.getVal(), a.getVal()); will give 2, 4 as desired.
I put a working example on JSBin which should help clear things up. Note how this example defines this.x globally and within the function, and see which ones get logged. Have a play with this and hopefully it might be of use.
The output you get is (2,2) because when you do
var that = this;
what you actually get is the global object (window),
the object that holds all the global methods and variables in your javascript code.
(Note that every variable that is not nested under an object or function is global and
every function that is not nested under an object is global, meaning that functions that are nested under a function are still global)
so, when you set:
var test = testVal;
this.getVal = function() {
return test;
}
you actually set the function "getVal" in the global object, and in the next run you will again set the same function - overriding the first.
To achieve the affect you wanted I would suggest creating and object and returning it in the inner function (as #Glutamat suggested before me):
var testFn = function(testVal) {
return new Object({
getVal: function() {
return testVal;
}
});
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
In this way, in the outer function we create an object with an inner function called "getVal" that returns the variable passed to the outer function (testVal).
Here's a JSBin if you want to play around with it
(thanks to #Glutamat for introducing this site, I never heard of it and it's really cool :D)

Categories

Resources