Understanding Javascript scope with var that = this
The most important thing to understand is that a function object does not have a fixed this
value -- the value of this
changes depending on how the function is called. We say that a function is invoked with some a particular this
value -- the this
value is determined at invocation time, not definition time.
- If the function is called as a "raw" function (e.g., just do
someFunc()
),this
will be the global object (window
in a browser) (orundefined
if the function runs in strict mode). - If it is called as a method on an object,
this
will be the calling object. - If you call a function with
call
orapply
,this
is specified as the first argument tocall
orapply
. - If it is called as an event listener (as it is here),
this
will be the element that is the target of the event. - If it is called as a constructor with
new
,this
will be a newly-created object whose prototype is set to theprototype
property of the constructor function. - If the function is the result of a
bind
operation, the function will always and forever havethis
set to the first argument of thebind
call that produced it. (This is the single exception to the "functions don't have a fixedthis
" rule -- functions produced bybind
actually do have an immutablethis
.)
Using var that = this;
is a way to store the this
value at function definition time (rather than function execution time, when this
could be anything, depending on how the function was invoked). The solution here is to store the outer value of this
in a variable (traditionally called that
or self
) which is included in the scope of the newly-defined function, because newly-defined functions have access to variables defined in their outer scope.
What is the scope of variables in JavaScript?
TLDR
JavaScript has lexical (also called static) scoping and closures. This means you can tell the scope of an identifier by looking at the source code.
The four scopes are:
- Global - visible by everything
- Function - visible within a function (and its sub-functions and blocks)
- Block - visible within a block (and its sub-blocks)
- Module - visible within a module
Outside of the special cases of global and module scope, variables are declared using var
(function scope), let
(block scope), and const
(block scope). Most other forms of identifier declaration have block scope in strict mode.
Overview
Scope is the region of the codebase over which an identifier is valid.
A lexical environment is a mapping between identifier names and the values associated with them.
Scope is formed of a linked nesting of lexical environments, with each level in the nesting corresponding to a lexical environment of an ancestor execution context.
These linked lexical environments form a scope "chain". Identifier resolution is the process of searching along this chain for a matching identifier.
Identifier resolution only occurs in one direction: outwards. In this way, outer lexical environments cannot "see" into inner lexical environments.
There are three pertinent factors in deciding the scope of an identifier in JavaScript:
- How an identifier was declared
- Where an identifier was declared
- Whether you are in strict mode or non-strict mode
Some of the ways identifiers can be declared:
var
,let
andconst
- Function parameters
- Catch block parameter
- Function declarations
- Named function expressions
- Implicitly defined properties on the global object (i.e., missing out
var
in non-strict mode) import
statementseval
Some of the locations identifiers can be declared:
- Global context
- Function body
- Ordinary block
- The top of a control structure (e.g., loop, if, while, etc.)
- Control structure body
- Modules
Declaration Styles
var
Identifiers declared using var
have function scope, apart from when they are declared directly in the global context, in which case they are added as properties on the global object and have global scope. There are separate rules for their use in eval
functions.
let and const
Identifiers declared using let
and const
have block scope, apart from when they are declared directly in the global context, in which case they have global scope.
Note: let
, const
and var
are all hoisted. This means that their logical position of definition is the top of their enclosing scope (block or function). However, variables declared using let
and const
cannot be read or assigned to until control has passed the point of declaration in the source code. The interim period is known as the temporal dead zone.
function f() {
function g() {
console.log(x)
}
let x = 1
g()
}
f() // 1 because x is hoisted even though declared with `let`!
What does 'var that = this;' mean in JavaScript?
I'm going to begin this answer with an illustration:
var colours = ['red', 'green', 'blue'];
document.getElementById('element').addEventListener('click', function() {
// this is a reference to the element clicked on
var that = this;
colours.forEach(function() {
// this is undefined
// that is a reference to the element clicked on
});
});
My answer originally demonstrated this with jQuery, which is only very slightly different:
$('#element').click(function(){
// this is a reference to the element clicked on
var that = this;
$('.elements').each(function(){
// this is a reference to the current element in the loop
// that is still a reference to the element clicked on
});
});
Because this
frequently changes when you change the scope by calling a new function, you can't access the original value by using it. Aliasing it to that
allows you still to access the original value of this
.
Personally, I dislike the use of that
as the alias. It is rarely obvious what it is referring to, especially if the functions are longer than a couple of lines. I always use a more descriptive alias. In my examples above, I'd probably use clickedEl
.
What underlies this JavaScript idiom: var self = this?
See this article on alistapart.com. (Ed: The article has been updated since originally linked)
self
is being used to maintain a reference to the original this
even as the context is changing. It's a technique often used in event handlers (especially in closures).
Edit: Note that using self
is now discouraged as window.self
exists and has the potential to cause errors if you are not careful.
What you call the variable doesn't particularly matter. var that = this;
is fine, but there's nothing magic about the name.
Functions declared inside a context (e.g. callbacks, closures) will have access to the variables/function declared in the same scope or above.
For example, a simple event callback:
function MyConstructor(options) { let that = this;
this.someprop = options.someprop || 'defaultprop';
document.addEventListener('click', (event) => { alert(that.someprop); });}
new MyConstructor({ someprop: "Hello World"});
What scope are variables globally defined by var?
var foo = 1;
window.foo === foo;
JavaScript is a functional language and therefore any variable declared inside the scope of a function is only available in that function.
JS will actually go through each functions scope and look for a variable declared.
function setGlobal() {
bar = 1; // gets set as window.bar because setGlobal does not define it
}
setGlobal();
// logs true and 1
console.log(window.bar === bar, bar);
http://jsfiddle.net/kXjrF/
So...
function logGlobal() {
var bar;
console.log( foo, window.foo ) // undefined, undefined
function setGlobal() {
// window.foo is now set because logGlobal did not define foo
foo = 1;
bar = 2; // logGlobal's bar not window.bar
function makePrivate() {
var foo = 3; // local foo
console.log( foo ); // logs 3
}
makePrivate(); // logs 3
}
setGlobal();
console.log( foo, window.foo ); // logs 1, 1
}
let vs var in the global scope
Let allows to declare variables which are limited to a particular scope, can be block or expression. Where as var is used for global or local declaration. We can use var instead of let but the reverse fails.
What is the difference between let and var?
Scoping rules
The main difference is scoping rules. Variables declared by var
keyword are scoped to the immediate function body (hence the function scope) while let
variables are scoped to the immediate enclosing block denoted by { }
(hence the block scope).
function run() {
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar); // Foo Bar
{
var moo = "Mooo"
let baz = "Bazz";
console.log(moo, baz); // Mooo Bazz
}
console.log(moo); // Mooo
console.log(baz); // ReferenceError
}
run();
Difference between local scope and function scope in JavaScript
There is no difference between "local scope" and "function scope".
Up to and including ES5 there were only two scopes: global and "not global". The latter one would be called "local" or "function". Both terms means the same thing:
var a = "this is global scope";
function foo() {
var b = "this is local/function scope";
if (true) {
var c = "this is in the same local/function scope as b";
}
for (var i = 0; i < 4; i++) {
var d = "this is also in the same local/
function scope as b and c";
}
}
It is function because it is inside the function.
As of ES6 the term "local" scope might be a bit of a misnomer, since there are more scopes than just two. For example block scope for variables like let
or const
:
const a = "this is global scope";;
function foo() {
const b = "this is function scope";
if (true) {
const c = "this is in block scope";
}
for (let i = 0; i < 4; i++) {
const d = "this is also in block scope but different to c";
}
}
as well as module scope for any file that is a module:
export const a = "this is in module scope";
Related Topics
Calling Method Using JavaScript Prototype
How to Remove an Object from an Array with JavaScript
How to Remove Leading and Trailing White Spaces from a Given HTML String
How to Get a Specific Parameter from Location.Search
Replace All Occurrences in a String
Batchsize Field Name Ignored in Field Projection
Checking Whether Something Is Iterable
Using Await Outside of an Async Function
How to Resolve Typeerror: Cannot Convert Undefined or Null to Object
How to Remove a Class from Elements in Pure JavaScript
Moment.Js - Utc Gives Wrong Date
Copy a Variable's Value into Another
Typescript Compile to Single File
What Is the Meaning of an Underscore in JavaScript Function Parameter