Node.js - Web Programming Full Stack

[Backend] Part 1-Javascript, Node.js, RESTful API, Routing, HTTPie, JSDoc, Stream, Promise, Refactoring

olivia_yj 2023. 4. 4. 05:36

the old process -> wasteful

 

No more blocking!

Offloading in Node.js refers to the practice of delegating certain tasks to separate processes or threads, instead of executing them in the main event loop of Node.js. The purpose of offloading is to improve the performance and scalability of Node.js applications, especially when dealing with heavy computational tasks or I/O operations.

There are different ways to achieve offloading in Node.js, such as:

Child Processes: Node.js provides a built-in module called child_process, which allows creating new processes and communicate with them through streams. By creating child processes, the main event loop can delegate CPU-intensive tasks to them, while continuing to handle other tasks.

Worker Threads: Since version 10.5.0, Node.js also provides a built-in module called worker_threads, which allows creating and managing JavaScript threads. Worker threads can share memory and communicate with each other, and can be used to offload CPU-intensive tasks from the main event loop.

Clustering: Clustering is a technique that involves creating multiple Node.js processes that can share the same network port. This allows distributing incoming requests across multiple processes, which can improve the scalability and availability of Node.js applications.

Overall, offloading is a powerful technique that can help improve the performance and scalability of Node.js applications, especially when dealing with heavy computational tasks or I/O operations.

 

 

Package.json is including meta data of package

 

Why do we need package-lock.json? because on the package.json file, the version is saved with "^" it means it's just using around that number of versions, so we need package-lock.json to know clearly what version it is.

 

 

Airbnb Javascript eslint plugin

npm install --save-dev eslint-config-airbnb-base eslint-plugin-import eslint-config-prettier
module.exports = {
	extends: ['airbnb-base', 'prettier']
}

to prevent the conflict of 'prettier' and 'eslint', we should use 'prettier' at the last part of array.

 

Typescript

npm install --save-dev @types/node

 

 

In JavaScript, the event loop, call stack, and callback queue are key concepts that are fundamental to understanding how the language handles asynchronous programming. Here's a brief overview of each one:

Call stack: The call stack is a data structure that keeps track of the currently executing function or code block. Whenever a function is called, it's added to the top of the call stack, and when the function returns, it's removed from the stack. JavaScript is a single-threaded language, meaning that it can only execute one function at a time, so the call stack determines the order in which functions are executed.

Event loop: The event loop is a mechanism in JavaScript that allows it to handle asynchronous operations by waiting for and processing events in a non-blocking way. When an asynchronous operation, such as a timer or network request, completes, an event is placed in the event queue.

Callback queue: The callback queue is a data structure that holds the callbacks for completed events that are waiting to be executed. When the call stack is empty, the event loop checks the callback queue for any callbacks that are waiting to be executed. If there are any, it dequeues them and adds them to the call stack, where they are executed in the order they were added.

Together, these three components allow JavaScript to handle asynchronous operations in a non-blocking way, which makes it possible to build responsive and interactive web applications.

 

 

 

 

 

Hoisting in JavaScript is a mechanism where variables and function declarations are moved to the top of their respective scopes (global or local) before the code is executed. This means that variables and functions can be used in a code block before they are declared, without throwing a ReferenceError.

In other words, JavaScript hoists variable and function declarations to the top of their current scope, so that they are available for use throughout that scope. However, only the declaration is hoisted, not the initialization or assignment. For example, consider the following code:

 

console.log(x);
var x = 5;

In this code, the variable x is declared and initialized with the value 5. However, when the code is executed, the console will output undefined, rather than 5. This is because the declaration of x is hoisted to the top of its current scope, but the initialization is not.

Similarly, function declarations are also hoisted to the top of their current scope. This means that a function can be called before it is declared. For example:

 

helloWorld(); // "Hello World!"

function helloWorld() {
  console.log("Hello World!");
}

In this code, the function helloWorld() is declared after it is called, but because function declarations are hoisted, the code will still output Hello World! to the console.

 

 

Lexical scope in JavaScript refers to the way variable names are resolved in nested functions. In a language with lexical scoping, the scope of a variable is determined by its location in the source code, and not by its location during runtime.


In JavaScript, every function has its own lexical scope. When a function is defined inside another function, the inner function has access to the variables and functions declared in the outer function, as well as those declared in the global scope. However, the outer function does not have access to the variables and functions declared in the inner function.

