TIL #0403

Adela·2020년 4월 5일

✍Today I Learned

목록 보기
6/15
post-thumbnail

클로저(Closure): scope의 연장

외부함수의 변수에 접근할 수 있는 내부함수 또는 이런 작동원리

function outerFn(){
  let outerVar = 'outer';
  console.log(outerVar);
  
  function innerFn(){
    // 이 함수가 클로저 함수
    let innerVar = 'inner';
    console.log(innerVar);
  }
  return innerFn; //함수자체를 return
}

outerFn();  // ???

클로저 함수 안에서는 지역변수, 외부함수의 변수, 전역변수 모두에게로 접근 가능하다.

출력결과
맨 위의 console.log에서 outer가 콘솔에 찍히고
아직 실행되지 않은 함수innerFn(){ };자체가 함께 리턴되어 나온다.

다음의 경우 각각 콘솔에 어떻게 찍힐까???

outerFn()();   //출력결과 1
let innerFn = outerFn();   //출력결과 2
innerFn();   //출력결과 3

출력결과 1 : outer inner
출력결과 2 : outerFn(); outer와 innerFn(){ }이므로 둘 다 변수에 할당된다
콘솔에서는 outer만 찍힌다
innerFn;로 변수를 조회해보면 outer와 실행되지 않은 함수가 나온다
출력결과 3 : inner

JavaScript의 특징

함수를 parameter로 받는다 ⇨ callback
함수자체를 return한다 ⇨ closure

커링

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

function adder(x){  //함수를 생성하는 함수
  return function(y){
    return x + y;
  }
}

adder(5);  //일반함수를 리턴하고 아무런 숫자를 주지 않는다
adder(2)(3);  // 2는 x의 인자로, 3은 y의 인자로 받아 5리턴

커링을 적용하려면 변수에 adder함수를 할당한다

let add100 = adder(100);  //변수 add100은 x=100이지만 함수만 반환하는 변수
add100(2);   // y값에 2를 지정한 후 실행하면 102리턴
add100(10);  // 110

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

클로저 모듈 패턴

클로저함수의 유용한 점

  • x의 값을 고정해놓고 재사용할 수 있다.

  • 외부 함수의 변수를 내부 함수가 계속 쓰면서 템플릿처럼 사용할 수 있다.

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

function makeCounter(){
  let privateCounter = 0;
  
  return {   //리턴값이 객체 let obj={{},{},{}}; return obj;한 것과 같다
    increment : function (){   //객체에 대한 key값을 function으로
      privateCounter++;   //외부함수의 변수를 내부함수에서 사용중
    },
    decrement : function (){
      privateCounter--;
    },
    getValue : function (){
      return privateCounter;
    }
  }
}
let counter1 = makeCounter();
//counter1 변수가 하나 생김
//counter1; 함수가 담겨있는 객체
counter1.increment();
counter1.iecrement();
counter1.getValue();  //???
// increment를 2번하면 privateCounter가 1씩 두번 늘어나면서 출력값 = 2

privateCounter라는 변수는 함수바깥에서 privateCounter; 이런식으로 호출하거나 접근하는 것이 불가능하다.

따라서 increment나 decrement같은 함수로 간접적으로 바꿀 수가 있다.

counter1.getValue();가 2일때

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

counter1과 counter2는 서로 값에 영향을 주지않는다.

왜냐하면 둘은 각각 독립적으로 private counter를 가지고 있기 때문에 재사용 가능해진다. : 클로저 모듈 패턴

객체 지향 JavaScript

객체 지향 프로그래밍

class를 바탕으로 instance를 만드는 프로그래밍 패턴

  • 하나의 모델이 되는 청사진 = class
    function Car (color){ }

  • 그 청사진을 바탕으로 한 객체 = instance

let avante = new Car ('blue');
let mini = new Car ('cyan');
let beetles = new Car ('red');
  • prototype
    = 원형객체(original form)
    청사진을 만들 때 쓰는 객체가 prototype 객체
    ex) Car.prototype.메소드

  • constructor
    instance가 초기화될 때 실행하는 생성자 함수
    ex)
    this.brand = brand;
    this.name = name;
    this.color = color;
    이 부분이 constructor에 해당한다.

  • this
    함수 실행 시, 해당 scope마다 생성되는 고유한 실행 컨텍스트context
    new 키워드로 instance를 생성했을 때에는 해당 instance가 바로 this의 값

OOP 정의

ES5 vs ES6

함수이름의 첫글자를 대문자
ES5

function Car(brand, name, color){
  인스턴스가 만들어질 때 실행되는 코드
}

ES6

class Car(){
  constructor (brand, name, color) {
    
  }
}

