TIL 9일차 - Javascript의 기본 문법 요약본2

박찬웅·2023년 2월 14일
0

항해99

목록 보기
14/105

23년 2월 14일


자바스크립트 기본 문법 강의자료
주로 아래에 있는 요약을 복붙을 했습니다. 본인이 이해 못한 부분은 강의에 적힌 내용을 사진 스샷해서 코드 적은 것까지 반영했습니다. 코드 복사 하시고 싶은 분은 저기 있는 위에 링크 들어가서 참고하시면 됩니다.

1. 함수

함수 선언 방식으로 함수를 만들 수 있습니다.

function 함수이름(복수의, 매개변수는, 콤마로, 구분합니다) {
  /* 함수 본문 */
}
  • 함수에 전달된 매개변수는 복사된 후 함수의 지역변수가 됩니다.
    함수는 외부 변수에 접근할 수 있습니다. 하지만 함수 바깥에서 함수 내부의 지역변수에 접근하는 건 불가능합니다.
  • 함수는 값을 반환할 수 있습니다. 값을 반환하지 않는 경우는 반환 값이 undefined가 됩니다.
  • 깔끔하고 이해하기 쉬운 코드를 작성하려면 함수 내부에서 외부 변수를 사용하는 방법 대신 지역 변수와 매개변수를 활용하는 게 좋습니다.

개발자는 매개변수를 받아서 그 변수를 가지고 반환 값을 만들어 내는 함수를 더 쉽게 이해할 수 있습니다. 매개변수 없이 함수 내부에서 외부 변수를 수정해 반환 값을 만들어 내는 함수는 쉽게 이해하기 힘듭니다.

함수 이름을 지을 땐 아래와 같은 규칙을 따르는 것이 좋습니다.

  • 함수 이름은 함수가 어떤 동작을 하는지 설명할 수 있어야 합니다. 이렇게 이름을 지으면 함수 호출 코드만 보아도 해당 함수가 무엇을 하고 어떤 값을 반환할지 바로 알 수 있습니다.
  • 함수는 동작을 수행하기 때문에 이름이 주로 동사입니다.
  • create…, show…, get…, check… 등의 잘 알려진 접두어를 사용해 이름을 지을 수 있습니다. 접두어를 사용하면 함수 이름만 보고도 해당 함수가 어떤 동작을 하는지 파악할 수 있습니다.

2. 함수 표현식

let sayHi = function() {
  alert( "Hello" );
};
  • 함수는 값입니다. 따라서 함수도 값처럼 할당, 복사, 선언할 수 있습니다.
  • “함수 선언(문)” 방식으로 함수를 생성하면, 함수가 독립된 구문 형태로 존재하게 됩니다.
  • “함수 표현식” 방식으로 함수를 생성하면, 함수가 표현식의 일부로 존재하게 됩니다.
    함수 선언문은 코드 블록이 실행되기도 전에 처리됩니다. 따라서 블록 내 어디서든 활용 가능합니다.
  • 함수 표현식은 실행 흐름이 표현식에 다다랐을 때 만들어집니다.
  • 함수를 선언해야 한다면 함수가 선언되기 이전에도 함수를 활용할 수 있기 때문에, 함수 선언문 방식을 따르는 게 좋습니다. 함수 선언 방식은 코드를 유연하게 구성할 수 있도록 해주고, 가독성도 좋습니다.

결론 : 함수 표현식은 함수 선언문을 사용하는게 부적절할 때에 사용하는 것이 좋습니다.

3. 화살표 함수

화살표 함수는 본문이 한 줄인 함수를 작성할 때 유용합니다. 본문이 한 줄이 아니라면 다른 방법으로 화살표 함수를 작성해야 합니다.

let func = (arg1, arg2, ...argN) => expression
let func = function(arg1, arg2, ...argN) {
  return expression;
};

아래에 적힌 코드를 축약한 것이 위에 적은 코드를 말하며 이게 화살표 함수이다.
중괄호 없이 작성: (...args) => expression – 화살표 오른쪽에 표현식을 둡니다. 함수는 이 표현식을 평가하고, 평가 결과를 반환합니다.
중괄호와 함께 작성: (...args) => { body } – 본문이 여러 줄로 구성되었다면 중괄호를 사용해야 합니다. 다만, 이 경우는 반드시 return 지시자를 사용해 반환 값을 명기해 주어야 합니다.

4. 나머지 매개변수와 스프레드 문법

"..."은 나머지 매개변수나 스프레드 문법으로 사용할 수 있습니다.

