Skip to content

Commit

Permalink
closure use case
Browse files Browse the repository at this point in the history
  • Loading branch information
rohan-paul committed Mar 13, 2020
1 parent 439540c commit 60a38a5
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 136 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
### How do we achieve data encapsulation with Javascript objects?

Private member variables in objects.

**All properties or let us say member variables defined with the "this" keyword inside a function object is of public type, which mean that they can be accessed from outside of a created object.**

```js
function Person(n) {
// name and getName ar both public
this.name = n
this.getName = function() {
return this.name
}
}
```

All variables, which are defined with the "var" keyword (and NOT 'this') and all the methods that a constructor function is private and therefore NOT accessible from outside of a created object. The same applies to all function arguments.

#### - One of the most important things in object-oriented programming (OOP) is data encapsulation, which means to make private properties and then define public access methods to change these.

- All public methods, which are defined inside a function object, have access to all defined private member variables and private methods in a created object.

- Public methods defined on the outside of a function object can not access private member variables as they are not defined in the same scope as those.

```js
function Rectangle(h, w) {
var width = w // Both the 'width' and 'w' is private
var height = h // Both the 'height' and 'h' is private
this.setWidth = function(w) {
width = w
}
this.setHeight = function(h) {
height = h
}
this.getWidth = function() {
return width
}
this.getHeight = function() {
return height
}
this.constructor.prototype.getDiagonal = function() {
return Math.sqrt(height * height + width * width)
}
}

Rectangle.prototype.getArea = function() {
// We must use accessors in a prototype kind of method,
// then these methods can not access the private members
// of a created object.
return this.getWidth() * this.getHeight()
}

var rect = new Rectangle(60, 70)
rect.setHeight(20)
console.log(rect.getArea()) // => 1400
```

**Private methods (in the below example the born() method) have no direct access to properties that are defined to be public with "this" keyword in a function object.**

**To achieve this, one can define a variable that has the same reference as "this" refers to. And thats exactly whey I have to create the var thisObj in the below Person()**

```js
function Person(n, y) {
var name = n
var year = y
// Set a variable to the same as this
var thisObj = this
this.setName = function(n) {
name = n
}
this.getName = function() {
return name
}
this.setYear = function(y) {
year = y
}
this.getYear = function() {
return year
}
var born = function() {
var nYear = new Date().getFullYear()
// Use the thisObj variable which is the same as this refer to.
return nYear - thisObj.getYear()
// If I just used 'this' directly here, will get error -
// this.getYear is not a function
// The "this" keyword inside this function refers to an object
// created by the constructor function of this function object.
}
this.getBornYear = function() {
return born()
}
}
var person = new Person("Nikita", 60)

console.log(person.getName()) // => Nikita
console.log(person.getBornYear()) // => 1959
console.log(person.born()) // => person.born is not a function
```
93 changes: 0 additions & 93 deletions Javascript/OOP-Prototypal-Inheritence/OOP-Encapsulation-Theory.md

This file was deleted.

18 changes: 18 additions & 0 deletions Javascript/js-basics/Closure/closure-why-its-needed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
function func() {
var privateFunc = function() {
console.log("I am private")
}
return {
publicFunc: function() {
privateFunc()
},
}
}

func() // shows nothing
// func().privateFunc() // TypeError: func(...).privateFunc is not a function
func().publicFunc() // I am private

/*
func(), has a method publicfunction ( func.publicfunction() ) which calls privatefunction, which only exists inside the closure. You can NOT call privatefunction directly (i.e. func().privatefunction() ), just publicfunction().
*/
73 changes: 45 additions & 28 deletions Javascript/js-basics/Closure/closure.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,34 @@ A closure is the combination of a function bundled together (enclosed) with **re
**A closure is a function** that retains access to variables of the context it was created in even after said function call has returned.

```js
var awFunc = function (first) {
var someFunc = function (second) {
return first + second;
}
return someFunc; // note that I did not invoke this function, but I did return the function
function init() {
var name = "Mozilla" // name is a local variable created by init
function displayName() {
// displayName() is the inner function, a closure
alert(name) // use variable declared in the parent function
}
displayName()
}
init()
```

init() creates a local variable called name and a function called displayName(). The displayName() function is an inner function that is defined inside init() and is available only within the body of the init() function. Note that the displayName() function has no local variables of its own. However, since inner functions have access to the variables of outer functions, displayName() can access the variable name declared in the parent function, init().

var someMoreFunc = awFunc('awe') // At this point awFunc has finished running
This is an example of lexical scoping, which describes how a parser resolves variable names when functions are nested. The word lexical refers to the fact that lexical scoping uses the location where a variable is declared within the source code to determine where that variable is available. Nested functions have access to variables declared in their outer scope.

console.log(someMoreFunc()) // This will return 'aweundefined'
```js
var awFunc = function(first) {
var someFunc = function(second) {
return first + second
}
return someFunc // note that I did not invoke this function i.e. I did not use someFunc(), but I did returned the function itself
}

console.log(someMoreFunc('some')) // returns awesome
var someMoreFunc = awFunc("awe") // At this point awFunc has finished running

console.log(someMoreFunc()) // This will return 'aweundefined'

console.log(someMoreFunc("some")) // returns awesome
```

## Using Closures (Examples)
Expand All @@ -26,10 +42,10 @@ To use a closure, simply define a function inside another function and expose it
#### In JavaScript, closures are the primary mechanism used to enable data privacy. When you use closures for data privacy, the enclosed variables are only in scope within the containing (outer) function. You can’t get at the data from an outside scope except through the object’s privileged methods. In JavaScript, any exposed method defined within the closure scope is privileged. For example:

```js
const getSecret = (secret) => {
return {
getPrivileged: () => secret
}
const getSecret = secret => {
return {
getPrivileged: () => secret,
}
}
```

Expand All @@ -39,17 +55,17 @@ In the example above, the `getPrivileged()` method is defined inside the scope o

```js
const outerFunc = () => {
let name = 'Rohan'
let name = "Rohan"

const closureFunc = () => {
console.log(name);
}
return closureFunc()
const closureFunc = () => {
console.log(name)
}
return closureFunc()
}

var name = 'Paul'
var name = "Paul"

outerFunc() // => Will Print 'Rohan'
outerFunc() // => Will Print 'Rohan'
```

### So whats going on above
Expand All @@ -67,12 +83,13 @@ If a variable is not found anywhere, that’s an error in strict mode. Without u
### Some overall key points

### Closure
* A closure is a function that remembers its outer variables and can access them.
* Combination of a function and the lexical environment within which that function was declared
* The `closure` is the function object itself.
* Accessing variables outside of the immediate lexical scope creates a closure.
* Happens when we have a nested functions.
* JavaScript engines also may optimize, discard variables that are unused to save memory.
* A `Lexical Environment` object lives in the `heap` as long as there is a function which may use it. And when there are none, it is cleared.
* All functions in JavaScript are closures.
* The internal property `[[Environment]]` of a function, refers to the outer lexical environment

- A closure is a function that remembers its outer variables and can access them.
- Combination of a function and the lexical environment within which that function was declared
- The `closure` is the function object itself.
- Accessing variables outside of the immediate lexical scope creates a closure.
- Happens when we have a nested functions.
- JavaScript engines also may optimize, discard variables that are unused to save memory.
- A `Lexical Environment` object lives in the `heap` as long as there is a function which may use it. And when there are none, it is cleared.
- All functions in JavaScript are closures.
- The internal property `[[Environment]]` of a function, refers to the outer lexical environment
28 changes: 13 additions & 15 deletions Javascript/js-basics/scope-basic-understanding.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
**"scope"** as the set of rules that govern how the *Engine* can look up a variable by its identifier name and find it, either in the current Scope, or in any of the Nested Scopes it's contained within.
**"scope"** as the set of rules that govern how the _Engine_ can look up a variable by its identifier name and find it, either in the current Scope, or in any of the Nested Scopes it's contained within.

