회고록 블로그

[공부 필기] JavaScript 문법 공부 20일차 (2) 본문

2. 프로그래밍 언어 공부/JavaScript

[공부 필기] JavaScript 문법 공부 20일차 (2)

김간장 2022. 5. 12. 16:50

※ 필자는 초초초보자입니다.

※ 틀린 내용에 대한 피드백은 언제든지 환영합니다.

 

 

공부하고 있는 자료 : modern JavaScript tutorial 

https://ko.javascript.info/garbage-collection

 

가비지 컬렉션

 

ko.javascript.info

 

✅ 가비지 컬렉터

- 자바스크립트 엔진 내에는 가비지 컬렉터(garbage collector)가 있다.

   » 가비지 컬렉터는 모든 객체를 모니터링하면서 [도달할 수 없는 객체]를 삭제한다.

 

- 그렇다면 도달 가능한 값은 무엇일까.

 

✅ 도달 가능한 값과 가비지 컬렉션

- 자바스크립트는 눈에 보이지 않는 곳에서 메모리 관리를 수행한다.

   » 도달 가능성(reachability) 이라는 개념을 사용해서 메모리 관리를 수행

   » 도달 가능성? 어떻게든 접근하거나 사용할 수 있는 값을 의미

   » 도달 가능한 값은 메모리에서 삭제되지 않는다.

 

- 도달 가능한 값은 다음과 같은 것들이 있다.

   » 현재 함수의 지역변수/매개변수, 중첩 함수의 체인에 있는 함수에서 사용되는 변수/매개변수, 전역 변수 등

       # 이런 값을 루트(root)라고 부른다.

       # 명백한 이유가 없으면 삭제되지 않는 값들이다.

   » 루트(root)가 참조하는 값이나 체이닝

       # 루트에서 참조할 수 있는 값은 모두 도달 가능한 값이 된다.

 

- 이렇게 쓸모 없어지게 된 것들을 엔진이 처리하는 것을 가비지 컬렉션이라고 한다.

   » 가바지 컬렉션은 엔진이 자동으로 수행한다. 따라서 개발자가 이를 억지로 실행하거나 막을 수 없다고 한다.

 

✅ 예시

- 개념이 너무 어렵다. 예시를 찾아보자.

- 아래와 같은 코드가 있다고 가정

let user1 = { // user1에 저장되는 값 = 참조값
	name: "soySauce",
	age: 1,
	school: {
		element: "aSchool",
		middle: "bSchool",
		high: "cSchool",
	}
};

- 이때, user1의 객체를 다른 값으로 덮어써보자.

user1 = null;

- 이렇게 되면 기존의 객체는 user1 변수를 통해 접근할 수 없다.

   » 즉, name/age/school 객체로 구성되어 있던 처음의 객체는 이제 도달할 수 없는 상태가 되었다.

- 가바지 컬렉터는 이 객체에 저장된 데이터를 삭제하고, 이 객체를 메모리에서 삭제한다.

 

- 더 구체적으로

   가비지 컬렉션이 어떤 데이터를 메모리에서 남기고, 어떤 데이터를 지우는지 확인하고 싶다면 모던 자바스크립트 참고

https://ko.javascript.info/garbage-collection

 

✅ 가비지 컬렉션 기본 알고리즘 : mark-and-sweep

- 가비지 컬렉션은 대게 다음 단계를 거쳐 수행된다.

   » 1) 가비지 컬렉터는 루트(root) 정보를 수집하고 이를 mark(기억)한다.

   » 2) 루트(root)가 참조하고 있는 모든 객체를 방문하고 이것들을 mark(기억)한다.

   » 3) mark된 모든 객체에 방문하고, 그 객체들이 참조하는 객체도 mark한다.

           한번 방문한 객체는 전부 mark 하기 때문에 같은 객체를 다시 방문하지는 않는다.

   » 4) 루트(root)에서 도달 가능한 모든 객체를 방문할 때까지 위 과정을 반복한다.

   » 5) mark 되지 않은 모든 객체를 메모리에서 삭제한다.

 

✅ 가비지 컬렉션: 최적화 기법

- 기본 알고리즘은 mark-and-sweep이고

- 좀 더 가비지 컬렉션을 빠르게 하기 위한 다양한 최적화 기법이 있다.

- 가령 다음과 같은 최적화 기법들이 있다.

   » generational collection(세대별 수집)

   » incremental collection(점진적 수집)

   » idle-time collection(유휴 시간 수집)

 

 


공부하고 있는 자료 : modern JavaScript tutorial 

