본문 바로가기

자바스크립트

this

this

자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수. this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다.

 

생성자 함수를 통해 객체를 생성하는 경우를 생각해 보자.

객체란 프로퍼티(상태)와 메서드(동작)를 하나의 단위로 묶은 자료구조로, 생성자 함수는 자신이 만들 인스턴스를 참조해 상태나 메서드를 추가해야 한다. 그런데 생성자 함수가 정의되는 순간에 생성자 함수는 자신이 만들 인스턴스를 가리킬 방법이 없다. 그래서 나온 게 자신이 속한 객체 혹은 자신이 만들 인스턴스를 가리키는 식별자, this이다.

 

this는 프로퍼티의 주체가 되는, this가 포함된 변수나 함수등을 감싸고 있는 대빵 객체!

 

this의 동적 바인딩

바인딩이란 식별자와 값을 연결하는 과정으로, this 바인딩은 this 식별자가 어떤 대상을 가리키는지 결정되는 것이다. 이 this 바인딩은 함수가 호출되는 방식에 따라 동적으로 결정된다. 즉, this는 상황에 따라 가리키는 대상이 다르다.

 

함수 호출 방식 네가지에 따른 this 바인딩을 살펴보자.

함수 호출

일반 함수로 호출했을 때는 기본적으로 this에 전역 객체가 바인딩된다. this의 정의를 보면 this는 자신이 속한 객체나 자신이 생성할 인스턴스를 가리키기 위한 용도로 사용되기 때문에 일반 함수를 호출하는 경우에서 this는 큰 의미가 없다.

function sum(a, b) {
    console.log('함수 내부 : 전역 객체 window 를 가리킨다',this); // window
    return a + b;
}
sum(1, 2);

 

메서드 호출

메서드 내부의 this 는 메서드를 호출한 객체를 가리킨다. icecream.getName() 예시에서는 icecream을 가리키고, jelly.getName() 예시에서는 jelly를 가리킨다. 이는 메소드(함수)가 객체에 포함된 것이 아니라 별도로 존재하고, 메서드 이름(getName)이 이 함수를 가리키고 있는 것을 말한다.

const icecream = {
    name: 'Jaws',
    getName() {
        // console.log('메서드 내부 : 메서드를 호출한 객체를 가리킨다',this);
        return this.name;
    }
}
const jelly = {
    name : 'JawsTaste',
};
jelly.getName = icecream.getName;
console.log('jelly 의 this :',jelly.getName()) // JawsTaste

 

생성자 함수 호출

생성자 함수 내부의 this 는 생성자 함수에 의해 생성될 인스턴스를 가리킨다.

function SquareArea (width) {
    this.width = width
    // 이것(내가 만들 인스턴스)의 width
}
SquareArea.prototype.getArea = function() {
    return this.width * this.width
    // 이것(내가 만들 인스턴스)의 width
}
  
  const square1 = new SquareArea(3);
  const square2 = new SquareArea(5);
  
// 말로 풀어보면 약간 편한데, 즉 this(인스턴스 : 폭 3을 가진 사각형)의 면적은 9
 console.log(square1.getArea()) // 9
// this(인스턴스 : 폭 5을 가진 사각형)의 면적은 25
 console.log(square2.getArea()) // 25

 

call, apply, bind 메서드에 의한 간접 호출

일단 call, apply, bind 이 세 가지 메서드는 모두 Function.prototype의 메서드다. 즉, 모든 함수는 이 세 가지 메서드를 상속받고 있기 때문에 사용 가능하다.

 

call과 apply 메서드를 사용하면 함수를 호출할 때 첫 번째로 전달된 인수에 함수의 this가 바인딩된다.

// 현재 this 가 무엇을 가리키는지 알려줄 함수
function getThis() {
    return this;
}

const arg = {hello: 'world'};

// 일반 함수로 호출하면 this 는 역시나 window 를 가리킨다
console.log('일반 함수로 호출 :',getThis()); // {window: ...}
// 하지만 call 이나 apply 를 사용하면 this 는 함수에 첫 번째 인수로 전달된 객체를 가리킨다
console.log('apply 로 바인딩 :',getThis.apply(arg)); // {hello: 'world'}
console.log('call 로 바인딩 :',getThis.call(arg)); // {hello: 'world'}

 

call이나 apply는 함수를 호출할 때 this를 명시적으로 가리키는 용도로 사용할 뿐이지, call과 apply가 있어도 마찬가지로 함수에 인수를 전달할 수 있다. 또한 인수 전달 방식만 다를 뿐, 하는 역할을 같다.

// apply 는 아래와 같이 배열에 넣어서 전달
getThis.apply(arg, [1, 2, 3]));
// call 은 아래와 같이 쉼표로 구분해서 전달
getThis.call(arg, 1, 2, 3));
let first = [1, 2];
let second = [3, 4, 5];
// 첫 번째 인자인 this(first) 에 두 번째로 전달된 second 를 푸시해라!
first.push.apply(first, second);
console.log('first :',first); // [1, 2, 3, 4, 5]
// max 는 최댓값만 계산해 주면 되고, 굳이 this 를 지정할 필요없으니 null 로 설정
let maxNum = Math.max.apply(null, [3, 5, 7, 9])
console.log('maxNum :',maxNum) // 9

 

call이나 apply와는 달리 bind는 함수는 호출하지 않고, this만 명시적으로 전달하고자 할 때 사용한다. 또 다른점은 call이나 apply의 본질은 함수를 호출하는 것이지만(함수를 바로 실행시켜서 결과를 리턴한다), bind는 함수를 즉시 호출하는 것이 아니라 “함수처럼 호출 가능한 특수 객체를 반환”한다. 이 객체를 호출하면 인수로 전달된 객체가 this로 고정된 함수가 반환된다.

const icecream = {
  name: 'Jaws'
}
function getName() {
  return this.name;
}
// getName 의 this 가 icecream 을 가리키도록 명시적으로 전달.
// icecreamName 은 호출 가능한 특수 객체이다.
let icecreamName = getName.bind(icecream);
// icecreamName 은 호출 가능하다고 했으니 호출을 해 보면
// this 가 icecream 으로 고정된 getName 이라는 함수를 호출하는 것과 같다.
console.log(icecreamName()) // Jaws

 

 

this 와 Dynamic scoping

자바스크립트 this

'자바스크립트' 카테고리의 다른 글

프로토타입 체이닝 (상속 흉내)  (0) 2023.03.24
클로저 (private 흉내)  (0) 2023.03.21
callback, promise, async/await  (0) 2023.03.14
이벤트 위임  (0) 2023.03.11
자바스크립트의 배열  (0) 2023.03.08