나머지 매개변수와 스프레드 문법은 아래의 방법으로 구분할 수 있습니다.

function sumAll(...args) { // args는 배열의 이름입니다.
  let sum = 0;

  for (let arg of args) sum += arg;

  return sum;
}

alert( sumAll(1) ); // 1
alert( sumAll(1, 2) ); // 3
alert( sumAll(1, 2, 3) ); // 6

... 이 함수 매개변수의 끝에 있으면 인수 목록의 나머지를 배열로 모아주는 '나머지 매개변수’입니다.

let arr = [3, 5, 1];

alert( Math.max(...arr) ); // 5 (스프레드 문법이 배열을 인수 목록으로 바꿔주었습니다.)

... 이 함수 호출 시 사용되거나 기타 경우엔 배열을 목록으로 확장해주는 '스프레드 문법’입니다.

사용 패턴:

  • 인수 개수에 제한이 없는 함수를 만들 때 나머지 매개변수를 사용합니다.
  • 다수의 인수를 받는 함수에 배열을 전달할 때 스프레드 문법을 사용합니다.
  • 둘을 함께 사용하면 매개변수 목록과 배열 간 전환을 쉽게 할 수 있습니다.
function showName() {
  alert( arguments.length );
  alert( arguments[0] );
  alert( arguments[1] );

  // arguments는 이터러블 객체이기 때문에
  // for(let arg of arguments) alert(arg); 를 사용해 인수를 펼칠 수 있습니다.
}

// 2, Bora, Lee가 출력됨
showName("Bora", "Lee");

// 1, Bora, undefined가 출력됨(두 번째 인수는 없음)
showName("Bora");

조금 오래된 방법이긴 하지만 arguments라는 반복 가능한(이터러블) 유사 배열 객체를 사용해도 인수 모두를 사용할 수 있습니다. 다만 과거에만 사용하고 현재는 잘 사용 안하는 함수이다.

5. 객체

let user = new Object(); // '객체 생성자' 문법
let user = {};  // '객체 리터럴' 문법

객체는 몇 가지 특수한 기능을 가진 연관 배열(associative array)입니다.

let user = {     // 객체
  name: "John",  // 키: "name",  값: "John"
  age: 30        // 키: "age", 값: 30
};
// 프로퍼티 값 얻기
alert( user.name ); // John
alert( user.age ); // 30

객체는 프로퍼티(키-값 쌍)를 저장합니다.
프로퍼티 키는 문자열이나 심볼이어야 합니다. 보통은 문자열입니다.
값은 어떤 자료형도 가능합니다.
아래와 같은 방법을 사용하면 프로퍼티에 접근할 수 있습니다.

점 표기법: obj.property
대괄호 표기법 obj["property"]. 대괄호 표기법을 사용하면 obj[varWithKey]같이 변수에서 키를 가져올 수 있습니다.
객체엔 다음과 같은 추가 연산자를 사용할 수 있습니다.

  • 프로퍼티를 삭제하고 싶을 때: delete obj.prop
  • 해당 key를 가진 프로퍼티가 객체 내에 있는지 확인하고자 할 때: "key" in obj
  • 프로퍼티를 나열할 때: for (let key in obj)
    지금까진 '순수 객체(plain object)'라 불리는 일반 객체에 대해 학습했습니다.

자바스크립트에는 일반 객체 이외에도 다양한 종류의 객체가 있습니다.

  • Array – 정렬된 데이터 컬렉션을 저장할 때 쓰임
  • Date – 날짜와 시간 정보를 저장할 때 쓰임
  • Error – 에러 정보를 저장할 때 쓰임
    기타 등등

6. 참조에 의한 객체 복사

객체는 참조에 의해 할당되고 복사됩니다. 변수엔 ‘객체’ 자체가 아닌 메모리상의 주소인 '참조’가 저장됩니다. 따라서 객체가 할당된 변수를 복사하거나 함수의 인자로 넘길 땐 객체가 아닌 객체의 참조가 복사됩니다.

그리고 복사된 참조를 이용한 모든 작업(프로퍼티 추가·삭제 등)은 동일한 객체를 대상으로 이뤄집니다.

객체의 '진짜 복사본’을 만들려면 '얕은 복사(shallow copy)'를 가능하게 해주는 Object.assign이나 '깊은 복사’를 가능하게 해주는 _.cloneDeep(obj)를 사용하면 됩니다. 이때 얕은 복사본은 중첩 객체를 처리하지 못한다는 점을 기억해 두시기 바랍니다.

7. 메서드와 this

