From 60a38a5c5c80f2c98fc6d509d84948f44624a53c Mon Sep 17 00:00:00 2001 From: Rohan Paul Date: Fri, 13 Mar 2020 16:11:19 +0530 Subject: [PATCH] closure use case --- ...heory-GOOD-Explanations-Private-Methods.md | 98 +++++++++++++++++++ .../OOP-Encapsulation-Theory.md | 93 ------------------ .../Closure/closure-why-its-needed.js | 18 ++++ Javascript/js-basics/Closure/closure.md | 73 ++++++++------ ...e-usecase-1.js => more-closure-example.js} | 0 .../js-basics/scope-basic-understanding.md | 28 +++--- 6 files changed, 174 insertions(+), 136 deletions(-) create mode 100644 Javascript/OOP-Prototypal-Inheritence/OOP-Encapsulation-Theory-GOOD-Explanations-Private-Methods.md delete mode 100644 Javascript/OOP-Prototypal-Inheritence/OOP-Encapsulation-Theory.md create mode 100644 Javascript/js-basics/Closure/closure-why-its-needed.js rename Javascript/js-basics/Closure/{closure-usecase-1.js => more-closure-example.js} (100%) diff --git a/Javascript/OOP-Prototypal-Inheritence/OOP-Encapsulation-Theory-GOOD-Explanations-Private-Methods.md b/Javascript/OOP-Prototypal-Inheritence/OOP-Encapsulation-Theory-GOOD-Explanations-Private-Methods.md new file mode 100644 index 0000000..4db91b1 --- /dev/null +++ b/Javascript/OOP-Prototypal-Inheritence/OOP-Encapsulation-Theory-GOOD-Explanations-Private-Methods.md @@ -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 +``` diff --git a/Javascript/OOP-Prototypal-Inheritence/OOP-Encapsulation-Theory.md b/Javascript/OOP-Prototypal-Inheritence/OOP-Encapsulation-Theory.md deleted file mode 100644 index 43cb4da..0000000 --- a/Javascript/OOP-Prototypal-Inheritence/OOP-Encapsulation-Theory.md +++ /dev/null @@ -1,93 +0,0 @@ -### 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 contains 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 heigth = h; // Both the 'height' and 'h' is private - this.setWidth = function(w) { - width = w; - }; - this.setHeight = function(h) { - heigth = h; - }; - this.getWidth = function() { - return width; - }; - this.getHeight = function() { - return heigth; - }; - this.constructor.prototype.getDiagonal = function() { - return Math.sqrt(heigth * heigth + 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 have no directly 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. - -```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(); - // 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 -``` diff --git a/Javascript/js-basics/Closure/closure-why-its-needed.js b/Javascript/js-basics/Closure/closure-why-its-needed.js new file mode 100644 index 0000000..30017ac --- /dev/null +++ b/Javascript/js-basics/Closure/closure-why-its-needed.js @@ -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(). +*/ diff --git a/Javascript/js-basics/Closure/closure.md b/Javascript/js-basics/Closure/closure.md index af1ad76..546b888 100755 --- a/Javascript/js-basics/Closure/closure.md +++ b/Javascript/js-basics/Closure/closure.md @@ -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) @@ -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, + } } ``` @@ -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 @@ -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 \ No newline at end of file + +- 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 diff --git a/Javascript/js-basics/Closure/closure-usecase-1.js b/Javascript/js-basics/Closure/more-closure-example.js similarity index 100% rename from Javascript/js-basics/Closure/closure-usecase-1.js rename to Javascript/js-basics/Closure/more-closure-example.js diff --git a/Javascript/js-basics/scope-basic-understanding.md b/Javascript/js-basics/scope-basic-understanding.md index 5e17e1b..6e74a42 100755 --- a/Javascript/js-basics/scope-basic-understanding.md +++ b/Javascript/js-basics/scope-basic-understanding.md @@ -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 @@ -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. @@ -44,15 +42,15 @@ 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 @@ -60,6 +58,6 @@ 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. \ No newline at end of file +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.