For example, consider the following code:

function outerFunction() {
  var outerVariable = "Hello, ";

  function innerFunction(name) {
    console.log(outerVariable + name);
  }

  return innerFunction;
}

var greet = outerFunction();
greet("John"); // Output: "Hello, John"

In this code, outerFunction() returns the innerFunction(), which is then assigned to the greet variable. When greet() is called with the argument "John", the output is "Hello, John". This is because innerFunction() has access to the outerVariable declared in outerFunction()'s lexical scope.

Lexical scoping in JavaScript is important because it allows for encapsulation and separation of concerns, enabling developers to write more modular and reusable code.

var:
var was the original keyword used for variable declaration in JavaScript.
var has function-level scoping, meaning that a variable declared with var is accessible within the function it is declared in, or, if declared outside a function, it is accessible globally.
var declarations are hoisted, meaning that the variable can be used before it is declared in the code.
var can be redeclared and reassigned.

 

function example() {
  var x = 10;
  if (true) {
    var x = 20; // same variable 'x'
    console.log(x); // Output: 20
  }
  console.log(x); // Output: 20
}
example();

let:
let was introduced in ECMAScript 6 (ES6) as a new way of declaring variables.
let has block-level scoping, meaning that a variable declared with let is only accessible within the block it is declared in, such as a loop or an if statement.
let declarations are not hoisted, so they cannot be used before they are declared.
let can be reassigned, but not redeclared.

function example() {
  let x = 10;
  if (true) {
    let x = 20; // different variable 'x'
    console.log(x); // Output: 20
  }
  console.log(x); // Output: 10
}
example();

const:
const was also introduced in ES6 as a new way of declaring variables.
const has block-level scoping, similar to let.
const declarations are not hoisted, so they cannot be used before they are declared.
const cannot be reassigned or redeclared, making it a good choice for declaring constants or values that should not change.

function example() {
  const x = 10;
  if (true) {
    const x = 20; // different variable 'x'
    console.log(x); // Output: 20
  }
  console.log(x); // Output: 10
}
example();

In general, it is recommended to use let or const instead of var in modern JavaScript development, as they offer better scoping and fewer opportunities for mistakes. let should be used for variables that may be reassigned, while const should be used for values that should not change.

In JavaScript, a closure is a combination of a function and its lexical environment, which allows the function to access variables and functions defined outside of its own scope, even after the outer function has returned.

Closures are created when a function is defined inside another function, and the inner function uses variables declared in the outer function. The inner function retains access to these variables, even after the outer function has returned, because the inner function's scope chain includes the outer function's scope.

Here is an example of a closure in JavaScript:

function outerFunction() {
  var outerVariable = "Hello, ";

  function innerFunction(name) {
    console.log(outerVariable + name);
  }

  return innerFunction;
}

var greet = outerFunction();
greet("John"); // Output: "Hello, John"

In this code, outerFunction() returns the innerFunction(), which is then assigned to the greet variable. When greet() is called with the argument "John", the output is "Hello, John". This is because innerFunction() has access to the outerVariable declared in outerFunction()'s lexical scope, even though outerFunction() has already returned.

Closures are powerful because they allow for data privacy and encapsulation, allowing a function to have access to private variables and functions defined in its outer scope, while still exposing a public interface for other code to use. Closures are commonly used in JavaScript for creating factory functions, as well as for creating objects with private properties and methods.

 

After give function Student(name) 'this.__proto__.constructor(name)', it is changed from 'undefined' to 'Jeongho'

It's ES5 style so we should practice with ES6

 

In JavaScript, every object has a property called prototype, which is essentially a blueprint for creating new objects of the same type. The prototype property is used for inheritance in JavaScript.

When a new object is created, it inherits properties and methods from its prototype. The prototype property is used to add methods and properties to an object's parent object, which are then inherited by all of its descendant objects.

For example, let's consider the following code:

 

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

var john = new Person("John", 30);
john.greet(); // Output: "Hello, my name is John and I am 30 years old."

In this code, we define a Person constructor function which takes in a name and an age. We then add a method called greet to the Person.prototype object. Finally, we create a new Person object called john and call the greet method on it.

The john object inherits the greet method from its prototype, and is able to call it as if it was defined directly on the john object. This allows us to avoid duplicating code and to create reusable methods and properties for our objects.

Prototypal inheritance is a key feature of JavaScript, and is used extensively in many libraries and frameworks, such as jQuery and React.