https://ko.javascript.info/object-methods

 

메서드와 this

 

ko.javascript.info

 

✅ 개요

- 객체는 실제 존재하는 개체(entity)를 표현하고자 할 때 생성된다.

- 자바스크립트에선 객체의 프로퍼티에 함수를 할당해서 객체에게 행동할 수 있는 능력을 부여해준다.

   » 객체 안에 데이터(문자, 객체 등)만 담는게 아니라

   » 함수도 추가해서 객체가 행동을 하고, 특정 기능을 할 수 있도록 할 수 있다는 이야기

 

✅ 메서드

"use strict";

let setMessage = prompt("저장하고 싶은 개인 메시지를 입력해주세요.", "");

let user = {
	name: "soySauce",
	age: 1,
	message: setMessage,
};

user.showMessage = function() { // user 객체에 프로퍼티 추가
	alert(this.message);
};

user.showMessage();

- user 객체에 showMessage라는 프로퍼티를 추가해주었으며

   이 showMessage라는 객체 프로퍼티에는 함수가 할당되어 있다.

- 이렇게 객체 프로퍼티에 할당된 함수를 메서드(method)라고 한다.

   » 즉, showMessage는 메서드이다.

- 이렇게 쓸 수도 있다. 어떻게 쓰든지 마음대로이다.

"use strict";

let setMessage = prompt("저장하고 싶은 개인 메시지를 입력해주세요.", "");

let user = {
	name: "soySauce",
	age: 1,
	message: setMessage,
	showMessage: function() {
		alert(this.message);
	}
};

user.showMessage();

- 메서드 단축 구문도 있다.

   » function 이라는 키워드와 콜론(:)을 제거

...
	showMessage() {
		alert(this.message);
	}
};

- 다만, [메서드 단축 구문]과 [일반적인 메서드 생성 구문]이 완전하게 동일한 방법인 것은 아니다.

   » 객체 상속과 관련된 미묘한 차이가 존재한다고 한다.

 

 

✅ 메서드와 this

- 메서드를 그저 함수처럼만 사용할 것이라면 굳이 객체 안에 만들어줄 필요가 없다!

 

- 메서드는 객체에 저장된 정보에 접근할 수 있어야 제 역할을 다할 수 있다.

- 메서드 내부에서 객체 내부의 데이터에 접근하고 싶다면 this 키워드를 이용하면 된다.

   » this는 [현재 객체]를 나타낸다.

   » 메서드 내부에서 this를 사용한 것이면 여기에서 [현재 객체]라는 의미는 [메서드를 호출할 때 사용된 객체]를 의미한다.

 

- this를 사용하지 않고, user라는 변수명(외부 변수)을 통해 접근하는 것도 가능하다.

   » 다만 이렇게 사용하면 예상치 못한 에러가 발생할 수 있다.

   » e.g. user를 (얕은) 복사해서 다른 변수에 할당하고, user는 다른 값으로 덮어버렸을 때 등

 

- this 키워드를 이용해서 꼭 원시타입에만 접근해야하는건 아니다.

   » 메서드도 가능

"use strict";

let setMessage = prompt("저장하고 싶은 개인 메시지를 입력해주세요.", "");

let user = {
	name: "soySauce",
	age: 1,
	message: setMessage,
	showMessage() {
		alert(this.message);
	},
	printMethod() {
		this.showMessage(); // 현재 객체의 showMessage 메서드
		return (true);
	},
};

 

✅ 자유로운 this

- 자바스크립트의 this는 다른 언어와 좀 다르다.

   » 일단, 자바스크립트에선 모든 함수(function)에 this를 사용할 수 있다.

   » 그리고 이때 this는 메서드가 정의된 객체를 참조하는게 아니다.

- 그 이유는 this의 값은 런타임(run-time)에 결정되기 때문이며,

   런타임에 컨텍스트(context)에 따라 달라지기 때문이다.

   » 메서드가 어디에 정의되어 있는지는 중요하지 않고, 점 앞의 객체가 무엇인가에 따라서 this가 자유롭게 결정된다.

   » this가 런타임에 결정되기 때문에 함수를 하나만 만들어서 여러 객체에서 재사용할 수 있다.

   » 하지만 이런 유연함이 실수로 이어질 수도 있다.

 

※ 예시

- 위의 내용은 즉슨 아래와 같은 코드를 사용할 수 있다는 의미이다.

"use strict";

function showName() {
	console.log(this.name);
}

   » 이 코드만 적고 실행시켜도 에러가 발생하지 않는다. (현재 객체의 message 프로퍼티가 없습니다 등 에러 발생하지 않음)

 

