<JavaScript> this

김민석·2021년 3월 30일
0

Web 

목록 보기
2/4

대부분의 경우 this의 값은 함수를 호출한 방법에 의해 결정됩니다. 실행중에는 할당으로 설정할 수 없고 함수를 호출할 때 마다 다를 수 있습니다. ES5는 함수를 어떻게 호출했는지 상관하지 않고 this 값을 설정할 수 있는 bind 메서드를 도입했고, ES2015는 스스로의 this 바인딩을 제공하지 않는 화살표 함수를 추가했습니다.
MDN Web Docs

위 글을 읽고 의문점이 하나도 없었다면 아래 글은 읽지 않으셔도 좋습니다. 의문점이 하나라도 있었다면 읽어보는걸 추천드립니다.

문맥(context) 별 this 사용 예시

this를 알아보기 위해 전역 문맥과 함수 문맥을 알아볼 필요가 있습니다.
왜냐하면 자바스크립트에서 this는 runtime에 문맥에 따라 결정되기 때문입니다.

이와 달리 다른 언어에서의 this는 함수가 정의된 객체를 가리키기 때문에 헷갈릴 수 있습닌다.

전역 문맥에서의 this

전역 실행 문맥에서 this는 전역 객체를 참조하며 웹 브라우저에서의 전역 객체는 window 입니다. 그래서 아래와 같이 this와 window를 비교하면 true를 확인할 수 있습니다.

console.log(this === window); // true

그래서 전역 문맥에서 변수를 선언하면 결국 전역 객체인 window의 property가 된다고 할 수 있고 아래와 같은 결과가 타당하다고 생각할 수 있게 됩니다.

a = 37;
console.log(window.a); // 37

this.b = "MDN";
console.log(window.b); // "MDN"
console.log(b); // "MDN"

함수 문맥에서의 this

함수 문맥에서의 this는 시작에 있던 글처럼 함수를 호출한 방법에 의해 좌우됩니다.

  • 직접 호출
function f2(){
  return this;
}

f2() === window; // true

위와 같은 결과가 된건 함수를 호출할 때 함수 f2를 객체의 메서드나(객체의 프로퍼티가 함수면 메서드) 속성으로써 호출한 것이 아니라 this의 값이 따로 설정되지 않았고 이런 상황에서 브라우저에서는 기본값인 window를 참조하기 때문입니다.

  • 객체의 메서드로 호출
const test = {
  prop: 42,
  func: function() {
    return this.prop;
  },
};

console.log(test.func());
// expected output: 42

함수가 호출될 때 test란 객체의 메서드로 호출되었으므로 func내에서의 this는 test를 가리키게 되고 test.prop인 42를 콘솔창에서 볼 수 있게 됐습니다.

bind

맨 위의 글을 다시 읽어보면 어떻게 호출됐는지 상관하지 않고 this 값을 설정할 수 있는 bind에 대해 언급하고 있습니다. bind는 언제 사용될까요?
먼저 일반적으로 함수의 호출방법에 따라 this가 가리키는 것이 달라졌었습니다. 그런데 내가 원하는 객체를 가리키고 싶다면 어떻게 할까요? 바로 bind를 사용하면 됩니다.

  • bind를 사용하지 않고 직접 호출
a = "저는 전역 변수입니다";

function f() {
  return this.a;
}

console.log(f());//저는 전역 변수입니다.

위의 결과를 이해하셨나요? 이해되지 않으셨다면 전역 문맥에서의 this를 다시 한번 확인해주세요.

  • bind를 사용하고 호출한 경우
a = "저는 전역 변수입니다";

function f() 
  return this.a;
}

var o = {
  a: "저는 객체 o의 프로퍼티 입니다.",
};

var g = f.bind(o);

console.log(g()); //저는 객체 o의 프로퍼티 입니다.

bind의 인자로 원하는 객체를 전달해주면 바인딩 된 새로운 함수를 return 해줍니다.

그러면 bind를 여러번 할 수 있을까요?

a = "저는 전역 변수입니다";

function f() {
  return this.a;
}

var o = {
  a: "저는 객체 o의 프로퍼티 입니다.",
};

var o1 = {
  a: "저는 객체 o1의 프로퍼티 입니다.",
};

var g = f.bind(o);
var h = g.bind(o1);
var i = f.bind(o1);

console.log(g()); //저는 객체 o의 프로퍼티 입니다.
console.log(h()); //저는 객체 o의 프로퍼티 입니다.
console.log(i()); //저는 객체 o1의 프로퍼티 입니다.

위의 결과를 정리해보면 이미 바인딩 된 함수는 다른 객체로 다시 바인딩되지 않고 바인딩 되지 않은 함수에 대해서는 여러번 바인딩 가능한 걸 알 수 있습니다.

화살표 함수에서의 this

우리는 이제 this를 어느정도 알고 바인딩이 무엇인지도 알고 있습니다. 그러면 스스로의 this 바인딩을 제공하지 않는 함수라는건 어떤 의미일까요?

화살표 함수에서 this를 참조하면 화살표 함수가 아닌 평범한 외부 함수에서 this 값을 가져옵니다. 이러한 특성은 콜백 함수로 전달할 때 매우 유용합니다. 아래 코드를 보고 결과값을 예상해주세요.

const classA = {
  className: "classA",
  students: [
    {
      name: "minseok",
      age: 28,
    },
    {
      name: "hee",
      age: 28,
    },
  ],

  printName() {
    this.students.forEach(function (student) {
      console.log(this.className + " " + student.name);
    });
  },
};

classA.printName();

오호 아래 결과를 예상하셨나요? 그럼 너무 잘 이해하셨네요. 혹시 저 코드의 의도는 아마 각 이름마다 클래스명을 붙여주고 싶었던 것 같아요. 바로 이럴 때 화살표 함수가 유용합니다.

undefined minseok
undefined hee
const classA = {
  className: "classA",
  students: [...],
  printName() {
    this.students.forEach((student) => {
      console.log(this.className +" "+ student.name);
    });
  },
};

classA.printName();

위처럼 화살표 함수를 사용하게 되면 가장 가까이에 있는 일반 함수의 this와 동일하게 사용하게 되고 그것은 바로 printName이죠 printName의 this는 classA를 가리키고 있으므로 아래와 같은 결과를 콘솔창에서 볼 수 있게 됐습니다.

classA minseok
classA hee

call, apply

call, apply 메서드 두개 모두 bind와 매우 유사하지만 전달하는 인자나 실행 순간이 조금 다릅니다. 예시를 통해 확인해봅니다.

call

a = "저는 전역 변수입니다";

function f(param) {
  console.log(this.a + " "+ param);
}

var o = {
  a: "저는 객체 o의 프로퍼티 입니다.",
};

f.call(o, "저는 params 입니다");
//저는 객체 o의 프로퍼티 입니다. 저는 params 입니다.

보시는 것과 같이 call은 바인딩과 동시에 parameter도 전달할 수 있고 실행까지 하는 메서드 입니다.

apply

apply는 call과 비슷하지만 parameter를 배열의 형태로 전달하여 실행시킵니다.

a = "저는 전역 변수입니다";

function f(param) {
  console.log(this.a + " " + param);
}

var o = {
  a: "저는 객체 o의 프로퍼티 입니다.",
};

f.apply(o, ["저는 params 입니다"]); //저는 객체 o의 프로퍼티 입니다. 저는 params 입니다.
profile
누구나 실수 할 수 있다고 생각합니다. 다만 저는 같은 실수를 반복하는 사람이 되고 싶지 않습니다. 같은 실수를 반복하지 않기 위해 기록하여 기억합니다.🙃

0개의 댓글