WTF is this
Scope
Scope is the current context of execution
Global scope
Global scope is the outermost scope. Variables declared in the global scope are accessible everywhere
const foo = 'bar';
function bar() {
console.log(foo);
}
bar(); // barFunction scope
Scoped to use the variables declared inside the function
function bar() {
const foo = 'bar1';
console.log(foo);
}
const foo = 'bar2';
bar(); // bar1
// throw `foo is not accessible here`Block scope
Scoped to use the variables declared inside {} can be called
{
const foo = 'bar1';
console.log(foo); // bar1
}
console.log(foo); // throw `foo is not accessible here`Lexical Scope
Lexical scope is the scope of the function defined at the time of declaration (Static Scope).
In other words, when the program is compiled, it already defined the scope. But when it comes to use case of dynamic scoping, the value is determined by the function that called the current function.
Example: bar() and baz() share the same lexical scope (global)
const foo = 'bar';
function bar() {
console.log(foo);
}
function baz() {
const foo = 'baz';
bar();
}
baz(); // barthis
When a function is invoked, an execution context is created. This context has a property called this which refers to the object that called the function.
- When a function is invoked in the global scope,
thisrefers to the global object - When a function is invoked in strict mode,
thisis undefined - When a function is invoked in the method,
thisrefers to the object that called the function - When a function is invoked with
newkeyword,thisrefers to the newly created object - When a function is invoked with
call,apply,bind,thisrefers to the object passed in
Global context
Function defined in global scope is called in global context
Object context
Inside object this refers to the object itself
const obj = {
name: "Example",
regularFunc: function() {
console.log(this.name); // Refers to 'name' property of 'obj'
},
arrowFunc: () => {
console.log(this.name); // 'this' inherits from the lexical scope
}
};
obj.regularFunc(); // Outputs: "Example"
obj.arrowFunc(); // Outputs: undefined (or whatever is in the global context)Arrow function
Arrow function does not have this context.
It directly inherits from lexical context (the context where it is defined). It is not possible to bind this to arrow function. Because of this, arrow function is not suitable for method definition.
// bug occurs as it is not possible to bind `this` to arrow function
// then it inherits from lexical context
// which is the global context
// so `this.name` is undefined
const foo = {
name: 'bar',
sayName: () => {
console.log(this.name);
return;
}
}Suggested use cases of arrow function are: callback function, event handlers.
const foo = {
name: 'bar',
sayName: function() {
setTimeout(() => {
console.log(this.name);
}, 1000);
}
}Prototype/__proto__
In JavaScript, every object/function (using new) has a prototype. The prototype is also an object. All objects inherit their properties and methods from their prototype. The idea of prototype is to share properties/methods between objects when defining similar objects.
Here is a catch: for function without new keyword, the prototype is undefined. For function using new keyword to create an object, the prototype is the object that is created by the function.
function Person() {
this.name = 'John';
}
Person.prototype.age = 24;
const foo = Person(); // no prototype
const foo2 = new Person(); // has prototypeWhat does new keyword do?
- Create an object
- Set the prototype of the object (i.e.
__proto__) to the prototype of the function, making prototype chaining - Execute the function with
thispointing to the newly created object - Return the newly created object
Prototype chaining
JavaScript uses prototype chaining to find the properties and methods of an object by traversing the prototype chain until undefined is found, which is the end of the prototype chain.
Example: String.prototype, Array.prototype, Object.prototype, Function.prototype, Number.prototype, Boolean.prototype, Date.prototype, RegExp.prototype, Error.prototype, Symbol.prototype, Promise.prototype, Map.prototype, Set.prototype, WeakMap.prototype, WeakSet.prototype, ArrayBuffer.prototype, SharedArrayBuffer.prototype, Atomics.prototype, DataView.prototype, JSON.prototype, Math.prototype, Reflect.prototype
const name : string = 'John';
console.log(name.toUpperCase()); // `name` is a string but does not have toUpperCase method
// find the method in the prototype chain (String.prototype)
const arr = [1, 2, 3];
console.log(arr.map((item) => item + 1)); // `arr` is an array but does not have map method
// find the method in the prototype chain (Array.prototype)
const obj = { name: 'John' };
console.log(obj.hasOwnProperty('name')); // `obj` is an object but does not have hasOwnProperty method
// find the method in the prototype chain (Object.prototype)__proto__
Only object has __proto__. It is a property of an object that points to the prototype of the object.
The object is inheriting the prototype as defined in function/class and saving it in __proto__.
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
return `Hello, my name is ${this.name}`;
};
let person1 = new Person("Alice");
console.log(person1.greet()); // "Hello, my name is Alice"
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person.prototype.constructor === Person); // trueGenerator
Generator is a function that can be paused and resumed, using function*/function *foo() syntax.
Generator function returns an iterator object, which has a next() method that returns an object with two properties: value and done.
When the generator function is called, the code inside the function is not executed. Instead, it returns an iterator object.
function* foo() {
yield 1;
yield 2;
yield 3;
}