- 아래와 같은 객체가 존재한다고 가정하자.

let user1 = {
	name: "Kim",
};

let user2 = {
	name: "Soy",
};

- 이때, showName이라는 함수를 각각의 객체 안에 추가하고, showName을 호출해보면..

   동일한 함수였지만 다른 객체에 추가했기 때문에 this가 참조하는 값은 완전히 달라진다.

"use strict";

let user1 = {
	name: "Kim",
};

let user2 = {
	name: "Soy",
};


function showName() {
	console.log(this.name);
}

user1.showName = showName; // 주의! showName() 아님 (showName 함수의 결과값이 필요한게 아님, 함수 그 자체가 필요한 것)
user2.showName = showName;

user1['showName']();
user2['showName']();

 

🌸 추가 설명 (혼돈의 this 파티..)

참고사항: 엄격모드가 아닐때 기준

사실 이해가 안되어서 검색을 좀 해봤다..

일단 정리해야할 점이 있다.

 

this가 함수 안에서 쓰였고, 해당 함수를 호출하면 함수 내부의 this는 지정되어 있지 않다는 점이다.

또한 this가 지정되어 있지 않은 경우, 기본적으로 전역 객체를 바라보기 때문에 this는 전역 객체가 된다고 한다.

 

- case1

   » 이건 딱히 어려운 상황은 아닌 것 같다. 이해하는데 문제가 없다. 

   » testFunc 함수 내부의 this는 지정되어 있지 않고

       때문에 전역 객체(Window)가 된다.

function testFunc() {
	console.log(this);
}

testFunc(); // Window

- case2

   » 이제 혼돈의 this 케이스이다.

   » 아래의 코드는 객체를 만들어서 obj 변수에 넣었고

       그 객체에는 원시타입의 프로퍼티 2개와 메서드 message()가 있는 모습이다.

   » 공교롭게도 message의 함수 안에는 함수(showMessage)가 선언되어있다. 실행도 된다.

       그리고 그 함수에서 바로 this를 사용하고 있다.

let obj = {
	name: "soy",
	age: 1,
	message: function() {
		let showMessage = function() {
			console.log(this);
		}
		showMessage();
	}
};


obj['message'](); // Window

   » 이때 결과는 "전역 객체(Window)"가 된다.

   » 이게 바로 혼란스러운 점이다.

   » 느낌 상으로는 showMessage 함수가 자신이 소속된 객체를 찾기 위해 코드상으로 위로 타고타고 올라가서

      결과적으로 message 메서드가 "포함되어 있는" obj 객체가 출력될 것 같지만..

      아니다. 전역 객체(Window)가 출력된다.

   » 그 이유는 this가 메서드가 정의되어 있는 객체를 찾는게 아니기 때문이다.

   » 그리고 이게 바로 앞에서 말한 "this는 메서드가 정의된 객체를 참조하지 않는다"는 의미이다.

 

- 다시 잘 정리해보자.

   this가 결정되는 것은 본인이 속한 상황(obj 객체 안)이 아니라 런타임의 컨텍스트이다.

- 만약 message 메서드 안에서 this를 사용했다면 점 앞에 obj가 있어서, obj가 출력됐을 것이다.

let obj = {
	name: "soy",
	age: 1,
	message: function() {
		console.log(this); 
        ...
    }
};        

obj['message'](); // this는 obj객체

- 하지만 이 경우는 showMessage 함수의 점 앞에(?) 객체가 없다. 지정되어 있지 않으니 전역 객체(Window)가 된다.

 

참고자료 : https://velog.io/@padoling/JavaScript-%ED%99%94%EC%82%B4%ED%91%9C-%ED%95%A8%EC%88%98%EC%99%80-this-%EB%B0%94%EC%9D%B8%EB%94%A9

 

 

✅ 함수에 this만 사용하면?
       메소드에서 this만 사용하면?

 

※ 메소드에서 this만 사용하면, 당연히 메소드를 호출할 때 사용된 객체 그 자체를 참조한다.

※ 함수에서 this만 사용하고 호출해보면?

     → undefined 

"use strict";

function showName() {
	console.log(this);
}

showName();

 

- 사실 엄격 모드("use strict";)가 아닐 때는 this가 전역 객체를 참조한다.

   » 브라우저 환경이라면 window라는 전역 객체를 참조

 

✅ 화살표 함수와 this

- 화살표 함수는 고유한 this를 가지지 않는다.

