JavaScript variables
In JavaScript, a variable is a named reference to a memory location that can hold different types of data, such as numbers, strings, booleans, objects, or functions. To access a variable or modify the data stored in a variable, we use its name.
If you have a basic understanding of computer memory then you must know that Computer memory has millions of cells on it and every cell has its own address
The problem is we can't use this address directly in our program. because you know the computer works in the binary system and in the binary system this memory address looks too weird and confusing. also, it's almost impossible to memorize.
Here variable comes in to solve this problem.
Variable gives us the simplest solution to handle it. We can simply create a variable and assign it to the value we want to store. now we don't need to memorize the weird and confusing memory address we can do the same using a simple and human-readable name.
That means,
Creating a variable
Giving a standard name to the variable
Assigning a value to it.
Think of these steps as
Taking a container
Labeling it
Putting something in this container.
Javascript has 5 data types that are passed by value: Boolean
, null
, undefined
, String
, and Number
. We’ll call these primitive types.
Javascript has 3 data types that are passed by reference: Array
, Function
, and Object
. These are all technically Objects, so we’ll refer to them collectively as Objects.
Primitives
If a primitive type is assigned to a variable, we can think of that variable as containing the primitive value.
var x = 10;
var y = 'abc';
var z = null;
x
contains 10
. y
contains 'abc'
. To cement this idea, we’ll maintain an image of what these variables and their respective values look like in memory.
When we assign these variables to other variables using =
, we copy the value to the new variable. They are copied by value.
var x = 10;
var y = 'abc';var a = x;
var b = y;console.log(x, y, a, b); // -> 10, 'abc', 10, 'abc'
Both a
and x
now contain 10
. Both b
and y
now contain 'abc'
. They’re separate, as the values themselves were copied.
Changing one does not change the other. Think of the variables as having no relationship to each other.
var x = 10;
var y = 'abc';var a = x;
var b = y;a = 5;
b = 'def';console.log(x, y, a, b); // -> 10, 'abc', 5, 'def'
Primitive Types
The in-memory value of a primitive type is its actual value (e.g. boolean true
, number 42
). A primitive type can be stored in the fixed amount of memory available.
null
undefined
Boolean
Number
String
Primitive types are also known as scalar types or simple types.
Objects
This will feel confusing, but bear with me and read through it. Once you get through it, it’ll seem easy.
Variables that are assigned a non-primitive value are given a reference to that value. That reference points to the object’s location in memory. The variables don’t actually contain the value.
Objects are created at some location in your computer’s memory. When we write arr = []
, we’ve created an array in memory. What the variable arr
receives is the address, the location, of that array.
When we assign and use a reference-type variable, what we write and see is:
1) var arr = [];
2) arr.push(1);
A representation of lines 1 and 2 above in memory is:
1.
2.
Notice that the value, the address, contained by the variable arr
is static. The array in memory is what changes. When we use arr
to do something, such as pushing a value, the Javascript engine goes to the location of arr
in memory and works with the information stored there.
Reference Types
A reference type can contain other values. Since the contents of a reference type can not fit in the fixed amount of memory available for a variable, the in-memory value of a reference type is the reference itself (a memory address).
Array
Object
Function
Reference types are also known as complex types or container types.
Assigning by Reference
When a reference type value, an object, is copied to another variable using =
, the address of that value is what’s actually copied over as if it were a primitive. Objects are copied by reference instead of by value.
var reference = [1];
var refCopy = reference;
The code above looks like this in memory.
Each variable now contains a reference to the same array. That means that if we alter reference
, refCopy
will see those changes:
reference.push(2);
console.log(reference, refCopy); // -> [1, 2], [1, 2]
We’ve pushed 2
into the array in memory. When we use reference
and refCopy
, we’re pointing to that same array.
Reassigning a Reference
Reassigning a reference variable replaces the old reference.
var obj = { first: 'reference' };
In memory:
When we have a second line:
var obj = { first: 'reference' };
obj = { second: 'ref2' }
The address stored in obj
changes. The first object is still present in memory, and so is the next object:
When there are no references to an object remaining, as we see for the address #234
above, the Javascript engine can perform garbage collection. This just means that the programmer has lost all references to the object and can’t use the object any more, so the engine can go ahead and safely delete it from memory. In this case, the object { first: 'reference' }
is no longer accessible and is available to the engine for garbage collection.
\== and ===
When the equality operators, ==
and ===
, are used on reference-type variables, they check the reference. If the variables contain a reference to the same item, the comparison will result in true
.
var arrRef = [’Hi!’];
var arrRef2 = arrRef;console.log(arrRef === arrRef2); // -> true
If they’re distinct objects, even if they contain identical properties, the comparison will result in false
.
var arr1 = ['Hi!'];
var arr2 = ['Hi!'];console.log(arr1 === arr2); // -> false
If we have two distinct objects and want to see if their properties are the same, the easiest way to do so is to turn them both into strings and then compare the strings. When the equality operators are comparing primitives, they simply check if the values are the same.
var arr1str = JSON.stringify(arr1);
var arr2str = JSON.stringify(arr2);console.log(arr1str === arr2str); // true
Passing Parameters through Functions
When we pass primitive values into a function, the function copies the values into its parameters. It’s effectively the same as using =
.
var hundred = 100;
var two = 2;function multiply(x, y) {
// PAUSE
return x * y;
}var twoHundred = multiply(hundred, two);
In the example above, we give hundred
the value 100
. When we pass it into multiply
, the variable x
gets that value, 100
. The value is copied over as if we used an =
assignment. Again, the value of hundred
is not affected. Here is a snapshot of what the memory looks like right at the PAUSE comment line in multiply
.
Pure Functions
We refer to functions that don’t affect anything in the outside scope as pure functions. As long as a function only takes primitive values as parameters and doesn’t use any variables in its surrounding scope, it is automatically pure, as it can’t affect anything in the outside scope. All variables created inside are garbage-collected as soon as the function returns.
Let’s go into an example of a pure vs. impure function.
function changeAgeImpure(person) {
person.age = 25;
return person;
}var alex = {
name: 'Alex',
age: 30
};var changedAlex = changeAgeImpure(alex);console.log(alex); // -> { name: 'Alex', age: 25 }
console.log(changedAlex); // -> { name: 'Alex', age: 25 }
Let’s look at a pure function.
function changeAgePure(person) {
var newPersonObj = JSON.parse(JSON.stringify(person));
newPersonObj.age = 25;
return newPersonObj;
}var alex = {
name: 'Alex',
age: 30
};var alexChanged = changeAgePure(alex);console.log(alex); // -> { name: 'Alex', age: 30 }
console.log(alexChanged); // -> { name: 'Alex', age: 25 }
Test
function changeAgeAndReference(person) {
person.age = 25;
person = {
name: 'John',
age: 50
};
return person;
}var personObj1 = {
name: 'Alex',
age: 30
};var personObj2 = changeAgeAndReference(personObj1);console.log(personObj1); // -> ?
console.log(personObj2); // -> ?
console.log(personObj1); // -> { name: 'Alex', age: 25 }
console.log(personObj2); // -> { name: 'John', age: 50 }
Remember that assignment through function parameters is essentially the same as assignment with =
. The variable person
in the function contains a reference to the personObj1
object, so initially it acts directly on that object. Once we reassign person
to a new object, it stops affecting the original.
This reassignment does not change the object that personObj1
points to in the outer scope. person
has a new reference because it was reassigned but this reassignment doesn’t change personObj1
.
Notes
All primitive datatype excepted null tested by typeof "typeof(null) == "object"".
Undefined
A
return
statement with no value (return;
) implicitly returnsundefined
.Accessing a nonexistent object property (
obj.iDontExist
) returnsundefined
.A variable declaration without initialization (
let x;
) implicitly initializes the variable toundefined
.Many methods, such as
Array.prototype.find()
andMap.prototype.get()
, returnundefined
when no element is found.
Null
null
is used much less often in the core language. The most important place is the end of the prototype chain — subsequently, methods that interact with prototypes, such as Object.getPrototypeOf()
, Object.create()
, etc., accept or return null
instead of undefined
.
null
is a keyword, but undefined
is a normal identifier that happens to be a global property.
Difference between null
and undefined
Undefined -> means that a var has been declared but has not yet been assigned a value.
Null -> is an assignment value it can be assigned to a var as a representation of nonvalue.
Undefined is a type itself, and null is an object.
null = "value"; //Syntaxerror invalid left-hand side undefined value
undefined = "value"; // non error
undefined
means a variable has been declared but has not yet been assigned a value :
var testVar;
console.log(testVar); //shows undefined
console.log(typeof testVar); //shows undefined
null
is an assignment value. It can be assigned to a variable as a representation of no value :
var testVar = null;
console.log(testVar); //shows null
console.log(typeof testVar); //shows object
From the preceding examples, it is clear that undefined
and null
are two distinct types: undefined
is a type itself (undefined) while null
is an object.
Proof :
console.log(null === undefined) // false (not the same type)
console.log(null == undefined) // true (but the "same value")
console.log(null === null) // true (both type and value are the same)
and
null = 'value' // Uncaught SyntaxError: invalid assignment left-hand side
undefined = 'value' // 'value'
In JavaScript we can have the following types of variables:
Undeclared Variables
Declared but Unassigned Variables
Variables assigned with literal
undefined
Variables assigned with literal
null
Variables assigned with anything other than
undefined
ornull
The following explains each of these cases one by one:
Undeclared Variables
Can only be checked with the
typeof
operator which returns string 'undefined'Cannot be checked with the loose equality operator (
== undefined
), let alone the strict equality operator (=== undefined
),
as well as if-statements and ternary operators (? :
) — these throw Reference Errors
Declared but Unassigned Variables
typeof
returns string 'undefined'==
check withnull
returnstrue
==
check withundefined
returnstrue
===
check withnull
returnsfalse
===
check withundefined
returnstrue
Is falsy to if-statements and ternary operators (
? :
)
Variables assigned with literal
undefined
These variables are treated exactly the same as Declared But Unassigned Variables.Variables assigned with literal
null
typeof
returns string 'object'==
check withnull
returnstrue
==
check withundefined
returnstrue
===
check withnull
returnstrue
===
check withundefined
returnsfalse
Is falsy to if-statements and ternary operators (
? :
)
Variables assigned with anything other than
undefined
ornull
- typeof returns one of the following strings: 'bigint', 'boolean', 'function', 'number', 'object', 'string', 'symbol'
Following provides the algorithm for correct type checking of a variable:
Get the
typeof
our variable and return it if it isn't 'object'Check for
null
, astypeof null
returns 'object' as wellEvaluate Object.prototype.toString.call(o) with a switch statement to return a more precise value.
Object
'stoString
method returns strings that look like '[object ConstructorName]' for native/host objects. For all other objects (user-defined objects), it always returns '[object Object]'
Number type
Positive floating-point numbers between 2-1074 (Number.MIN_VALUE
) and 21024 (Number.MAX_VALUE
) as well as negative floating-point numbers between -2-1074 and -21024.
Values outside the range ±(2-1074 to 21024) are automatically converted:
Positive values greater than
Number.MAX_VALUE
are converted to+Infinity
.Positive values smaller than
Number.MIN_VALUE
are converted to+0
.Negative values smaller than -
Number.MAX_VALUE
are converted to-Infinity
.Negative values greater than -
Number.MIN_VALUE
are converted to-0
.
The Number type has only one value with multiple representations: 0
is represented as both -0
and +0
(where 0
is an alias for +0
). In practice, there is almost no difference between the different representations; for example, +0 === -0
is true
. However, you are able to notice this when you divide by zero:
console.log(42 / +0); // Infinity
console.log(42 / -0); // -Infinity