💡 이 포스트에는 Unit 10. javascript 핵심 개념과 주요 문법과 관련된 코딩과제를 하고 몰랐던 점이나 부족했던 점을 정리했다!
(누군가에게는 너무 쉬워 하품이 나올 수 있습니다.🥲)
이번에는 코딩 연습문제가 아니라 주석으로 쓰여진 요구사항을 해결하며 스크립트 파일을 작성하고 테스트를 통과하는 과제를 했다.
koans는 불교(선종)에서 깨달음을 목적으로 하는 선문답을 의미한다.
깨달음을 얻기 위해 질문하고 답하는 것처럼 이제까지 배웠던 javascript의 개념을 직접 답하며 깨달아가는 과제이다.
이 과제를 해결하며 헷갈리거나 새로 알게된 것들을 정리해 보았다.
핵심 : console.log()를 활용해 결과를 확실히 확인해본다.
직접 확인해보면 기억에도 남고 좋다.
let a=1+'0'; //숫자랑 문자를 더하면 문자
let b=100-'1'; //숫자를 문자를 빼면 숫자계산을 한다.
let c=0+true; //숫자에 true를 더하면 true를 숫자1로 간주한다.
let d=0+false; //숫자에 false를 더하면 false를 숫자0으로 간주한다.
let e='1'+true; //문자열에 불린값을 더하면 그대로 문자열을 반환한다.
let f='1'+false;
console.log(a); //'10'
console.log(b); //99
console.log(c); //1
console.log(d); //0
console.log(e); //'1true'
console.log(f); //'1false'
아래의 링크로 들어가보면 온갖 이상한(?) 값을 확인해볼 수 있다.
https://github.com/denysdovhan/wtfjs
핵심 : 함수 선언식과 함수 표현식의 차이를 확실히 알고 호이스팅에 대해 알아본다.
호이스팅에 대해 알기 전에 함수 선언식과 함수 표현식에 대해 확실하게 알아야 한다.
함수 선언은 지정된 매개변수(parameter)를 갖는 함수를 정의하며 함수 이름이 필요하다.
함수 표현식은 표현식 내에서 함수를 정의하는 것으로 함수 이름을 생략할 수 있다.
함수 선언과 함수 표현식은 매우 유사한 생김새를 가졌으나 함수 이름의 생략 여부에 따라 큰 차이가 있다.
//함수 선언. 함수 이름은 myFunc1
fucntion myFunc1(){
return '나는 함수 선언';
}
//함수 표현식. 함수 이름 생략됨(익명 함수)
let myFunc2=function(){
return '나는 함수 표현식';
}
그리고 가장 큰 차이점은 호이스팅 여부이다.
호이스팅(hoisting)이란, 변수와 함수의 메모리 공간이 선언 전에 미리 할당되는 것을 뜻한다. 때문에 함수는 코드를 실행하기 전 함수 선언에 대한 메모리부터 할당하기에 함수 선언은 먼저 저장된다.
따라서 함수를 호출하는 코드를 함수 선언보다 앞서 배치할 수 있다.
반면 함수 표현식은 호이스팅 되지 않는다.
myFunc1(10); //10
myFunc2(20); //Uncaught ReferenceError
function myFunc1(value){
return `함수 선언, 값: ${value}`;
}
let myFunc2=function(value){
return `함수 표현식, 값: ${value}`;
}
참고:
javascript MDN - 함수 선언
javascript MDN - 함수 표현식
javascript MDN - 호이스팅
핵심 : 아무런 인자를 받지 못했을 때 매개변수도 기본값을 설정할 수 있다.
함수가 실행될 때 매개변수를 받아야 하지만 인자 값이 없거나 undefined가 전달될 경우, 매개변수에 설정된 기본값을 부여할 수 있다.
만약 인자를 재대로 전달했을 경우 전달된 인자를 우선 매개변수에 할당한다.
function myFunc(name='John Doe'){ //기본값은 'John Doe'
return name;
}
myFunc();//'John Doe' -> 전달인자가 없어서 기본값 출력
myFunc('Jane Doe');//'Jane Doe' -> 전달인자 출력
핵심 : 화살표 함수 자체를 이해하기보다 주요 특징을 알아본다.
// function 키워드를 생략하고 화살표 => 를 붙입니다
const add = (x, y) => {
return x + y
}
expect(add(10, 20)).to.eql(30)
// 리턴을 생략할 수 있습니다
const subtract = (x, y) => x - y
expect(subtract(10, 20)).to.eql(-10)
// 필요에 따라 소괄호를 붙일 수도 있습니다
const multiply = (x, y) => (x * y)
expect(multiply(10, 20)).to.eql(200)
// 파라미터가 하나일 경우 소괄호 생략이 가능합니다
const divideBy10 = x => x / 10
expect(divideBy10(100)).to.eql(10)
저번에 정리하긴 했으나 (포스트 -- 참고) 매번 헷갈리므로 다시 정리해보았다.
핵심 : 배열.slice(strat, end)는 배열의 start를 포함하여 end를 미포함하여 end이전까지 배열로 리턴한다.
기본 원리를 알고 있다면 나머지는 이해하기 어렵지 않다.
그러나 여러 용도로 활용하면 헷갈리므로 한번에 정리해보았다.
임의의 배열 arr=[1,2,3,4]이 있다고 가정하자.
핵심 : 전달인자>매개변수면 전달인자 순서대로 매개변수에 적용이 되고, 전달인자<매개변수면 매개변수에 자동으로 undefined가 할당이 된다.
핵심 : 매개변수가 전달인자를 받아 할당할 때 참조자료형은 주소를 복사한다.(즉 매개변수로 값을 바꾸면 원본도 바뀐다.)
this는 현재 요소가 속한 객체를 나타낸다.
객체 내에서 this를 사용하면 이 때의 this는 그 객체이다.
전역에서 생성한 객체의 this는 window 객체로 이는 생략 가능하다.
그리고 this는 그 메서드를 호출할 때의 시점을 기준으로 정해진다.
향후 this에 대해서는 좀 더 배울 예정이다.
원시 자료형은 변수에 값 자체를 보관하며
참조 자료형은 변수에 그 값이 있는 주소를 보관한다.
때문에 원시 자료형을 복사하며 그 값 자체가 복사되지만
참조 자료형 변수를 복사하면 주소가 복사된다.
만약 복사한 새로운 변수의 값을 변경하면 원본(처음 주소가 담긴 변수)도 변경된다. 다른 것 같지만 같은 주소이기 때문이다.
이렇게 값 자체가 복사되는 것을 깊은 복사, 주소만 복사되는 것을 얕은 복사라고 한다.
그리고 만약 배열이나 객체를 복사할 때 배열과 객체를 복사한 것 같아도
배열, 객체 안의 원시자료형의 값은 복사되지만
배역, 객체 안의 또다른 참조자료형은 주소만 복사된다. 이것도 얕은 복사이다.
또한 전개 구문으로 배열, 객체를 복사하는 것도 얕은 복사이다.
let arr1=[1,2,3];
let arr2=arr1;
arr2.push(4);
console.log(arr1); //[1,2,3,4]
//-> arr2에 주소가 복사되었기에 원본인 arr1도 바뀐다.
let arr3=['a','b','c',['d','e']];
let arr4=Array.from(arr3); //배열을 얕게 복사하는 메서드
arr4.push('f');
console.log(arr3); //['a','b','c',['d','e']]
console.log(arr4); //['a','b','c',['d','e'], 'f']
//-> 원시자료형의 값은 복사되었기에 원본이 변경되지 않았다.
arr4[3].push('f');
console.log(arr3); //['a','b','c',['d','e','f']]
console.log(arr4); //['a','b','c',['d','e','f'], 'f']
// -> 복사된 배열 안의 배열 ['d', 'e']은 주소가 복사되어서 원본도 같이 변경된 것을 확인할 수 있다!
깊게 복사하기 위해서는 참고 링크의 내용을 확인하면 어느 정도 해결이 가능하나 나중에 좀 더 자세히 학습할 예정이다.
참고 링크: javascript MDN - Objcet.assign()-깊은 복사 주의점
핵심 : 나머지 매개변수는 배열의 형태로 인자를 저장하지만, arguments는 객체 형태로 인자를 저장한다.
arguments는 브라우저의 콘솔로 확인해보면 인자 외에 여러 값을 객체로 저장한다.
그런데 Object.keys()를 사용해 키 값을 배열로 확인해보면
인자의 갯수만큼만 키를 반환하고,
Object.values()를 사용해 키 값을 확인해보면 인자만 있다.
function checkRest(...args){
return args
}
function checkArgsObj(){
return arguments
}
let resultRest=checkRest(1,2,3); //[1,2,3]
let resultArgsObj=checkArgsObj(1,2,3);
//[1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
console.log(typeof resultRest); //'object'
console.log(typeof resultArgsObj); //'object'
console.log(Array.isArray(resultRest)); //true
console.log(Array.isArray(resultArgsObj)); //false
핵심 : 구조 분해 할당은 이름 그대로 구조를 분해하여 할당하는 것을 의미한다.
만약 배열이나 객체의 값 하나하나를 변수로 할당해야 할 때 여러 줄로 일일이 작성하면 비효율적일 수 있다.
이를 효율적으로 처리할 때 구조 분해 할당을 사용하면 편리해진다.
//배열 값을 각 변수에 한 번에 할당할 수 있다.
let pizza=['cheese', 'pepperoni', 'shrimp', 'combination'];
let [menu1, menu2, menu3, menu4]=pizza;
console.log(menu1); //'cheese'
console.log(menu2); //'pepperoni'
console.log(menu3); //'shrimp'
console.log(menu4); //'combination'
//인자를 객체로,매개변수에 키를 입력하면 자동으로 분해되어 할당된다.
let person={name:'코난',job:'탐정'};
function introduce({name,job}){
return `내 이름은 ${name}, ${job}이죠.`;
}
introduce(person);//'내 이름은 코난, 탐정이죠.'
//전개 문법, 나머지 매개변수와도 함께 사용할 수 있다.
//다만, 순서대로 할당된다.
let fruitsBox={
apple:1,
banana:5,
shineMusket:1,
orange:1
}
let appleBoyCart={
...fruitsBox,
apple:10, //apple:10 재할당
banana:1 //banana:1 재할당
}
let bananaGirlCart={
apple:4,
...fruitsBox, //apple:1 재할당
banana:10 //banana:10 재할당
}
console.log(appleBoyCart);
//{apple: 10, banana: 1, shineMusket: 1, orange: 1}
console.log(bananaGirlCart);
//{apple: 1, banana: 10, shineMusket: 1, orange: 1}
이제까지 배운 내용 + 추가로 알게 된 내용을 한 번에 정리해보는 시간이었다.
페어와 함께 차근차근 문제를 풀어보면서 모르는 내용을 그때그때 정리했고
배우지 않았던 내용은 직접 검색하며 찾아보았다.
확실히 한 번에 정리하다보니 알고있던 것도 헷갈려서 다시 공부할 수 있었고,
모르는 내용은 다음에 유용하게 쓸 수 있게 잠깐 정리할 수 있었다.
몰랐던 내용은 이제 확실히 잘 안다고 할 수는 없지만, 전부 다음 학습에서 배운다고 하니 그때가서 좀 더 이해되지 않을까?
오늘은 위의 부족한 점을 보완하기 위해 아래와 같이 공부했다!
과제는 다행히 제시간에 제출했지만
공부량은 많아서 오늘도 이렇게 나머지 공부를 한다.
피곤하지만 괜찮다! 확실히 공부하는 좋은 기회였다.
오늘도 이렇게 돌다리를 두드리며 넘어가고
머릿속 지식전구에 불을 하나 켜본다.
🤓