this 키워드는 자바스크릡트 function과 밀접한 관련을 갖는다. this와 관련해 중요한 건, 함수가 호출되는 위치를 이해하는 것이다.
자바스크립트에서, Lexical Environment
란 코드가 물리적으로 작성되는 곳을 말한다.
아래 코드예제에서 변수 name은 함수 sayName() 내부에 lexically하게 존재한다.
function sayName() {
let name = 'someName';
console.log('The name is, ', name);
}
Execution Context
는 현재 실행 중인 코드와 이를 실행하는 데 도움이 되는 모든 것을 나타낸다. 사용 가능한 수많은 lexical environments이 존재하지만, 그중에서 실행 중인 것만 Execution Context에 의해 관리된다.
각각의 Execution Context는 Environment Record
를 포함한다. 자바스크립트 엔진이 코드를 실행하면, 변수와 함수명이 Environment Record에 추가되며, 이를 자바스크립트에서는 Binding
이라고 한다.
Binding
은 Execution Context에서 identifiers(변수, 함수명)을 this 키워드와 연결하는 데 도움을 준다.
객체의 메서드를 호출할 때 .
표기법을 사용한다. 암시적 바인딩에서는 호출 시 메서드에 인접한 객체를 확인해야 하며, 이는 this가 어디로 바인딩될지를 결정한다.
function greeting(obj) {
obj.logMessage = function() {
console.log(`${this.name} is ${this.age} years old!`);
}
};
const tom = {
name: 'Tom',
age: 7
};
const jerry = {
name: 'jerry',
age: 3
};
greeting(tom);
greeting(jerry);
tom.logMessage ();
jerry.logMessage ();
메서드 내에서 this
는 .
앞에 작성한 객체(정확히는 메서드를 갖고 있는 객체)를 가리킨다(참조한다).
메서드란, 객체 안에 있는 함수를 말한다. 객체명.함수명
const obj1 = {
func() {
console.log(this);
return;
},
obj2: {
subFunc() {
console.log(this);
return;
}
}
}
obj1.func();
// obj1 객체
// { func: [Function: func], obj2: { subFunc: [Function: subFunc] } }
obj1.obj2.subFunc();
// obj2 객체
// { subFunc: [Function: subFunc] }
함수명 : function () {}으로 써도 되고, 함수명 () {}으로 작성해도 된다.
const obj1 = {
obj2 : {
subFunc () { console.log(this) },
// subFunc : function () { console.log(this) },
}
}
obj1.obj2.subFunc(); // obj2 { subFunc: [Function: subFunc] }
자바스크립트에서 코드를 실행할 때, creation phase에서 변수, 함수, 객체 등을 모두 처리한다. 그리고 마지막으로 execution phase에서 코드를 실행하는데, 이를 Execution Context라고 하는 것이다.
자바스크립트에서는 이러한 Execution Context가 많이 있을 수 있고, 이러한 Execution Contexts는 서로 독립적이다.
이때 하나의 Execution Context에서 다른 Execution Context의 어떤 것들을 적용하고 싶을 때가 있다. 이떄 Explicit Binding을 사용한다.
Explicit Binding을 도와주는 메서드 3가지
call()
context가 매개변수로 전달된다.
let getName = function() {
console.log(this.name);
}
let user = {
name: 'Tapas',
address: 'Freecodecamp'
};
getName.call(user);
여러 parameters를 보낼 수도 있다.
let getName = function(hobby1, hobby2) {
console.log(this.name + ' likes ' + hobby1 + ' , ' + hobby2);
}
let user = {
name: 'Tapas',
address: 'Bangalore'
};
let hobbies = ['Swimming', 'Blogging'];
getName.call(user, hobbies[0], hobbies[1]);
apply()
apply()는 call()과 동일하지만 더 편리하게 쓸 수 있다. call()은 인자들을 일일이 보내야 했지만 apply는 그럴 필요가 없기 때문이다.
let getName = function(hobby1, hobby2) {
console.log(this.name + ' likes ' + hobby1 + ' , ' + hobby2);
}
let user = {
name: 'Tapas',
address: 'Bangalore'
};
let hobbies = ['Swimming', 'Blogging'];
getName.apply(user, hobbies);
bind()
call binding과 유사하지만 call()의 경우 함수를 직접호출하는 데 반해, bind()는 새로운 함수를 반환하고 대신 호출한다.
let getName = function(hobby1, hobby2) {
console.log(this.name + ' likes ' + hobby1 + ' , ' + hobby2);
}
let user = {
name: 'Tapas',
address: 'Bangalore'
};
let hobbies = ['Swimming', 'Blogging'];
let newFn = getName.bind(user, hobbies[0], hobbies[1]);
newFn();
함수가 new 키워드로 호출되면, 자바스크립트는 함수 안에 내부 객체를 생성한다. 새로 생성된 객체는 this 키워드를 사용해 생성 중인 객체에 바인딩된다.
constructor 안에서 this를 사용하면 새로 생성되는 객체를 가리킨다.
function Person(age) {
this.age = age,
}
const wejaan = new Person(27)
console.log(wejaan.name) // 27
this
를 일반 함수에서 쓰거나 기본적으로 쓰면, this는 전역 객체(window)를 가리킨다(참조한다).
function func() {
console.log(this);
return;
}
func();
위처럼 함수를 생성하는 순간 window 객체에 func() 함수가 생성되는 것과 같다. window는 모든 전역 변수, 함수, DOM를 보관/관리하는 전역 객체.
하지만 strict mode에서 this
는 undefined이다.
'use strict';
function func() {
console.log(this);
return;
}
func(); // undefined
EventListener 안에서 this는 e.currentTarget을 가리킨다.
document.querySelector().addEventListner('click', function () {
console.log(this == e.currentTarget); // true
})
arrow function의 경우 this가 함수 밖 객체를 가리킨다.
const obj1 = {
obj2 : {
subFunc : () => { console.log(this) },
}
}
obj1.obj2.subFunc(); // window
object 반복문 돌릴 때는 강제로 this가 window가 아니라 object를 가리키도록 arrow function을 사용한다. 기본적으로 object는 전역객체 window에 있기 때문이다.
let obj = {
name : ['김', '이', '박'],
func : function(){
console.log(this); // {name: Array(3), func: ƒ}
obj.name.forEach(function () {
console.log(this);
/**
* window
* window
* window
*/
});
obj.name.forEach(() => {
console.log(this);
/**
* {name: Array(3), func: ƒ}
* {name: Array(3), func: ƒ}
* {name: Array(3), func: ƒ}
*/
})
}
}
obj.func();
The JavaScript this
Keyword + 5 Key Binding Rules Explained for JS Beginners