[TIL] this, 콜백함수, 클로져

이현동·2023년 1월 25일
1

TIL

목록 보기
14/59

this

javascript에서 this는 가장 혼란스러운 개념중 하나입니다. 일반적으로 객체지향언어에서 예약어 'this'는 함수가 속해있는 객체 자기 자신과 연관이 깊습니다. 하지만 javascript에서는 함수가 일급객체입니다. 따라서 javascript에서 함수는 다양하게 호출이 될 수 있어서 일반적인 객체지향언어와는 다르게 단순하지 않습니다.

상황에 따라 달라지는 this

javascript에서는 완벽히 같은 함수라도 어떤 실행 환경, 즉 어떤 객체에 호출되느냐에 따라 this의 의미가 달라집니다. 의미가 달라지는 것이 상황에 따라 this가 가리키는 객체가 동적으로 결정된다는 말인데, ‘this binding’이라고 하고, 'this binding component'에 정보가 담기게 됩니다.

전역 공간에서의 this

전역 공간에서의 'this'는 전역 객체를 가리킵니다.

console.log(this); // 브라우저에서는 window 객체 출력
console.log(window); // window 객체 출력

console.log(this); // node.js에서는 global 출력
console.log(global); // global 객체 출력

javascript의 모든 변수는 실은 특정 객체의 프로퍼티로서 동작을 합니다.
다시 말해서, 전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당합니다.

var a = 1;
window.b = 2;
console.log(a, window.a, this.a); // 1 1 1 이 출력됨
console.log(b, window.b, this.b); // 2 2 2 이 출력됨

메서드 내부에서의 this

this에는 호출한 주체에 대한 정보가 담깁니다. 어떤 함수를 메서드로서 호출을 하게 되면 호출의 주체는 함수명(프로퍼티명) 앞의 객체 입니다.

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

object.methodA(); // object 출력
object.inner.methodB(); // object.inner 출력

함수로서 호출할 때 그 함수 내부에서의 this와 메서드의 내부함수에서의 this

어떤 함수를 함수로서 호출할 때는 개발자가 코드에 직접 관여하지 않았을 때 this는 전역 객체를 바라봅니다.
하지만 메서드 내부에 함수가 정의되어 있다고 해서 무조건 메서드로서 호출되는 것이 아니다. javascript에서는 메서드로서 호출했는지(object1.function) 그냥 함수로서 호출했는지(function)에 따라 가리키는 this의 값이 달라지게 됩니다.

var object1 = {
    outer: function () {
        console.log(this); 
        var innerFunc = function () {
            console.log(this);
        };
        innerFunc(); // 2. window를 출력

        var object2 = {
            innerMethod: innerFunc,
        };
        object2.innerMethod(); // 3. object2를 출력
    },
};
object1.outer(); // 1. object1을 출력

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

콜백 함수 내에서 this를 호출하게 되면 전역 객체를 가리키게 됩니다.

setTimeout(function () { // (1)
    console.log(this);
}, 300);

[1, 2, 3, 4, 5].forEach(function (x) { // (2)
    console.log(this, x);
});

document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener('click', function (e) { // (3)
    console.log(this, e);
});

(1) 전역객체가 출력됩니다.
(2) 전역 객체와 배열이 각 5회 출력됩니다.
(3) addEventListener는 지정한 HTML 엘리먼트에 'click' 이벤트가 발생할 때마다 그 이벤트 정보를 콜백 함수의 첫 번째 인자로 삼아 함수를 실행. 버튼을 클릭하면 지정한 엘리먼트와 클릭 이벤트에 관한 정보가 담긴 객체가 출력됩니다.

생성자 함수 내부에서의 this

javascript에서는 new명령어와 함께 함수를 호출하게 되면 해당 함수가 생성자로서 동작하게 되는데, 이때 내부에서의 this는 곧 새로 만들 구체적인 인스턴스 자신이 됩니다.

var Cat = function (name, age) {
    this.bark = '야옹';
    this.name = name;
    this.age = age;
};

var choco = new Cat('초코', 7);
var nabi = new Cat('나비', 5);
console.log(choco, nabi);

/*
출력 결과
Cat { bark: '야옹', name: '초코', age: 7 } 
Cat { bark: '야옹', name: '나비', age: 5 }
*/

명시적인 this 바인딩

