I did read that the this keyword can refer to either the global scope or to the object it is being referred in or to the object to which an event is bound but the below behavior is not understood.
function outer() {
var inner = 4;
function innerFunc() {
var inner = 5;
console.log('inner called with ' + this.inner);
}
return innerFunc;
}
var obj = outer();
obj();
Why does this log 4 instead of 5. AFAIK this should refer to the function it is being referred in and should be available via closure.
TL;DR - this doesn't work like you think it should.
Read more here, here and here.
The this (otherwise known as context) of a function is determined at call time, not at function definition time. This is widely known as this being dynamically bound, and variables from closure being lexically bound.
var obj = outer();
obj();
Calls outer with this = window (or global in the case of Node.js) because you are not in strict mode (otherwise, this = undefined). outer doesn't use this, so this doesn't impact it greatly.
outer then returns a function (innerFunc), and you assign it to obj. You then call obj, again, with this = window (because you didn't specify anything other with .bind() or .call() or .apply()).
You then log this.inner which is equivalent to window.inner which is undefined unless you happen to have a global variable named inner with some value.
The value of this, then, is determined by the caller.
If you had invoked obj like so:
obj.call({inner: 42}); // first argument to fn.call() is the context
You would have seen 42 in your console, regardless of how the function was defined.
A way to mitigate this and retain control over your this is to either use fn.bind() or an arrow function.
Your code snippet is returning undefined, not 4. The reason for this is because the window is calling innerFunc by invoking obj(). Thus, this is referring to the window as that is what called innerFunc (and window.inner is undefined). You can see this by adding a variable inner = 3 at the top of your code (this will make window.inner = 3) and so your function will log 3.
inner = 3; // ie: window.inner = 3
function outer() {
var inner = 4;
function innerFunc() {
var inner = 5;
console.log('inner called with ' + this.inner); // this is window
}
return innerFunc;
}
var obj = outer();
obj();
In this example you are using a function instead of an object. Additionally, you used a variable instead of the this keyword to assign the value. I think the concept that you're thinking of is local scoping
For example,
function parent(){
var n = 5
function child(){
var n = 4 //n is 4 here
}
//n is 5 here
}
Related
In the example bellow, I am trying to access the x that is in the function outer.
I was expecting to get 20 as an output, however the output is undefined.
Can someone explain why is that happening, and is there a way to access the outer x?
var x = 10;
function outer() {
var x = 20;
function inner() {
var x = 30;
function printX() {
console.log(outer.x);
}
printX();
}
inner();
}
outer();
Scopes aren't designed like that in JavaScript. In order to attach the variable x to its scope, you would need to reference the object for that scope either by name or by this reference.
What is happening in your example is that your call to printX attempts to log the variable x attached to the function outer. functions derive from objects in JavaScript, and as a result they may have properties attached to them, so instead of giving you a reference error, you get undefined instead, as the variable does not exist.
For more information on scoping, please see my answer on scope in JavaScript.
var x = 10; // Globally scoped variable named "x"
function outer() {
var x = 20; // Locally scoped to outer function variable named "x"
// in outer function, this variable takes precedence over the
// globally scoped x which was 10
function inner() {
var x = 30; // Locally scoped to inner function variable named "x"
// in inner function, this variable takes precedence over the
// parent scoped x which was 20
function printX() {
console.log(outer.x); // Tries to read "x" property of the outer function
// If this had been console.log(x) it would give 30 because it is scoped to the function inner's variable environment
}
printX();
}
inner();
}
outer();
As for what to do going forward, it really depends on what the end goal was. The simple way to fix this would be, as pointed out in comments here, to simply rename the variables. However, that still wouldn't fix the main issue of trying to access the variable by property name instead of by variable name. In order to access a variable by name, simply use its name (and differentiate the names if they share scope), as opposed to trying to access the property name which in this case doesn't exist.
Since it hasn't been mentioned here yet, I will add another possible way to do this by leveraging this and scope by calling the functions with .apply(), like so:
var x = 10;
function outer() {
var x = 20;
function inner() {
var x = 30;
function printX() {
// this now contains all 3 x variables without adding any parameters to any of the functions
console.log("Window x:", this.windowX);
console.log("Outer x:", this.outerX);
console.log("Inner x:", this.innerX);
}
// pass through existing context (which we got from inner.apply(...) down below, as well as add
// inner() x value to the new context we pass to printX()
printX.apply({...this, innerX: x});
}
// pass through existing context (which we got from outer.apply(...) down below, as well as add
// outer() x value to the new context we pass to inner()
inner.apply({...this, outerX: x});
}
// pass through window level x as "this" to outer(). Technically it's still available via window.x,
// but this will be consistent with the others
outer.apply({windowX: x});
You are not creating an object attribute but internal variables. Which you also shadow (that is that you define another with the same name in an inner scope) so that you cannot reach them.
Basically you can access outer.x but you have not set it (only a function scoped variable named x). And to answer your question "if you can get to that variable": No sorry. since you have shadowed it by defining an inner variable with the same name.
What you could do is this:
var x = 10;
function outer() {
outer.x = 20;
function inner() {
inner.x = 30;
function printX() {
console.log(outer.x);
}
printX();
}
inner();
}
outer();
but that would just make the other variables useless, and also setting variables on functions just because you can is not the best practice.
Keep learning.
You can take a look at the concept of scope to get more clarity, but the first x is in the global scope and can be accessed within the function, but you reassigned the variable value to be 20 within the outer function. If you console log the value of x within the outer function, but not inside the inner function, the result will be 20. You assigned the value 30 to x in the inner function, so when you access x within the inner function, it will be 30. If you console.log(x) in each location, you will see the different results.
var x = 10;
function outer() {
var x = 20;
function inner() {
var x = 30;
function printX() {
console.log(x);
}
printX();
}
inner();
console.log(x);
}
outer();
console.log(x);
Why is it that when I do the following:
var a = 1;
function foo(a) {
a = 2;
}
foo();
console.log(a); // a = 1
But I get a different result for the following:
var a = 1;
function foo() {
a = 2;
}
foo();
console.log(a); // a = 2
In the first example the function foo parameter a is shadowing the global variable a and thus the value of the global variable never changes. The code in the first example is equivalent to this one:
var a = 1;
function foo(x) {
x = 2;
}
In the second example you are referencing the global variable a inside the body of the function foo. Here no variable shadowing occurs so that you get the expected result - a is asigned the value of 2.
JavaScript doesn't really have "parameters on the left/right side." Your first example passes a parameter. Your second example uses a closure.
In programming languages, closures (also lexical closures or function closures) are techniques for implementing lexically scoped name binding in languages with first-class functions. Operationally, a closure is a record storing a function together with an environment: a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created. A closure—unlike a plain function—allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.
In your first example, the variable a inside the function foo is a different variable than a outside of the function. Changing foo's parameter a has no effect on the global variable a that gets passed to console.log.
In your second example, the variable a is not a parameter, but rather a part of the environment that is captured by the function foo. The log shows a modified value of a because the assignment inside foo and the console.log call outside of foo are actually referring to the same variable.
if a is passed in as an argument to that function -- then the value of a within that function is isolated and only accessible within that function. otherwise your a defined outside the function is shared across all functions and objects.
agree with #RomanPerekhrest -- read up.
**UPDATE in reply to comment **
var a = 1;
function foo(a) {
a = 2;
}
foo();
console.log(a); // a = 1
In the code above, the reference to a on lines 2 and 3 are a different variable from the a on lines 1 and 6. In the code from your comment, you are setting a to the value of x within the foo function. That's a bit different from the original question. No?
Given this code:
const numberManipulator = (function() {
this.defaultNumber = 5;
const doubleNumber = function(num) {
console.log(this.defaultNumber);
return num !== undefined ? num * 2 : new Error('no number provided!');
}
return {
doubleNumber
}
})();
const test = numberManipulator.doubleNumber(20);
Why is console.log(this.defaultNumber showing undefined but if I console.log(defaultNumber, it shows 5? I thought that it would show undefined for the latter.
Thanks
You should read How does the “this” keyword work?
When the IIFE is called, its this is not set so it defaults to the global object, so in:
const numberManipulator = (function() {
this.defaultNumber = 5;
...
})();
this.defaultNumber = 5 creates a property of the global (window in a browser) object called defaultNumber and assigns it a value of 5.
If the return statement should really be:
return {doubleNumber: doubleNumber}
then the IIFE returns an object reference to numberManipulator. When called as:
numberManipulator.doubleNumber(20)
then this within doubleNumber is numberManipulator, which doesn't have a defaultNumber property so this.defaultNumber returns undefined.
Long story short:
1. The IIFE is executing in the context of the window I was wrong,
see RobG's comment.
2. this thus refers to the window inside the IIFE, since it is not set by default.
3. Your doing this.defaultNumber = 5; is hence equivalent to
window.defaultNumber = 5;
4. The second method executes in the context of numberManipulator and so logging this.defaultNumber is equivalent to numberManipulator.defaultNumber which was never set by you. Hence, it is undefined.
Explanation on this:
Inside any function, the this keyword refers to the current context in which the function is executing. It doesn't matter where you defined that function inside your codebase. All it matters in what context are you calling it. In your current case, you're calling the IIFE in the context of the window. Doing:
const numberManipulator = (function() {
// MAGIC
})();
amounts to:
running an IIFE in the window context.
assigning the result it returned to a constant.
However, the context of a function may be changed by using .call, .apply, or by simply calling that method as a property of another object. In your second log, this.defaultNumber, this refers to the object since you called the method using numberManipulator.doubleNumber(20);. Calling methods using object.prop() sets their context (=this) to that object.
Hope that helps! :D
This code results in "!" being logged on the console.
var g = {};
(function() {
var t = this;
t.x = "x";
g.a = function() {
console.log(t.x);
};
})();
(function() {
var t = this;
t.x = "!";
g.b = function() {
console.log(t.x);
};
})();
g.a();
Do anonymous functions share a this? Am I using this wrong? I don't really understand what's going on here.
I'd like for g.a() to continue returning the value of x defined in the first anonymous function.
I'm using node.js if it makes a difference.
In the immediate functions, this refers to the global object [docs]. So in this case in both functions this indeed refers to the same element and you are overwriting x with the second call.
What object this refers to is determined by how the function is called.
If you just execute a function with funcName();, then this refers to the global object.
If the function is assigned to a property of an object, obj.funcName() , this refers to the object.
If you call the function with the new operator, new funcName();, this refers to an empty object that inherits from the functions prototype.
You can also explicitly set this by using call [docs] or apply [docs].
Instead referring to this, you could create a new object in both functions:
var t = {};
Additional note: It makes no difference whether you run the code in the browser or with node.js. The global object is part of the specification and has to be provided by the execution environment. In browsers it is the window object, I don't what it is in node.js, but it does not matter as long as it follows the specification.
Felix Kling is the right long answer. But I wanted to chime in with what I think you actually want:
var g = {};
(function() {
var x = "x";
g.a = function() {
console.log(x);
};
})();
(function() {
var x = "!";
g.b = function() {
console.log(x);
};
})();
g.a(); // "x"
g.b(); // "!"
Now g.a() and g.b() both print out x, but each function has their own separate x shared with the closure. If these vars should be private an only accessible internally to each of these functions, this is how you hide them and persist them through multiple calls.
When I look at this script in the debugger in Chrome as you've shown it, the "this" value in both anonymous functions is set to the global variable "window". That means that each anonymous function is setting the value of window.x so the last one executed wins and is the value that survives, thus window.x == "!" after the second anonymous function executes.
It's unclear to me what you expected "this" to be or what you're actually trying to accomplish with this code so I don't know what alternative to suggest. If you just want the previous state in the anonymous function to survive for the internal function, then you can just rely on local variables (which will survive in the closure) and not use the "this" reference at all. Squeegy's example shows that.
What is the difference between these?
var a = 13;
this.b = 21;
document.write(a);
document.write(b);
For global code (code that is not part of any function), they are almost equivalent, both at the end create a property on the global object.
The difference is that a, which has been declared with the var statement, the Variable Instantiation process will use the global object as the Variable Object (1), and it will define that property as non-deleteable on it, e.g.:
var a = 13;
delete a; // false
typeof a; // "number"
Then, b since the this value in global code, points to the global object itself, will be also a global property, but this one can be deleted:
this.b = 21;
delete b; // true
typeof b; // "undefined"
Don't try the first snippet in Firebug, since the Firebug's console runs the code internally with eval, and in this execution context the variable instantiation process behaves differently, you can try it here.
(1) The Variable Object (VO) is an object that is used by the variable instantiation process to define the identifiers of FunctionDeclarations, identifiers declared with the var statements, and identifiers of function formal parameters, in the different execution contexts, all those identifiers are bound as properties of the VO, the Scope chain is formed of a list of VO's.
For global code, the VO is the global object itself, that's why a ends being a property of it. For function code, the VO (also known as the Activation Object for FunctionCode), is a new object is created behind the scenes when you invoke a function, and that is what creates a new lexical scope, in short I'll talk about functions.
Both a and this.b can be resolved simply as by a and b because the first object in the scope chain, is again the global object.
Also, I think is work knowing that the Variable Instantiation process takes place before than the code execution, for example:
alert(a); // undefined, it exists but has no value assigned to it yet
alert(b); // ReferenceError is thrown
var a = 13;
this.b = 21;
These differences may be trivial but I think is worth knowing them.
Now, if the code snippet you posted is within a function, it is completely different.
The a identifier, declared with the var statement in your example will be a local variable, available only to the lexical scope of the function (and any nested functions).
Keep in mind that in JavaScript blocks don't introduce a new scope, only functions do, and to declare a variable in that scope, you should always use var.
The this.b identifier will become a property bound to the object that is referred by the this value, but... What is this???.
The this value in JavaScript is implicitly set when you call a function, it is determined by how do you invoke it:
When you use the new operator, the this value inside the function, will point to a newly created object, e.g.:
function Test() {
this.foo = "bar";
}
var obj = new Test(); // a new object with a `foo` property
When you call a function that is member of an object, the this value inside that function will point to the base object, e.g.:
var obj = {
foo: function () {
return this == obj;
}
};
obj.foo(); // true
When you invoke a function without any base object, the this value will refer to the global object:
function test() {
return this == window;
}
test(); // true
The this value can be set explicitly, when you invoke a function using call or apply:
function test() {
alert(this);
}
test.call("hello world!"); // alerts "hello world!"
To understand in short, if you use these in a function then -
this.a; //will create a public property
var b; //will create a member variable
e.g. here is a Student class in javascript
var Student = function()
{
// Member variable
var studentId;
// Public property
this.Name = "";
}
for more - See Object Oriented Programming with JavaScript