Pandaman Blog

[JS] Javascript의 this란? 본문

Front end/Javascript

[JS] Javascript의 this란?

oyg0420 2020. 1. 19. 14:20

Javascript의 This

this란? 무엇인가요?라고 누가 물어본다면 명확하게 말하기 어렵다고 생각했습니다.. 그래서 바로 MDN 공식 문서와 서적을 통해 알아보았습니다.

대부분의 경우 this의 값은 함수를 호출한 방법이 결정한다. 실행하는 중 할당으로 설정할 수 없고 함수를 호출할 때마다 다를 수 있습니다.

위 말은 함수를 호출하는 객체가 무엇인가에 따라 this가 달라진다는 의미입니다.

한번 예제를 통해 알아보겠습니다.

// 1) this는 전역객체 Window
console.log(this); // Window

const obj1 = {
  f1: function() {
    console.log(this);
  }
}

// 2) this는 f1()메소드를 호출한 객체 obj1
console.log(obj1.f1()); // {f1: f}

위 예제 중 1번 상황의 this는 전역 객체인 Window객체는 나타내는 것을 확인할 수 있습니다. 2번 상황은 f1() 메서드를 호출했습니다. f1()은 객체 obj1이 호출한 메서드이므로 this는 obj1이 되는 겁니다.

엄격 모드(Strict mode)

엄격 모드에서는 실행 문맥이 this의 값을 설정하지 않으면 undefined가 됩니다. 정말인지 확인해보겠습니다.

// 1) simple call
function f1() {
  return this;
};

// 2) strict mode
function f2() {
  "use strict"
  return this;
};

f1(); // Window
f2(); // undefined
window.f2(); // Window

엄격 모드에서는 f2()가 어느 객체에 속해있지 않은 함수를 호출하게 되어 this === undefined가 됩니다. 그러나 앞에 f2() 앞에 window 객체를 붙여주면 해결됩니다.

객체의 메서드의 this

어떤 객체의 메서드를 호출하면 this는 그 객체를 나타냅니다.

const obj1 = {
  age: 31,
  f1: function() {
    return this.age;
  }
}

/*
obj1의 f1()메서드 호출함
따라서, this는 obj1
*/
console.log(obj1.f1());

생성자 함수에서의 this

함수를 new 키워드와 함께 생성자로 사용하면 this는 새로 생긴 객체에 묶입니다.

확인해보겠습니다.

function Person(age, name) {
  this.age = age;
  this.name = name;
};

const p1 = new Person(31, 'oyg0420');
console.log(p1.age);
console.log(p1.name);

const p2 = new Person(20, 'pandaman');
console.log(p2.age);
console.log(p2.name);

생성자는 new로 객체를 만들어 생성하는 방식입니다. new키워드와 함께 생성자로 사용하면 this는 새로 생긴 객체에 묶입니다. 그래서 age, name 속성이 해당 객체를 생성할 때 매개변수로 넘겨준 값으로 설정이 되는 이유입니다. 또한 p1, p2 모두 같은 생성자 함수로 만들어진 객체지만 서로 다른 객체입니다.

한 가지 실험으로 new를 붙이지 않는다면 어떻게 될까요?

function Person(age, name) {
  this.age = age;
  this.name = name;
};

const p3 = Person(20, 'test');
console.log(p3.name); // VM623:2 Uncaught TypeError: Cannot read property 'name' of undefined
console.log(p3.age);

에러가 발생합니다. 그 이유는 new라는 키워드를 사용하여 객체를 생성할 땐 this가 새로 생긴 객체에 묶이지만 new 없이 변수에 할당하게 되면 일반적인 함수 동작과 동일하므로, this는 window 객체가 됩니다.

call과 apply, bind

일반적인 방법으로 this를 사용하는 방법에 대해서는 위에서 살펴보았습니다. 자바스크립트에서는 함수를 어디서, 어떻게 호출했는지에 대해 상관없이 this를 지정해주는 함수가 있습니다.

call

call메서드는 this를 특정값으로 지정할 수 있습니다. 무슨 말인지 예제를 통해 알아보겠습니다.

const person1 = {
  name: 'oyg0420'
};
const person2 = {
  name: 'pandaman'
};

function introduce() {
  return `hello! i am ${this.name}~ nice to meet you`;
}

console.log(introduce()); // hello! i am ~ nice to meet you
console.log(introduce.call(person1)); // hello! i am oyg0420~ nice to meet you
console.log(introduce.call(person2)); // hello! i am pandaman~ nice to meet you

call() 메서드를 통해 this로 사용할 객체를 넘겨주면 함수를 객체의 메서드인 것처럼 사용할 수 있습니다. call을 사용하지 않고 함수를 호출하면 this는 window객체이고, name은 존재하지 않아 name이 출력되지 않은 나머지만 반환하는 것을 확인할 수 있습니다.

call의 첫 번째 매개변수는 this로 사용할 값이고, 매개변수가 더 있다면 매개변수는 호출하는 함수로 전달됩니다.

