Scope & Closure

katsukichi·2021년 1월 26일
0

CodeStates_PRE

목록 보기
14/27

scope

스코프란 범위의 의미를 가진다.

"변수의 유효범위" 로 사용된다.

각종 선언 키워드 let, const의 범위가 어떻게되는집 보자.

scope의 범위?


let greeting = "hello";
function greetSomeone() {
  let firstName = "Josh";
  return greeting + " " + firstName;
}
greetSomeone(); // => Hello Josh
firstName; // ReferenceError

함수 내부는 Local Scope로 지역적인 스코프를 가진다. ( 지역변수 , 로컬변수 이런말 쓰던거 기억해보자.)

전체의 스코프를 가지는것은 전역변수 글로벌변수 라는말을 쓴다. Global Scope를 가진다.

즉 함수내부에서 선언된 변수는 밖에서는 모른다. 그러므로 사용할수없다.

다른언어와 동일할지 모르겠지만 함수가 종료되면서 메모리에 저장되어있던 값을 풀어준다.

즉 더이상 할당되지않은 메모리 주소가 되는것이다. ( 아마 비슷하게 동작하지않을까? )

JavaScript는 기본적으로, 함수가 선언되는(lexical) 동시에 자신만의 Scope를 가집니다.

local Scope vs global Scope

안쪽 Scope에서 바깥 변수/함수를 접근하는 것은 가능
빠깥쪽 Scope에서 안쪽 변수/함수를 접근하는것은 불가능

스코프는 중첩이 가능하다.
- 함수안에 함수 넣을 수 있다.

global scope는 최상단 Scope로 전역변수는 어디서든 접근이 가능하다.

지역변수는 함수 내에서 전역변수보다 더 높은 우선순위를 가진다.


let name = "Richard";

function showName(){
  let name = "Jack"; // 지역변수
  // ShowName 함수 안에서만 접근 가능
  console.log(name); //
}
console.log(name); // Richard
showName();// Jack
console.log(name); // Richard

서로다른 취급을 받는 name 변수


let name = "Richard"

function showName(){
  name = "Jack"
  // 선언( let ) 이 없기 때문에 , 바깥 scope에 있는 name이라는 변수를 가져옵니다.
  console.log(name);
}
console.log(name); // Richard
showName();// Jack
console.log(name); // Jack

function Scope vs block Scope

Block ?

중괄호로 시작하고 , 끝나는 단위

if(true) {
cosole.log("i am in th block");
}

for( ; ; ) {
console.log(i);
}

{
console.log("katsukichi");
}


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

for문이 종료되면 for내부에 초기화식으로 선언된 i는 메모리할당이 해제된다.

그러므로 i는 선언되어있지 않다고 뜰것이다.

Error

var ? vs let ?

  • JS는 기본적으로 함수단위의 자신만의 스코프를 가진다. var
  • 그러나 , Block단위로 Scope를 구분했을때에 예측하기 쉬운 코드를 작성할 수 있다. let

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

const

값이 변하지 않는 변수, 즉 상수를 정의할때 사용하는 키워드

  • let 키워드와 동일하게 Block Scope를 따른다.

  • 값을 재정의 하려ㅑ고 하면 TypeError를 낸다.

재선언을 막아주는 친구 : let,const 애용하자. ( 실수로인한 버그를 막아준다 )

전역변수와 window 객체

전역 범위를 대표하는 객체 window
global Scope에서 선언된 함수, 그리고 var키워드를 이용해 선언된 변수는 window 객체와 연결
전역범위에 너무 많은 변수를 선언하지말자 ( 임베디드는 예외인듯.. )

window 객체 내부를보면 전역범위에서 쓸수있는것들이 쫙 나열되어있다.

var키워드로 선언된 변수,함수 는 window.foo() 처럼 window전역 객체를통해서 볼수도 있다.

선언없이 초기화된 전역 변수


function showAge() {
  //age 는 전역 변수로 취급됩니다.
  age = 90;
  console.log(age);
}

showAge(); // 90
console.log(age); //90
// age === window.age

이렇게 되는것. 조심해서 선언 키워드없이 변수를 초기화지 말자!

이런것을 방지하려면 ?

Strict Mode를 사용하세요.

'use strict'; 를 최상단에 선언시 사용가능하다.

closure

자바스크립트에서는 다른 컴퓨터 언어와는 조금 다른 특성을 종종 가지고 있습니다. 그 중 종종 사용되는 클로져 라는 개념에 대해서 알아보겠다.

함수와 함수가 선언된 어휘적(lexical)환경의 조합

클로져 어려워하지말자! -> 스코프의 연장이구나.


