Java와 같은 programming language에서 this keyword는 class를 통해 생성될 instance를 가리키지만 javascript에서는 this keyword가 가리키는 대상은 상황에 따라 달라질 수 있으므로 주의가 필요하다. This keyword가 가리키는 대상이 상황에 따라 어떻게 달라지는지 예제를 통해 살펴보자.
일반 함수에서 this
일반 함수에서 this는 global object가 binding된다.
function testFn() {
console.log(this); //Window {window: Window, …}
}
testFn();
하지만 strict mode가 적용된 상황이라면 일반 함수에서 this는 다음과 같이 undefined이 binding된다. this keyword는 보통 자신이 속해 있는 object는 가리키거나 class로 생성한 instance를 가리키는 용도이므로 일반 function 내부에서 this는 불필요하기 때문이다.
"use strict"
function testFn() {
console.log(this)
}
testFn();
Object method에서 this
다음과 같이 object method에서 this keyword는 method를 호출한 object를 가리킨다. 아래의 예제를 살펴보자.
const person = {
name:"test",
getName(){
console.log(this); // {name: 'test', getName: ƒ}
return this.name;
}
}
person.getName();
위의 예제에서 getName method는 person object를 통해 호출되고 있으므로 this는 person object를 가리킨다. 하지만 다음의 경우는 어떨까?
const person = {
name:"test",
getName(){
console.log(this);
return this.name;
}
}
const anoterPerson = {
name:"another name"
}
anoterPerson.getName = person.getName;
anoterPerson.getName(); // {name: 'another name', getName: ƒ}
위의 예제와 같이 person object의 getName을 anotherPerson object의 getName에 할당하고 method를 호출해보면 getName method는 anotherPerson object를 가리킨다. 즉, method에서의 this keyword는 method가 처음 정의된 object가 아니라 method를 호출하는 object를 가리킨다.
그리고 아래와 같은 경우는 어떨까?
const person = {
name:"test",
getName(nameList){
return nameList.map(function(name){ // 일반 function
console.log(this); // Window {window: Window, ...}
return name
})
}
}
person.getName(["a", "b"])
위의 예제에서 person object의 method인 getName 내부에서 전달 받은 nameList parameter를 통해 Array map method를 실행하고 map method에 일반 함수를 parameter로 전달하여 실행하고 있다. 하지만 map method에 전달한 일반 함수 안에서 this keyword를 조회 해보면 person object가 아닌 global object를 가리키는 것을 확인할 수 있다. ( strict mode가 적용되면 위의 예제에서 this keyword는 undefined이 된다 )
이렇듯 object method 내부의 nested function에서 this keyword를 조회하면 this keyword가 method를 호출한 object를 가리키지 않으므로 주의가 필요하다.
Class method에서 this
Class의 method에서 this keyword는 class를 통해 생성되는 instance를 가리킨다.
class Person {
name;
constructor(name){
this.name = name;
}
getName(){
console.log(this);
return this.name;
}
}
const testPerson = new Person("test name");
testOrder.getName(); // Person {name: 'test name'}
Arrow function에서 this
arrow function은 일반 function과는 달리 this binding이 없으므로 arrow function이 정의된 상위 scope의 this를 참조하여 반환한다. 다음 예제를 살펴보자.
const testArrowFn = () => {
console.log(this); // Window {window: Window, ...}
}
testArrowFn();
위의 예제에서 testArrowFn의 상위 scope는 global scope이므로 this keyword는 window object를 return한다. 그리고 이는 object의 method로 arrow function을 사용해도 마찬가지다.
const person = {
name:"test",
getName:() => {
console.log(this); // Window {window: Window, ...}
...
}
}
person.getName();
위의 예제와 같이 object method를 arrow function으로 선언하면 method 내부의 this는 상위 scope의 this를 가리키므로 object method는 arrow function으로 선언하지 않는 것이 좋다.
하지만 “method에서의 this” section에서 봤던 method 내부의 nested function this binding 문제는 arrow function을 적용하면 상위 scope의 this를 참조하여 반환하므로 아래와 같이 method를 호출한 object를 정상적으로 return한다.
const person = {
name:"test",
getName(nameList){
return nameList.map((name) => { // arrow function
console.log(this); // {name: 'test', getName: ƒ}
return name
})
}
}
person.getName(["a", "b"])
위의 예제에서 Array map의 첫 번째 parameter로 일반 function이 아닌 arrow function을 전달하는 것을 확인할 수 있다. arrow function은 상위 scope의 this를 return 하므로 위의 예제에선 this keyword가 person object를 return한다.
만약 다음과 같이 arrow function 안의 또 다른 arrow function에서 this를 참조하면 window object가 return되는 것을 확인할 수 있다. this를 log하는 arrow function의 상위 scope도 arrow function이여서 this binding이 없으므로 한 단계 더 상위 scope인 global scope에서 window object가 this에 binding된다.
const testArrowFn = () => {
return () => {
console.log(this)
}
}
testArrowFn()();
class의 method를 arrow function으로 선언하면 어떻게 될까?
class Person {
name;
constructor(name) {
this.name = name
}
getPerson = () => {
return this;
}
}
const result = new Person ("test1")
console.log(result) // Person {name: 'test1', getPerson: ƒ}
class의 method를 arrow function으로 선언하면 생성된 class를 통해 instance를 반환하는 것을 확인할 수 있다. 하지만 class method를 arrow function으로 선언하면 method가 proptotype method가 아닌 instance method가 되므로 class method는 다음과 같이 method 축약 표현으로 선언해주는 것이 좋다.
class Person {
name;
constructor(name) {
this.name = name
}
getPerson() {
return this;
}
}
const result = new Person ("test1")
console.log(result) // Person {name: 'test1'}
Strict Mode
Strict Mode가 적용되어 있느냐 없느냐에 따라서도 this keyword가 가리키는 대상에 차이가 발생한다. default로 일반 함수에서 this를 참조하면 global object의 this를 가리키지만 strict mode가 적용된 상태에서 일반 함수에서 this를 참조하면 undefiend이 return된다.
"use strict"
function testNormalFn() {
console.log(this); // undefined
}
testNormalFn();
이는 object method의 nested method에서 this를 참조할 때 역시 마찬가지다.
"use strict"
const person = {
name:"test",
getName(nameList){
return nameList.map(function(name){
console.log(this) // undefined
return name
})
}
}
person.getName(["a", "b"])
Apply, call, bind
apply, call, bind method를 통해 method가 가리킬 object를 변경할 수 있다. 다음 예제를 살펴보자.
const testObj = {
address:"address 123"
}
const testFn = function() {
return this
}
const result1 = testFn.call(testObj)
const result2 = testFn.apply(testObj)
console.log(result1) // {address: 'address 123'}
console.log(result2) // {address: 'address 123'}
call과 apply method는 같은 역할을 하지만 object를 binding하는 function에 parameter를 전달하는 방식이 다르다.
const testObj = {
address:"address 123"
}
const testFn = function(param1, param2) {
return this
}
const result1 = testFn.call(testObj, "test1","test2")
const result2 = testFn.apply(testObj, ["test1", "test2"])
반면 bind method는 새로운 object를 bind할 뿐 apply, call과는 달리 함수를 바로 호출하지는 않는다
const testObj = {
address:"address 123"
}
const testFn = function() {
return this
}
const resultBind = testFn.bind(testObj)
console.log(resultBind)
/*
ƒ (param1, param2) {
return this
}
*/
const result = resultBind()
console.log(result) // {address: 'address 123'}
위의 예제와 같이 bind는 parameter로 전달받은 object를 this로 bind한 function을 return 하므로 호출은 별도로 해야 한다. 또한 function에 대한 parameter 역시 bind된 function을 호출할 때 함께 전달하면 된다.
const testObj = {
address:"address 123"
}
const testFn = function(param1, param2) {
console.log(param1) // 11
console.log(param2) // 22
return this
}
const resultBind = testFn.bind(testObj)
const result = resultBind(11, 22)
console.log(result) // {address: 'address 123'}
apply, call, bind method는 strict mode이 적용 유무와는 관계 없이 parameter로 전달하는 object를 function 내부의 this로 bind 한다. 하지만 arrow function은 this binding이 적용되지 않으므로 apply, call, bind를 통해 function 내부의 this가 참조하는 object를 변경할 수 없다.
const testFn = function() {
return this;
}
const result = testArrowFn.call({name:"test name"}, 1)
console.log(result) // Window {window: Window, …}