function personUpdate(name, age) {
  this.name = age;
  this.age = age;
};

const person1 = {
  nation: 'korea'
};

personUpdate.call(person1, 'oyg0420', 31);
person1 // {nation: "korea", name: 31, age: 31}

apply

apply는 함수는 매개변수를 처리하는 방법만 제외하면 call메서드와 동일합니다. apply메서드는 매개변수를 배열(array)로 받습니다. 한번 확인해보겠습니다.

function personUpdate(name, age) {
  this.name = age;
  this.age = age;
};

const person1 = {
  nation: 'korea'
};

personUpdate.apply(person1, ['oyg0420', 31]);
person1 // {nation: "korea", name: 31, age: 31}

bind

마지막으로 this값을 변경할 수 있는 함수는 bind() 함수입니다. bind를 사용하면 this값을 영구적으로 바꿀 수 있습니다.
위의 personUpdate메서드를 여기저기서 호출해도 this가 항상 고정되도록 bind를 사용해서 나타내도록 해보겠습니다.

const person1 = {
  name: 'oyg0420'
};
const person2 = {
  name: 'pandaman'
};

function personUpdate(age) {
  this.age = age;
};

const updatePersonByBind = personUpdate.bind(person1);

updatePersonByBind(31); // {name: "oyg0420", age: 31}
updatePersonByBind.call(person2, 20); // {name: "oyg0420", age: 20}

person1을 바인딩하여 this가 고정됩니다. name은 항상 oyg0420으로 고정되고, 매개변수로 넘겨주는 age만 변경할 수 있게 됩니다.
bind는 함수의 동작을 영구적으로 바꾸므로 찾기 어려운 버그의 원인이 될 수 있다고 말합니다. 그래서 bind를 사용할 때 어디에 묶이고 정확히 파악하고 사용해야 한다고 경고하고 있습니다.

화살표 함수(arrow function)

화살표 함수에서 this는 자신을 감싼 정적 범위라고 합니다. 전역 코드에서는 전역 객체를 가리킵니다.

const test = () => {
  console.log(this);
} 

console.log(this); // window

다음 예제를 통해서 화살표 함수 내부 this가 자신을 감싼 객체를 나타내는지 간단한 예제를 통해 알아보겠습니다.

// 1) obj객체에 메서드 bar를 생성, bar의 this는 본인을 감싸고 있는 객체를 나타냄.
const obj = {
  bar: function() {
    const x = () => this;
    return x;
  }
};

// 2) f1에 this 즉 obj를 할당
const f1 = obj.bar();

// 3) f1()은 this 즉 obj 이므로, obj와 동일
console.log(fn1() === obj); // true

1번에서는 obj객체에 bar메서드를 나타내고 있습니다. 그리고 bar메서드는 this를 리턴하는데, 화살표 함수에서의 this는 자신을 감싼 객체를 나타내기 때문에, 결국 obj.bar()를 호출하면 obj를 리턴하게 됩니다. 따라서 3번과 같은 결과를 얻을 수 있습니다.

 

아래 예제는 bind 와 화살표 함수를 통해서 this 원하는 객체로 나타내는 예제입니다.

let cow = {
      sound: '음매',
      play: function () {
          console.log(this) // 1번
          setTimeout(function () {
            console.log(this) // 2번 
            console.log(this.sound); // 3번 
        }, 1000);
    }
}

console.log(cow.play()); 
// 1번 {sound: "음매", play: ƒ}
// 2번 Window
// 3번 undefined

cow 객체 play의 내부 함수 this는 window 객체입니다. window객체에는 sound라는 변수가 없기 때문에 this.sound는 undefined가 됩니다. 어떻게 하면 play의 내부 함수의 this가 cow객체를 나타나도록 할 수 있을까요??? 바로 bind() 함수를 사용하면 쉽게 해결할 수 있습니다. bind()는 객체와 함수를 묶어주는 기능을 합니다.

let cow = {
      sound: '음매',
      play: function () {
          setTimeout(function () {
            console.log(this) 
            console.log(this.sound);
        }.bind(this), 1000);
    }
}

console.log(cow.play()); 
// {sound: "음매", play: ƒ}
// "음매"

setTimeout을 bind(this)를 통해 this가 cow객체임을 나타내고, this.sound에 접근한 것을 확인할 수 있습니다. 하지만 매번 bind()를 사용하기 귀찮을 수 있습니다. 이 경우 arrow function을 사용하면 됩니다.

let cowWithArrow = {
      sound: '음매',
      play: function() {
          setTimeout(() => {
            console.log(this); // {sound: "음매", play: ƒ}
            console.log(this.sound); // '음매'
        }, 1000);
    }
}

cowWithArrow에서 this.sound가 원하는 데이터로 출력된 이유는 arrow function의 this는 상위 스코프의 this를 가리키기 때문입니다. 그러한 이유로 play함수를 arrow function으로 사용하지 않은 이유입니다. arrow function으로 사용했다면 this는 widow 객체를 가리킬 것입니다.

 

파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음

Comments