REACT_심화_review_0편

박하영·2021년 7월 4일
0
post-thumbnail

2021-07-02

var와 let, const의 차이점:
1) var는 ES5버전까지 사용했던 변수 생성 키워드이다. 같은 변수를 여러가지 만들어도 되고, 심지어는 생략도 가능하다.
2) var는 함수레벨 스코프를 가진다. -> 스코프란? -> 이 변수를 어디까지 쓸 수 있는가, 즉 사용 범위에 대한 내용이다.(전역, 함수내에서만 등등)
3) let과 const는 block 레벨 스코프를 가진다. 즉, {} 중괄호를 블록이라고 하는데, 이 안에서만 살아 있기 때문에, 벗어나면 사용할 수 없게 된다. 즉, 사용 범위에 있어서 let + const는 같은 블록 레벨 스코프이고, var는 함수 레벨 스코프로 차이가 있다.
4) let으로 선언시 재선언이 불가능하다. (but, 재할당은 가능)
5) const는 재선언, 재할당이 둘 다 불 가능.
6) var는 선언 전에도 사용할수가 있다. -> 이게 호이스팅 개념 때문인데, 호이스팅이란 것은 '선언'에 대한 부분을 맨 위로 끌어올려서 가장 먼저 실행되는 개념이다.

실행 컨텍스트 - 실행할 환경을 파일이 로드되면 미리 준비해놓는것. (파일을 읽어온다는 개념과 실제로 실행시키는 것의 개념은 다르다. (훑어보는 것과 실제로 한줄 한줄 코드에 작성된 내용을 그대로 실행하는 것은 엄연히 차이가 있다))

let과 var, const 모두 호이스팅 자체는 된다, 하지만 let과 const는 초기화 단계에서 메모리에 올라가지 않기 떄문에 선언 전에 호출하면 에러가 나는 것 이다. -> 이걸 TDZ 때문이라고도 말한다. -> TDZ는 뭘까? -> 변수 생성을 할 때, 선언이 호이스팅 된 후에 실제로 구문이 실행되기 전 까지는 변수가 초기화가 안되니까, 메모리에 공간 확보를 못 받은 것이고, 그래서 선언시 에러가 뜬다. 이 에러가 나는 구간을 TDZ (일시적 사각 지대)라고 한다.

++ var는 가급적 사용하지 말자.

JS (ES6 기준) 8가지 기본 자료형
객체형을 제외한 나머지 7가지 자료형은 원시형(primitive type)이다.

typeof 연산자로 자료형을 알아낼 수 있다.

객체형은 복잡한 자료구조를 저장하는데 사용하는데, 한가지 타입의 자료형이 아니라, 여러가지 타입의 자료형들이 다양하게 들어가 있는 형태이다.

심볼형: 고유 식별자를 만들 때 쓴다.

원시형은 오직 딱 한가지 타입의 데이터만 담는데, 객체형은 여러 타입의 데이터를 담을 수 있다. 배열이나 딕셔너리 같은 것들이 객체형 타입이다.

'key : value' 한쌍을 property라고 부르는데, 여기서 key에는 문자형 타입만 들어갈 수 있고, 'value'에는 모든 자료형이 들어갈 수 있다. -> 그렇다면, 어떤 key에 대응하는 값을 가져오려면 어떻게 해야할까? -> ex) a = {b = 3, c = 5} 라면 여기서 3과 5라는 값을 가져오려면, a.b, a.c 로 가져오거나 a['b'], a['c']로 총 두 가지 방식으로 가져올 수 있다.

객체를 생성하기 위한 두가지 방법:

1) let obj = new Object(); // 객체 생성자(new 키워드)로 만드는 방법.
2) let obj = {}; // 객체 리터럴로 만드는 방법. (중괄호로 객체를 선언하는 걸 리터럴이라고 한다. ++ 리터럴은 데이터 값 그 자체를 말하는 것인데, js에서 리터럴한다는 의미는 2번방법처럼 {} 값을 바로 줘서 생성하는 것을 의미한다.

const로 선언한 값, 즉 상수는 재할당이 안된다고 했지만, const로 선언한 객체형 타입, 즉 객체는 수정될 수 있다. (각각의 값들은 수정이 가능) -> ??? 왜요? const로 상수를 만들면 값은 변하지 못한다면서요? -> 이 질문에 대한 답은 바로 메모리상에서 객체형 타입의 변수를 어떻게 처리하는지를 살펴보면 된다.

