Scope & Closure (D13)

devfish·2023년 1월 2일
1

Javascript

목록 보기
10/30
post-thumbnail

Extra Reading

Key Points

Scope:

  • do not declare variables without a keyword or the variables become global!
  • var allows redeclaring the same variable, which might interfere with browser's built-in functions! (e.g. var console)
  • use strict helps by e.g. classifying undeclared variables as an error
  • passing arguments into functions that do not specify parameters, or longer list of arguments than specified, will not produce errors. it will just not use the arguments at all, period
  • arrow functions follow block (not function) scope

Closures

  • applications: data preservation for reusable functions, data access restriction / hiding, event handlers, module design patterns etc.
  • disadvantages: 1. variables declared inside a closure are not garbage collected, and 2. too many closures can slow down your app (due to duplication of code in the memory)

Scope

  • scopes can be nested

variable shadowing

  • local scope is prioritized over global scope

block scope | function scope

  • arrow functions get treated as block scope, not function scope!
  • function scope starts when the function is called & ends when the function ends
  • variables declared without keywords are part of the biggest scope

let vs const vs var

  • var ignores block scope, and only follows function scope
  • let blocks redeclaring the same variable (bug)
  • const variables cannot be reassigned (TypeError)

window object (browser-only)

  • functions and variables declared with var belongs to this windows object
  • accessible from anywhere
  • using var can block browser's built-in functions from functioning properly

Closures

According to Eloquent JS:

The ability to treat functions as values, combined with the fact that local bindings are re-created every time a function is called, brings up an interesting question. What happens to local bindings when the function call that created them is no longer active?
...
This feature—being able to reference a specific instance of a local binding in an enclosing scope—is called closure. A function that references bindings from local scopes around it is called a closure. This behavior not only frees you from having to worry about lifetimes of bindings but also makes it possible to use function values in some creative ways.

According to mdn:

the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

In essence:

a closure is a combination of an inner function and its lexical scope, which includes the parent or outer scope. It can access its lexical scope even if it is executed outside of it.

closure functions:

  • a function that returns a function
  • the inner functions keeps reference of its outer function scope
    • i.e. inner functions can access an outer function's scope reference and variables at any time, even if the outer function finished executing
  • accessibility of variables is managed by scope
    • variables of the outer scope are accessible inside the inner scope, not the other way around
    • return function and position of variable declarations determine how the variables can be accessed
  • how to identify a closure: if inside a function you see an alien variable (not defined inside that function), most likely that function is a closure because the alien variable is captured

what is lexical scope?

The set of bindings visible inside a block is determined by the place of that block in the program text. Each local scope can also see all the local scopes that contain it, and all scopes can see the global scope. This approach to binding visibility is called lexical scoping. ~E. JS

  • lexical environment => think "surrounding words"
  • lexical scope means that the scope chain (“What is the parent scope for this variable?”) is determined by where functions are defined in the code base, not where functions are executed
  • the lexical scope allows a function scope to access statically the variables from the outer scopes
    • called lexical (static) scope because the nested scopes are determined at lexing time just by the source code, without executing it

lexing time

The lexing phase of compilation determines where and how all identifiers are declared, and thus how they will be looked up during execution. This is the same mechanism which results in “hoisting” variables. The variables are not actually moved within the source code, the declarations simply occur during the lexing phase and so the JavaScript engine is aware of these before execution.

Applications

  • when you need to create private variables or define a behavior
    that is attached to an event

data preservation

  • variables in closures can help you maintain a state that you can use later
    • outer function's parameter variable still holds the argument value even after the function has ended
  • HTML string construction (reusable functions!)
    const tagMaker = tag => content => `<${tag}>${content}</${tag}>`
    const divMaker = tagMaker('div'); 
    //
    divMaker('hello') // '<div>hello</div>'
    divMaker('codestates') // '<div>codestates</div>'
    //
    const anchorMaker = tagMaker('a');
    anchorMaker('go') // '<a>go</a>'
    anchorMaker('urclass') // '<a>urclass</a>'

data access restriction

  • closure module pattern

  • private methods & properties (ref)

    var myModule = (function() {
    'use strict';
    
    var _privateProperty = 'Hello World';
    
    function _privateMethod() {
     console.log(_privateProperty);
    }
    
    return {
     publicMethod: function() {
       _privateMethod();
     }
    };
    })();
    //.
    myModule.publicMethod(); // outputs 'Hello World'
    console.log(myModule._privateProperty); // is undefined protected by the module closure
    myModule._privateMethod(); // is TypeError protected by the module closure
  • Revealing module pattern (ref)
    Using the return statement we can return a object literal that 'reveals' only the methods or properties we want to be publicly available

    var myModule = (function() {
    'use strict';
    
    var _privateProperty = 'Hello World';
    var publicProperty = 'I am a public property';
    
    function _privateMethod() {
     console.log(_privateProperty);
    }
    
    function publicMethod() {
     _privateMethod();
    }
    
    return {
     publicMethod: publicMethod,
     publicProperty: publicProperty
    };
    })();
    //.
    myModule.publicMethod(); // outputs 'Hello World'
    console.log(myModule.publicProperty); // outputs 'I am a public property'
    console.log(myModule._privateProperty); // is undefined protected by the module closure
    myModule._privateMethod(); // is TypeError protected by the module closure
    

  • 변수 value는 직접 수정하는 것이 불가능하고, 리턴하는 객체가 제공하는 메서드를 통해서만 조작이 가능
  • 이렇게 캡슐화를 함으로써 변수 value를 makeCounter 함수로 보존해서 전역 변수로 인한 side effect를 방지

practice

<!DOCTYPE html>
<html>
<body>
  <button class="toggle">toggle</button>
  <div class="box" style="width: 100px; height: 100px; background: red;">box</div>

  <script>
    var box = document.querySelector('.box');
    var toggleBtn = document.querySelector('.toggle');

    var toggle = (function () {
      var isShow = false;

      return function () {
        if(!isShow) box.style.display="none";
        else {box.style.display="block"; changeText()}

        isShow = !isShow;
        console.log(isShow);

      };
    })();

    var changeText = (function(){
        var isNew= false;

        return function () {
            if (!isNew) box.textContent="oldBox";
            else box.textContent="newBox";

            isNew = !isNew;
        }

    })();

    // ② 이벤트 프로퍼티에 클로저를 할당
    toggleBtn.onclick = toggle;

  </script>
</body>
</html>
profile
la, di, lah

0개의 댓글