Scope와 Closure

나연·2020년 4월 5일
0

Javascript

목록 보기
3/8
post-thumbnail

Scope

Scope란?

변수 접근 규칙에 따른 유효범위를 말한다.
변수는 어떠한 환경 내에서만 사용 가능하며, 프로그래밍 언어는 각각의 변수 접근 규칙을 갖고 있다. 변수와 그 값이 어디서부터 어디까지 유효한지를 판단하는 범위를 scope이라고 한다.
Javascript는 기본적으로, 함수가 선언되는 동시에 자신만의 scope를 가진다.

let greeting = 'Hello';
function greetSomeone(){                //
  let firstName = 'Josh';               // Local scope                           
  return greeting + '' + firstName;     //
}                                       //

greetSomeone(); //'Hello Josh'
firstName; //ReferenceError

➡변수 firstName에 접근할 수 있는 범위가 존재한다.
➡Local scope 안쪽에서 선언 된 변수는 밖에서 사용할 수 없다.


Local Scope vs. Global Scope

안쪽 Scope에서 바깥 변수/함수를 접근하는 것은 가능하다. (greeting)
작은 버블({})에서 큰 버블 접근 가능
바깥쪽 Scope에서 안쪽 변수/함수를 접근하는 것은 불가능하다. (firstName)
큰 버블에서 작은 버블 접근 불가능

Scope는 중첩이 가능하다.(함수 안에 함수를 넣을 수 있다.)
Global Scope는 최상단의 Scope로 전역 변수(Global variable)는 어디서든 접근이 가능하다.
지역 변수(Local variable)는 함수 내에서 전역 변수보다 더 높은 우선순위를 가진다.

let name = "Richard";
function showName(){
  let name = 'Jack'; // 지역변수
  console.log(name); // ? <2> Jack
}

console.log(name); // ?  <1> Richard
showName(); 
console.log(name); // ? <3> Richard


let name = "Richard";
function showName(){
  name = "Jack" ; // 전역 변수
  console.log(name); //? <2> Jack
}

console.log(name); // ? <1> Richard
showName();
console.log(name);// ? <3> Jack

**다시 말해, 작은 버블({})에서 큰 버블의 접근은 가능하지만, 큰버블에서 작은 버블의 접근은 불가능하다.

function a(){ 
  const b = 'b'; // d와 nn에 접근 불가능
  function c(){  
    const d = 'd'; //b에 접근 가능, nn 접근 불가능 
    function e(){  // d와 b에 접근 가능
      const nn = 'nn'
      }
  }
}

Function Scope vs. Block Scope

(Block Scope: if문,for문 처럼 중괄호로 시작하고 끝나는 단위)

for(let i=0; i<5; i++){
  console.log(i); // 다섯번 iteration
}
  console.log('final i:', i); //ReferenceError

➡block 범위를 벗어나는 즉시 변수를 사용할 수 없다.


var vs. let vs. const

Javascript는 기본적으로 function 단위로 자신만의 scope를 가진다. (var 키워드)
그러나 Block 단위로 Scope를 구분했을 때에 예측하기 쉬운 코드를 작성할 수 있다. (let 키워드)

for(var i=0; i<5; i++){
  console.log(i); //다섯번 iteration
}
console.log('final i:', i); //5

➡var는 block 범위를 벗어나도 (같은 function scope에서는) 사용이 가능하다.

function greetSomeone(firstName) { //firstName = 'Steve'
  let time = 'night';  //time='night'
  if(time==='night){
     let greeting = 'Good Night';
     }
  return greeting + '' + firstName; //Reference Error
}
greetSomeone('Steve');

➡let으로 선언한 greeting 변수는 해당 block 안쪽에서만 접근이 가능하다.

const pi = 3.14
pi = 3.141592//Reference Error

➡const는 값이 변하지 않는 변수, 즉 상수를 정의할 때 사용하는 키워드로 let 키워드와 동일하게 Block Scope를 따른다. 값을 재정의하려고 하면 type error를 낸다.

letconstvar
유효 범위Block ScopeBlock ScopeFunction Scope
값 재정의가능불가능가능
재선언불가능불가능가능

전역 변수와 window 객체

전역 범위를 대표하는 객체 window
Globla Scope에서 선언된 함수, 그리고 var 키워드를 이용해 선언된 변수는 window 객체와 연결된다.

