11-15일차 스터디 정리본
한 주간 배운 것들: 배열/객체/원시자료형과 참조자료형/스코프/클로저/spread rest 문법 /구조분해/ 화살표함수
Mocha Chai framework가 그래서 뭔데?
모카Mocha란? 테스트 러너를 지원하는 테스트 프레임워크다.
단순한 JS 개발 상황에서는 테스트가 별로 필요하지 않았지만, 이후 여러모로 테스트의 필요성이 생겨났다. 따라서 테스트하기 쉽게 지원하는 모카가 등장하였다. Node.js에서 지원하며 npm install mocha -g 로 설치 가능하다.
여기서 Chai 구문이 등장하는데, Mocha 기반 테스트 코드를 작성할 때, 여러 assertion(변수가 원하는 값을 갖고 있는지 확인하는 것!)을 제공하는 모듈module이다. 이 또한 npm을 이용해 설치할 수 있다. 다양한 인터페이스를 제공하지만 그 중에 expect를 사용하면 한 문장으로 보기 좋게 테스트를 작성할 수 있어 직관적이며 깔끔한 느낌을 준다.
ex)
it("Matcher .equal 의 사용법을 학습합니다.", function () {
let expectedValue = 1 + 1; // TODO
// .equal은 두 값이 타입까지 엄격하게 같은지 검사(strict equality, ===)합니다.
expect(1 + 1).to.equal(expectedValue);
//위와 같이 직관적으로 to와 equal을 사용하여 직관적으로 같음을 표현한다.
});
equal vs deep.equal vs eql in Chai
to.equal()은 type까지 고려하여 비교하는 연산 === 와 같다. 테스트 값이 기대 값과 같은지 확인하는 matcher다.
to.deep.equal()는 배열의 요소, 객체의 속성 같은 것들을 비교할 때 확인하는 matcher다.
to.eql()은 to.deep.equal과 유사하게, 배열 요소나 객체 속성 같은 것들을 비교할 때 확인하는 matcher다. 원시형 자료처럼 두 객체가 같은지 여부를 판단한다.
가르쳐주지도 않았는데 테스트 내부에서 나온 세 가지 개념이 궁금해서 영어자료도 한국어 자료도 뒤져봤다. 아직도 eql과 deep.equal의 완벽한 차이는 잘 모르겠지만 일단...
- TDZ에서 시작해서 var, let, const에서 발생하는 호이스팅까지
설명에 앞서 알아야 할 것이 있다. JS내부에서 변수가 생성되는 과정이다.
JS에서는 총 3단계에 걸쳐서 변수를 생성한다.
선언(Declaration): 스코프와 변수 객체가 생성되고, 스코프가 변수 객체를 참조.
초기화(Initialization): 변수 객체 값을 위한 공간을 메모리에 만든다. 이때 할당된 메모리는 undefined로 초기화된다.
할당(Assignment): undefined로 초기화된 메모리에 값을 할당하는 단계
또한 선언이 어디에 되었건 상관없이, 다른 코드보다 먼저 실행되는 현상을 호이스팅이라고 부른다. JS엔진이 코드를 한줄씩 순차적으로 실행하기 전에 모든 선언문을 찾아내어 이것들부터 먼저 실행하기 때문에 발생한다.
var는 선언과 동시에 초기화가 이루어진다, 즉 'undefined'가 할당되는 것. 따라서 변수선언문 이전에 변수에 접근해도 에러 없이 undefined를 반환받게 된다. 변수가 존재하긴 하는 것이다.
하지만 let로 선언된 변수는 선언과 초기화가 동시에 이루어지지 않고 분리 진행된다. 선언은 되어 있지만, 초기화는 변수 선언문에 도달한 순간(코드 실행 후)이루어지는 것. 바로 이 시점에서 값이 TDZ에 들어가 있게 된다. 초기화가 되기 전에 변수에 접근하면 참조 에러Refference Error가 발생한다. 변수를 위한 메모리 공간이 준비되지 않은 상태라는 의미로...
따라서 스코프의 시작 지점부터 초기화 시작까지는 변수를 참조할 수 없다! -> 여기서 스코프의 시작 지점{}부터 초기화 시작 지점까지의 구간을 ‘일시적 사각지대 (Temporal Dead Zone; TDZ)’라고 부른다.
const는 반드시 선언+초기화+할당까지 동시에 이루어져야 한다.(const age; 같이 선언만 할 시에 바로 SyntaxError가 발생).
사실 호이스팅은 진짜로 뭔가가 끌어 올려지는 것이 아니라, 변수나 함수를 선언 전에 사용할 수 있기 때문에 끌어 올려지듯 보이는 것이다. var 와 달리 const, let 은 호이스팅이 발생하지 않듯이 보이는데, 값이 TDZ에 들어가기 때문이다.
그렇다면 TDZ란 무엇일까?
TDZ란 일시적 사각 지대를 말한다. Temporal Dead Zone. 변수 사용을 허용하지 않는 개념 상의 공간이며, 이 안에 있는 값에 함부로 접근할 시 참조 에러Refference Error가 발생한다. 이는 초기화되지 않은 바인딩에 접근할 경우 다른 결과를 내는 대신 에러를 표시해주기에 유용하다.
var는 변수를 생성하자마자 초기화가 이루어지며 즉시 메모리에 공간을 할당해준다. 하지만 var과는 달리 const나 let은 초기화되기 전까지 TDZ에 값이 머무르기 때문에 참조가 불가능하다. 실제로 호이스팅이 발생하지 않은 게 아니라, 참조가 불가능해서 호이스팅 되지 않은 것처럼 보인다. const와 let은 해당 코드의 위치만 의미하는 position값을 정해준다. 선언은 되어 있지만, 변수에 값을 담기위한 메모리에는 공간이 확보되어있지 않다. 이 시점에 생성된 변수들은 TDZ안에 머무른다. 즉, 선언은 되어 있되, 초기화 되지 않아 메모리에 공간이 할당되지 못한 상태기에 참조할 수 없다!
+)class선언문 또한 TDZ에 변수들이 들어가 있어서 마치 호이스팅이 발생하지 않는 것처럼 동작한다. 즉, 선언문 이전에 참조할 수 없다.
-> 그렇다면 class 선언문이 대체 뭔가?
클래스Class? => 객체를 정의하는 틀. 객체=쿠키, 클래스=쿠키틀.
class선언문은 ES6부터 신규로 도입된 '객체 설계 문법'을 말한다. class를 정의하는 한 가지 방법인데, 클래스 이름(대문자로 지정하는 것이 관례)과 함께 class 키워드를 사용한다.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
클래스가 호이스팅 될 때 초기화는 되지 않아서 정의한 뒤에 사용해야 참조 오류를 내지 않는다.(위와 같은 내용! TDZ때문.) 또한 class의 본문은 strict mode에서 실행되기에, 더 엄격한 문법이 적용되어서 오류를 방지한다.
https://wonjaetech.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C
결론적으로! const, let, class는 호이스팅이 발생하지 않는 것이 아니다. 선언문을 통해 모든 식별자(변수, 함수, 클래스 등)는 호이스팅된다. 그러나 var과 다르게 초기화되기 전까지 TDZ에 머물러있기 때문에 호이스팅이 발생하지 않는 것처럼 보이며 참조가 불가능하다.
- rest parameter vs arguments ?
rest parameter(나머지 매개변수)는 이름 앞에 ...을 붙인 매개변수를 말한다. 함수에 전달되는 인수argument들을 전부 배열 형태로 전달 받는다.
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
a; // 10
b; // 20
rest; // { c: 30, d: 40 }
function getAllParamsByArgumentsObj() {
return arguments;
}
반면, arguments는 함수에 전달되는 인수들을, key가 숫자이고 length 값을 가지고 있는 형태의 객체인 유사배열객체로 만들어준다.
유사배열 객체 array-like objects
- 표준 배열 메서드가 사용 가능하다.
- 배열처럼 생겼지만 객체이기에 배열에 쓰이는 배열 메서드 forEach나 map, filter 등을 사용할 수 없다. 따라서 Array.from() 메서드를 사용하여 유사배열객체 내부의 value를 얕게 복사한 뒤, 새 배열로 만들어준다.
이전까지는 (1)함수 내부에 arguments를 리턴시켜서 유사배열객체를 만들고, (2)이것을 다시 배열로 만들어 사용했다. 하지만 이런 과정 없이 바로 배열로 만들어주는 rest parameter가 등장하면서 이쪽을 쓰는 게 더 깔끔해졌다. 또 화살표 함수 안에서는 arguments를 사용할 수 없기에 여러모로 rest parameter를 사용하는게 권장된다.
- spread operator(전개구문) 와 rest parameter(나머지 매개변수)의 차이
spread operator은 객체, 배열 등의 요소를 하나씩 펼쳐주는 역할이며 ...을 붙여서 나타낸다. 반면 rest parameter은 매개변수 앞에 …를 붙이며 함수의 전달인자 목록을 배열로 다룰 수 있도록 한다.
따라서 ...을 사용하는 방법에 따라서 Spread Syntax가 될 수도 있고, Rest Syntax가 될 수도 있다는 걸 기억하자. 요소를 하나하나 전개시킬 때는 Spread, 나머지 요소를 묶을 때는 Rest라고 생각하면 편하다.
spread와 rest parameter을 혼동하지 않도록 유의한다. Rest 파라미터는 특성상 마지막 위치에만 올 수 있지만, spread operator는 전개하고 싶은 어디든 붙여 사용할 수 있다.
- Object in 연산자.
처음엔 chai에서 쓰는 건 줄 알았는데 JS 연산자였다.
명시된 속성이 명시된 객체에 존재하면 true를 반환한다. 보기에는 심플해보이지만 잘못 이해하고 실수하지 않기 위해서는 ‘속성’에 주목할 것.
const myDog = {name: "kira", type: "jindo", color:"white"}
console.log('name' in myDog); //true
//속성 name이 존재하기 때문에 true를 리턴한다.
console.log('kira' in myDog); //false
//kira가 속성이 되지 못하기 때문에 false를 리턴한다.
위는 객체에 적용한 경우이다. 아래의 배열을 살펴보자.
const name = ["Jenny", "Diva", "Rose"]
console.log(0 in name); //true
//배열에 0 인덱스가 존재하기 때문에 true를 리턴한다.
console.log(5 in name); //false
//배열에서 인덱스가 객체의 속성 같이 취급됨을 볼 수 있다.
console.log("Jenny" in name); //false
//Jenny는 인덱스가 아닌 배열 내용이므로 false를 리턴한다.
또한 delete 연산자로 제거되었다면 false를 리턴하지만, 속성을 undefined로 설정하고 제거는 하지 않으면 in 연산자는 true를 반환한다는 점을 주의해야 한다.
stack과 heap : 원시 자료형을 저장하는 곳/참조 자료형을 저장하는 곳
호이스팅(끌어올림): 함수는 실행되기 전에 함수 안에 필요한 변수값들을 모두 모아서 유효 범위의 최상단에 선언하는데, 이처럼 해당 스코프의 최상단으로 올려진 '것과 같은 현상'. 모든 선언에는 호이스팅이 일어난다. 실제로 끌어 올려진 것이 아니다. let, const, class를 이용한 선언문에서는 호이스팅이 발생하지 않는 것처럼 동작한다->그래도 호이스팅이 일어나긴 함.(Reference Error이 발생한다. var은 발생x)
파라미터: 매개변수parameter. 함수를 구현하는 쪽과 함수를 사용하는 쪽을 매개하는 변수.
syntactic sugar문법 설탕(편의 문법): 기능은 동일하나 기존 문법을 쉽게 읽을 수 있게 만든 문법(ex 화살표 함수, i++)
use strict: 엄격 모드.
원시값(원시 데이터): 객체가 아니면서 메서드도 가지지 않는 데이터. 원시 자료형이라고도 불린다. 7종류가 존재.