this
는 기본적으로 자바스크립트에 내장된 명령어입니다.
전역 문맥에 this
를 작성하면, this
의 값은 strict mode 여부와 상관없이 전역 객체를 참조하게 됩니다.
자바스크립트의 this
키워드는 함수 내부에서 사용됩니다.
function
키워드를 이용해 생성한 일반 함수 내부에 존재합니다.
화살표 키워드를 이용해 만든 함수 내부에는 arguments
가 존재하지 않는 것처럼,
this
도 존재하지 않습니다.
화살표 함수에서 이 키워드들(this, arguments
)을 사용할 경우
스코프 체인을 따라 상위 스코프를 탐색해 값을 가져옵니다.
this
키워드를 사용하면 함수를 더욱 유연하고, 재사용성이 뛰어나게 만들 수 있습니다.
하지만 this
키워드를 무분별하게 사용하는 것은 좋지 않습니다.
"this
값" 은 this
가 사용된 함수가 어떤 방식으로 실행되었느냐에 따라
"this
값" 이 결정됩니다.
this
값은 함수 선언, 할당과 전혀 관련없이, "함수가 실행되는 순간에 결정"됩니다.
this
값을 판별하기 위해서는 반드시 함수의 실행문을 찾고 그 함수가 어떻게 실행되었는지 판별해야합니다.
함수가 실행될 수 있는 방식에는 크게 4가지가 있습니다.
4가지 함수 실행 방식에 따라 this
값은 달라집니다.
var name = "hyesik"
function foo () {
var name = "sikhye";
bar();
}
function bar () {
console.log(this.name);
}
foo(); // "hyesik"
위 함수의 실행문을 보면 특별한 특징 없이 함수이름 만을 이용하여
함수를 실행시키고 있습니다. 이를 일반 함수 실행 이라고 합니다.
일반 함수 실행 방식으로 실행을 하게 되면 해당 함수의
this
값은 global object
가 됩니다.
(= 브라우저에서는 window object
)
함수의 선언이나 할당은 this
값과 전혀 무관합니다.
"use strict"
var name = "hyesik";
function foo () {
console.log(this.name);
}
foo(); // Uncaught TypeError: Cannot read property 'name' of undefined
여기서 주의해야 할 점은
자바스크립트에는 strict mode
라는 것이 있습니다.
strict mode
는 자바스크립트의 엄격 모드로 예전에는 자바스크립트가 묵인했던 오류들을 그냥 지나치지 않고 오류 메세지를 발생시킵니다.
strict mode
를 사용하게 되면 지나친 오류의 위치를 찾을 수도 있어 시간을 절약할 수 있습니다.
strict mode
이 실행된다면 this
값이 바뀌게 됩니다.
strict mode
전에는 일반 함수 실행 방식으로 실행했을 때 this
값은 global object
이지만
strict mode
가 실행되면 this
값은 undefined
입니다.
그래서 strict mode
가 실행됐을 때 일반 함수 실행 방식으로 함수를 실행시켰을 때
this
값은 undefined
이고, 위 예제에서this.name
= undefined.name
가 됩니다.
이것은 당연히 오류이기 때문에 오류 메세지가 출력됩니다.
var age = 25;
const hyesik = {
age: 23,
foo: function () {
console.log(this.age);
},
};
const func = hyesik.foo;
// Dot Notation
hyesik.foo(); // 23
// 일반 함수 실행 방식
func(); // 25
Dot Notation 으로 함수를 실행시켰을 때 this
값은 함수실행문 전에 . ( = dot ) 앞에 있는 객체가 this
값이 됩니다.
함수 실행 방식을 볼 때 화살표 함수 안에 this
가 존재하는지 보면서 this
값을
판별하려고 하면 this
값을 찾기 수월할 거 같습니다.
Explicit Binding ( = 명백하게 설정한다. 라고 이해하면 될 거 같다.)
이러한 메소드들을 사용하면 this
값을 설정할 수 있습니다.
.call()
자바스크립트에서 모든 함수는 .call()
이라는 메소드를 사용할 수 있습니다.
.call()
메소드의 기본 기능은 함수를 호출합니다.
.call()
메소드는 첫 번째 인자를 this 값으로 지정합니다.
.call()
메소드를 사용하면 첫 번째 인자를this
값이라고 확실하게 명시해줄 수 있습니다.
this
값을 확실히 설정해서 사용하고 싶다 하면.call()
메소드를 사용하면 됩니다.
var age = 23;
function fnc () {
console.log(this.age)
}
fnc.call(); // 23; <-- 전역 객체를 참조한 this 값
var age = 25;
function fnc () {
console.log(this.age);
}
const hyesik = {
age : 23
};
fnc.call(hyesik); // 23; <-- hyesik 객체를 참조한 this 값
.call()
메소드를 사용할 때,
this
값 설정을 위해 전달할 인자를 제외한 나머지 인자를 넘겨주고 싶다면
첫번째 인자 뒤로 인자의 개수 상관없이 주고 싶은 인자를 적어주면 됩니다.
여기서 첫 번째 인자를 this
값으로 준다는 것이 제일 중요합니다.
.apply()
.apply()
메소드는 .call()
메소드와 유사합니다.
함수를 호출하는 것과 첫 번째 인자를 this
값으로 설정하는 것은 똑같습니다.
여기서 차이점이 있다면 .call()
메소드는 this
값을 전달할 인자를 제외하고
특정 함수한테 넘겨주고 싶은 인자가 있다면 하나하나 넘겨줘야 하는데
.apply()
메소드는 특정 함수한테 넘겨주고 싶은 인자가 있다면
그 인자들을 배열에 담아 넘겨줘야 합니다. 그리고 이 배열에 담긴 요소들을 인자로 넘겨줍니다.
.call()
메소드는 인자의 개수가 얼마이든 간에 상관 없습니다. (this 값, a,b,c,---)
.apply()
메소드는 2개의 인자만 받습니다. .apply(this 값, [a,b,c,--])
동적인 상황일 때, 인자로 넘겨줘야 하는 값을 모를 때 .apply()
를 사용할 수 있습니다.
넘겨주고 싶은 인자값을 직접 가지고 있다면 .call()
을 사용할 수 있습니다.
.bind()
.bind()
메소드도 .call()
, .apply()
메소드와 유사합니다.
첫 번째 인자를 this
값으로 설정해준다는 점
나머지 받고 싶은 인자들은 .call()
메소드와 유사하게 받는 인자의 개수의 제한이 없는 점이 유사합니다.
차이점은
.bind()
메소드는 원본 함수를 복제한 "새로운 함수"를 반환해줍니다.
복제된 함수를 변수에 담아서 실행시켜줘야지만 원본 함수가 실행됩니다.
const sik = {
age: 23
};
const soo = {
age: 25
}
function foo () {
console.log(this.name);
}
const bar = foo.bind(sik);
bar(); // 23;
여기서 헷갈릴 수 있는 점은 .bind()
메소드를 사용하고 그 함수를 실행시키기 위해
bar();
처럼 일반 함수 실행으로 함수를 실행합니다. 하지만,
이 함수의 this
값은 일반 함수 실행을 했다고 this
값을 전역 객체
를 참조한다거나 undefined
가 아닙니다. 이 함수 실행문의 히스토리를 따라 올라가다보면 .bind()
에 this
값이 있는 것을 알 수 있습니다.
new
키워드와 함께 사용된 함수는 "생성자 함수" 라고 부릅니다.
"생성자 함수"의 경우에는 함수명을 지을 때 대문자로 시작하는 것이 통상적인 표기법입니다.
new
키워드를 사용한 생성자 함수를 실행했을 경우,
해당 함수 내부의 this
는 새로운 빈 객체가 됩니다.
그리고 return
값이 명시되지 않는다면 기본적으로 undefined
가 반환되는데,
new
키워드를 사용한 생성자 함수를 실행했을 때는 return
값을 명시하지 않아도 기본 반환값인 this
가 반환됩니다.
여기서 주의할 점은 생성자 함수를 실행했을 때
생성자 함수 내부에 object
를 return
값으로 반환한다면 this
값은 반환되지 않습니다.
하지만 보통 생성자 함수는 return
값을 명시하지 않습니다.
function foo () {
console.log(this);
}
new foo(); // {};
function foo (age) {
this.myAge = age;
// this = {myAge : 23}; < -- 이런 식으로 객체가 생성된다.
console.log(this.name);
}
new foo(23); // 23;
this 값을 찾을 때는 이슈가 되는 부분을 먼저 찾은 후
역으로 추적해서 찾아 들어가보자.