메모리상에서 객체형 타입의 변수를 처리하는 방법:
-> 객체형은 메모리상에서 보았을 때, 일반적인 원시형 타입의 변수들과는 다르게 접근하는데, 원시형으로 어떤 값을 설정했을 때, 메모리상에서는 그 값 자체가 올라가지만, 객체 같은 경우엔 b로 객체를 선언하면, 실제 메모리상 b에는 참조할 수 있는 주소값이 들어가는 것이지, 실제 해당 객체, 예를 들어 배열이라고 한다면 배열 데이터 자체가 b라는 곳에 들어가는 것이 아니다. -> 이것을 '객체에 대한 참조가 들어간다'라고 말한다. 즉 해당 객체에 들어있는 데이터값이 변하더라도, 실제 b라는 변수에는 해당 객체를 참조할 수 있는 주소값이 들어있기 때문에 주소값은 변하지 않는것이다. -> 배열의 데이터가 변해도, b의 값은 주소이기 때문에 변하지 않기 때문에, 상수 const로 b를 선언했어도, 객체 자체는 수정을 할 수가 있는 것 이다.

-> 위의 상황과 같은 경우를 보고 "객체의 프로퍼티는 보호되지 않는다" 라고 표현하기도 한다.

2021-07-03

"함수는 호출이 가능한 어떤 행동을 할 수 있는 객체" 정도로 이해하면 좋다. 즉, 어떤 '값'인데 행동이 되는, 즉 동작하는 값이다.

함수를 만드는 두 가지 방법:
1) 함수 선언문 //
2) 함수 표현식 // ++ 화살표 함수(애로우 펑션)
이 두가지 방법의 차이점은 '독립된 구문이 존재를 하느냐', '표현식의 일부로 존재를 하느냐' 정도의 차이다. -> 함수 선언식은 독립된 구문으로 존재를 한다.
ex)

function cat() {
	console.log('perl');
} 
let cat = function() {
	console.log('perl');	
}

let cat2 = () => {
	console.log('perl2');
}

(참고로, 화살표 함수는 함수 표현식의 단축형이다.)
-> 함수 선언문 방식으로 함수를 선언하면, 함수가 바로 초기화가 되버린다. -> 즉, 바로 메모리상에 올라간다. 함수를 선언한 것 보다 훨씬 위에서 이 함수를 호출해도, 에러나지 않고 동작한다.
-> 반면에 함수 표현식으로 선언한 함수는 바로 초기화가 되지 않는다. -> 실행 컨텍스트가 한줄 한줄 읽으면서 내려가다가, 위 예제에 'let cat = function() {' 이 구문을 만나면 그때 함수가 초기화 된다. (만들어진다)

화살표 함수는 생성자와 generator로 못 쓴다. (this 키워드 사용할 때 차이점도 존재하는데, 예를 들어 함수 표현식에서의 this는 함수 그 자체를 가리키는데, 화살표 함수에서의 this는 부모를 가리킨다.)

++ 사실 함수를 생성하는 방법은 한 가지가 더 있는데, 바로 생성자를 이용하는 방식이다. ~ = new Function("함수에 들어갈 내용") 는 구조로 만드는 방식인데, 이 방식도 var를 통한 변수 생성 방식처럼 되도록 사용하지 말자. 이런 방식은 문제가 생길 요소가 다분하기 때문이다.

자바스크립트는 로우레벨에서 어떻게 동작하는가

함수는 자기가 태어난 환경을 property에 저장을 한다. -> 프로퍼티는 속성인데, 자기가 어디서 태어났는지를 속성에 저장을 해둔다. 이걸 이론상으로 "함수는 어떤 프로퍼티에 자기가 만들어질 당시의 렉시컬 환경을 참조한다"라고 말한다.

이 생성자를 사용해서 함수를 만들면, [[Environment]], 이 프로퍼티 즉 인바이러먼트 속성에 저장이 되는데, 쉽게 말해서 이 함수의 위치를 저장한다라고 생각해도 좋다. a라는 함수 안에 b라는 함수를 생성자를 통해 만들었다고 가정해보자, b의 위치는 이론상 a의 위치를 속성에 기록해두어야 한다. 하지만, b는 생성자를 통해 함수를 만들었기 때문에 전역 렉시컬 참조를 하게 된다. 그렇다면 이게 왜 문제가 되는걸까?

아까의 가정에서, 이번엔 c라는 변수에 1이라는 값을 주고 a라는 함수 내에 선언했다고 가정해보자. b라는 함수의 소괄호 내부에 console.log(c)를 찍는다면, 원하는대로 1이라는 결과값이 잘 출력될까? 답은 No이다. 생성자를 통해 만들어진 b는 전역 렉시컬 참조를 하기 때문에, a라는 함수 내에서 선언된 c라는 변수는 함수 스코프를 가지기 때문에, b에서는 c의 값을 참조할 수 없기 때문이다. 이렇게 실행을 시켜보면, refence 에러로 c가 선언되지 않았다고 에러 메시지가 나오게 된다.

정리하자면, 전역 렉시컬 참조를 하게 되면 스코프 내의 함수는 참조할 수가 없게 되고, 전역 변수에만 접근을 할 수 있게 되므로, 외부 변수를 받아다 쓰는데 문제가 생길 가능성이 다분해지고, 그렇기 때문에 생성자를 통한 함수 생성은 좋은 방법이라고 할 수 없다는 것 이다.

전역변수, 내부 변수, 그리고 스코프 이야기..

외부 변수, 내부 변수는 상대적인 이야기다. 예를 들어 a라는 함수를 기준으로 보았을 때는 외부 변수를 참조해서 가져가 쓸 수 있기 때문에, 전역 변수이거나 a 밖에서 선언된 변수값을 가져다 쓸 수 있지만, b라는 함수가 a라는 함수 내부에서 선언되었다고 가정했을 때, b 내부에서 선언된, 즉 b라는 함수 내부의 스코프를 가지고 있는 변수에는 a에서 접근할 수가 없다. ++모든 함수는 외부 변수를 참조해서 가져다 쓰는데 자유롭다.

또한, 내부 변수와 외부 변수가 같은 함수명으로 존재한다면, 내부 변수의 우선 순위가 높기 때문에, b라는 함수 내에서 console.log를 통해 외부에서 선언된 a = 1과, 내부에서 선언된 a = 2 라는 값이 있다면 2가 출력되는 것이고, 이건 중복으로 덮어씌워진 값이 아닌 전혀 다른 변수 a = 2 의 데이터 값이 출력되는 것이다.

콜백 함수

자바스크립트에서는 함수를 특별한 값으로 취급한다. -> 자바스크립트에서는 함수를 인수로 전달할 수도 있는데, 이때 매개변수 즉 파라미터 값으로 전달되서 호출되는 함수를 콜백함수라고 부른다.

a라는 함수가 인수, 즉 파라미터 값으로 어떤 사람의 이름을 받아서 그 이름을 출력해준다고 가정해보자. b라는 함수는 파라미터 두개를 각각 문자열과, 함수를 받아서 해당 함수에 문자열을 매개변수값으로 넘겨주는 형태라고 해보면, b라는 함수에 b(하영, a) 로 미리 선언해둔 a라는 함수를 두번째 인수로 호출해주어보자. 그럼 하영이라는 결과값이 출력될 것 이다. -> 여기서 a는 콜백함수인 것 이다.

매개변수와 인수는 같은개념?

매개변수와 인수는 명확하게 말하면 다른 친구들이다. 개념이 다르다는 의미인데, 조금 자세하게 살펴보자면..

객체는 메모리에 저장이 된다. -> 그럼, 값이 수정이 된다면? 즉, 함수를 매개변수로 다른 함수에 넘겨주었는데, 다른 함수에서 수행하다가 데이터가 바뀌어버리면 넘겨받은 원래 함수 값은 어떻게 되는거지..? -> 그래서 함수는 기본적으로 값을 넘겨받을때 복사를 해버린다. 값을 복사를 해서 함수내에서 사용하고 버려버린다. 즉, 매개변수는 "인수를 복사한 값"이라는 것이다. 그렇기 때문에, 다른 함수로 넘겨받아 복사한 값인 매개변수를 아무리 타 함수에서 데이터를 바꾸고, 수정해도 원본 값이 손상되거나 변경되지는 않는 것 이다.

