quiz/js: update some solutions

This commit is contained in:
Yangshun 2024-06-01 17:29:05 +08:00
parent aabc846ef5
commit cdd4d2ba0b
7 changed files with 496 additions and 92 deletions

View File

@ -2,29 +2,145 @@
title: Describe the difference between a cookie, `sessionStorage` and `localStorage`.
---
Local storage is useful for storing data that the user will need to access later, such as offline data, because it stores the data in the browser and the system. This data will persist even if the user closes and reopens the browser.
## TL;DR
Session storage is a great way to improve the performance of your web applications. It stores data locally on the browser but is specific to (and only accessible by) the respective site/browser tab and is only available while the user is on the site/tab. This is a more secure storage method due to the restrictive access and promotes better site performance due to reduced data transfer between server and client.
All of the following are mechanisms of storing data on the client, the user's browser in this case. `localStorage` and `sessionStorage` both implement the [Web Storage API interface](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API).
Cookies are a good choice for storing data that should not be persisted for a long time, such as session IDs. Cookies allow you to set an expiry time at which point it would be deleted. Cookies can only be smaller sized data compared to the other two storage methods.
- **Cookies**: Suitable for server-client communication, small storage capacity, can be persistent or session-based, domain-specific. Sent to the server on every request.
- **`localStorage`**: Suitable for long-term storage, data persists even after the browser is closed, accessible across all tabs and windows of the same origin, highest storage capacity among the three.
- **`sessionStorage`**: Suitable for temporary data within a single page session, data is cleared when the tab or window is closed, has a higher storage capacity compared to cookies.
## Similarities
Cookies, `localStorage`, and `sessionStorage`, are all:
- Storage mechanisms on the client side. This means the clients can read and modify the values.
- Key-value based storage.
- They are only able to store values as strings. Objects will have to be serialized into a string (`JSON.stringify()`) in order to be stored.
## Differences
Here's a table summarizing the 3 client storage mechanisms.
| Property | Cookie | `localStorage` | `sessionStorage` |
| --- | --- | --- | --- |
| Initiator | Client or server. Server can use `Set-Cookie` header | Client | Client |
| Expiry | Manually set | Forever | On tab close |
| Persistent across browser sessions | Depends on whether expiration is set | Yes | No |
| Sent to server with every HTTP request | Cookies are automatically being sent via `Cookie` header | No | No |
| Capacity (per domain) | 4kb | 5MB | 5MB |
| Access | Any window | Any window | Same tab |
| Lifespan | As specified | Until deleted | Until tab is closed |
| Persistent across browser sessions | If a future expiry date is set | Yes | No |
| Sent to server with every HTTP request | Yes, sent via `Cookie` header | No | No |
| Total capacity (per domain) | 4kb | 5MB | 5MB |
| Access | Across windows/tabs | Across windows/tabs | Same tab |
| Security | JavaScript cannot access `HttpOnly` cookies | None | None |
---
## Storage on the web
Cookies, `localStorage`, and `sessionStorage`, are all storage mechanisms on the client (web browser). It is useful to store data on the client for client-only state like access tokens, themes, personalized layouts, so that users can have a consistent experience on a website across tabs and usage sessions.
These client-side storage mechanisms have the following common properties:
- This means the clients can read and modify the values (except for `HttpOnly` cookies).
- Key-value based storage.
- They are only able to store values as strings. Non-strings will have to be serialized into a string (e.g. `JSON.stringify()`) in order to be stored.
### Use cases for each storage mechanism
Since cookies have a relatively low maximum size, it is not advisable to store all your client-side data within cookies. The distinguishing properties about cookies are that cookies are sent to the server on every HTTP request so the low maximum size is a feature that prevents your HTTP requests from being too large due to cookies. Automatic expiry of cookies is a useful feature as well.
With that in mind, the best kind of data to store within cookies is small pieces of data that needs to be transmitted to the server, such as auth tokens, session IDs, analytics tracking IDs, GDPR cookie consent, language preferences that are important for authentication, authorization, and rendering on the server. These values are sometimes sensitive and can benefit from the `HttpOnly`, `Secure`, and `Expires`/`Max-Age` capabilities that cookies provide.
`localStorage` and `sessionStorage` both implement the [Web Storage API interface](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API). Web Storages have a generous total capacity of 5MB, so storage size is usually not a concern. The key difference is that values stored in Web Storage are not automatically sent on HTTP requests. While you can manually include values from Web Storage when making AJAX/`fetch` requests, the initial browser request will contain them.
Hence Web Storage should not be used to store data that is relied on by the server for the initial rendering of the page if server-side rendering is being used (typically authentication/authorization-related information). `localStorage` is most suitable for user preferences data that do not expire, like themes and layouts (if it is not important for the server to render the final layout). `sessionStorage` is most suitable for temporary data that only needs to be accessible within the current browsing session, such as form data (useful to preserve data during accidental reloads).
The following sections dive deeper into each client storage mechanism.
### Cookies
Cookies are used to store small pieces of data on the client side that can be sent back to the server with every HTTP request.
- **Storage capacity**: Limited to around 4KB for all cookies.
- **Lifespan**: Cookies can have a specific expiration date set using the `Expires` or `Max-Age` attributes. If no expiration date is set, the cookie is deleted when the browser is closed (session cookie).
- **Access**: Cookies are domain-specific and can be shared across different pages and subdomains within the same domain.
- **Security**: Cookies can be marked as `HttpOnly` to prevent access from JavaScript, reducing the risk of XSS attacks. They can also be secured with the `Secure` flag to ensure they are sent only when HTTPS is used.
```js
// Set a cookie for the name/key `auth_token` with an expiry.
document.cookie =
'auth_token=abc123def; expires=Fri, 31 Dec 2024 23:59:59 GMT; path=/';
// Read all cookies. There's no way to read specific cookies using `document.cookie`.
// You have to parse the string yourself.
console.log(document.cookie); // auth_token=abc123def
// Delete the cookie with the name/key `auth_token` by setting an
// expiry date in the past. The value doesn't matter.
document.cookie = 'auth_token=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
```
It is a pain to read/write to cookies. `document.cookie` returns a single string containing all the key/value pairs delimited by `;` and you have to parse the string yourself. The [`js-cookie`](https://github.com/js-cookie/js-cookie) npm library provides a simple and lightweight API for reading/writing cookies in JavaScript.
A modern native way of accessing cookies is via the the [Cookie Store API](https://developer.mozilla.org/en-US/docs/Web/API/Cookie_Store_API) which is only available on HTTPS pages.
```js
// Set a cookie. More options are available too.
cookieStore.set('auth_token', 'abc123def');
// Async method to access a single cookie and do something with it.
cookieStore.get('auth_token').then(...);
// Async method to get all cookies.
cookieStore.getAll().then(...);
// Async method to delete a single cookie.
cookieStore.delete('auth_token').then(() =>
console.log('Cookie deleted')
);
```
The CookieStore API is relatively new and may not be supported in all browsers (supported in latest Chrome and Edge as of June 2024). Refer to [caniuse.com](https://caniuse.com/mdn-api_cookiestore) for the latest compatibility.
### `localStorage`
`localStorage` is used for storing data that persists even after the browser is closed and reopened. It is designed for long-term storage of data.
- **Storage capacity**: Typically around 5MB per origin (varies by browser).
- **Lifespan**: Data in `localStorage` persists until explicitly deleted by the user or the application.
- **Access**: Data is accessible within all tabs and windows of the same origin.
- **Security**: All JavaScript on the page have access to values within `localStorage`.
```js
// Set a value in localStorage.
localStorage.setItem('key', 'value');
// Get a value from localStorage.
console.log(localStorage.getItem('key'));
// Remove a value from localStorage.
localStorage.removeItem('key');
// Clear all data in localStorage.
localStorage.clear();
```
### `sessionStorage`
`sessionStorage` is used to store data for the duration of the page session. It is designed for temporary storage of data.
- **Storage Capacity**: Typically around 5MB per origin (varies by browser).
- **Lifespan**: Data in `sessionStorage` is cleared when the page session ends (i.e., when the browser or tab is closed). Reloading the page does not destroy data within `sessionStorage`.
- **Access**: Data is only accessible within the current tab or window. Different tabs or windows with the same page will have different `sessionStorage` objects.
- **Security**: All JavaScript on the same page have access to values within `sessionStorage` for that page.
```js
// Set a value in sessionStorage.
sessionStorage.setItem('key', 'value');
// Get a value from sessionStorage.
console.log(sessionStorage.getItem('key'));
// Remove a value from sessionStorage.
sessionStorage.removeItem('key');
// Clear all data in sessionStorage.
sessionStorage.clear();
```
## Notes
There are also other client-side storage mechanisms like [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) which is more powerful than the above-mentioned technologies but more complicated to use.
## References
- [What is the difference between localStorage, sessionStorage, session and cookies?](https://stackoverflow.com/questions/19867599/what-is-the-difference-between-localstorage-sessionstorage-session-and-cookies)

View File

@ -1,7 +1,7 @@
{
"slug": "describe-the-difference-between-a-cookie-sessionstorage-and-localstorage",
"languages": [],
"companies": ["bytedance"],
"companies": ["amazon", "bytedance"],
"premium": false,
"duration": 5,
"published": true,

View File

@ -2,29 +2,120 @@
title: Describe the difference between `<script>`, `<script async>` and `<script defer>`
---
## TL;DR
All of these ways (`<script>`, `<script async>`, and `<script defer>`) are used to load and execute JavaScript files in an HTML document, but they differ in how the browser handles loading and execution of the script:
- `<script>` is the default way of including JavaScript. The browser blocks HTML parsing while the script is being downloaded and executed. The browser will not continue rendering the page until the script has finished executing.
- `<script async>` downloads the script asynchronously, in parallel with parsing the HTML. Executes the script as soon as it is available, potentially interrupting the HTML parsing. `<script async>` do not wait for each other and execute in no particular order.
- `<script defer>` downloads the script asynchronously, in parallel with parsing the HTML. However, the execution of the script is deferred until HTML parsing is complete, in the order they appear in the HTML.
Here's a table summarizing the 3 ways of loading `<script>`s in a HTML document.
| Feature | `<script>` | `<script async>` | `<script defer>` |
| --- | --- | --- | --- |
| Parsing behavior | Blocks HTML parsing | Runs parallel to parsing | Runs parallel to parsing |
| Execution order | In order of appearance | Not guaranteed | In order of appearance |
| DOM dependency | No | No | Yes (waits for DOM) |
---
## What `<script>` tags are for
`<script>` tags are used to include JavaScript on a web page. The `async` and `defer` attributes are used to change how/when the loading and execution of the script happens.
## Plain `<script>`
## `<script>`
For normal `<script>` tags without any `async` or `defer`, when they are encountered, HTML parsing is blocked, the script is fetched and executed immediately. HTML parsing resumes after the script is executed.
For normal `<script>` tags without any `async` or `defer`, when they are encountered, HTML parsing is blocked, the script is fetched and executed immediately. HTML parsing resumes after the script is executed. This can block rendering of the page if the script is large.
Use `<script>` for critical scripts that the page relies on to render properly.
```html
<!DOCTYPE html>
<html>
<head>
<title>Regular Script</title>
</head>
<body>
<!-- Content before the script -->
<h1>Regular Script Example</h1>
<p>This content will be rendered before the script executes.</p>
<!-- Regular script -->
<script src="regular.js"></script>
<!-- Content after the script -->
<p>This content will be rendered after the script executes.</p>
</body>
</html>
```
{/* TODO: Add CSB examples for blocking scripts */}
## `<script async>`
In `<script async>`, the script will be fetched in parallel to HTML parsing and executed as soon as it is available (potentially before HTML parsing completes) and it will not necessarily be executed in the order in which it appears in the HTML document. Use `async` when the script is independent of any other scripts on the page, for example, analytics.
In `<script async>`, the browser downloads the script file asynchronously (in parallel with HTML parsing) and executes it as soon as it is available (potentially before HTML parsing completes). The execution will not necessarily be executed in the order in which it appears in the HTML document. This can improve perceived performance because the browser doesn't wait for the script to download before continuing to render the page.
Use `<script async>` when the script is independent of any other scripts on the page, for example, analytics and ads scripts.
```html
<!DOCTYPE html>
<html>
<head>
<title>Async Script</title>
</head>
<body>
<!-- Content before the script -->
<h1>Async Script Example</h1>
<p>This content will be rendered before the async script executes.</p>
<!-- Async script -->
<script async src="async.js"></script>
<!-- Content after the script -->
<p>
This content may be rendered before or after the async script executes.
</p>
</body>
</html>
```
## `<script defer>`
In `<script defer>`, the script will be fetched in parallel to HTML parsing and executed when the document has been fully parsed, but before firing `DOMContentLoaded`. If there are multiple of them, each deferred script is executed in the order they appeared in the HTML document.
Similar to `<script async>`, `<script defer>` also downloads the script in parallel to HTML parsing but the script is only executed when the document has been fully parsed and before firing `DOMContentLoaded`. If there are multiple of them, each deferred script is executed in the order they appeared in the HTML document.
If a script relies on a fully-parsed DOM, the `defer` attribute will be useful in ensuring that the HTML is fully parsed before executing.
```html
<!DOCTYPE html>
<html>
<head>
<title>Deferred Script</title>
</head>
<body>
<!-- Content before the script -->
<h1>Deferred Script Example</h1>
<p>This content will be rendered before the deferred script executes.</p>
<!-- Deferred script -->
<script defer src="deferred.js"></script>
<!-- Content after the script -->
<p>This content will be rendered before the deferred script executes.</p>
</body>
</html>
```
## Notes
- Generally, the `async` attribute should be used for scripts that are not critical to the initial rendering of the page and do not depend on each other, while the `defer` attribute should be used for scripts that depend on / is depended on by another script.
- The `async` attribute should be used for scripts that are not critical to the initial rendering of the page and do not depend on each other, while the `defer` attribute should be used for scripts that depend on / is depended on by another script.
- The `async` and `defer` attributes are ignored for scripts that have no `src` attribute.
- `<script>`s with `defer` or `async` that contain `document.write()` will be ignored with a message like "A call to document.write() from an asynchronously-loaded external script was ignored".
- `<script>`s with `defer` or `async` that contain `document.write()` will be ignored with a message like "A call to `document.write()` from an asynchronously-loaded external script was ignored".
- Even though `async` and `defer` help to make script downloading asynchronous, the scripts are still eventually executed on the main thread. If these scripts are computationally intensive, it can result in laggy/frozen UI. [Partydown](https://partytown.builder.io/) is a library that helps relocate script executions into a [web worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) and off the [main thread](https://developer.mozilla.org/en-US/docs/Glossary/Main_thread), which is great for third-party scripts where you do not have control over the code.
## References
{/* TODO: Add image https://media.licdn.com/dms/image/D5612AQEYCnsZ1f9GFQ/article-cover_image-shrink_600_2000/0/1699370054733?e=2147483647&v=beta&t=MeKuUXk5QMblVoausf5p3swTkBCICOHoRsijQqMfxZE */}
## Further reading
- [`<script>`: The Script element | MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#defer)
- [async vs defer attributes](https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html)

View File

@ -2,9 +2,40 @@
title: Explain "hoisting"
---
Hoisting is a term used to explain the behavior of variable declarations in your code. Variables declared or initialized with the `var` keyword will have their declaration "moved" up to the top of their module/function-level scope, which we refer to as hoisting. However, only the declaration is hoisted, the assignment (if there is one), will stay where it is.
## TL;DR
Note that the declaration is not actually moved - the JavaScript engine parses the declarations during compilation and becomes aware of declarations and their scopes. It is just easier to understand this behavior by visualizing the declarations as being hoisted to the top of their scope. Let's explain with a few examples.
- **Variable declarations (`var`)**: Declarations are hoisted, but not initializations. The value of the variable is `undefined` if accessed before initialization.
- **Variable declarations (`let` and `const`)**: Declarations are hoisted, but not initialized. Accessing them results in `ReferenceError` until the actual declaration is encountered.
- **Function expressions (`var`)**: Declarations are hoisted, but not initializations. The value of the variable is `undefined` if accessed before initialization.
- **Function declarations (`function`)**: Both declaration and definition are fully hoisted.
- **Class declarations (`class`)**: Declarations are hoisted, but not initialized. Accessing them results in `ReferenceError` until the actual declaration is encountered.
- **Import declarations (`import`)**: Declarations are hoisted, and side effects of importing the module are executed before the rest of the code.
The following behavior summarizes the result of accessing the variables before they are declared.
| Declaration | Accessing before declaration |
| ------------------------------ | ---------------------------- |
| `var foo` | `undefined` |
| `let foo` | `ReferenceError` |
| `const foo` | `ReferenceError` |
| `class Foo` | `ReferenceError` |
| `var foo = function() { ... }` | `undefined` |
| `function foo() { ... }` | Normal |
| `import` | Normal |
---
## Hoisting
Hoisting is a term used to explain the behavior of variable declarations in your code. Variables declared or initialized with the `var` keyword will have their declaration "moved" up to the top of their containing scope during compilation, which we refer to as hoisting.
Only the declaration is hoisted, the initialization/assignment (if there is one), will stay where it is. Note that the declaration is not actually moved the JavaScript engine parses the declarations during compilation and becomes aware of variables and their scopes, but it is easier to understand this behavior by visualizing the declarations as being "hoisted" to the top of their scope.
Let's explain with a few code samples. Note that the code for these examples should be executed within a module scope instead of being entered line by line into a REPL like the browser console.
### Hoisting of variables declared using `var`
Hoisting is seen in action here as even though `foo` is declared and initialized after the first `console.log()`, the first `console.log()` prints the value of `foo` as `undefined`.
```js
console.log(foo); // undefined
@ -12,42 +43,96 @@ var foo = 1;
console.log(foo); // 1
```
Function declarations have the body hoisted while the function expressions (written in the form of variable declarations) only has the variable declaration hoisted.
You can visualize the code as:
```js
// Function Declaration
console.log(foo); // [Function: foo]
foo(); // 'FOOOOO'
function foo() {
console.log('FOOOOO');
}
console.log(foo); // [Function: foo]
// Function Expression
console.log(bar); // undefined
bar(); // Uncaught TypeError: bar is not a function
var bar = function () {
console.log('BARRRR');
};
console.log(bar); // [Function: bar]
var foo;
console.log(foo); // undefined
foo = 1;
console.log(foo); // 1
```
Variables declared via `let` and `const` are hoisted as well. However, unlike `var` and `function`, they are not initialized and accessing them before the declaration will result in a `ReferenceError` exception. The variable is in a "temporal dead zone" from the start of the block until the declaration is processed.
### Hoisting of variables declared using `let`, `const`, and `class`
Variables declared via `let`, `const`, and `class` are hoisted as well. However, unlike `var` and `function`, they are not initialized and accessing them before the declaration will result in a `ReferenceError` exception. The variable is in a "temporal dead zone" from the start of the block until the declaration is processed.
```js
x; // undefined
y; // ReferenceError: Cannot access 'y' before initialization
var x = 'local';
let y = 'local';
```
In reality, JavaScript creates all variables in the current scope before it even tries to executes the code. Variables created using `var` keyword will have the value of `undefined` where variables created using `let` and `const` keywords will be marked as `<value unavailable>`. Thus, accessing them will cause a `ReferenceError` preventing you to access them before intialization.
```js
z; // ReferenceError: Cannot access 'z' before initialization
const z = 'local';
```
```js
Foo; // ReferenceError: Cannot access 'Foo' before initialization
class Foo {
constructor() {}
}
```
### Hoisting of function expressions
Function expressions are functions written in the form of variable declarations. Since they are also declared using `var`, only the variable declaration is hoisted.
```js
console.log(bar); // undefined
bar(); // Uncaught TypeError: bar is not a function
var bar = function () {
console.log('BARRRR');
};
```
### Hoisting of function declarations
Function declarations use the `function` keyword. Unlike function expressions, function declarations have both the declaration and definition hoisted, thus they can be called even before they are declared.
```js
console.log(foo); // [Function: foo]
foo(); // 'FOOOOO'
function foo() {
console.log('FOOOOO');
}
```
The same applies to generators (`function*`), async functions (`async function`), and async function generators (`async function*`).
### Hoisting of `import` statements
Import declarations are hoisted. The identifiers the imports introduce are available in the entire module scope, and their side effects are produced before the rest of the module's code runs.
```js
foo.doSomething(); // Works normally.
import foo from './modules/foo';
```
## Under the hood
In reality, JavaScript creates all variables in the current scope before it even tries to executes the code. Variables created using `var` keyword will have the value of `undefined` where variables created using `let` and `const` keywords will be marked as `<value unavailable>`. Thus, accessing them will cause a `ReferenceError` preventing you to access them before initialization.
In ECMAScript specifications `let` and `const` declarations are [explained as below](https://tc39.es/ecma262/#sec-let-and-const-declarations):
> The variables are created when their containing Environment Record is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated.
However, this statement is [a litle bit different for `var` keyword](https://tc39.es/ecma262/#sec-variable-statement):
However, this statement is [a litle bit different for the `var` keyword](https://tc39.es/ecma262/#sec-variable-statement):
> Var variables are created when their containing Environment Record is instantiated and are initialized to `undefined` when created.
## Modern practices
In practice, modern code bases avoid using `var` and use `let` and `const` exclusively. It is recommended to declare and initialize your variables and import statements at the top of the containing scope/module to eliminate the mental overhead of tracking when a variable can be used.
ESLint is a static code analyzer that can find violations of such cases with the following rules:
- [`no-use-before-define`](https://eslint.org/docs/latest/rules/no-use-before-define): This rule will warn when it encounters a reference to an identifier that has not yet been declared.
- [`no-undef`](https://eslint.org/docs/latest/rules/no-undef): This rule will warn when it encounters a reference to an identifier that has not yet been declared.
## Further reading
- [Hoisting | MDN](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting)

View File

@ -2,18 +2,38 @@
title: What are the differences between variables created using `let`, `var` or `const`?
---
## TL;DR
In JavaScript, `let`, `var`, and `const` are all keywords used to declare variables, but they differ significantly in terms of scope, initialization rules, whether they can be redeclared or reassigned and the behavior when they are accessed before declaration:
| Behavior | `var` | `let` | `const` |
| --- | --- | --- | --- |
| Scope | Function or Global | Block | Block |
| Initialization | Optional | Optional | Required |
| Redeclaration | Yes | No | No |
| Reassignment | Yes | Yes | No |
| Accessing before declaration | `undefined` | `ReferenceError` | `ReferenceError` |
---
## Differences in behavior
Let's look at the difference in behavior between `var`, `let`, and `const`.
### Scope
Variables declared using the `var` keyword are scoped to the function in which they are created, or if created outside of any function, to the global object. `let` and `const` are _block scoped_, meaning they are only accessible within the nearest set of curly braces (function, if-else block, or for-loop).
```js
function foo() {
// All variables are accessible within functions.
var bar = 'bar';
let baz = 'baz';
const qux = 'qux';
var bar = 1;
let baz = 2;
const qux = 3;
console.log(bar); // bar
console.log(baz); // baz
console.log(qux); // qux
console.log(bar); // 1
console.log(baz); // 2
console.log(qux); // 3
}
console.log(bar); // ReferenceError: bar is not defined
@ -21,59 +41,82 @@ console.log(baz); // ReferenceError: baz is not defined
console.log(qux); // ReferenceError: qux is not defined
```
In the following example, `bar` is accessible outside of the `if` block but `baz` and `quz` are not.
```js
if (true) {
var bar = 'bar';
let baz = 'baz';
const qux = 'qux';
var bar = 1;
let baz = 2;
const qux = 3;
}
// var declared variables are accessible anywhere in the function scope.
console.log(bar); // bar
// let and const defined variables are not accessible outside of the block they were defined in.
// var variables are accessible anywhere in the function scope.
console.log(bar); // 1
// let and const variables are not accessible outside of the block they were defined in.
console.log(baz); // ReferenceError: baz is not defined
console.log(qux); // ReferenceError: qux is not defined
```
`var` ,`let` and `const` declared variables are all hoisted. `var` declared variables are auto-initialised with an undefined value. However, `let` and `const` variables are not initialised and accessing them before the declaration will result in a `ReferenceError` exception because they are in a "temporal dead zone" from the start of the block until the declaration is processed.
### Initialization
`var` and `let` variables can be initialized without a value but `const` declarations must be initialized.
```js
console.log(foo); // undefined
var foo = 'foo';
console.log(baz); // ReferenceError: can't access lexical declaration 'baz' before initialization
let baz = 'baz';
console.log(bar); // ReferenceError: can't access lexical declaration 'bar' before initialization
const bar = 'bar';
var foo; // Ok
let bar; // Ok
const baz; // SyntaxError: Missing initializer in const declaration
```
### Redeclaration
Redeclaring a variable with `var` will not throw an error, but `let` and `const` will.
```js
var foo = 'foo';
var foo = 'bar';
console.log(foo); // "bar"
var foo = 1;
var foo = 2;
console.log(foo); // 2
let baz = 'baz';
let baz = 'qux'; // Uncaught SyntaxError: Identifier 'baz' has already been declared
let baz = 3;
let baz = 4; // Uncaught SyntaxError: Identifier 'baz' has already been declared
```
`let` and `const` differ in that `let` allows reassigning the variable's value while `const` does not.
### Reassignment
`let` and `const` differ in that `var` and `let` allow reassigning the variable's value while `const` does not.
```js
// This is fine.
let foo = 'foo';
foo = 'bar';
var foo = 1;
foo = 2; // This is fine.
// This causes an exception.
const baz = 'baz';
baz = 'qux';
let bar = 3;
bar = 4; // This is fine.
const baz = 5;
baz = 6; // Uncaught TypeError: Assignment to constant variable.
```
### Accessing before declaration
`var` ,`let` and `const` declared variables are all hoisted. `var` declared variables are auto-initialized with an `undefined` value. However, `let` and `const` variables are not initialized and accessing them before the declaration will result in a `ReferenceError` exception because they are in a "temporal dead zone" from the start of the block until the declaration is processed.
```js
console.log(foo); // undefined
var foo = 'foo';
console.log(baz); // ReferenceError: can't access lexical declaration 'baz' before initialization
let baz = 'baz';
console.log(bar); // ReferenceError: can't access lexical declaration 'bar' before initialization
const bar = 'bar';
```
## Notes
- Since most browsers support `let` and `const` these days, using `var` is no longer recommended. If you need to target older browsers, write your code using `let`, and use a transpiler like Babel to compile your code to older syntax.
- In modern JavaScript, it's generally recommended to use `const` by default for variables that don't need to be reassigned. This promotes immutability and prevents accidental changes.
- Use `let` when you need to reassign a variable within its scope.
- Avoid using `var` due to its potential for scoping issues and hoisting behavior.
- If you need to target older browsers, write your code using `let`/`const`, and use a transpiler like Babel compile your code to older syntax.
## Further reading
- [The Difference of "var" vs "let" vs "const" in Javascript](https://medium.com/swlh/the-difference-of-var-vs-let-vs-const-in-javascript-abe37e214d66)

View File

@ -1,7 +1,7 @@
{
"slug": "what-is-event-loop-what-is-the-difference-between-call-stack-and-task-queue",
"languages": [],
"companies": ["salesforce"],
"companies": ["amazon", "salesforce"],
"premium": false,
"duration": 5,
"published": true,

View File

@ -1,16 +1,40 @@
---
title: What is the difference between `==` and `===`?
title: What is the difference between `==` and `===` in JavaScript?
---
`==` is the abstract equality operator while `===` is the strict equality operator. The `==` operator will compare for equality after doing any necessary type conversions. The `===` operator will not do type conversion, so if two values are not the same type `===` will simply return `false`. When using `==`, funky things can happen, such as:
## TL;DR
`==` is the abstract equality operator while `===` is the strict equality operator. The `==` operator will compare for equality after doing any necessary type conversions. The `===` operator will not do type conversion, so if two values are not the same type `===` will simply return `false`.
| Operator | `==` | `===` |
| --- | --- | --- |
| Name | (Loose) Equality operator | Strict equality operator |
| Type coercion | Yes | No |
| Compares value and type | No | Yes |
---
### Equality operator (`==`)
The `==` operator checks for equality between two values but performs type coercion if the values are of different types. This means that JavaScript will attempt to convert the values to a common type before making the comparison.
```js
42 == '42'; // true
0 == false; // true
null == undefined; // true
[] == false; // true
'' == false; // true
```
In these examples, JavaScript converts the operands to the same type before making the comparison. For example, `42 == '42'` is true because the string `'42'` is converted to the number `42` before comparison.
However, when using `==`, unintuitive results can happen:
```js
1 == '1'; // true
1 == [1]; // true
1 == true; // true
0 == ''; // true
0 == '0'; // true
0 == false; // true
'' == '0'; // false
```
As a general rule of thumb, never use the `==` operator, except for convenience when comparing against `null` or `undefined`, where `a == null` will return `true` if `a` is `null` or `undefined`.
@ -20,3 +44,48 @@ var a = null;
console.log(a == null); // true
console.log(a == undefined); // true
```
### Strict equality operator (`===`)
The `===` operator, also known as the strict equality operator, checks for equality between two values without performing type coercion. This means that both the value and the type must be the same for the comparison to return true.
```js
console.log(42 === '42'); // false
console.log(0 === false); // false
console.log(null === undefined); // false
console.log([] === false); // false
console.log('' === false); // false
```
For these comparisons, no type conversion is performed, so the statement returns `false` if the types are different. For instance, `42 === '42'` is `false` because the types (number and string) are different.
```js
// Comparison with type coercion (==)
console.log(42 == '42'); // true
console.log(0 == false); // true
console.log(null == undefined); // true
// Strict comparison without type coercion (===)
console.log(42 === '42'); // false
console.log(0 === false); // false
console.log(null === undefined); // false
```
### Bonus: `Object.is()`
There's one final value-comparison operation within JavaScript, that is the [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) static method. The only difference between `Object.is()` and `===` is how they treat of signed zeros and `NaN` values. The `===` operator (and the `==` operator) treats the number values `-0` and `+0` as equal, but treats `NaN` as not equal to each other.
## Conclusion
- Use `==` when you want to compare values with type coercion (and understand the implications of it). Practically, the only valid use case for the equality operator is when against `null` and `undefined` for convenience.
- Use `===` when you want to ensure both the value and the type are the same, which is the safer and more predictable choice in most cases.
### Notes
- Using `===` (strict equality) is generally recommended to avoid the pitfalls of type coercion, which can lead to unexpected behavior and bugs in your code. It makes the intent of your comparisons clearer and ensures that you are comparing both the value and the type.
- ESLint's [`eqeqeq`](https://eslint.org/docs/latest/rules/eqeqeq) rule enforces the use of strict equality operators `===` and `!==` and even provides an option to always enforce strict equality except when comparing with the `null` literal.
### Further reading
- [Equality (==) | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Equality)
- [Strict equality (===) | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality)