Back to the Basics

[JS/Node][Codestates] 핵심 개념과 주요 문법 - lesson2 본문

Programming Languages/JavaScript & Node.js

[JS/Node][Codestates] 핵심 개념과 주요 문법 - lesson2

9Jaeng 2021. 7. 24. 13:00
728x90

1. 클로저

  • 자바스크립트에서는 다른 언어와 달리 크롤저 라는 개념이 있다.
  • JavaScript MDN 에서의 클로저 정의 :
    • "클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저는 독립적인 변수를 참조하는 함수이다.
    • 독립적인 변수란 로컬에 선언되지도 않고, 파라미터로 넘져지지도 않는 변수를 말한다. 클로저에 선언된 함수는 생성되었을 때의 환경을 기억한다.
    • 클로저를 이해하려면 JavaScript가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping) 먼저 이해해야 한다
  • 어휘적환경(Lexical Environment) 란?
    • 어휘적 환경이란 선언 당시의 환경에 대한 정보을 담은 객체이다. 정적인 환경 이라고도 한다
    • 스코프와 클로저는 밀접한 관계가 있는데 스코프는 변수의 유효 범위이고, 클로저는 그 유효 범위로 인한 현상이나 상태를 말한다.
    • 스코프에서 외부로 정보를 노출시킬 수 있는 유일한 방법은 return하는 것이다. 이때 스코프 밎 어휘적 환경은 변하지 않는다.
      즉, 최소 선언시의 정보를 유지한다.
    • 함수가 참조하는 변수는 ** 함수가 선언되는 순갼 ** 이다.
    • lexical 이란, 어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다. 중첩된 함수는 외부 범위에서 선언한 변수에도 접근할 수 있다.

Achievement Goals

  • 클로저 함수의 정의와 특징에 대해 이해 할 수 있다.
  • 클로저가 갖는 스코프 범위를 이해할 있다.
  • 클로저를 이요 해 유영하게 쓰이는 몇 가지 코등 패턴을 이해할 수 있다.

클로저 함수의 특징

1) 함수를 리턴한다

// 화살표 함수로 작성
const adder=x=>y=>x+y;

// function 키워드를 하용하여 작성
const adder=function(x){
  return function(y){
    return x+y;
  }
}
  • 위의 코드에서 익명함수 function(y)를 리턴하는 함수가 클로저의 형태를 만든다.
  • 함수를 리턴한다 --> 함수와 함수가 선언된 형태
  • 어휘적 환경 (Lexical Environmenta) --> 변수 및 함수 선언의 형태
  • 클로저는 함수를 리턴하기 때문에 스코프(변수의 접근 범위) 가 구분된다.

2) 내부 함수는 외부 함수에 선언된 변수에 접근이 사가능하지만 외부함수는 내부함수의 변수에 접근이 불가능하다.

  • 클로저의 핵심은 변수의 접근 범위를 닫는데 있다 (clisure, 폐쇄)
    위의 코드에서 x는 외부함수 , y는 내부함수에 선언되어있다. 따라서, 위의 클로저 함수는 스코프라 분리되어있다.

클로저의 활용

1) 데이터를 보존하는 함수

일반적인 함수는 실행이 끝나면 함수 내부의 변수 사용이 불가능하지만, 클로저는 외부 함수의 실행이 끝나도 외부 함수 내의 변수가 메모리 상에 저장된다.
리는 어휘적 환경을 메모리에 저장하기 때문에 가능한 일이다.

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

const add7=adder(7);

위의 코드에서 add7은 adder 함수에서 인자로 넘긴 5라는 값을 변수x에 계속 담은 채로 남아있다.

ass7(3) //10
add7(10) // 17

그러므로 위의 코드와 같이 add7을 사용 가능하다.

클로저를 활용한 실용적인 예제

const tagMaker=tag =>content=>`<${tag}>${content}</${tag}>` ;

const divMaker=tagMaker('div');
divMaker('hello') // '<div>hello</div>'
divMaker('codestates') //<'div>codeststes</div>'

const anchorMaker=tagMaker('a');
anchrMaker('go'); // '<a>go</a>'