let user = {
  name: "John",
  age: 30,

  sayHi() {
    // 'this'는 '현재 객체'를 나타냅니다.
    alert(this.name);
  }

};

user.sayHi(); // John

객체 프로퍼티에 저장된 함수를 '메서드’라고 부릅니다.
object.doSomthing()은 객체를 '행동’할 수 있게 해줍니다.
메서드는 this로 객체를 참조합니다.
this 값은 런타임에 결정됩니다.

함수를 선언할 때 this를 사용할 수 있습니다. 다만, 함수가 호출되기 전까지 this엔 값이 할당되지 않습니다.
함수를 복사해 객체 간 전달할 수 있습니다.
함수를 객체 프로퍼티에 저장해 object.method()같이 ‘메서드’ 형태로 호출하면 this는 object를 참조합니다.

let user = {
  firstName: "보라",
  sayHi() {
    let arrow = () => alert(this.firstName);
    arrow();
  }
};

user.sayHi(); // 보라

화살표 함수는 자신만의 this를 가지지 않는다는 점에서 독특합니다. 화살표 함수 안에서 this를 사용하면, 외부에서 this 값을 가져옵니다.

8. new 연산자와 생성자 함수

function User(name) {
  this.name = name;
  this.isAdmin = false;
}

let user = new User("보라");

alert(user.name); // 보라
alert(user.isAdmin); // false
  • 생성자 함수(짧게 줄여서 생성자)는 일반 함수입니다. 다만, 일반 함수와 구분하기 위해 함수 이름 첫 글자를 대문자로 씁니다.
  • 생성자 함수는 반드시 new 연산자와 함께 호출해야 합니다. new와 함께 호출하면 내부에서 this가 암시적으로 만들어지고, 마지막엔 this가 반환됩니다.
    생성자 함수는 유사한 객체를 여러 개 만들 때 유용합니다.

9. 배열

배열은 특수한 형태의 객체로, 순서가 있는 자료를 저장하고 관리하는 용도에 최적화된 자료구조입니다.

선언 방법:

// 대괄호 (가장 많이 쓰이는 방법임)
let arr = [item1, item2...];

// new Array (잘 쓰이지 않음)
let arr = new Array(item1, item2...);

new Array(number)을 호출하면 길이가 number인 배열이 만들어지는데, 이 때 요소는 비어있습니다.

length 프로퍼티는 배열의 길이를 나타내줍니다. 정확히는 숫자형 인덱스 중 가장 큰 값에 1을 더한 값입니다. 배열 메서드는 length 프로퍼티를 자동으로 조정해줍니다.

length 값을 수동으로 줄이면 배열 끝이 잘립니다.

다음 연산을 사용하면 배열을 데큐처럼 사용할 수 있습니다.

  • push(...items) – items를 배열 끝에 더해줍니다.

  • pop() – 배열 끝 요소를 제거하고, 제거한 요소를 반환합니다.

  • shift() – 배열 처음 요소를 제거하고, 제거한 요소를 반환합니다.

  • unshift(...items) – items를 배열 처음에 더해줍니다.
    아래 방법을 사용하면 모든 요소를 대상으로 반복 작업을 할 수 있습니다.

  • for (let i=0; i<arr.length; i++) – 가장 빠른 방법이고 오래된 브라우저와도 호환됩니다.

  • for (let item of arr) – 배열 요소에만 사용되는 모던한 문법입니다.

  • for (let i in arr) – 배열엔 절대 사용하지 마세요.

10. 배열과 메소드

배열의 관한 메소드는 종류가 엄청 많습니다. 여기에 있는 요약본으로도 자주 쓰는 일부 내용만 있고 더 많은 정보와 코드 예시는 배열을 사용하는 메소드 해당 링크를 참고 할 것.
해당부분은 실제 개발자들도 전부 외워서 적는게 아니라 그때마다 메소드 기록한거 참고해서 적는다고 한다.

요소를 더하거나 지우기

push(...items) – 맨 끝에 요소 추가하기
pop() – 맨 끝 요소 추출하기
shift() – 첫 요소 추출하기
unshift(...items) – 맨 앞에 요소 추가하기
splice(pos, deleteCount, ...items) – pos부터 deleteCount개의 요소를 지우고, items 추가하기
slice(start, end) – start부터 end 바로 앞까지의 요소를 복사해 새로운 배열을 만듦
concat(...items) – 배열의 모든 요소를 복사하고 items를 추가해 새로운 배열을 만든 후 이를 반환함. items가 배열이면 이 배열의 인수를 기존 배열에 더해줌
원하는 요소 찾기

