참고자료: 인프런 코어자바스크립트(클래스 편)
⭐️ this
- 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수
- this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티/메서드를 참조
- 암묵적으로 생성
- 코드 어디에서든 참조 가능
- 객체의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수이므로 일반적으로 객체의 메서드 또는 생성자 함수 내부에서만 의미가 있음
- 함수를 호출하면 인자와 this가 암묵적으로 함수 내부에 전달
- 인자를 지역 변수처럼 사용할 수 있는 것처럼, this도 지역변수처럼 사용 가능
- this가 가리키는 값, 즉, this 바인딩은 함수 호출 방식에 의해 동적으로 결정
- 크게 전역에서 사용할 때와 함수 안에서 사용할 때로 나눔
✨ 참고로, 바인딩이란 ❓
- 식별자와 값을 연결하는 과정
- 변수 선언은 변수 이름과 확보된 메모리 공간의 주소를 바인딩
- this 바인딩은 this와 this가 가리킬 객체를 바인딩
this는 저번 강의에 이어 이부분에 해당 !
✅ CASE 1 : 전역공간에서 호출
📚 전역공간에서 호출시 : window(자바스크립트 런타임) / global (Node.js)
- 브라우저라는 자바스크립트 런타임의 경우 this는 항상 windows라는 전역객체를 참조
- 전역 객체란 ❓ 전역 범위에 항상 존재하는 객체 ( node.js에서 전역객체는 global)
a= 'a';
//"a"
this.a
//"a"
window.a
//"a"
// in browser console
this === window // true
// in Terminal
node
this === global // true
💻 코드 )
function myFn(){
return this;
}
📌 이 함수를 호출하는 순간의 주체가 전역공간이기 때문에, 함수 안에서도 전역객체가 가능하다
✅ CASE 2. 함수 내부
📚 this를 함수 내부에서 사용한 경우 : window/global 객체 출력
- 함수는 전역에 선언된 일반 함수와 객체 안에 메소드로 크게 구분
- 객체 안에 선언된 함수를 전역에 선언된 함수와 구분하기 위해 메소드라고 함
- 그런데 전역에 선언된 일반 함수도 결국 window 전역 객체의 메소드. 즉, 모든 함수는 객체 내부에 있음
- 이때 this는 현재 함수를 실행하고 있는 그 객체를 참조
- 정리하면, 함수 내부에서 this의 값은 함수를 호출하는 방법에 의해 바뀜
- 엄격모드 여부에 따라 참조 값이 달라짐
- 엄격모드에서 일반 함수 내부의 this는 undefined가 바인딩 됨
💻 코드 1 )
var d = {
e : function() {
function f() {
console.log(this);
}
f(); // 전역객체
}
}
📌 f 메소드 안에 this를 출력할 때, 전역객체가 나옴.
📌 이유는 뒤에서 말하겠지만, f 메소드 앞에 아무런 지정값이 없어서 바로 전역객체가 출력
💻 코드 2)
function a() {
function b() {
console.log(this);
}
c(); // 전역객체 호출
}
b(); // 전역객체 호출
📌 암기할 수밖에 없는 부분.. c 함수를 호출했을 때, c함수의 주체는 b 함수이지만 전역객체를 호출한다(this의 값을 따로 정해놓지 않음)
📌 b 함수도 메서드 앞에 this를 참조할 함수가 없어 전역객체로 호출
💻 코드 3 )
var value = 1;
var obj = {
value: 100,
foo: function() {
console.log("foo's this: ", this); // obj
console.log("foo's this.value: ", this.value); // 100
function bar() {
console.log("bar's this: ", this); // window
console.log("bar's this.value: ", this.value); // 1
}
bar();
}
};
obj.foo();
📌 foo 메소드 함수에서는 바로 앞 주체가 obj라는 함수. 그렇기 때문에 foo의 this는 obj이고, this.value의 값은 obj의 value값이 100이 출력
📌 bar 메소드 함수는 이중함수로써 bar 함수 앞 주체가 foo함수. 하지만, foo 함수에서 명확히 어떤 주체할 만한 값이 없어서 this의 값도 정해놓은 것이 없기 때문에 전역객체가 출력. this는 window라는 전역객체, this.value는 전역변수인 1이 출력
✅ 뒤에서 전역객체와 전역변수가 다르게
💻 코드 4 )
var value = 1;
var obj = {
value: 100,
foo: function() {
setTimeout(function() {
console.log("callback's this: ", this); // window
console.log("callback's this.value: ", this.value); // 1
}, 100);
}
};
obj.foo();
📌 this가 전역객체를 가져와야 되지만, 전역변수 a를 호출.
📌 전역객체는 전역객체고, 전역변수는 전역변수로 생각하면 맞지만 별개의 개념으로 너무 생각하지 말자!
📚 예외적으로 ) that을 사용하는 경우 ((참고만))
var value = 1;
var obj = {
value: 100,
foo: function() {
var that = this; // Workaround : this === obj
console.log("foo's this: ", this); // obj
console.log("foo's this.value: ", this.value); // 100
function bar() {
console.log("bar's this: ", this); // window
console.log("bar's this.value: ", this.value); // 1
console.log("bar's that: ", that); // obj
console.log("bar's that.value: ", that.value); // 100
}
bar();
}
};
obj.foo();
✅ CASE 3. 메서드
📚 참고, 메서드에서 this의 영역
📚 객체의 메소드 함수에서 this
💻 코드 1)
const fn = {
title: 'Hello World',
showTitle(){
console.log(this.title);
}
};
fn.showTitle();
//Hello World
📌 showTitle 메서드 앞에 fn이 this로 정해져 showTitle() 메서드를 호출했을 때, 콘솔에 this가 fn으로 결정되고 fn의 title를 콘솔에 찍혀서 "Hello World"가 출력
💻 코드 2)
var a = {
b : function(){
console.log(this);
}
}
a.b(); //a가 this
📌 메서드에서는 점 앞에 있는 a가 this가 되는 것
📌 b 함수를 a 객체의 메서드로서 호출
✅ 메서드라는 것이 원래는 함수인데, 어떤 객체와 관련된 동작을 하게 되면 그때는 메서드로서 호출
💻 코드 3)
var a = {
b : {
c: function(){
console.log(this);
}
}
}
a.b.c(); //a.b가 this
📌 여기서는 a.b까지가 this를 가리킴
💻 코드 3-1)
const fn = {
title: "hello world",
tags : [1,2,3,4],
showTag(){
this.tags.forEach(function(tag){
console.log(tag);
console.log(this); //window
})
}
}
fn.showTag();
// 1
// window 객체 출력
// 2
// window 객체 출력
// 3
// window 객체 출력
// 4
// window 객체 출력
📌 고차 함수의 콜백 함수 안에서 this는 콜백함수가 일반 함수이기 때문에 전역객체를 참조
📌 showTag 함수 안에 tag 함수가 존재함. 여기서 fn.showTag()에서 this가 fn이라 생각할 수 있지만, 고차 함수에서 콜백함수는 그저 일반함수일 뿐.
💻 코드 3-2) 함수에 두번째 인자 넣어서 this 인자 객체 넘겨주기
const fn = {
title: "hello world",
tags : [1,2,3,4],
showTag(){
this.tags.forEach(function(tag){
console.log(tag);
console.log(this); //fn
}, this); //여기는 일반 함수 바깥, fn 객체를 참조할 수 있음
}
}
fn.showTag();
// 1
// fn 객체 출력
// 2
// fn 객체 출력
// 3
// fn 객체 출력
// 4
// fn 객체 출력
📌 코드 3-1에서 해결하지 못한 고차 함수 안에서 전역객체 참조 제외한 원하는 값을 참조하게끔 만들어줄 때 사용하는 것이 바로 콜백함수 bind (뒤에서 설명을 읽으면 이 코드 내용이 이해될 것임)
📌 두번째 인자로 바로 this를 넣음으로써 바로 바깥에 있는 fn이라는 객체를 this로 넘길 수 있음
💻 코드 3-3) 화살표 함수 사용하기
✨ 화살표 함수란 ❓
- function 키워드로 생성한 일반 함수와 화살표 함수의 가장 큰 차이점은 바로 this. 이를 Lexical this라고 말함
- 화살표 함수 안에서 this는 언제나 상위 스코프의 this를 가리킴
- 일반 함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정되지 않고, 함수를 호출할 때 함수가 어떻게 호출되는지에 따라 this에 바인딩할 객체를 동적으로 결정함
- 화살표 함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정
- 화살표 함수의 this 바인딩 객체 결정 방식은 함수의 상위 스코프를 결정하는 방식인 렉시컬 스코프와 유사
- 화살표 함수는 call, apply, bind 메소드를 사용하여 this를 변경할 수 없음
const fn = {
title: "hello world",
tags : [1,2,3,4],
showTag(){
this.tags.forEach((tag)=>{
console.log(tag);
console.log(this); //fn
}); //여기는 일반 함수 바깥, fn 객체를 참조할 수 있음
}
}
fn.showTag();
// 1
// fn 객체 출력
// 2
// fn 객체 출력
// 3
// fn 객체 출력
// 4
// fn 객체 출력
✅ CASE 4. 콜백함수
- call, apply, bind
📚 명시적으로 this를 바인드하는 방법
function a(x,y,z){
console.log(this, x,y,z);
}
var b = {
bb:'bbb'
};
a.call(b, 1,2,3);
a.apply(b, [1,2,3]);
var c = a.bind(b);
c(1,2,3);
var d = a.bind(b, 1,2);
d(3);
//결과
{ bb: 'bbb' } 1 2 3
{ bb: 'bbb' } 1 2 3
{ bb: 'bbb' } 1 2 3
{ bb: 'bbb' } 1 2 3
📌 call : 첫 번째 인자로 넘긴 것이 this, 넘기 두 번째부터는 차례대로 함수의 매개변수로 넘김
📌 apply : 첫 번째 인자로 넘긴 것이 this, 배열로 넘긴 각각의 요소들이 함수의 매개변수가 됨
📌 첫번째 bind : this를 a로 넘겨주고, b라는 매개변수를 넘겨준 상태. 여기서 c라는 바인드된 함수에 매개변수 1, 2, 3을 넘겨주면 결과값은 다른 결과값과도 같게 만들 수 있음
📌 두번째 bind : this를 a로 넘겨주고, 매개변수로 1, 2를 넘겨준 상태에서 d라는 바인드 함수를 만듦. 만든 후 그 함수에 3을 넘기면서 다시 호출
💻 예시 코드 1-1)
var callback = function() {
console.log(this);
};
var obj = {
a: 1,
b: function(cb){
cb();
}
}
obj.b(callback);
📌 obj.b의 함수를 호출했을 때, 매개변수로 callback 함수를 받는데, 이 callback함수는 this를 출력하는 함수
📌 그럼, obj의 메소드는 b를 보면, 함수 cb가 cb를 호출하는데, 여기서의 this는 따로 정해놓지 않았으므로 ( 메소드 앞에 this를 정한 값이 없기 때문) callback 함수에서 this를 출력하면 window(전역객체)라고 결과값이 나올 것이다
📌 즉, 그냥 함수로서 호출하면 무조건 전역객체
💻 예시 코드 1-2)
var callback = function() {
console.log(this);
};
var obj = {
a: 1,
b: function(cb){
cb.call(this);
}
}
obj.b(callback);
📌 만약 이 경우라면, cb.call(this)에서 this를 바로 넘겨주었을 때, this는 b 메서드 앞에 있는 obj 함수가 b 메서드의 주체이기 때문에 이때의 this는 obj로 출력
💻 코드 2)
var callback = function(){
console.dir(this); //따로 this를 지정한 값이 없어서 전역객체로 나옴
};
var obj = {
a:1
};
setTimeout(callback, 100);
//this의 값을 지정할려면
setTimeout(callback.bind(this), 100);
📌 setTimeout(callback, 100) : setTimeout 함수에서는 callback 함수에 this가 딱히 정해진 것이 없어 전역객체가 나옴
📌 setTimeout(callback.bind(this), 100) : callback 함수에 this를 원하는 값으로 지정하고 싶다면, bind를 쓰면 됨
💻 코드 3-1)
document.body.innerHTML += '<div id="a"> 클릭하세요 </div';
document.getElementById('a').addEventListener(
'click',
function(){
console.dir(this);
}
);
📌 이때 this는 html dome element가 나옴. 왜냐하면, this는 이벤트가 발생한 그 타겟 대상 엘리먼트로 하도록 정의가 되어있기 때문에📌 addEventListner처럼 콜백함수의 this를 별도로 지정해놓은 경우도 얼마든지 있음
💻 코드 3-2) 만약 this에 특정 obj로 바꾸고 싶다면? bind 콜백함수를 사용해라
document.getElementById('a').addEventListener(
'click',
function(){
console.dir(this);
}.bind(obj)
);
✅ 5. new 연산자
- 생성자 함수 방식으로 인스턴스를 생성한 경우
- 생성자 함수 MyFn가 빈 객체를 만들고 이 생성자 함수에서 this가 빈객체를 가리키도록 설정
💻 코드 1)
function MyFn(){
this.title = "hello world";
return this;
}
//new 연산자를 통해 새로운 객체를 얻음
const myfn = new MyFn()
myfn;
//MyFn{title: "hello world"}
💻 코드 2)
function Person(n, a){
this.name=n;
this. age = a;
}
var roy = new Person ('재남', 30); //new
console.log(roy);
console.log(window.name, window.age); //재남 30
✨ 결론
- this는 이처럼 어떤 위치에 있느냐, 어디에서 호출되느냐, 어떤 함수에 있느냐에 다라 참조 값이 달라지는 특성이 있음
- 제어권을 가진 함수는 this를 이미 지정해둔 경우도 있음 (예를들면, addEventListner)
-이를 직접 바꾸고 싶다면, 바인딩을 해라 !
'자바스크립트의 정석 🟡' 카테고리의 다른 글
[자바스크립트] 프로토타입 정리 ✨ (0) | 2024.01.26 |
---|---|
[자바스크립트] closure 정리 ✨ (0) | 2024.01.24 |
자바스크립트 - Destructuring assignment (0) | 2023.09.19 |
자바스크립트 - Function etc (0) | 2023.09.19 |
자바스크립트 - arrow function (0) | 2023.09.19 |