HTML 문자욜을 만드는 예제에서 , divMaker 한수는 div 하는 문자열을 tag 라는 변수에 담고있고, anchorMaker 함수는 a 라는 문자열을 tag 에 담고있다. 클로저는 특정 데이터를 스코프 안에 가우어 둔 채로 계속 사용이 가능하게 해준다.

2) 정보의 접근 제한 (캡슐화)

  • 클로저 코듈 패턴

아래의 코드는 여러 개의 내부 함수를 객체레 담아 리턴한다.

const makeCounter=()=>{
    let value=0;
    return {
        increase:() => {
        value=value+1
        },decrease:() =>{
        value=value-1
        },
        getValue:() => value 
       }
}

const counter1=makeCounter();

// counter1
// {increase: ƒ, decrease: ƒ, getValue: ƒ}
  • 위의 코드는 객체에 담아 여러 개의 내부 함수를 리턴한다. counter1 은 세 개의 함수를 담고있는 객체이다.
  • 위의 코드에서 스코프 규칙에 의해 makeCounter 함수의 선언식을 변경하지 않고는 vlue 의 값을 변경하는 것은 불가능하다.
    정보의 접근 제한 --> 캡슐화
  • 만약 캡슐화를 하지 않는다면 value 는 전역변수 일것이고, 전역변수는 다른 로직 등에 의해 의도되지 않은 변경을 초래한다.
    • 이를 side effect 하고 한다.
    • side effect를 최소화하면, 의도되지 않은 변경을 줄일 수 있다.
    • 이는 오류로부터 데이터를 보다 안전하게 닶ㅇ을 보호할 수 있음을 의미한다.
  • 클로저를 통해 불필요한 전역 변수 사용을 줄이고, 스코프를 이용해 값을 보다 안전하게 다룰 수 있다.

3) 모듈화


const counter1=makeCounter();
undefined
counter1.increase();
undefined
counter1.increase();
undefined
counter1.decrease();
undefined
counter1.getValue();
// 1

const counter2=makeCounter();
undefined
counter2.decrease();
undefined
counter2.decrease();
undefined
counter2.decrease();
undefined
counter2.getValue();
// -3
  • 위의 코드에서 getValue 메소드는 외부 함수에 선언된 value 값을 리턴하는 함수이다.
  • makeCounter에 의래 return 된 객체는 value 값을 각자 독릭적으로 갖게 되므로 서로에게 영향을 끼치지 않고, 각각의 값을 보존한다.

** 이와 같이 함수 재사용성을 극대화 하여, 함수 하나를 완전히 독립적인 "부품" 의 형태로 분리하는 것을 모듈화 라고 한다.

** 클로저를 통해 데이터와 메소드를 같이 묶어서 다룰 수 있다 --> 클로저는 모듈화에 매우 유리하다. **

Checkpoint

  1. 클로저로간주되는 function은 ?
let seenYet = function() {
  let archive = {};
  return function(val) {
    if (archive[val]) {
      return true;
    } 
    archive[val] = true;
    return false;
  }
}

--> seenYet 함수가 리턴하고있는 익명함수
SeenYet이 반환하는 익명함수는 외부함수 seenYet의 스코프에 선언된 변수 acrchive에 접근할 수 있기 때문에 seenyet이 리턴하는 익명함수가 클로저이다.

  1. 클로저 사용을 보여주는 함수는 ?
let multiplyByX = function(x) {
  return function(y) {
    return x * y;
  }
}

let multiplyBy5;
multiplyBy5 = multiplyByX(5);

multiplyBy5(4);




let multiplyByFive = function() {
  return function(y) {
    return 5 * y;
  }
}

let multiplyBy5 
multiplyBy5 = multiplyByFive();
multiplyBy5(4);
  • 리턴 함수가 x에 접근할 수 있기 때문에 muptiplyByX가 클로저를 사용하고있다.
    • 리턴되는 함수는 x에 접근할 수 있고 x가 위치한 곳은 외부함수의 context, x에 접근 가능한 스코프 안 이다.
    • x를 포함하여 쓰여있는 코드 자체를 어쉬적 환경 (lexical environment) 라고 한다.
    • 클로저의 정의인 "함수와 함수가 선언된 어휘적 환경의 조합" 의 의미로부터 x에 접근할 수 있는 이유는 , 리턴되는 함수가 선언된 주변의 언어적 환경 중 x가 포함되어있기 때문이다. 이런 함수를 통칭하여 클로져함수 (외부함수의 context에 접근할 수 있는 내부함수)

