자바스크립트를 이해해보자 (2)

손성호·2025년 3월 5일

지난번 실행컨텍스트에 알아보던 중 구성요소에 this에 대해서 설명을 생략했는데요. 이번편에서 this에 대해서 자세하게 알아보려고 합니다.
this는 엄격모드와 비엄격모드에서 다르게 동작하는데요 해당 게시글은 비엄격모드를 기준으로 설명을 했습니다.
또한 브라우저의 this를 기준으로 설명했습니다. (Node.js에선 global객체가 됩니다)

This is this

정리

  1. 전역상태의 this는 전역객체(Window객체)를 가리킨다.
  2. 메소드의 this는 해당 메소드의 객체를 가리킨다.
  3. 함수 호출방식에 따라 this는 동적으로 결정된다.
    3.1 일반함수 호출
    3.2 콜백함수 호출
    3.3 생성자 함수 호출
    3.4 call,apply,bind메소드를 이용
    3.5 화살표 함수에서 this

결론

this는 함수 호출방식에 의해 동적으로 결정된다.

다양한 함수 호출 예제

전역상태에서 호출

개발자 탭을 열고 콘솔창에서

this; // [object Window]

를 쳐봅시다.

일반적으로 this는 Window 객체를 가리킵니다.

일반함수에서 호출

(function(){
    console.log(this); 
})(); //[object Window]

일반 함수내에서도 this는 Window객체를 가리킵니다.

객체의 메소드에서 호출

일반 함수가 아닌 객체의 메서드에서 this는 어떻게 될까요?
처음에 정리했다싶이, this가 호출한 메소드의 객체로 바인딩됩니다.

let obj = {
  name:'son',
  sayHi:function(){
    console.log('hi ', this.name);
  }
};
obj.sayHi(); // 'hi son'

다만 여기서 한가지 유의할 부분이 있습니다.

객체안에서 내부함수는 예시코드 봅시다.

let obj = {
  name:'son',
  sayHi:function(){
    console.log('hi', this.name); // 'hi son'
    function sayBI(){
     console.log('bye ', this); // bye [object Window]
    }
    sayBI();
  }
};
obj.sayHi();

이런식으로 객체안의 메소드에서 내부함수를 호출하면 해당 내부함수는 this를 Window객체로 바인딩 시켜버립니다.

근데 또 어떻게 보면 당연하죠? 객체안에서 일반함수 호출이니깐요.

그럼 당연하게도 메소드안에서 다른 객체의 메소드호출을 하게된다면?

let obj1 = {
  name:'hong',
  sayGoodbye:function(){
    console.log('Goodbye ', this.name);
  }
};

let obj2 = {
  name:'son',
  obj : obj1,
  sayHi:function(){
    this.obj.sayGoodbye(); // Goodbye hong
  }
};
obj2.sayHi();

호출한 다른 객체메소드내에서도 해당 객체로 this를 바인딩한걸 볼 수 있습니다.

콜백함수에서 호출

콜백함수에서 this는 어떻게 될까요? 예상해봅시다.

  1. 일반함수처럼 Window객체로 바인딩
  2. 함수도 객첸데? 함수객체로 바인딩

고르셨나요? 자 확인들어갑니다.

let obj = {
  name : 'None',
  setName:function(name){
    this.name = name;
  }
}

function setObjName(name, callback) {
  callback(name);
}

setObjName('son', obj.setName);

console.log(obj.name); //None
console.log(window.name); //son

정답은 1번입니다.

애초에 2번에서 페이크가 있었는데요.

콜백함수를 넘길 때, call by value형식으로 넘기게 됩니다. 즉, 함수를 복사하게 되는데 이 때 복사되는 함수들은 다 function처럼 복사가 됩니다.
그래서 이게 콜백으로오는 함수가, function인지, 메소드함수인지 구별이 안되는 것이죠.

생성자함수에서 호출

생성자 함수는 객체를 생성하는 함수입니다.

자바랑 유사합니다. 기존함수에 new연산자만 붙이면 생성자 함수처럼 동작합니다.

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

let obj = new Person('son');
console.log(obj); // Person {name:'son'};

//유의! new를 안붙이면 생성자 함수로 동작하지 않는다.
let obj = Person('son');
console.log(obj); // undefined

call,bind,apply메소드 함수에서 호출

기본적으로 3개의 메소드는 함수객체의 메소드입니다.

func.call(thisArg, argsArray);
func.apply(thisArg, [argsArray]);
func.bind(thisArg)(argsArray)

//thisArg: 함수 내부에 this에 바인딩할 객체
//argsArray: 함수에 전달할 argument배열
  • call 함수
function setName(name) {
  this.name = name;
  console.log(this.age); // 27
}

let person = {
  age:27
}

setName.call(person,'son');
console.log(person); // {age: 27, name : 'son'};

call을 사용하면 함수를 실행하고 함수의 첫번째 인자로 전달하는 값에 this를 바인딩 합니다. 여기선 person을 바인딩했습니다.

  • apply 함수

call함수와 인자 전달 방식만 다를 뿐 동일하게 동작합니다. apply에선 인자를 배열로 묶어서 전달합니다.

function setName(name=['','']) {
  this.firstName = name[0];
  this.lastName = name[1];
  console.log(this.age); // 27
}

let person = {
  age:27
}

setName.call(person,['son', 'ho']);
console.log(person); // {age: 27, firstName : 'son', lastName : 'ho'};
  • bind 함수

bind는 위에 call,apply함수와 다르게 함수를 반환하는 특징이 있습니다.

function setName(name) {
  this.name = name;
  console.log(this.age); // 27
}

let person = {
  age:27
}

setName.bind(person,'son')(); //함수를 반환하기에 다시한번 함수호출을 해주어야한다.
console.log(person); // {age:27, name:'son'}

화살표함수에서 호출

화살표함수는 this는 자신의 스코프에 존재하는 this를 가리킵니다.

let sayHi = () => console.log(this); 
sayHi(); // [object Window]

여기선 전역 스코프상에 위치하므로 외부 스코프의 this는 Window객체가 됩니다.

화살표 함수의 응용을 살펴봅시다.

class Counter {
  constructor() {
    this.count = 0;
  }
  
  start() {
    setInterval(() => {
      	console.log(this.count++;);
    }, 2000);
  }
}

const counter = new Counter();
counter.start();

여기선 상위 스코프가 Counter클래스 환경이됩니다.
그래서 Counter 클래스의 count변수에 접근하여 값을 올리는 걸 볼 수 있습니다.

This is the End

정리하자면 this는 동적으로 결정되니, 컨텍스트 생성시에 어떻게 호출할지를 잘 생각하여야 한다.

저번시간에 실행컨텍스트에 대해 알아보며 활성객체(변수객체)에 대해 알아보았었다. 활성객체에 this라는 구성 요소가 있는데, 이 this에 동적으로 바인딩 되는 것이다. 일반함수 호출이라면 this는 window객체를 가리키고, 메소드 호출시에 this라면 메소드를 호출한 객체를 가리킨다.

그래서 객체내에 메소드 호출시(활성객체 생성 시)에 없는 변수가 있다면, this를 통해 호출한 객체에 접근하여 변수를 가져와 사용할 수 있게 된 것이다!

참고문헌

Inpa Dev
[JavaScript] this 란

profile
사용자를 위한 웹화면을 개발하고 있습니다.

0개의 댓글