this
is often used to refer to particular context (or we can call it object). It refers to the object which the function belongs to.
If this
exists in function declarations, if the function is not invoked in any object or it is not assigned to object, we can think of the function belongs to window
object so this
will return window
object.
window
object will look something like this:
Window {0: Window, 1: Window, 2: global, 3: global, window: Window, self: Window, document: document, name: '', location: Location, …}
this
will return as empty object if it's declared outside function, which also means it doesn't have context.
function f1(){
console.log(this); // returns `window` object
}
console.log(this); // returns {}
f1();
Example 1:
let f1 = {
method1(){
console.log(this);
},
method2(){
console.log(this);
}
}
f1.method1(); // (2) {method1: method1(), method2: method...}
Example 2:
let dog = {
name: "Doggo",
age: 5,
getName(){
console.log("My name is : " + this.name);
}
}
dog.getName(); // returns "My name is : Doggo"
Example 3: (this
in class
)
class Dog {
constructor(){
this.name = "Doggo"
}
getName(){
console.log(this); // returns Dog {name: "Doggo"} (returns the class instance)
console.log("My name is : " + this.name); // returns "My name is : Doggo"
}
}
let dog = new Dog(); // `this` will point to dog (class instance)
dog.getName();
You need to be careful because you might lose your instance variable and this
by directly accessing methods
let dog = {
name: "Doggo",
age: 5,
getName(){
console.log("My name is : " + this.name);
}
}
dog.getName(); // returns "My name is : Doggo"
let dogGetName = dog.getName;
dogGetName(); // returns "My name is : undefined"
In this case, you will need .bind()
to bind the this
back to the dogGetName method to preserve the this
.
let dog = {
name: "Doggo",
age: 5,
getName(){
console.log("My name is : " + this.name);
}
}
dog.getName(); // returns "My name is : Doggo"
let dogGetName = dog.getName.bind(dog);
dogGetName(); // returns "My name is : Doggo"
We don't usually call valueOf() method because Javascript will invoke it automatically when a primitive value is expected from an object.
Primitive values are values which are:
- Not object
- No methods or properties
Primitive values are:
- String
- Number
- BigInt
- Boolean
- Undefined
- Symbol
- Null
Array is not primitive because Array is a special type of objects.
let s1 = new String("i am a string");
let s2 = "i am a string";
s1 is an object created from String object, whereas s2 is a string literal (a primitive value)
s1.toUpperCase(); // returns "I AM A STRING"
s2.toUpperCase(); // returns "I AM A STRING"
s2 will still has the access to all String object methods because Javascript will temporarily cast literals to String object in order to run the method.
Each object has a "private" property which holds a link to another object called its prototype. And .toUpperCase() is a method from String.prototype.
let pet = {
play(){
console.log("I am playing!");
}
}
let dog = {
bark(){
console.log("Woof woof!");
}
}
// Dog inherits all the methods from pet.
dog.__proto__ = pet;
dog.play(); // returns "I am playing!"
dog.__proto__.play(); // returns "I am playing!"
Object.setPrototypeOf(dog, pet); // similar to dog.__proto__ = pet
Object.getPrototypeOf(dog).play(); // returns "I am playing"
Can also be written as
let dog = {
__proto__: pet,
bark(){
console.log("Woof woof!");
}
}
When we initialize an object with the new
keyword, the new
keyword will generate an empty constructor, unless we write it again to override it.
Once the object instance is created, the prototype will point itself to the empty constructor.
Do take note that function expression doesn't have a constructor, only function declaration has.
function Machine(){}
console.log(Machine.prototype); // this will return {constructor: f}
let Machine = {}
console.log(Machine.prototype); // this will return undefined
let coffeeMachine = new Machine();
console.log(coffeeMachine.__proto__); // this will return {constructor: f} (Machine / parent constructor)
We can also programatically set the prototype for our function / object
Example 1:
let electronics = {
useElectricity: true,
}
Machine.prototype = electronics;
let coffeeMachine = new Machine();
console.log(coffeeMachine.__proto__); // this will return {useElectricity: true}
Example 2:
const arr = [1, 2, 3];
// Can write a better version of .map() to override the default method if you want
// Array.prototype.map = () => {
// Write your better .map() algorithm
// };
Array.prototype.customMethod = () => {
return "AA";
};
arr.customMethod(); // returns "AA"
Everything created in the form of variables, objects, functions will be allocated in memory and will be freed once we don't need them.
We don't really need to control the garbage collection because V8 Javascript engine will do the work for us but it's always good to know!
The reason we need garbage collection is to prevent a well-known problem called memory leak.
When it happens, the program will start to incorrectly manage the memory allocation, applications will fail, devices stopped working because memory the device ran out of memory. So that's the reason we need to clean up unused memory and constantly free them.
The common concept of garbage collection is to keep those that are in-use, and free those that are not in-use.
For example:
// It simply means a dog variable is referencing an object in the heap memory
// {name : "Goofy"} is the object that exists in the heap and it's referenced so garbage collector will keep it.
let dog = {
name: "Goofy"
}
// Once we set the reference to null, which means that the dog is not pointing to the object anymore
// And because of nothing is referencing the object now, it will be garbage collected once it's detected and it will be freed from the memory.
dog = null;
A very common algorithm that V8 Javascript engine uses is Mark and Sweep.
- Why do we get undefined in the next line after we console.log() our value in the console?
The reason of this is that because console.log() does not return anything. Same goes to declaration of variables.
> console.log("Yes");
Yes
undefined
> console.log(1 + 2 + 3);
6
undefined
> 1 + 2 + 3
6
> let test = 1
undefined
> test
1