- 화살표 함수에서 this를 참조하면, 화살표 함수가 아닌 평범한 외부 함수에서 this를 가지고 온다.

 

🌸 추가 설명 (혼돈의 this 파티..)

참고사항: 엄격모드가 아닐때 기준

 

이것도 이해가 안돼서 더 찾아봤다..

 

앞서 위에서 obj 객체 안에 -> message 메서드 안에 -> showMessage 함수 안에 -> 그 곳에서 this가 사용됐던 예시를 보였었다.

► 이때 문제는 showMessage 함수 안에서 this가 사용돼서 전역 객체(Window)가 출력되는 문제가 있었다.

 

화살표 함수에서 this를 사용하면 이 혼돈의 상황을 해결할 수 있다.

왜냐하면 화살표 함수는 this가 없기 때문이다.

 

자바스크립트는 어떤 식별자를 찾을 때 현재 환경에서 상위 환경으로 타고타고 올라가면서 식별자를 찾고,

최종적으로 가장 상위로 갔는데도 없으면 그만두게 된다고 한다.

 

화살표 함수에서의 this도 이와 유사하다.

화살표 함수 안에서는 this를 사용한다고 해도, 그들 안에서 this는 세상에 없는 존재이다.

(= 화살표 함수 안에서는 this가 본인의 기능(메서드가 호출된 현재의 객체)을 하지 않는다)

그럼 자바스크립트는 화살표 함수 밖의 상황(상위 환경)을 보면서 this에게 적당한 값을 찾아준다(?)

(= 상위 환경에서의 this를 참조하게 된다)

 

그래서 선언될 시점(정의될 시점)에서의 상위 스코프가

this가 참조하는 값이 되는 것이라고 한다.

let obj = {
	name: "soy",
	age: 1,
	message: function() {
		let showMessage = () => console.log(this);
		showMessage();
	}
};


obj['message'](); // showMessage 화살표 함수 안의 this는 obj 객체를 참조한다.

 

 

※ 그렇다면 message 메서드 안에 -> 화살표 함수 showMessage를 쓰고, 그 안에 또 -> 화살표 함수 show를 써놓고 -> 거기에서 this를 사용하면 어떻게될까

- [메서드]를 만들고 그 안에 -> [화살표 함수]를 만들고 그 안에 -> [화살표 함수]를 또 만들고 그 안에서 -> this를 사용

- 여기에서의 this는 상위 객체의 obj를 참조한다.

let obj = {
	name: "soy",
	age: 1,
	message: function() { // 메서드 message
		let showMessage = () => { // 화살표 함수 showMessage
			let show = () => console.log(this); // 화살표 함수 show
			show();
		}
		showMessage();
	}
};


obj['message'](); // obj 출력

 

※ 그렇다면 message 메서드 안에 -> 화살표 함수 showMessage를 쓰고, 그 안에 -> 일반 함수 show를 써놓고 -> 또 그 안에 화살표 함수 s를 써놓고 -> 거기에서 this를 사용하면 어떻게될까

- 보기만해도 복잡하다.

- 이런 경우가 있을지는 모르겠지만 일단 해보자.

- 어쨌든 여기에서 중요한건

   [메서드]를 만들고 그 안에 -> [화살표 함수] 만들고 그 안에 -> [일반 함수] 만들고 그 안에 -> [화살표 함수] 만들고 그 안에서 -> this를 사용

    인 것이다.

let obj = {
	name: "soy",
	age: 1,
	message: function() { // 메서드 message
		let showMessage = () => { // 화살표 함수 showMessage
			let show = function() { // 일반 함수 show
				let s = () => console.log(this); // 화살표 함수 s
				s();
			}
			show();
		}
		showMessage();
	}
};


obj['message'](); // Window 출력

- 일반적으로 함수를 선언하는 방식 때문에 this는 전역 객체(Window)를 참조하게 된다.

 

 

화살표 함수의 주의사항은 이 글을 읽어보자. 나머지는 정리가 너무 힘들어서..ㅠㅠ

https://velog.io/@padoling/JavaScript-%ED%99%94%EC%82%B4%ED%91%9C-%ED%95%A8%EC%88%98%EC%99%80-this-%EB%B0%94%EC%9D%B8%EB%94%A9

 

[JavaScript] 화살표 함수와 this 바인딩

본래 JavaScript에서 함수를 선언할 땐 function 이란 키워드를 쓰죠. 하지만 ES6가 도입되면서 함수를 선언하는 새로운 문법이 등장했습니다. 바로 화살표 함수입니다.

velog.io

 

Comments