Koans: 불교에서 유래된 단어로, 결론을 내리기 전에 이게 왜 맞는지 깊게 고민한다는 의미
오늘은 koans 과제를 통해 지금까지 배운 것들을 정리하고 되새기는 시간을 가졌다.
풀면서 헷갈리거나 다시 한번 짚고 넘어갈 필요가 있는 것들을 정리해 본다.
1. 암묵적 타입 변환
expect(123 - "1").to.equal(122);
산술 연산자는 피연산자 중 숫자 타입이 아닌 것을 숫자 타입으로 암묵적 타입 변환한다.
expect("1" + true).to.equal(Fill_Me_In); //"1true"
그러나 + 연산자는 산술 연산자로도 쓰이지만 피연산자 중 하나 이상이 문자열인 경우 문자열 연결 연산자로도 쓰인다.
위 경우 "1"이 문자열이므로 불리언 true가 문자열로 암묵적 타입 변환돼 "1"과 연결되었다.
2. 함수 호이스팅
let funcExpressed = "to be a function";
expect(typeof funcDeclared).to.equal(Fill_Me_In); //"function"
expect(typeof funcExpressed).to.equal(Fill_Me_In); //"string"
function funcDeclared() {
return "this is a function declaration";
}
funcExpressed = function () {
return "this is a function expression";
};
자바스크립트에서 함수도 호이스팅 대상이다.
그러나 함수 표현식으로 선언된 함수는 호이스팅 대상이 아니다.
참고로 함수는 객체지만, typeof를 했을 때 함수는 'function'으로 표기된다.
3. 전달인자에 값을 재할당
let message = "Outer";
function shadowParameter(message) {
message = "Do not use parameters like this!";
return message;
}
expect(shadowParameter("Parameter")).to.equal(Fill_Me_In); //"Do not use parameters like this!"
이 문제가 좀 아리송해서 검색을 해봤다. 도대체 여기서 전달인자로 입력된 "Parameter"는 어디로 가버리는 것인지?
이 글을 읽고 나서 전달인자로 문자열이 사용됐기 때문에 어색하게 느꼈던 것이지,
함수에서 숫자를 전달인자로 받아 그 값을 재할당하는 일은 자주 있다는 것을 떠올리게 되었다.
그러나 위 글에서 경고하듯 전달인자를 객체로 받았을 때는 함수 안에서 변경하는 것에 주의해야 한다.
객체의 참조 값이 전달되기 때문에 함수 안에서의 변경이 원본 객체에도 영향을 미치기 때문이다.
4. 클로저
let age = 27;
let name = "jin";
let height = 179;
function outerFn() {
//외부 함수에서 새로운 변수 age를 선언
let age = 24;
//전역 변수 name을 재할당
name = "jimin";
//외부 함수에서 새로운 변수 height를 선언
let height = 178;
function innerFn() {
//외부 함수에서 선언된 age를 재할당
age = 26;
//내부 함수에서 새로운 변수 name을 선언
let name = "suga";
//외부 로컬 변수 height를 리턴
return height;
}
//내부 함수를 실행
innerFn();
//현재 스코프는 외부 함수. 따라서 외부 함수에서 선언되었고 내부 함수에서 재할당된 age값이 되어야 함.
expect(age).to.equal(Fill_Me_In); //26
//내부 함수에서 선언된 새로운 변수 name이 아닌 전역에서 선언되었고 외부 함수에서 재할당된 name값.
expect(name).to.equal(Fill_Me_In); //"jimin"
//외부 함수 밖으로 내부 함수를 리턴해 클로저를 생성.
return innerFn;
}
//외부 함수가 호출돼 내부 함수가 리턴돼 새로운 변수 innerFn에 할당.
const innerFn = outerFn();
//현재 스코프는 전역이므로 전역에서 선언된 age는 전역에서 할당된 값을 가짐.
expect(age).to.equal(Fill_Me_In); //27
//name도 전역에서 선언된 값을 가리키나 외부 함수가 실행될 때 재할당되었음.
expect(name).to.equal(Fill_Me_In); //"jimin"
//새로 정의된 innerFn의 리턴값은 전역에서 선언된 height가 아닌 외부 로컬 변수이자 클로저에 의해 기억된 height 값.
expect(innerFn()).to.equal(Fill_Me_In); //178
5. 원시 자료형과 참조 자료형
let currentYear = 2020;
function afterTenYears(year) {
year = year + 10;
}
afterTenYears(currentYear);
expect(currentYear).to.equal(2020);
원시 자료형 또는 원시 자료형의 데이터를 함수의 전달인자로 전달할 경우, 값 자체의 복사가 일어난다.
따라서 afterTenYears 함수의 실행 시점에 재할당된 currentYear은
처음 선언된 currentYear이 복사된 별개의 값이고 그 변화가 원래 값에 영향을 주지 않는다.
const person = {
son: {age: 9}
};
const boy = person.son;
boy.age = 20;
expect(person.son.age).to.equal(Fill_Me_In); //20
expect(person.son === boy).to.equal(Fill_Me_In); //true
참조 자료형을 변수에 할당할 경우, 데이터의 주소가 저장된다.
즉, boy와 person.son은 같은 주소값을 담고 있고, 참조하는 객체는 동일하다.
한 변수를 통해 프로퍼티를 변경하면 다른 변수에서 참조할 때도 변경된 값이 적용된다.
6. 얕은 복사와 깊은 복사
const obj = {
mastermind: "Joker",
henchwoman: "Harley",
relations: ["Anarky", "Duela Dent", "Lucy"],
twins: {
"Jared Leto": "Suicide Squad",
"Joaquin Phoenix": "Joker",
"Heath Ledger": "The Dark Knight",
"Jack Nicholson": "Tim Burton Batman",
},
};
//새로운 변수에 기존 객체를 할당하면 같은 주소를 참조한다.
const assignedObj = obj;
assignedObj["relations"] = [1, 2, 3];
expect(obj["relations"]).to.deep.equal(Fill_Me_In); //[1, 2, 3]
//Object.assign을 사용하면 다른 주소값을 가진 객체가 복사된다.
const copiedObj = Object.assign({}, obj);
copiedObj.mastermind = "James Wood";
expect(obj.mastermind).to.equal(Fill_Me_In); //"Joker"
//그러나 얕은 복사만 이뤄졌기 때문에 중첩된 객체는 같은 주소를 참조한다.
delete obj.twins["Jared Leto"];
expect("Jared Leto" in copiedObj.twins).to.equal(Fill_Me_In); //false
const fullPre = {
cohort: 7,
duration: 4,
mentor: "hongsik",
};
const me = {
time: "0156",
status: "sleepy",
todos: ["coplit", "koans"],
};
const merged = { ...fullPre, ...me };
// 변수 'merged'에 할당된 것은 'obj1'과 'obj2'의 value
merged.cohort = 0;
console.log(fullPre.cohort) //7
// 얕은 복사가 수행됨
console.log(merged.todos === me.todos) //true
7. arguments
function getAllParamsByArgumentsObj() {
return arguments;
}
const argumentsObj = getAllParamsByArgumentsObj("first", "second", "third");
expect(Object.keys(argumentsObj)).to.deep.equal(["0", "1", "2"]);
expect(Object.values(argumentsObj)).to.deep.equal(["first", "second", "third"])
함수에는 숨겨진 '유사 배열' arguments가 있다.
이 배열은 유사 배열로, 실제로는 배열처럼 사용할 수 있는 객체다.
위 예시에서 argumentObj의 내용을 들여다 보면 다음과 같다.
Array.from()을 통해 유사 배열을 배열로 가져올 수 있다.
const argsArr = Array.from(argumentsObj);
expect(argsArr).to.deep.equal(["first", "second", "third"]);
8. rest 파라미터
function getAllParams(required1, required2, ...args) {
return [required1, required2, args];
}
expect(getAllParams(123)).to.deep.equal([123, undefined, []]);
rest 파라미터는 언제나 배열이다.
'프로젝트 > 미니 프로젝트 & 과제' 카테고리의 다른 글
[미니 프로젝트] 나만의 아고라스테이츠 만들기 (0) | 2023.03.11 |
---|---|
[과제] 유효성 검사 (0) | 2023.03.08 |
[과제] 계산기 기능 구현하기 (0) | 2023.02.23 |
[과제] 반복문으로 별찍기 (0) | 2023.02.22 |
[과제] 계산기 목업 만들기 (0) | 2023.02.17 |