[자바스크립트] 함수 호출 시 결정되는 this

minidoo·2020년 11월 6일
1

자바스크립트 / NodeJS

목록 보기
17/28
post-thumbnail

자바스크립트에서 this함수를 호출할 때 결정된다.
함수를 어떤 방식으로 호출하느냐에 따라 그 값이 달라지며 즉, 실행 컨텍스트가 생성될 때 함께 결정된다.

'전역 공간' 에서의 this

전역 공간에서 this전역 객체를 가리킨다.
브라우저 환경에서의 전역객체는 window 이고 Node.js 환경에서는 global 이다.

  • 브라우저 환경
console.log(this);
console.log(window);

// Window {window: Window ... }
  • Node.js 환경
console.log(this);
console.log(global);

// Object [global] { global: [Circular] ... }

자바스크립트의 모든 변수는 특정 객체의 프로퍼티로서 동작한다.
특정 객체란 바로 실행 컨텍스트의 LexicalEnvironment이다.

var a = 1;

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

'함수 vs 메서드' 에서의 this

프로그래밍 언어에서 함수와 메서드는 미리 정의한 동작을 수행하는 코드 뭉치로, 이 둘을 구분하는 유일한 차이는 독립성에 있다. 함수 는 그 자체로 독립적인 기능을 수행하는 반면, 메서드 는 자신을 호출한 대상 객체에 관한 동작을 수행한다.

var func = function(x) {
    console.log(this, x);
};
func(1);	// (1) Window { window: Window ... } 1

var obj = {
    method: func
};
obj.method(2);	// (2) { method: f } 2

(1) 은 함수로서 (2) 는 메서드로서 호출한 것이다.
함수로서 호출메서드로서 호출은 함수 앞에 점(.)이 있는지 여부로 구분한다. 앞에 점이 없으면 함수로서, 점이 있으면 메서드로서 호출한 것이다.

obj.method(2);
obj['method'](2);

점 표기법과 대괄호 표기법 모두 어떤 함수를 호출할 때 그 함수 이름 앞에 객체가 명시돼 있는 경우 메서드로서 호출한 것이고, 그렇지 않은 경우는 함수로 호출한 것이다.

메서드 내부에서의 this

어떤 함수를 메서드로서 호출하는 경우 호출 주체는 함수명(프로퍼티명) 앞의 객체이다.

var obj = {
    methodA: function() { console.log(this); },
    inner: {
        methodB: function() { console.log(this); }
    }
};

obj.methodA();		// { methodA: f, inner: {...} }
obj.inner.methodB();	// { methodB: f }

함수 내부에서의 this

어떤 함수를 함수로서 호출할 경우에는 this가 지정되지 않는다.
실행 컨텍스트를 활성화할 당시에 this가 지정되지 않은 경우 this는 전역 객체를 바라본다. 따라서 함수에서의 this는 전역 객체를 가리킨다.

var obj = {
    outer: function() {
        console.log(this);		// obj
        var innerFunc = function() {
            console.log(this);		// Window
        }
        innerFunc();
    }
};
obj.outer();

메서드 내부 함수에서의 this 우회하는 방법

변수 활용

var obj = {
    outer: function() {
        console.log(this);		// obj
        
        let self = this;
        var innerFunc = function() {
            console.log(self);		// obj
        }
        innerFunc();
    }
};
obj.outer();

self, that, _this 등의 변수를 사용하여 상위 스코프의 this를 저장한다.

화살표 함수

var obj = {
    outer: function() {
        console.log(this);
        var innerFunc = () => {
            console.log(this);
        }
        innerFunc();
    }
};
obj.outer();

ES6에서는 this를 바인딩하지 않는 화살표 함수(Arrow Function)를 새로 도입했다. 화살표 함수는 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠지게 되어, 상위 스코프의 this를 그대로 활용할 수 있다.

콜백 함수 내부에서의 this

어떤 함수(A)의 제어권을 다른 함수(B)에게 넘겨주는 함수(A)를 콜백 함수라 한다.

이때 함수(A)는 함수(B)의 내부 로직에 따라 실행된다. 콜백 함수도 함수이기 때문에 기본적으로 전역 객체를 참조하지만, 제어권을 받은 함수에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조한다.

  • 전역 객체 참조
[1, 2, 3].forEach(function(x) {
    console.log(this, x);
});

// Window { ... } 1
// Window { ... } 2
// Window { ... } 3
  • this 지정
[1, 2, 3].forEach(function(x) {
    console.log(this, x);
}, 'callback');

// 'callback' 1
// 'callback' 2
// 'callback' 3

생성자 함수 내부에서의 this

생성자 함수는 어떤 공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수이다. 객체지향 언어에서는 생성자를 클래스, 클래스를 통해 만든 객체를 인스턴스라고 한다.

var Dog = function(name, age) {
    this.bark = '멍멍';
    this.name = name;
    this.age = age;
};

var myDog = new Dog('뚱이', 1);
console.log(myDog);

// Dog {bark: "멍멍", name: "뚱이", age: 1}

var myDog = new Dog('뚱이', 1) 에서 실행한 생성자 함수 내부에서의 this는 myDog 인스턴스를 가리킨다.

명시적으로 this 바인딩하기

call 메서드

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

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

var obj = {
    a: 1,
    method: function(x, y) {
        console.log(this.a, x, y);
    }
};

obj.method(2, 3);		// 1 2 3
obj.method.call({a: 4}, 5, 6);	// 4 5 6

apply 메서드

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

apply 메서드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다. 그 외에는 call 메서드와 완전히 동일하다.

var obj = {
    a: 1,
    method: function(x, y) {
        console.log(this.a, x, y);
    }
};

obj.method(2, 3);			// 1 2 3
obj.method.apply({a: 4}, [5, 6]);	// 4 5 6

bind 메서드

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

bind 메서드는 call과 비슷하지만 즉시 호출하지 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하는 메서드이다.

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

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

name 프로퍼티

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

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

console.log(func.name);		// func
console.log(bindFunc.name);	// bound func

bind 메서드를 적용해서 새로 만든 함수는 독특한 성질이 있다.
name 프로퍼티에 동사 bind의 수동태인 bound라는 접두어가 붙는다는 것이다. 어떤 함수의 name 프로퍼티가 'bound xxx'라면 함수명이 xxx인 원본 함수에 bind 메서드를 적용한 새로운 함수라는 의미이다.


참고 서적

코어 자바스크립트(Core JavaScript)
https://book.naver.com/bookdb/book_detail.nhn?bid=15433261

0개의 댓글