var myName = 'Paul';
console.log(window.n=myName); //Paul
function foo(){
console.log('bar');
}
console.log(foo===window.foo);//true

var myName = 'paul';
window
myName === window.myName

*선언 없이 초기화된 변수는 위험성이 있다. 되도록 선언 키워드 없이 변수를 초기화하지말기.
'use strict' 를 적용하면 문법적으로 실수할 수 있는 부분들을 에러로 판단함


Closure

클로저란 외부 함수의 변수에 접근할 수 있는 내부 함수, 또는 이러한 작동 원리를 일컫는 용어이다.
더 쉽게 말해서 원래 함수 내부에 선언한 변수는 scope에서도 살펴봤듯이 사라진다. 하지만 클로저가 스코프를 계속 들고 있으므로 그 함수 내부의 변수를 참조할 수 있게 되는 것이다. Closure! 즉, 함수를 닫아주는 역할을 하는 것

🚩다음은 모두 같은 클로저의 정의이다. 의미를 생각해보면서 이해도를 높여보자.
➡Closures are nothing but FUNCTIONS WITH PRESERVED DATA (클로저는 저장된 데이터가 있는 함수일 뿐이다)
➡외부함수가 실행 종류된 이후에도 반환된 내부함수가 계속 이 외부함수의 스코프를 참조할 수 있다.

다음코드에서 innerFn 함수에서 접근할 수 있는 Scope는 총 몇개일까.
function outerFn() {
  let outerVar = 'outer';
  console.log(outerVar);

  function innerFn() {
    let innerVar = 'inner';
    console.log(innerVar);
 }
}

let globalVar = 'global';
outerFn();

//정답은 3개! innerVar, outerVar, globalVar 가장 안쪽 버블에서 바깥쪽 버블에 모두 접근 가능하다.
function outerFn() {
  let outerVar = 'outer';
  console.log(outerVar);

    function innerFn() {
      let innerVar = 'inner';
      console.log(innerVar);
  }
  return innerFn;
}
outerFn(); //outerVar의 값이 찍히고, 아직 실행되지 않은 함수 innerFn이 리턴된다.
function outerFn() {
  let outerVar = 'outer';
  console.log(outerVar);

    function innerFn() {
      let innerVar = 'inner';
      console.log(innerVar);
  }
  return innerFn;
}

outerFn()(); //'inner'
let innerFn = outerFn(); // innerFn()
innerFn(); //'inner'

✔function innerFn()이 여기서 클로저 함수이며, 이런 내부함수는 지역변수(innerVar), 외부 함수의 변수(outerVar), 젼역변수(globalVar)의 접근이 모두 가능하다.

클로저, 왜 필요할까?

1. 커링: 함수 하나가 n개의 인자를 받는 대신, n개의 함수를 만들어 각각 인자를 받게 하는 방법
➡특정 값을 고정해놓고 재사용할 수 있는 장점이 있다.

function adder(x) {
  return function(y){`
    retrun x+y;
  }
}

adder(2)(3); //5

let add100 = adder(100);
add100(2); //102
add100(10); //110

2. 외부 함수의 변수가 저장되어 마치 템플릿 함수와 같이 사용 가능하다.

function htmlMaker(tag){
  let startTag = '<' + tag + '>' ;
  let endTag = '</' +tag + '>' ;
    return function(content) {
      return startTag + content + endTag ;
  }
}

let divMaker = htmlMaker('div');
divMaker('code'); // <div>code</div>

3. 클로저 모듈 패턴: 변수를 스코프 안쪽에 가두어 함수 밖으로 노출시키지 않는 방법, 간접적으로만 바꿀 수 있도록한다.
➡어떤 주요 정보를 아무나 수정하지 못하도록 할 때 사용한다.

function makeCounter() {
  let privateCounter = 0;

  let obj = {
    increment : function() {
      privateCounter++;
    },
    decrement: function() {
      privateCounter--;
    },
    getValue: function() {
      return privateCounter;
    }
  }
  return obj;
}

let counter1 = makerCouter();
counter1.increment()
counter1.increment()
counter1.getValue() //2
profile
아름다운 상상을 실현하는 개발자입니다🌈🤍

0개의 댓글