## Lex-time

Expand All @@ -12,22 +12,20 @@ It is considered best practice to treat lexical scope as, in fact, lexical-only,

Let's consider this block of code:

```
```js
foo = a => {
let b = a * 2

let b = a * 2;
bar = c => {
console.log(a, b, c)
}

bar = c => {
console.log(a, b, c)
}
bar ( b * 3)
bar(b * 3)
}

foo(2) // => 2 4 12
foo(2) // => 2 4 12
```


There are three nested scopes inherent in this code example. It may be helpful to think about these scopes as bubbles inside of each other.

Bubble 1 encompasses the global scope, and has just one identifier in it: foo.
Expand All @@ -44,22 +42,22 @@ Notice that these nested bubbles are strictly nested. We're not talking about Ve

### Look-ups

The structure and relative placement of these scope bubbles fully explains to the *Engine* all the places it needs to look to find an identifier.
The structure and relative placement of these scope bubbles fully explains to the _Engine_ all the places it needs to look to find an identifier.

In the above code snippet, the *Engine* executes the `console.log(..)` statement and goes looking for the three referenced variables `a`, `b`, and `c`. It first starts with the innermost scope bubble, the scope of the `bar(..)` function. It won't find `a` there, so it goes up one level, out to the next nearest scope bubble, the scope of `foo(..)`. It finds `a` there, and so it uses that `a`. Same thing for `b`. But `c`, it does find inside of `bar(..)`.
In the above code snippet, the _Engine_ executes the `console.log(..)` statement and goes looking for the three referenced variables `a`, `b`, and `c`. It first starts with the innermost scope bubble, the scope of the `bar(..)` function. It won't find `a` there, so it goes up one level, out to the next nearest scope bubble, the scope of `foo(..)`. It finds `a` there, and so it uses that `a`. Same thing for `b`. But `c`, it does find inside of `bar(..)`.

Had there been a `c` both inside of `bar(..)` and inside of `foo(..)`, the `console.log(..)` statement would have found and used the one in `bar(..)`, never getting to the one in `foo(..)`.

**Scope look-up stops once it finds the first match**. The same identifier name can be specified at multiple layers of nested scope, which is called "shadowing" (the inner identifier "shadows" the outer identifier). Regardless of shadowing, scope look-up always starts at the innermost scope being executed at the time, and works its way outward/upward until the first match, and stops.

**Note:** Global variables are also automatically properties of the global object (`window` in browsers, `global` in node env), so it *is* possible to reference a global variable not directly by its lexical name, but instead indirectly as a property reference of the global object.
**Note:** Global variables are also automatically properties of the global object (`window` in browsers, `global` in node env), so it _is_ possible to reference a global variable not directly by its lexical name, but instead indirectly as a property reference of the global object.

```js
window.a
```

This technique gives access to a global variable which would otherwise be inaccessible due to it being shadowed. However, non-global shadowed variables cannot be accessed.

No matter *where* a function is invoked from, or even *how* it is invoked, its lexical scope is **only** defined by where the function was declared.
No matter _where_ a function is invoked from, or even _how_ it is invoked, its lexical scope is **only** defined by where the function was declared.

The lexical scope look-up process *only* applies to first-class identifiers, such as the `a`, `b`, and `c`. If you had a reference to `foo.bar.baz` in a piece of code, the lexical scope look-up would apply to finding the `foo` identifier, but once it locates that variable, object property-access rules take over to resolve the `bar` and `baz` properties, respectively.
The lexical scope look-up process _only_ applies to first-class identifiers, such as the `a`, `b`, and `c`. If you had a reference to `foo.bar.baz` in a piece of code, the lexical scope look-up would apply to finding the `foo` identifier, but once it locates that variable, object property-access rules take over to resolve the `bar` and `baz` properties, respectively.

0 comments on commit 60a38a5

Please sign in to comment.