indexOf/lastIndexOf(item, pos) – pos부터 원하는 item을 찾음. 찾게 되면 해당 요소의 인덱스를, 아니면 -1을 반환함
includes(value) – 배열에 value가 있으면 true를, 그렇지 않으면 false를 반환함
find/filter(func) – func의 반환 값을 true로 만드는 첫 번째/전체 요소를 반환함
findIndex는 find와 유사함. 다만 요소 대신 인덱스를 반환함
배열 전체 순회하기

forEach(func) – 모든 요소에 func을 호출함. 결과는 반환되지 않음
배열 변형하기

map(func) – 모든 요소에 func을 호출하고, 반환된 결과를 가지고 새로운 배열을 만듦
sort(func) – 배열을 정렬하고 정렬된 배열을 반환함
reverse() – 배열을 뒤집어 반환함
split/join – 문자열을 배열로, 배열을 문자열로 변환함
reduce(func, initial) – 요소를 차례로 돌면서 func을 호출함. 반환값은 다음 함수 호출에 전달함. 최종적으로 하나의 값이 도출됨
기타

Array.isArray(arr) – arr이 배열인지 여부를 판단함
sort, reverse, splice는 기존 배열을 변형시킨다는 점에 주의하시기 바랍니다.

지금까지 배운 메서드만으로 배열과 관련된 작업 99%를 해결할 수 있습니다. 이 외의 배열 메서드도 있긴 한데 잠시 언급하고 넘어가겠습니다.

arr.some(fn)과 arr.every(fn)는 배열을 확인합니다.

두 메서드는 map과 유사하게 모든 요소를 대상으로 함수를 호출합니다. some은 함수의 반환 값을 true로 만드는 요소가 하나라도 있는지 여부를 확인하고 every는 모든 요소가 함수의 반환 값을 true로 만드는지 여부를 확인합니다. 두 메서드 모두 조건을 충족하면 true를, 그렇지 않으면 false를 반환합니다.

arr.fill(value, start, end)은 start부터 end까지 value를 채워 넣습니다.

arr.copyWithin(target, start, end)은 start부터 end까지 요소를 복사하고, 복사한 요소를 target에 붙여넣습니다. 기존 요소가 있다면 덮어씁니다.

11. Object.keys, values, entries

일반 객체엔 다음과 같은 메서드를 사용할 수 있습니다.

Object.keys(obj) – 객체의 키만 담은 배열을 반환합니다.
Object.values(obj) – 객체의 값만 담은 배열을 반환합니다.
Object.entries(obj) – [키, 값] 쌍을 담은 배열을 반환합니다.
Map, Set, Array 전용 메서드와 일반 객체용 메서드의 차이를 (맵을 기준으로) 비교하면 다음과 같습니다.

12. 구조 분해 할당

구조 분해 할당을 사용하면 객체나 배열을 변수로 연결할 수 있습니다.

객체 분해하기:

let {prop : varName = default, ...rest} = object

object의 프로퍼티 prop의 값은 변수 varName에 할당되는데, object에 prop이 없으면 default가 varName에 할당됩니다.

연결할 변수가 없는 나머지 프로퍼티들은 객체 rest에 복사됩니다.

배열 분해하기:

let [item1 = default, item2, ...rest] = array

array의 첫 번째 요소는 item1에, 두 번째 요소는 변수 item2에 할당되고, 이어지는 나머지 요소들은 배열 rest 저장됩니다.

할당 연산자 좌측의 패턴과 우측의 구조가 같으면 중첩 배열이나 객체가 있는 복잡한 구조에서도 원하는 데이터를 뽑아낼 수 있습니다.

13. 실전 코딩테스트 문제

문자열 내 마음대로 저장하기
금일 문제는 배열 안에 영어 단어들이 있는데, 각 단어들의 n번째 알파벳 철자를 지정했을때 그 기준으로 사전 순서대로 정렬을 하는 것이였다. 그리고 나서 만약 같은 알파벳 철자가 동일 하면, 원래 사전 순서대로 정렬을 하라는 문제였다.
나는 그래서 오늘 배웠던 것 중에서 정렬을 하는 메서드인 sort 함수를 찾아보았다.

function compare(a, b) {
  if (a > b) return 1; // 첫 번째 값이 두 번째 값보다 큰 경우
  if (a == b) return 0; // 두 값이 같은 경우
  if (a < b) return -1; //  첫 번째 값이 두 번째 값보다 작은 경우
}

일반적으로 두 단어의 비교를 하게 된다면 바로 if의 세 가지를 전부 실행하면 정렬이 된다.

