You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: JavaScript/functions.md
+41-98Lines changed: 41 additions & 98 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -370,104 +370,47 @@ let creates a new binding for i in each iteration of the loop, so each setTimeou
370
370
### Summary:
371
371
Closures allow inner functions to retain access to variables from their outer scope. They are essential for data encapsulation, creating private variables, and handling asynchronous operations correctly.
372
372
373
-
## Scope
374
-
## 1. Lexical (Static) Scope:
375
-
376
-
* Definition: Lexical scope (also called static scope) is determined by the position of a variable's declaration within the source code. It's resolved at compile time (or when the code is parsed). You can visualize it by looking at the nested structure of functions.
377
-
* How it works: When the JavaScript engine encounters a variable, it first looks for the variable in the current scope. If it doesn't find it, it moves outward to the enclosing (parent) scope, and then to the parent's parent scope, and so on, until it finds the variable or reaches the global scope.
378
-
* Example:
379
-
```
380
-
function outerFunction() {
381
-
let outerVar = "I'm from the outer function";
382
-
383
-
function innerFunction() {
384
-
let innerVar = "I'm from the inner function";
385
-
console.log(innerVar); // Output: I'm from the inner function
386
-
console.log(outerVar); // Output: I'm from the outer function (lexical scoping)
387
-
}
388
-
389
-
innerFunction();
390
-
console.log(outerVar); // Output: I'm from the outer function
391
-
// console.log(innerVar); // Error: innerVar is not defined in the outer function's scope
392
-
}
393
-
394
-
outerFunction();
395
-
```
396
-
In this example:
397
-
* innerFunction has access to both innerVar (its own variable) and outerVar (from its parent scope, outerFunction). This is lexical scoping.
398
-
* outerFunction has access to outerVar but not innerVar. Scope lookup goes outward, not inward.
399
-
* If you tried to access innerVar outside of innerFunction, you'd get an error.
400
-
401
-
### 2. Function Scope (Older JavaScript - with var):
402
-
* Definition: Variables declared with var have function scope. They are accessible anywhere within the entire function, regardless of where they are declared inside that function.
403
-
* Example:
404
-
```
405
-
function myFunction() {
406
-
var x = 10;
407
-
408
-
if (true) {
409
-
var y = 20;
410
-
console.log(x); // Output: 10
411
-
}
412
-
413
-
console.log(y); // Output: 20 (y is still accessible here because of function scope)
414
-
}
415
-
416
-
myFunction();
417
-
```
418
-
* Important Note: Function scope with var can lead to unexpected behavior because variables can be accessed even before their declaration (due to hoisting). It's generally recommended to use let and const (block scope) whenever possible to avoid these issues.
419
-
420
-
### 3. Block Scope (Introduced with let and const):
421
-
* Definition: Variables declared with let and const have block scope. They are only accessible within the block of code (e.g., inside curly braces {}) where they are defined.
422
-
* Example:
423
-
```
424
-
function myBlockScopeFunction() {
425
-
let x = 10;
426
-
427
-
if (true) {
428
-
let y = 20;
429
-
const z = 30; // const variables are also block-scoped
430
-
console.log(x); // Output: 10
431
-
console.log(y); // Output: 20
432
-
console.log(z); // Output: 30
433
-
}
434
-
435
-
console.log(x); // Output: 10
436
-
// console.log(y); // Error: y is not defined (block scope)
437
-
// console.log(z); // Error: z is not defined (block scope)
438
-
}
439
-
440
-
myBlockScopeFunction();
441
-
```
442
-
* Benefits of Block Scope: Block scope helps prevent accidental variable overwriting and makes code more predictable and easier to reason about. It's a best practice to use let and const whenever you can.
443
-
444
-
### 4. Global Scope:
445
-
* Definition: Variables declared outside of any function or block have global scope. They are accessible from anywhere in your code.
446
-
* Example:
447
-
```
448
-
var globalVar = "I'm global"; // Using var (generally avoid)
449
-
let globalLet = "I'm also global (but better)"; // Using let (preferred)
450
-
const globalConst = "I'm a global constant";
451
-
452
-
function myFunction() {
453
-
console.log(globalVar); // Output: I'm global
454
-
console.log(globalLet); // Output: I'm also global (but better)
455
-
console.log(globalConst); // Output: I'm a global constant
456
-
}
457
-
458
-
myFunction();
459
-
console.log(globalVar); // Output: I'm global
460
-
```
461
-
* Caution: Overuse of global variables can make your code harder to maintain and debug. It's best to minimize their use and keep variables within the smallest scope necessary.
| Block | let, const | Within the block (e.g., {}) where defined |
469
-
| Global | N/A (outside functions/blocks) | Anywhere in the code |
470
-
373
+
Closures are a powerful feature in JavaScript that allows inner functions to access variables from their outer (enclosing) functions, even after the outer function has returned.
374
+
375
+
### Advantages:
376
+
*##### Data Encapsulation and Privacy:
377
+
Closures enable you to create private variables and methods within a function. This helps in data hiding and prevents external access to sensitive data, promoting better code organization and security.
378
+
*##### State Maintenance:
379
+
Closures can be used to maintain state across function calls. The inner function retains access to the variables of the outer function, allowing it to remember and update values between invocations.
380
+
*##### Partial Application and Currying:
381
+
Closures facilitate partial application and currying techniques, where you can create new functions by pre-filling some arguments of an existing function. This enhances code reusability and flexibility.
382
+
*##### Module Pattern:
383
+
Closures are fundamental to the module pattern, a common design pattern for creating self-contained and reusable code modules. Modules can encapsulate private data and expose public methods through closures.
384
+
*#### Event Handling and Callbacks:
385
+
Closures are often used in event handling and callbacks to maintain access to relevant variables from the outer scope when the event or callback is triggered.
386
+
### Disadvantages:
387
+
*#### Memory Consumption:
388
+
Closures can lead to increased memory consumption because they retain references to variables in their outer scope, even if those variables are no longer needed elsewhere. This can potentially cause memory leaks if not managed carefully.
389
+
*#### Performance Overhead:
390
+
The process of creating and maintaining closures can introduce some performance overhead, as the JavaScript engine needs to manage the scope chain and variable access. However, this overhead is usually negligible in most cases.
391
+
*#### Debugging Complexity:
392
+
Closures can make debugging more challenging due to the complex scope chain and the interaction between inner and outer functions. Tracing the origin of variables and understanding their values might require careful inspection.
393
+
*#### Potential for Unexpected Behavior:
394
+
If not used carefully, closures can sometimes lead to unexpected behavior, especially when dealing with asynchronous operations or shared variables. It's crucial to understand how closures interact with the event loop and variable scope to avoid potential pitfalls.
395
+
396
+
#### Summary:
397
+
* Closures are a valuable tool in JavaScript, offering powerful capabilities for data encapsulation, state management, and code organization.
398
+
* However, it's important to be aware of the potential drawbacks related to memory consumption, performance, and debugging complexity.
399
+
400
+
401
+
402
+
403
+
Let's delve deeper into call, apply, and bind in JavaScript, including their advantages and disadvantages.1. call() * Purpose: Invokes a function directly, explicitly setting the this value and providing arguments individually. * Syntax: function.call(thisArg, arg1, arg2, ...) * Example: const person = { name: "Alice" };
404
+
function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`);}
* Advantages: * Clarity: Arguments are passed individually, making the code readable, especially when dealing with a small, fixed number of arguments. * Flexibility: Allows for direct control over the this context. * Disadvantages: * Cumbersome for many arguments: Passing a large number of arguments individually can become tedious and less maintainable.2. apply() * Purpose: Invokes a function directly, explicitly setting the this value and providing arguments as an array. * Syntax: function.apply(thisArg, [argsArray]) * Example: const person = { name: "Bob" };const args = ["Greetings", "!!"];
407
+
function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`);}
* Advantages: * Handles variable number of arguments: Ideal when you don't know the number of arguments beforehand or when you have them stored in an array. This is very useful for functions that accept a variable number of parameters. * Useful with array-like objects: You can apply methods to array-like objects (e.g., arguments object, DOM NodeList). * Disadvantages: * Less readable for fixed arguments: For a small, fixed number of arguments, call() might be more readable.3. bind() * Purpose: Creates a new function where the this value is permanently bound to the provided value. It does not immediately execute the function. * Syntax: function.bind(thisArg, arg1, arg2, ...) * Example: const person = { name: "Charlie" };
410
+
function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`);}
411
+
const greetCharlie = greet.bind(person, "Hi"); // Creates a new functiongreetCharlie("?"); // Output: Hi, Charlie? (The "Hi" is pre-filled)
* Advantages: * Event Handling: Crucial for event handlers where you need to maintain the correct this context (e.g., within a setTimeout or event listener). * Partial Application/Currying: Allows you to create new functions by pre-filling some arguments. This is a powerful functional programming technique. * Creating Bound Functions: Useful when you want to pass a method as a callback, ensuring the correct this context. * Disadvantages: * Doesn't execute immediately: It creates a new function, which might be a slight overhead if you need to execute it right away (use call or apply in that case). * Slightly more complex: The concept of creating a new bound function might be a little harder to grasp initially compared to call and apply.Summary Table:| Method | Invokes Function? | Arguments | this Binding | Use Cases ||---|---|---|---|---|| call() | Yes | Individually | Explicitly set | Direct invocation, fixed arguments || apply() | Yes | Array | Explicitly set | Variable arguments, array-like objects || bind() | No (creates new function) | Individually (for pre-filling) | Permanently bound | Event handling, partial application, callbacks |Which to use when? * call(): Use when you want to call a function immediately with a specific this value and a small, fixed number of arguments. * apply(): Use when you want to call a function immediately with a specific this value and you have a variable number of arguments (or they're in an array). * bind(): Use when you want to create a new function with a permanently bound this value, often for event handling, callbacks, or partial application.Understanding these three methods is essential for mastering JavaScript's function manipulation capabilities. They give you fine-grained control over the this context and argument passing, leading to more flexible and powerful code.
0 commit comments