View

this

자바스크립트에서의 this는 실행 컨텍스트가 생성될 때 thisBinding에 저장됩니다. 즉, 함수를 호출할 때 결정된다고 할 수 있습니다. 따라서 상황에 따라 this는 바뀌게 됩니다. 다음 다섯 가지 상황에 따라 this가 어떻게 결정되는지 알아보도록 하겠습니다.

 

1. 전역 공간에서의 this

전역 공간에서 this는 전역 객체를 가리킵니다. 브라우저 환경에서는 window, Node.js 환경에서는 global입니다.

 

전역 변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로도 할당합니다. 다음과 같이 말입니다.

var a = 1;
console.log(a); // 1
console.log(window.a); // 1
console.log(this.a); // 1

자바스크립트 엔진은 var 연산자를 이용해 변수를 선언하면 실행 컨텍스트의 L.E의 프로퍼티로 인식합니다. 전역 컨텍스트의 경우 L.E는 전역객체 그대로를 참조합니다.

console.log(a);의 경우에도 스코프 체인에서 a를 검색하다가 가장 마지막에 도달하는 전역 스코프의 L.E, 즉 전역객체에서 해당 프로퍼티 a를 발견해서 그 값을 반환하기 때문에 1이 출력됩니다.

 

그렇다면 const와 let으로 선언하면 어떨까요? window.a 와 this.a에 undefined가 출력됩니다.

이는 const, let을 전역 변수로 선언하면 전역 객체로 저장되지 않고, 보이지 않는 개념적인 블록 내에 존재하게 되기 때문입니다. (전역 객체와 전역 컨텍스트는 다른 개념)

const와 let은 블록 레벨 스코프기 때문에 전역 변수를 사용하지 못하기도(?) 합니다. 따라서 아마도 전역 객체에 저장되지 않게 되어 있는 것 같습니다.

 

 

 

2. 메서드로서 호출할 때 그 메서드 내부에서의 this

호출한 주체가 this에 담깁니다. 함수로 호출할 때와 메서드로 호출할 때는 2장에서 다루었으므로 따로 설명하지 않겠습니다.

obj.method()와 같이 실행한 경우 method 내부에서의 this는 obj 정보가 담기는 것입니다.

 

3. 함수로서 호출할 때 함수 내부에서의 this

함수로서 호출하는 것은 호출 주체를 명시하지 않고 개발자가 코드에 직접 관여해서 실행한 것이기 때문에 호출 주체의 정보를 알 수 없습니다. 그래서 전역 객체가 담깁니다.

 

ES6에서 함수 내부에서 this가 전역객체를 바라보는 문제를 보완하고자, this를 바인딩하지 않는 화살표 함수를 새로 도입했습니다. 화살표함수는 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠지게 되어, 상위 스코프의 this를 그대로 활용할 수 있습니다.

 

4. 콜백 함수 호출 시 그 함수 내부에서의 this

다음 장 콜백 함수에서 제대로 다루겠지만, 간단하게 말하자면 기본적으로 전역 객체로 참조합니다. 하지만 별도의 인자로 this가 될 대상을 지정하는 경우, 그 대상이 this가 되게 됩니다.

예를 들어 forEach, map, filter와 같은 메서드들이 첫 번째 인자로 콜백 함수를 필수로 받게 되고, 두 번째 인자로 this가 될 대상을 선택적으로 받게 됩니다. 

var report = {
    sum: 0,
    count: 0,
    add: function () {
        var args = Array.prototype.slice.call(arguments);
        args.forEach(function (entry) {
            this.sum += entry;
            ++this, count;
        }, this);
    },
    
   ...
   
   
 }
 report.add(60, 80, 95);

 

forEact에서 콜백 함수 다음으로 this를 받게 되죠. 원래 forEach 콜백 함수 내부에서의 this는 전역 객체이지만, report.add(60, 80, 95); 형태로 메서드로서 호출을 했기 때문에 add 안에서 this는 report의 정보가 담기고, forEach의 인자로도 그 this를 넘겨 주었기 때문에 콜백 함수에서의 this는 report가 됩니다.

 

또한 addEventListener 메서드와 같은 경우들은 콜백 함수를 호출할 때 자신의 this를 상속하도록 정의되어 있습니다.

 

5. 생성자 함수 내부에서의 this

생성자 함수는 어떤 공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수입니다. 생성자 안에서의 this는 생성될 객체 자체를 가리킵니다.

 

var Cat = function (name, age) {
    this.bark = '야옹';
    this.name = name;
    this.age = age;
}
var choco = new Cat('초코', 7);
console.log(choco); // Cat { bark: '야옹', name: '초코', age: 7 }

 

 

명시적으로 this를 바인딩하는 방법

규칙을 깨고 명시적으로 this를 바인딩할 수도 있습니다.

 

Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])

call 메서드는 호출 주체인 함수를 즉시 실행하도록 합니다. 첫 번째 인자를 this로 바인딩하고, 이후 인자들을 호출할 함수의 매개변수로 사용합니다.

 

Function.prototype.appl(thisArg[, argsArray])

apply 메서드는 call 메서드와 기능적으로 완전히 동일합니다. 반면 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다는 점에서 차이가 있습니다.

 

 

>> 유사 배열 객체에 배열 메서드 적용하기

객체에는 배열 메서드를 직접 적용할 수 없습니다. 하지만 키가 0 또는 양의 정수인 프로퍼티(ex: 0, 1, 2...)가 존재하고, length 프로퍼티의 값이 0 또는 양의 정수(ex: length: 3)인 객체, 즉 배열의 구조와 유사한 객체의 경우 call 또는 apply 메서드를 이용해 배열 메서드를 차용할 수 있습니다.

 

var obj = {
    0: 'a',
    1: 'b',
    2: 'c',
    length: 3
} // 유사배열객체

Array.prototype.push.call(obj, 'd');
console.log(obj); // { 0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4 }

+ ES6에서는 유사 배열 객체를 배열로 전환하는 Array.from 메서드가 도입되었습니다.

var obj = {
    0: 'a',
    1: 'b',
    2: 'c',
    length: 3
} // 유사배열객체

var arr = Array.from(obj);
console.log(arr); // ['a', 'b', 'c']

 

Function.prototype.bind[thisArg[, arg1[, arg2[, ...]]])

bind 메서드는 call과 비슷하지만 즉시 호출하지는 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 합니다. 반환된 함수를 호출할 때 인수를 넘기면, 그 인수들은 기존 bind 메서드를 호출할 때 전달했던 인수들의 뒤에 이어서 등록됩니다.

즉 bind 메서드는 함수에 this를 미리 적용하는 것과 부분 적용 함수를 구현하는 두 가지 목적을 모두 지닙니다.

var func = function (a, b, c, d) {
    console.log(this, a, b, c, d);
};
func(1, 2, 3, 4);

var bindFunc1 = func.bind({ x: 1});
bindFunc1(5, 6, 7, 8); // { x: 1 } 5 6 7 8

 

상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달하는 방법으로 call, apply, bind를 사용할 수 있습니다.

var obj = {
    outer: function () {
        console.log(this); // obj 정보 출력
        var innerFunc = function () {
            console.log(this); // 원래 같으면 전역 객체인데 call로 호출돼서 obj 출력
        };
        innerFunc.call(this);
    }
};

obj.outer();

 

 

Share Link
reply
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30