++ 인수는 영어로 argument, 매개변수는 영어로 parameter라고 부른다.

프로토타입

자바스크립트는 동적언어로써, 클래스가 없는 언어이다.
-> 그렇다면, 리액트에서 쓰는 클래스형 컴포넌트는..? -> 이건 자바스크립트 ES6부터 지원하는 새로운 키워드로써의 '클래스'이다. 즉, 자바스크립트가 그냥 클래스형 언어를 흉내낸 것일 뿐, 근본적인 클래스 기반 언어의 클래스개념과는 엄연히 다르다.

그럼 자바스크립트에서는 객체를 뭘로 만들지?

1) 리터럴을 통해 만든다.
-> 하지만, 컴퓨터 엔진에서는 사실 리터럴을 통해 객체를 생성했다고 해도, 인식하기에는 이 녀석을 생성자로 변환을 해서 인식하게 된다.

2) 생성자 함수를 통해 만든다.

즉, 결론적으로 자바스크립트에서는 객체를 함수(생성자)를 통해서 만든다.

클래스 기반 언어:
객체를 생성하기 위해, 클래스를 생성하고 - 이를 통해 객체(인스턴스)를 생성.

자바스크립트:
클래스 없이 객체를 생성(리터럴, 생성자).

그렇다면, 자바스크립트의 진짜 정체는 프로토타입 기반 동작 언어인데, 여기서 프로토타입이란?

사실 프로토타입은 디자인패턴중 하나이다. 자바스크립트에서만 사용하는 개념이 아니고, 다른 언어에서도 쓰는 디자인 패턴이다.

이 패턴은 객체 생성을 할때 들어가는 리소스를 최대한 절감하려는데 집중하는 패턴이다.

이 패턴(프로토타입)의 동작원리는 원본 객체가 있고, 애를 복사를 해서 새로운 객체를 만들어내는것 이다. 패턴 자체는 굉장히 단순하지만, 자바스크립트랑 엮어서 생각하다보니 약간은 복잡한 구조로 느껴질 수 있다.

명심할 두 가지:

1) 객체는 함수의 원본을 복사한 복사본을 통해서 만들어진다.

객체는 함수를 통해서 만들어진다. 그런데, 자바스크립트는 프로토타입 기반이다. -> 즉, 자바스크립트에 모든 객체는 프로토타입(원본)을 전부 가지고 있다. 왜? -> (이유는 상속받는 등 이후의 처리를 위해 다 가지고 있는 것)

모든 객체는 함수를 통해서 만들어지지만, 정확히 보면 여기서 말하는 함수의 원형(프로토타입)을 "복사"해서 그 복사본을 가지고 객체를 만드는 것 이다.

2) 모든 객체는 자신이 어디로부터 복사가 되었는지 알고 있다.(렉시컬 개념)

자신이 어디서 복사가 되었는지에 대한 정보를 모든 객체는 가지고 있다.

모든 객체는 자신의 부모와 연결되어 있다. -> 엄밀히 말하면 부모의 프로토타입과 연결이 되어 있는 것. 이 연결되어 있는 것을 링크라고 부르는데, 부모 객체를 참조하는 것을 두고 프로토타입 링크라고 부른다.

부모가 메소드를 다섯개 정도 가지고 있다고 가정해보자, 자식은 부모가 가지고 있는 메소드를 상속 받아 사용할 수 있는데, 이것도 엄밀히 말하면 그때그때 부모가 가진 메소드가 필요할 때 마다 링크를 타고 올라가서 실행시킨다. 때문에, 엄밀히 말하면 본인이 가지고 있는 건 아니다. -> 때문에, 상속이라기 보단 "위임"에 가까운 개념이다.

모든 객체를 선언할 때 해당 객체의 원형인 프로토타입 객체도 같이 생성된다.
프로토타입 링크는 부모의 부모의 부모까지 쭈욱 연결되어 있는 형태인데, 이렇게 쭈욱 연결된 것을 프로토타입 체인이라고 부른다.

프로토타입은 "자바스크립트가 어떻게든 객체를 저렴하게 리소스 낭비 없이 생성해보려는 노력이다"정도로 이해하면 좋다.

profile
RM_young

0개의 댓글