개발자가 명시적으로 this를 바인딩 할 수 있는 방법이 있습니다. call, apply, bind 메서드 등을 사용하여 명시적으로 this를 지정하면서 함수 또는 메서드를 호출 할 수 있습니다.

화살표 함수

ES6부터 나온 화살표 함수는 특별한 this 바인딩을 가집니다. 여러 부분에 있어 일반 함수와는 다른 점을 가지는데, 이번에 this와 관련된 점을 설명하자면, 화살표 함수에는 this가 없습니다. 실행 컨텍스트 생성 시 this를 바인딩하는 과정이 제외됐습니다.

var object = {
    outer: function () {
        console.log(this);
        var innerFunc = () => {
            console.log(this);
        };
        innerFunc(); // { outer: [Function: outer] }
    },
};
object.outer(); // { outer: [Function: outer] }

접근하고자 하면 스코프체인상 가장 가까운 this에 접근하게 됩니다.

콜백함수

프로그래밍에서 콜백(callback) 또는 콜백 함수(callback function)는 다른 코드의 인수로서 넘겨주는 실행 가능한 코드를 말한다. 콜백을 넘겨받는 코드는 이 콜백을 필요에 따라 즉시 실행할 수도 있고, 아니면 나중에 실행할 수도 있습니다.

콜백함수가 필요한 이유는 다음에 배울 동기/비동기와 관련이 있습니다.
콜백함수는 때로는 가독성이나 코드 재사용 면에서도 사용 되고, 비동기 방식으로 작성된 함수를 동기 처리하기 위해 필요합니다.

클로져

코어 자바스크립트의 말을 빌려 클로져란 어떤 함수 A에서 선언한 변수 a를 참조하는 내부 함수 B를 외부로 전단할 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상을 말합니다.

function sum(x) {
	return function (y) {
		return x + y;
	};
};

const add = sum(2); // sum()이 실행 컨텍스트에서 실행되고 사라짐
console.log(add(7)); 
// add()가 outerRecord에 있는 사라져있는 sum의 정보를 참조함
  1. 글로벌 Record에 add와 sum함수를 입력 후 outer에는 Null이 입력되고, call stack에 push합니다.

  2. sum을 호출, sum에 렉시컬을 만들고, record에 x와 function anonymous가 입력됩니다.

  3. sum의 outer에는 글로벌 렉시컬 환경이 저장, 콜스택에 쌓입니다.

  4. 코드 실행, sum에 인자로 넘긴 2가 x에 할당. sum 렉시컬 Record에 x=2 기록됩니다.

  5. 그리고 실행할 함수가 없으니, sum의 렉시컬은 pop으로 사라집니다.

  6. 다시 글로벌에 렉시컬로 돌아와,
    add 변수에 sum 함수의 리턴값인 익명함수를 글로벌 렉시컬 레코드에 할당됩니다.

  7. 마지막 콘솔 로그를 실행하기 위해 add 변수를 실행하게 되고,

  8. 여기서 add의 렉시컬 환경이 만들어집니다. 그리고 add 함수의 매개변수 y를 호이스팅하여 add의 레코드에 기록됩니다.

  9. outer에는 sum의 렉시컬 환경을 가르키게 하고 콜스택에 쌓입니다.
    → 실행 컨텍스트 내에서는 sum()이 사라졌지만, outer에서 sum의 렉시컬 환경을 참조하고 있습니다. → 클로져

  10. 매개변수로 전달된 7이 y에 할당되고, 렉시컬에 y =7이라고 기록됩니다.

  11. 다음으로 x를 실행하게 되는데 이때 add 렉시컬 환경 Record에는 존재하지 않는 x를 만납니다.

  12. 그럼 outer가 가르키고 있는 sum의 렉시컬로 향하게 됩니다.

  13. 그곳에서 x=2라는 것을 확인합니다.

  14. x=2, y=7이어서, 9가 리턴되고 add 렉시컬 환경은 사라집니다.

  15. 마지막으로 콘솔로그가 실행되어 9가 출력되고 글로벌도 사라집니다.


참고자료

코어자바스크립트, 정재만 지음
자바스크립트-클로저, 이한결님
모던 JavaScript 튜토리얼 - 화살표 함수
콜백 - 위키백과
[10분 테코톡] 꼬재의 클로저

profile
https://hdlee.dev

0개의 댓글