본문 바로가기

프론트엔드/자바스크립트

this 와 클로저

this

this가 필요한 이유?

👉 특정 함수를 여러 객체에서 사용할 수 있기 위해서이다. this는 해당 함수를 실행한 객체를 가리키기 때문에 this를 활용하면 여러 객체가 호출하여 사용할 수 있게 해준다.

this를 제어하는 방법.

 >> this에 할당되는 객체를 원하는대로 지정하는법

  1. Call

함수.call() 하면서 첫번째 인자로는 객체를 넘김 그러면 해당 함수내의 this는 첫번째 인자 객체의 this 이다.

  1. apply

함수.apply() 하는데 call과 동일하게 첫번째 인자는 객체 그리고 두번째 인자는 배열로 넘겨줌.

위 두개는 다시말해 this에 할당되는 객체를 지정할 수 있다.!

  1. bind

객체를 전달하면 메소드의 this는 해당 객체로 고정됨.

 

Arrow function

arrow function내의 this는 호출한 객체의 this가 아닌 바로 상위 스코프의 this를 가리킨다.

항상 헷갈렸던 this.. 예제를 통해서 한번 공부해봤다.

function thisStudy(...item){
  console.log(this);
  item.forEach(function(el) {
    console.log(this);
    console.log(el + ' : ' + this.name);
  })
}

let obj1 = {
  name : "안녕"
}

let obj2 = {
  name : "환환"
}

thisStudy.apply(obj1, ['1', '2', '3'])

apply를 통해 thisStudy 내부의 this에 obj1을 할당하며 thisStudy를 실행해봤다. 이렇게 하고 실행 결과를 보면 다음과 같다.

forEach 외부 this = obj1 출력
forEach 내부 this = 윈도우 출력

forEach 함수에 this를 전달하지 않았기 때문에 forEach의 콜백 함수 실행 객체는 윈도우이니 this는 윈도우 객체를 가리키게 된다. 그래서 forEach 내부의 console.log(this)가 윈도우 객체를 출력하는 건 당연한것. forEach에 this를 전달하려면 두번째 인자로 this를 주면 된다. thisStudy 함수의 this는 obj1이기 때문에 두번째 인자로 넘어가는 this도 obj1을 의미하기 때문이다.

 이때 forEach 내부의 함수를 arrow function으로 바꿔주면 좀 더 쉽게 this를 전달할 수 있다. arrow function 의 this는 바로 상위 스코프의 this를 가리키기 때문에 this는 obj1을 가리키게 된다. 

 

클로저

js 에서는 내부 함수에서 외부 함수의 변수들에 접근이 가능하다. 재밌는 점은 외부 함수가 사라진다고 해도 내부 함수가 살아있으면 내부 함수에서 외부 함수로의 접근이 가능하다는 것이다. 즉 외부함수에 변수를 하나 선언해놓고 내부 함수만 살려둔다면 외부 함수에 선언해뒀던 변수는 내부 함수에서만 접근이 가능한 변수가 된다!! 이 개념을 클로저라 한다.

코드를 통해 이해해보자.

let codeContainer = function() {
  let secretCode = '7777';
  return {
    setCode : function(code) {
      secretCode = code;
    },
    getCode : function() {
      return secretCode;
    }
  }
}()

console.log(codeContainer.getCode());

(codeContainer... 막 지었지만 변수 네이밍이 좀 마음에 안든다 ㅋㅋㅋㅋㅋㅋ ㅠㅠ)
codeContainer가 받는 객체는 setCode와 getCode를 가지고있다.
secretCode 를 선언하는 함수는 즉시 실행함수로, 실행되자마자 사라지게 되어서 외부에서 접근이 불가능하다. console.log(codeContainer.secretCode) 해봤자 undefined가 찍힌다는 말씀..
그럼 secretCode에 접근은 어떻게 하지?? 그렇다 바로 codeContainer를 통해서만 가능하다(클로저 개념 등장)

codeContainer를 출력해보면 위처럼 getCode와 setCode 함수를 확인할 수 있다. 저 두 함수가 secretCode 값을 가져오거나 업데이트 할 수 있는 기능을 해주는, secretCode에 접근하는 유일한 수단이다. 즉 클로저란 폐쇄된 공간에 대한 접근 권한을 가진 함수를 의미한다.

 

생성자

자바에서는 new 라는 키워드가 익숙한데 자바스크립트에서는 뭔가 익숙하지 않은 느낌이다. 그나마 new Array() 정도?자바 스크립트에서 생성자는 어떤 역할을 할까? 함수 앞에 new 연산자를 붙이게 되면 두가지 변화가 일어난다. new를 붙인 함수의 실행 결과는 인스턴스가 반환 되며 이때 함수 내부의 this는 전역 객체가 아닌 이 인스턴스를 가리킨다.

function construct(name) {
  this.name = name;
  this.func = () => {
    console.log("함수");
  }
}

let Con1 = new construct("james");

인스턴스를 반환하므로 return 할 필요가 없다. 위와 같이 Con1 객체를 생성하게 되면 출력 결과는 아래와 같다.

Con1은 name 이라는 변수와 func 라는 함수를 가지고 있는 것을 확인할 수 있다.
하지만 이렇게 Con1 ~ 1000 까지 생성한다고 가정했을때 단점은 뭘까?? 바로 func 라는 함수는 다를게 없는데 1000개나 생성되어서 메모리가 매우 낭비되는 상황이 생길 수 있다는 점이다.

이런 경우에는 prototype 내부에 함수를 집어넣으면 된다.

function construct(name) {
    this.name = name;
}

construct.prototype.func = () => {
    console.log("함수");
}

let Con1 = new construct("james");
console.log(Con1.func)

이와 같은 사용자 지정 타입 생성 방법을 자바처럼 하기 위해서 클래스라는 것이 등장했다. js 초심자에게 클래스라면 위의 방식보단 익숙한... ㅎㅎ

 

관련글

https://www.inflearn.com/course/%EC%BD%94%EB%94%A9%EC%9D%B8%ED%84%B0%EB%B7%B0-js-%EC%96%91%EC%84%B1%ED%95%99%EA%B5%90