ES6에서 사용하는 Rest parameter 및 ES5의 방법인 arguments 키워드를 이용할 수 있다

function Car(brand, name, color){
  
};
  • new 키워드를 통해 class의 instance를 만들어낼 수 있다
let avante = new Car ('hyundai', 'avante', 'blue');
let mini = new Car ('bmw', 'mini', 'cyan');
let beetles = new Car ('volkswagen', 'beetles', 'red');
avante;  // Car{}
// constructor: ƒ Car(brand, name, color)

각각의 instance는 Car라는 class의 고유한 속성과 메소드를 갖는다. (상속)

객체 지향 프로그래밍(OOP)의 기본 컨셉

class, 객체지향 javascript를 쓰는 이유는??

현실세계에 있는 것을 기반으로 프로그래밍 모델로 만들 때에 유용하기 때문이다.

객체지향에는 속성과 메소드가 있다.

속성과 메소드 : class에는 속성과 메소드를 정의만 하고 사용은 instance에서 한다.

  • 속성 : class의 특징, 속성
    brand, name, color, currentFuel, maxSpeed ...
  • 메소드 : 실행하는 함수, class를 통해 하고자 하는 action. 즉, Car가 할 수 있는 일.
    정의할 때 prototype에다가 .으로 체이닝한다.
    refuel(), setSpeed(), drive() ...
    method들의 이름자체가 현실세계와 비슷하다.

속성과 메소드

  • ES5
function Car( brand, name, color ){
  this.brand = brand; this.name = name;
  this.color = color;
}
  • ES6
class Car() {
  constructor( brand, name, color ) {
    this.brand = brand;
    this.name = name;
    this.color = color;
  }
}

this 키워드를 이용해서 this에 정의한 속성 brand,name,color을 집어넣는다.

class : 속성 정의

  • 위의 샘플에 새로운 instances를 만든다.
let avante = new Car ('hyundai', 'avante', 'blue');
let mini = new Car ('bmw', 'mini', 'red')
  • instance에서 사용
    avante.brand;하면 "hyundai"가 찍힌다.
    mini.color;하면 "red"

Car가 갖는 성격들을 동일하게 갖고있지만 성격, 즉 속성값은 각각 다르다.

class : 메서드 정의

  • ES5
Car.prototype.drive = function(){
  //drive라는 method는 avante,mini둘 다 갖는다
  console.log(this.model + '출발!!!');
}
  • ES6
refuel(){ };
drive(){ };
  • instance에서 사용
mini.drive();
//mini출발!!! , drive는 함수이므로 ()를 써서 실행시킨다
avante.drive();
//avante출발!!!

해당하는 속성들을 갖고 위처럼 사용할 수 있다.

배열과 객체지향프로그래밍의 유사성

let avante = new Car ('hyundai', 'avante', 'blue');
avante.color;  // 'blue'
avante.drive;  // avante출발!!


let arr = ('code', 'states', 'pre');
//배열을 정의하는 것은 Array instance를 만들어내는 것과 동일
//따라서 let arr = new Array('code', 'states', 'pre'); 도 동일
arr.length;  // 3
arr.push('course');   // 새 element추가

Parameter의 수가 유동적인 함수라면?

  • Rest parameter 키워드를 이용해 매개변수를 지정한다.
    매개변수가 배열 형태로 전달되서 -> 배열method 사용 가능
function getMaxNum(...nums){
  console.log(nums);
  // [3, 5, 8, 10] 배열이 찍힌다.
}

getMaxNum(3, 5, 8, 10);
  • arguments 키워드를 사용할 수도 있다.
function getMaxNum(){
  console.log(arguments);
  // {0:3, 1:5, 2:8, 3:10} 유사배열, 배열아님
}

getMaxNum(3, 5, 8, 10);

전달인자를 받은 arguments{ 0:3, 1:5, 2:8, 3:10 } 가 배열같아 보이지만 배열이 아니다.
유사배열, 배열 method 사용할 수 없다.

매개변수에 기본값을 할당하고 싶을 경우?

Default Parameter
Default Parameter의 값으로는 문자열/숫자/객체 등 어떤 타입이든 가능하다.

function getRoute(destination , departure='ICN'){
  return '출발지: ' + departure + ', ' +'도착지: ' + destination;
}

getRoute('PEK');   //'출발지: ICN, 도착지: PEK'
function getRoute(destination='ICN' , departure){
  return '출발지: ' + departure + ', ' +'도착지: ' + destination;
}

getRoute(undefined, 'PEK');   //'출발지: ICN, 도착지: PEK'
// 중간에 매개변수 기본값을 넣어주면 undefined를 넘겨줬을 때 기본값으로 처리
profile
👩🏼‍💻 SWE (FE)

0개의 댓글