function compareNumeric(a, b) {
  if (a > b) return 1;
  if (a == b) return 0;
  if (a < b) return -1;
}

let arr = [ 1, 2, 15 ];

arr.sort(compareNumeric);

alert(arr);  // 1, 2, 15

이게 바로 sort의 함수를 사용하면 arr 배열이 변수를 compareNumeric로 선언한 함수를 통해서 작은숫자부터 큰숫자까지 정렬된 것을 볼 수 있었다.
그래서 나는 sort 함수를 이용해서 다음과 같이 풀게 되었다.

strings이 단어고, n이 그 단어의 n번째 철자라고 선언을 한다. 그러면 두 단어 a와 b가 있을텐데 각 단어의 n번째 철자를 지정을 해서 비교를 해서 정렬 하면 되었다. sort 함수를 사용하면 다음과 같이 정렬을 하면 된다. 각 단어의 n번째 철자를 쓰는 것이기에 a[n]과 b[n]을 비교하면 되었다.

// 인접한 두 리스트의 n번째 글자를 비교해서 정렬
answer = strings.sort((a, b) => {
	if(a[n] > b[n]) return 1; 
    if(a[n] < b[n]) return -1;
    if(a[n] = b[n]) return 0;
    }

이러면 n번째 철자를 비교해서 정렬을 완료 하였다. 다만 두 단어의 철자가 같다면 원래 대로 기존처럼 정렬 해야 한다는 조건이 남아 있었다 이 부분은 강의 자료에 있던거 그대로 쓰면 되었다.

		// 만약 n번째 글자가 같으면 사전순으로 정렬
		if(a[n] == b[n]){ 
            if(a > b) return 1;
            if(a < b) return -1;
            if(a = b) return 0;
        }

만약 n번째 철자가 동일 하면 강의 자료에 있던 if문 세 가지 모두 그대로 쓰면 되었다.

function solution(strings, n) {
    var answer = []
    // 인접한 두 리스트의 n번째 글자를 비교해서 정렬
    answer = strings.sort((a, b) => { 
        if(a[n] > b[n]) return 1; 
        if(a[n] < b[n]) return -1;
        // 만약 n번째 글자가 같으면 사전순으로 정렬
        if(a[n] = b[n]){ 
            if(a > b) return 1;
            if(a < b) return -1;
            if(a = b) return 0;
        }
    });
    return answer;
}

그렇게 해서 나온 내 최종 해답은 이거였다. 팀원들과 의논하고 궁리해보니까 겨우 풀 수 있었다. 강의자료를 잘 찾아보면 해결 할 수 있었다.

알게 된 점

어제는 그럭저럭 문법적으로 사전에 배웠던 것들이 많아서 쉬웠는데, 오늘 자체 문법은 심각하게 어려웠다. 물론 이전에 알고 있었던 배열, 리스트문은 다 알고 있었어도 그 안에 있는 메소드들이 나를 이렇게 괴롭혔다. 자바스크립트에는 여러가지 메소드들이 엄청 많은데, 이걸 전부다 외워서 다 기억해야 해야 할지 막막했었다. 사실 오늘 회고록을 적고 있는 이 순간에도 여전히 이해 못한 것들이 많이 있었다. 이 부분은 기술매니저님 한테 물어보니까, 지금 당장 전부 외우는 것이 아니라 나중에 필요할 때 찾아서 쓰면 된다고 말씀 해 주셨다. 웹개발 종합반에서도 언급 했던 부분이 다시 언급 했던 부분이였다.
또한 전날에는 어떻게 요점 정리 해야 할지 막막해서 중요 부분을 코드를 직접 적지 않고 스샷으로 가져와다 써서 가독성이 많이 좋지 않았지만, 이번에는 코드 양식에 맞추고 진행해서 대부분 코드를 복사 쉽게 할 수 있게 정리를 하였다.
그리고 오늘 코딩테스트 문제를 풀었는데 어제보다는 그나마 괜찮아서 풀기는 풀었다. 코딩테스트 문제 풀면서 sort 메소드를 다시 한번 더 공부 하는데 도움이 되었다.

앞으로 계획

내일부터 토요일까지는 코딩테스트 문제들을 본격적으로 풀어보는 시간을 가지게 된다. 미리 어제와 오늘 합쳐서 2문제를 맛보기로 봤는데 만만치 않은 난이도에 겁먹을 것 같지만 그래도 한번 내가 아는 선에서 최대한 풀어 볼 예정이다.

profile
향해 13기 node.js 백앤드

0개의 댓글