개발일지
코어 자바스크립트 2주차 발표 본문
3강 this
this란?
this란 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수입니다. this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있습니다.
var daySix = function () {
this.name = "김원필";
this.age = function () {
console.log("20살");
};
};
그렇다면 this가 가리키는 값은 어떻게 결정될까요?
javascript에서 this의 값은 결정되어 있지 않고, 문맥에 따라 바인딩되는 객체가 변합니다.
this가 특정 객체에 연결되는 것을 바인딩이라고하는데, 함수를 호출하는 방식에 따라 바인딩 객체가 달라집니다.
전역 공간에서의 this
전역 공간에서 this는 전역 객체를 가리킵니다. 개념상 전역 컨텍스트를 생성하는 주체가 바로 전역 객체이기 때문입니다. 전역 객체는 자바스크립트 런타임 환경에 따라 다른 이름과 정보를 가지고 있습니다. 브라우저 환경에서 전역객체는 window이고 Node.js 환경에서는 global입니다.
전역공간에서의 this(브라우저 환경)
console.log(this);
console.log(window);
console.log(this === window); // true
전역 공간에서의 this(Node.js 환경)
console.log(this);
console.log(golbal);
console.log(this === global); // true
어떤 함수를 실행하는 방법의 여러가지가 있는데, 가장 일반적인 방법 두 가지는 함수로서 호출하는 경우와 메서드로서 호출하는 경우입니다.
// 함수로서 호출
var func = function(x) {
console.log(this, x);
};
func(1); // Window {...} 1
// 메서드로서 호출
var obj = {
method: func
};
obj.method(2); // {method: f} 2
1번째 줄에서 func라는 변수에 익명함수를 할당했습니다. 4번째 줄에서 func를 호출했더니 this로 전역객체 Window가 출력됩니다. 6번째 줄에서 obj라는 변수에 객체를 할당하는데, 그 객체의 method를 호출했더니, 이번에는 this가 obj라고 합니다. obj의 method 프로퍼티에 할당한 값과 func 변수에 할당한 값은 모두 1번째 줄에서 선언한 함수를 참조합니다.
함수로서 호출과 메서드로서 호출 구분방법 함수 앞의(.)의 여부로 구분이 가능합니다.
함수 내부에서의 this
어떤 함수를 함수로서 호출할 경우에는 this가 지정되지 않습니다. this에는 호출한 주체에 대한 정보가 담깁니다. 그런데 함수로서 호출하는 것은 호출 주체를 명시하지 않고 개발자가 코드에 직접 관여해서 실행한 것이지 때문에 호출 주체의 정보를 알 수 없는 것입니다.
메서드의 내부에서의 this
var obj1 = {
outer: function () {
console.log(this);
var innerFunc = function () {
console.log(this);
}
innerFunc();
var obj2 = {
inerMethod: innerFunc
};
obj2.innerMethod();
}
};
obj1.outer();
7번째 줄에서는 outer 메서드 내부에 있는 함수를 호출 함수로서 호출했습니다. 반면 12번째 줄에서는 같은 함수를 메서드로서 호출했습니다. 같은 함수임에도 7번째 줄에 의해 바인딩되는 this와 12번째 줄에 의해 바인딩되는 this의 대상이 서로 달라졌습니다.
this를 바인딩하지 않는 함수
ES6에서는 함수 내부에서 this가 전역객체를 바라보는 문제를 보완하고자, this를 바인딩하지 않는 화살표 함수를 새로 도입하였습니다.
var obj = {
outer: function () {
console.log(this); // (1) { outer: f }
var innerFunc = () => {
console.log(this); // (2) { outer: f }
};
innerFunc();
}
};
obj.outer();
생성자 함수 내부에서의 this
javascript에서는 공통된 성질을 지니는 객체들을 생성할 때 생성자 함수를 사용합니다.
function daySix() {
this.name = "김원필";
}
var obj = new daySix();
console.log(obj); // {name: "김원필" }
생성자 함수 daySix을 호출하면 daySix에 대한 prototype 프로퍼티가 있는 객체가 생성됩니다.
그리고 생성된 daySix 객체를 this 바인딩을 하기 때문에 this에서는 daySix 객체의 프로퍼티(name)내용을 알고 있습니다.
명시적으로 this를 바인딩하는 방법
call 메서드
call 메서드는 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령입니다. 함수를 실행하고 첫 번째 인자로 전달한 값에 this를 바인딩합니다. apply과의 차이점은 인자를 배열로 넘기지 않고 요소를 하나씩 넘깁니다.
var func = function (a, b, c) {
console.log(this, a, b, c);
};
func(1, 2, 3); // Window{...} 1 2 3
func.call({x: 1}, 4, 5, 6); // {x: 1} 4 5 6
메서드에 대해서도 마찬가지로 객체의 메서드를 그냥 호출하면 this는 객체를 참조하지만 call 메서드를 이용하면 임의의 객체를 this로 지정할 수 있습니다.
apply 메서드
apply 메서드와 call 메서드는 기능적으로 완전히 동일합니다. apply 메서드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다는 점만 차이가 있습니다.
var func = function (a, b, c) {
console.log(this, a, b, c);
};
func.apply({x: 1}, [4, 5, 6]); // {x: 1} 4 5 6
var obj = {
a: 1,
method: function (x, y) {
console.log(this.a, x, y);
}
};
obj.method.apply({ a: 4 }, [5, 6]); // 4 5 6
bind 메서드
함수를 실행하고 첫 번째 인자로 전달하는 값에 this를 바인딩합니다.
apply과 call의 차이점은 함수를 실행하지 않고 새로운 함수를 반환합니다. 그리고 반환된 함수를 실행해야 원본 함수가 실행됩니다.
4강 콜백 함수
콜백 함수란 파라미터로 함수를 전달받아, 함수의 내부에서 실행하는 함수를 말합니다.
예를 들어보겠습니다.
function checkMusic(count, link, good) {
count < 3 ? link() : good();
}
function linkMusic() {
console.log("노래를 3번 들어주세요");
console.log("한 페이지가 될 수 있게");
}
function goodMusic() {
console.log("오늘의 할당량을 전부 채우셨습니다")
}
checkMusic(2, linkMusic, goodMusic);
checkMusic, linkMusic, goodMusic 총 3가지 함수를 선언하고 checkMusic 함수를 호출할 때 매개변수로 count에 숫자값, link와 good에 각자 linkMusic과 goodMusic함수를 전달하였습니다.
여기서 linkMusic와 goodMusic함수가 콜백함수 인 것입니다.
checkMusic함수가 먼저 호출되고, 매개변수로 들어온 count의 값에 따라 linkMusic과 goodMusic함수 둘 중 하나의 함수가 나중에 호출 됩니다.
제어권
콜백 함수 예제
var count = 0;
var cbFunc = function () {
console.log(count);
if(++count > 4) clearInterval(timer);
};
var timer = setInterval(cbFunc, 300);
timer 변수에는 setInterval의 ID값이 담깁니다. setInterval에 전달한 첫 번째 인자인 cbFunc 함수는 0.3초마다 자동으로 실행될 것입니다. 콜백 함수 내부에서는 count 값을 출력하고, count를 1만큼 증가시킨 다음, 그 값이 4보다 크면 반복 실행을 종료하라고 합니다.
이 코드를 실행하면 콘솔창에는 0.3초마다 한 번씩 0부터 숫자가 증가하며 출력되다 4가 되면 종료됩니다. setInterval이라고 하는 '다른 코드'에 첫 번째 인자로서 cbFunc 함수를 넘겨주자 제어권을 넘겨받은 setInterval이 스스로의 판단에 따라 적절한 시점에 이 익명함수를 실행했습니다. 이처럼 콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수 호출 시점에 대한 제어권을 가집니다.
인자
var newArr = [10, 20, 30].map(function (currentValue, index) {
console.log(currentValue, index);
return currentValue + 5;
});
console.log(newArr);
1번째 줄에서 newArr 변수를 선언하고 우항의 결과를 할당했습니다. 5번째 줄에서 그 결과를 확인하고자 합니다. 1번째 줄의 우항은 배열[10, 20, 30]에 map 메서드를 호출하고 있습니다. 이때 첫 번째 매개변수로 익명 함수를 전달합니다. Array의 prototype에 담긴 map 메서드는 다음과 같은 구조로 이루어져 있습니다.
Array.prototype.map(callback[, thisArg])
callback: function(currentValue, index, array)
map 메서드는 첫번째 인자로 callback 함수를 받고, 생략 가능한 두 번째 인자로 콜백 함수 내부에서 this로 인식할 대상을 특정할 수 있습니다. thisArg를 생략할 경우에는 일반적인 함수와 마찬가지로 전역객체가 바인딩됩니다. map 메서드의 재상이 되는 배열의 모든 요소들을 처음부터 끝까지 하나씩 꺼내어 콜백 함수를 반복하고, 콜백 함수의 실행결과들을 모아 새로운 배열을 만듭니다. 콜백 함수의 첫 번째 인자에는 배열의 요소 중 현재값이, 두 번째 인자에는 현재값의 인덱스가, 세 번째 인자에는 map 메서드의 대상이 되는 배열 자체가 담깁니다.
this
콜백 함수도 함수이기 때문에 기본적으로는 this가 전역객체를 참조하지만, 제어권을 넘겨받을 코드에서 콜백 함수에 별로도 this가 될 대상을 지정한 경우에는 그 대상을 참조하게 된다. 별도의 this를 지정하는 방식 및 제어권에 대한 이해를 높이기 위해 map 메서드를 직접 구현해 보겠습니다.
Array.prototype.map = function (callback, thisArg) {
var mapperdArr = [];
for (var i = 0; i < this.length; i ++) {
var mappedValue = callback.callback.call(thisArg || window, this[i], i, this);
mappedArr[i] = mappedValue;
}
return mappedArr;
};
this에는 thisArg 값이 있을 경우에는 그 값을, 없을 경우에는 전역객체를 지정하고, 첫 번째 인자에는 메서드 this가 배열을 가리킬 것이므로 배열의 i번째 요소값을, 두 번째 인자에는 i 값을, 세 번째 인자에는 배열자체를 지정해 호출합니다. 그 결과 mappedValue에 담겨 mappedArr의 i번째 인자에 할당됩니다.
이제 this에 다른 값이 담기는 이유를 정확히 알 수 있습니다. 바로 제어권을 넘겨받을 코드에서 call/apply 메서드의 첫 번째 인자에 콜백 함수 내부에서의 this가 될 대상을 명시적으로 바인딩하기 때문입니다.
콜백 함수 내부의 this에 다른 값 바인딩하기
콜백 함수 내부에서 this가 객체를 바라보게 하고 싶다면 어떻게 해야 할까요? 별도의 인자로 this를 받는 함수의 경우에는 여기에 원하는 값을 넘겨주면 되지만 그렇지 않은 경우에는 this의 제어권도 넘겨주게 되므로 사용자가 임의로 값을 바꿀 수 없습니다. 그래서 this를 다른 변수에 담아 콜백 함수로 활용할 함수에서는 this 대신 그 변수를 사용하게 하고, 이를 클로저로 만드는 방식이 많이 쓰였습니다.
콜백 지옥과 비동기 제어
콜백 지옥은 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여 쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상으로, 자바스크립트에서 흔히 발생하는 문제입니다. 주로 이벤트 처리나 서버 통신과 같이 비동기적인 작업을 수행하기 위해 이런 형태가 자주 등장하곤 하는데, 가독성이 떨어질 뿐만 아니라 코드를 수정하기도 어렵습니다.
비동기 작업의 동기적 표현 - Promaise
new Promise(function (resolve) {
setTimeout(function() {
var name = '에스프레소';
console.log(name);
resolve(name);
}, 500);
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimout (function () {
var name = preName + '아메리카노';
console.log(name);
resolve(name);
}, 500);
});
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimout (function () {
var name = preName + '카페모카';
console.log(name);
resolve(name);
}, 500);
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimout (function () {
var name = preName + '카페라떼';
console.log(name);
resolve(name);
}, 500);
});
new 연산자와 함께 호출한 Promise의 인자로 넘겨주는 콜백 함수는 호출할 때 바로 실행되지만 그 내부에 resolve 또는 reject 함수를 호출하는 구문이 있을 경우 둘 중 하나가 실행되기 전까지는 다음 또는 오류구문으로 넘어가지 않습니다. 따라서 비동기 작업이 완료될 때 비로소 resolve 또는 reject를 호출하는 방법으로 비동기 작업의 동기적 표현이 가능합니다.
'코어 자바스크립트 스터디' 카테고리의 다른 글
코어 자바스크립트 6장 프로토타입 (0) | 2024.07.22 |
---|---|
코어 자바스크립트 3주차 발표 (0) | 2024.07.19 |
코어 자바스크립트 4장 콜백 함수 (0) | 2024.07.11 |
코어 자바스크립트 3강 this (0) | 2024.07.08 |
코어 자바스크립트 1주자 발표 (0) | 2024.07.05 |