function outerFn(){
  let outerVar = "outer";
  console.log(outerVar);
  
  function innerFn() {
    let innerVar = "inner";
    console.log(innerVar);
  }
}

let globalVar = "global";
outerFn();

다음중 innerFn함수에서 접근할수있는 Scope는 총 몇개 인가요 ?

아마도 확신할수있는것은 innerFn인 자기 자체블럭과 outerFn의 function block 까지는 확정적으로 접근가능한데

globarVar 를 잘생각해보자. 음 먼저 선언되었다 늦게선언되었다 차이인데.

이것을 hoisting개념이라고해야하나 아직 배우지않는 내용을 섞어서 받아들여야할지 고민이된다.

기본적으로 global도 접근가능하다고 알자. ( 순차적으로 알려주겠지.. )

이때 코드를좀 바꿔보겠다.


function outerFn(){
  let outerVar = "outer";
  console.log(outerVar);
  
  function innerFn() {
    let innerVar = "inner";
    console.log(innerVar);
  }
  return innerFn;  // 함수자체를 리턴해준다면 ?
}
outerFn(); // 과연 ?

함수가 실행된것은 아니지만, 함수 자체를 리턴한다.

좀더 문제를 꼬아보겠다 .


function outerFn(){
  let outerVar = "outer";
  console.log(outerVar);
  
  function innerFn() {
    let innerVar = "inner";
    console.log(innerVar);
  }
  return innerFn;  // 함수자체를 리턴해준다면 ?
}
outerFn()(); // 1. "outer"한번 찍히고 function innerFn()함수자체가 자리에남고 그자리에서 다시 함수실행문이 사용되어서 innerFn이 실행된다. 그래서 inner가 찍힌다?
let innerFn = outerFn(); // 2. "outer"찍히고 innerFn에는 이제 전역변수에 함수가 들어가게되고
innerFn(); // 3. innerFn()는 모르지않을까? referenceError가 뜨나 ? 

각각 무엇이 찍힐까?정답은 ?

  1. outer 찍히고 inner를찍는다.(정답) 리턴이 함수인경우 다시한번 실행시키는것이 가능!!
  2. outer 찍히고 innnerFn 변수 안에 innerFn()함수가 들어있다 . (정답)
  3. inner 가 찍힌다. (틀림) -> 왜지 ? 아> 위에있는 전역변수 innerFn이 실행되는것이다.

그래서

클로저가 뭔데? : 외부 함수의 변수에 접근할 수 있는 내부 함수 또는 , 이러한 작동 원리를 일컫는 용어!!!

위에 예제에서는 function innerFn()자체가 클로저 함수 라고할수있다.

이러한 패턴 자체를 클로저 패턴이라고 한다.

이런패턴이 자주쓰이고 유용하기때문에 이름붙여준거다.~!!!

유용한 closure 예제

커링 ? : 함수 하나가 n개의 인자를 받는 대신, n개의 함수를 만들어 각각 인자를 받게 하는 방법


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

adder(2)(3) ; // 5

let add100 - adder(100); // x의 값을 고정해놓고 재사용 할 수 있다.
add100(2); //102
add100(10); // 110

let add5 = adder(5);
add5(2) ; // 7

html태그를 만들어주는


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

let divMaker = htmlMaker('div');

클로저 모듈 패턴 : 변수를 스코프 안쪽에 가두어 함수 밖으로 노출 시키지 않는 방법


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

문제!!


let counter1 = makeCounter();
counter1.increment();
counter1.increment();
counter1.getValue(); // 2 ? 정답 : 2

let counter2 = makeCounter();
counter2.increment();
counter2.decrement();
counter2.increment();
counter2.getValue(); // 1 ?

객체를 리턴으로 돌려주느데 그 객체 내부에 매서드 3개 ? 존재하고있고

let counter1 = makeCounter() 하면 counter1은 객체가 된거처럼 매소드 이용이 가능하다.

privateCounter가 증가한다.

privateCounter는 스코프에 의해서 밖에서 직접 거드릴수없다.

increment , decrement 등의함수들로 간접적으로 바꿔줄수있다.

그럼 왜 밖에서 수정하게 못하게 만들어야하는가 ?

let type = "현금"; // 반드시 현금 또는 카드 여야함

내가 직접 건드리지않고 내부에서 제어하면 안전하다고 생각할수있다.

Java나 c++하면 private 한 변수를 기억해보자. 실제로 그런 안정성때문에 사용했던거 같다.

counter1 , counter2 각각 다르게 독립적으로 privateCounter를 가지고있기때문에

재사용이 가능해진다

핵심
클로저란 : 결국 외부함수에 접근할수있는 내부함수 또는 이러한 작동 원리

profile
front-back / end developer / Let's be an adaptable person

0개의 댓글