** 클로저에 대한 심화학습 : Excution contetxt , lexical environment 에 대한 포스팅은 주말에 진행해보자**

1. Spread/Rest 문법

Achievement Goals

  • Spread/Rest 문법, 수조 분해 할당을 사용할 수 있다.

Spread 문법/Rest 문법

Spread 문법

spread는 주로 배열을 풀어서 인자로 전달하거나, 배열을 풀어서 각각의 요소로 넣을 때에 사용한다.

function sum(x,y,z){
    return x+y+z;
}
undefined
const numbers=[1,2,3];
undefined
sum(...numbers);
//6

Rest 문법

파라미터를 배열의 형태로 받아서 사용할 수 있다. 파라미터 개수가 가변적일 때 유용하다.

function sum(...theArgs){
    return theArgs.reduce((previous,current)=>{
        return previous+current;
    });
}
undefined
sum(1,2,3)
// 6
sum(1,2,3,4)
// 10

배열에서 사용하기

  1. 배열 합치기
let parts=['shoulders','knees'];
undefined
let lyrics=['head',...parts,'and','toes'];
undefined
lyrics
// (5) ["head", "shoulders", "knees", "and", "toes"]
let arr1=[0,1,2];
undefined
let arr2=[3,4,5];
undefined
arr1=[...arr1,...arr2];
// (6) [0, 1, 2, 3, 4, 5]
arr1
// (6) [0, 1, 2, 3, 4, 5]

** Spread 문법은 기존 배열을 변경하지 않으므로 immutable 이다. **

  1. 배열 복사
arr=[1,2,3]
// (3) [1, 2, 3]
arr2=[...arr]
// (3) [1, 2, 3]
//arr.slice() 와 유사하다

arr
//(3) [1, 2, 3]
arr2.push(4);
//4
arr2
//(4) [1, 2, 3, 4]
  1. 객체에서 사용하기
let obj1={foo:'bar',x:42};

let obj2={foo:'baz',y:13};

let clonedObj={...obj1};

let mergedObj={...obj1,...obj2};
undefined
clonedObj
// {foo: "bar", x: 42}
mergedObj
// {foo: "baz", x: 42, y: 13}


let mergedObj2={...obj2,...obj1}
mergedObj2
// {foo: "bar", y: 13, x: 42}

위의 코드를 확인해보면 객체에서 같은 키가 존재할 경우엔 마지막에 선언된 객체의 값을 따른다.

  1. 함수에서 나머지 파라미터 받아오기
function myFun(a,b,...manyMoreArgs){
    console.log("a",a);
    console.log("b",b);
    console.log('manyMoreArgs',manyMoreArgs);
}

myFun("one","two","three","four","five","six");
//a one
//b two
//manyMoreArgs (4) ["three", "four", "five", "six"]

전개구문

Rest 파라미터

구조 분해 (Destructing)

구조분해 할당은 Spread 문법을 이용하여 값을 해체한 후, 개별 값ㄷ을 변수에 새로 할당하는 과정을 말한다.

배열

onst [a, b, ...rest] = [10, 20, 30, 40, 50];
a
//10
b
//20
rest
//(3) [30, 40, 50]
rest[1]
//40

객체

  • 객체에서 구조 분해 할당을 사용하는 경우 선언(let, const, var) 와 함께 사용하지 않으면 에러가 발생할 수 있다.
const {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
undefined
a
//10
b
//20
rest
//{c: 30, d: 40}

함수에서 객체 분해

function whois({displayName: displayName, fullName: {firstName: name}}){
  console.log(displayName + " is " + name);
}

let user = {
  id: 42,
  displayName: "jdoe",
  fullName: {
      firstName: "John",
      lastName: "Doe"
  }
};

whois(user)
//jdoe is John

구조분해할당

728x90
Comments