자바스크립트 - 핵심 개념(2)

김태완·2024년 11월 6일

<-- 프로그래밍과 데이터 in javascript -->

  • <객체와 프로퍼티>

    • 변수에는 한가지 값만 담았는데, 여러가지 값을 담고싶다면 객체라는 개념을 사용하면 된다.
    • 객체는 중괄호를 통해서 만들수있다. 이 중괄호안에는 쉼표로 여러가지 값을 구분한다.
    • 그리고 콜론과 함께 값(value / property value)의 이름(key / property Name)을 붙여준다.
    • key, value 한쌍을 객체의 속성(property)이라고 한다.
    • property name 은 문자열의 형태를 지닌다. 따옴표를 붙여야하지만 생략해도 자동으로 형변환 하기에 상관없다.
    • 따옴표를 생략할때 주의사항
        1. 첫 글자는 반드시 문자, 밑줄, 달러 기호 중 하나로 시작!
        1. 띄어쓰기 금지!
        1. 하이픈 금지!
    • property value는 모든 자료형이 다 들어갈수있으며 객체 안에 객체를 넣을 수도 있다.
  • <객체에서 데이터 접근하기>

      1. 점 표기법
        console.log(person.name) 과 같은 형식으로 객체의 데이터에 접근할 수 있다.
        근데 이렇게 하면 따옴표를 생략할 수 없는 프로퍼티 네임으로는 접슨할 수가 없다.

        그래서
      1. 대괄호 표기법
        console.log(person[' name'])
        => 객체이름 다음에 대괄호를 열고 그안에 프로퍼티 네임을 문자열로 작성하는 방식
        -> (객체 이름에 띄어쓰기가 있다는 전제하에) 프로퍼티 이름을 유연하게 할 수있다는 장점이 있다.
      1. 존재하지 않는 객체에 접근하려하면 undefined가 출력된다.
  • <객체 다루기>

    • 객체 수정할때는 객체에 접근해서 다시 값을 할당해주면 된다!

    • 존재하지 않는 프로퍼티를 할당해주면서 추가할 수도 있다.

    • 객체의 프로퍼티를 삭제할때는 delete person.name; 이런식으로 delete 명령어를 사용해주면 객체의 프로퍼티가 삭제된다.

      • 객체 내에 프로퍼티가 존재하는지 확인하는 방법
        - 1. undefined 비교
        console.log(person.name !== undefined)
        - 2. 프로퍼티 네임 in 객체
        console.log('name' in person)
        -> 있는지 확인해서 불린형태로 반환한다.
        - 조금 더 안전하게 확인하기 위해서는 in 연산자를 확인하는게 좋다.
        - 왜냐하면 실수로 프로퍼티에 undefined를 할당했거나, 다른 함수나 변수에 의해서 의도치않게 undefined가 할당될수도 있기 때문이다.
        - 그리고 if문에서 조건으로 활용하기도 좋다. 불린형태로 반환하기 때문이다.
        - 굉장히 많이 사용하는 방식이다. 잘 알아두자
  • <객체와 메소드>

    • 메소드: 연관성있는 여러 함수들을 하나로 묶고싶을때도 객체를 사용하면되는데,객체의 프로퍼티 값으로 함수를 넣어준다면, 이 함수를 객체의 메소드라고 한다.
    • 객체에 함수를 동일하게 선언해주고, 호출할때도 점표기법으로 동일하게 호출 가능하다.
    • 함수 안에 파라미터를 넣어서 활용도 가능하다.
    • 대괄호 표기법으로 프로퍼티에 접근하고 소괄호를 열어서 메소드를 호출한다.
      ex) greetings['sayHello']('name')
  • <for...in 반복문>

    • 객체 안에 있는 프로퍼티를 가지고 반복적인 동작을 수행할때 사용한다.
    • 일반적인 for문으로는 대체할 수 없다.

for (변수 in 객체) {
   동작부분
}

  • 객체의 프로퍼티 네임이 변수에 할당되고, 객체의 프로퍼티 갯수만금 반복동작을 하게된다.
  • 정수형 프로퍼티 네임
    • 객체의 프로퍼티 네임으로 숫자형을 그대로 사용할 수도 있다.
    • for...in문을 사용할 때 주의해야 할 순간은 바로 정수형 프로퍼티 네임이 있을때다.
    • 객체는 정수형 프로퍼티 네임을 오름차순으로 먼저 정렬하고, 나머지 프로퍼티들은 추가한 순서대로 정렬하는 특징이 있습니다.

let myObject = {
3: '정수3',
name: 'codeit',
1: '정수1',
birthDay: '2017.5.17',
2: '정수2',
};
for (let key in myObject) {
console.log(key);
}

  • 이 코드를 그대로 실행해보면 아래와 같은 결과가 출력된다.

1
2
3
name
birthDay

  • 분명히 3, name, 1, birthday, 2순서로 프로퍼티가 작성되었음에도 불구하고, for...in문이 실행될 때는 오름차순으로 정렬이 되어 출력이 된 모습을 확인할 수 있다.

  • 굳이 for문을 그대로 작성하지 않고도 그냥 콘솔에 myObject를 콘솔에 출력만 해봐도

    {1: "정수1", 2: "정수2", 3: "정수3", name: "codeit", birthDay: "2017.5.17"}

  • 객체가 정수형 프로퍼티에 한해서 오름차순으로 정렬이 되고 나머지는 추가한 순서대로 정렬이 되는 걸 확인할 수 있다.

  • 처음에 살펴봤던 것처럼 정수형 프로퍼티에 따옴표를 붙여 문자열처럼 만들더라도, 정렬방식은 동일하게 처리된다.

  • <내장객체>
    - 자바스크립트가 미리 가지고 있는 객체를 내장객체라고 한다. 대표적인 예로 Date가 있다.
    let myDate = new Date();

  1. Date 객체를 생성한 순간의 시간이 저장되는데, 소괄호 안에 숫자를 넣어주면 UTC 기준 1970년 1월 1일 00:00:00 에서 입력한 숫자 밀리초 만큼 지난 객체가 만들어진다.

  2. 근데 복잡하니깐 숫자 보단 문자열을 넣어주는 방법도 있다.
    소괄호 안에 연-월-일T00:00:00 이런식을호 넣어주면된다.
    시간을 지정해주면 그시간, 아니면 00:00:00 기준

  3. 소괄호안에 여러가지 값을 입력해주는 방식
    연, 월, 일, 시, 분, 초 이런식으로 하되 월은 0부터 시작한단다.

    생각보다 시간 계산이 필요할때 자주 쓰인다.

    • getTime 메소드
      1970.1.1. 0시0분0초 기준으로 현재가 몇 밀리초를 지났는지 알려준다. 이를 타임스탬프(time stamp) 라고부른다.

    • Date 객체 정보 수정하기
      set으로 시작하는 다양한 메서드를 활용하면, 생성된 Date객체의 정보를 수정할 수도 있습니다.

(대괄호로 감싸진 요소들은 선택적인 요소이다. 참고!)
setFullYear(year, [month], [date])
setMonth(month, [date])
setDate(date)
setHours(hour, [min], [sec], [ms])
setMinutes(min, [sec], [ms])
setSeconds(sec, [ms])
setMilliseconds(ms)
setTime(milliseconds)(1970년 1월 1일 00:00:00 UTC부터 밀리초 이후를 나타내는 날짜를 설정)

  • 간단하게 시간 정보를 표현하고 싶다면 아래와 같은 메소드를 활용해 볼 수도 있습니다.

    let myDate = new Date();
    console.log(myDate.toLocaleDateString()); // myDate가 가진 날짜에 대한 정보 (년. 월. 일)
    console.log(myDate.toLocaleTimeString()); // myDate가 가진 시간에 대한 정보 (시:분:초)
    console.log(myDate.toLocaleString()); // myDate가 가진 날짜와 시간에 대한 정보 (년. 월. 일 시:분:초)
    toLocaleDateString(), toLocaleTimeString(), toLocaleString() 메소드는 사용자의 브라우저에 설정된 국가의 표기에 맞춰 날짜와 시간을 보여줍니다.

  • 똑똑한 Date?!
    Date 객체엔 자동으로 날짜를 수정해주는 유용한 기능이 있습니다. 범위를 벗어나는 값을 설정하려고 하면 자동으로 날짜를 수정해줍니다.

    let myDate = new Date(1988, 0, 32);
    // 1988년 1월 32일은 없습니다
    // 2월 1일로 자동고침 되는걸 확인할 수 있습니다.
    console.log(myDate) // Mon Feb 01 1988 00:00:00

  • 지금 이 순간..!?
    Date.now() 메소드는 이 메소드가 호출된 시점의 타임스탬프를 반환합니다. 이렇게 하면 새로운 객체를 만들지 않아도 바로 현 시점의 날짜 값을 얻어낼 수 있는 것!

let myDate = new Date();
console.log(Date.now() === myDate.getTime()); // true

  • 위 코드를 보면 알 수 있듯이 새로운 객체를 만들어서 getTime 메소드를 호출한 값과 일치한다는 사실을 확인할 수 있는데요.

  • 새로운 객체를 만들지 않는다는 점은 일단 우리 눈에 코드 한 줄을 줄일 수 있다는 이점도 있고, 눈에는 드러나지 않지만 코드가 실행될 때 메모리의 부담을 줄여주기도 합니다.

  • 그래서 특정한 시점이 아니라 단순히 순간순간 그때 당시 시간 값이 필요한 경우에는 Date.now() 메소드를 활용하는 것이 코드의 가독성 뿐만아니라 성능적인 측면에서도 좀 더 유리합니다.

  • Date객체의 형변환

let myDate = new Date(2017, 4, 18);
console.log(typeof myDate); // object
console.log(String(myDate)); // Thu May 18 2017 00:00:00 GMT+0900 (Korean Standard Time)
console.log(Number(myDate)); // 1495033200000
console.log(Boolean(myDate)); // true

  • 이 코드를 천천히 살펴봅시다. 두번째 줄에서 Date 객체의 자료형을 확인해보니 'object', 즉 객체라는 것을 확인할 수 있는데요.
  • Date 객체를 boolean 타입으로 변환하면 true가 되고, string 타입으로 변환하면 날짜값이 그대로 문자열로 변환이 됩니다.
  • 우리가 눈여겨 볼 부분은 number 타입으로 변환할 경우입니다. 이 값은 아무 의미없는 단순한 숫자값이 아니라 getTime() 메소드를 활용한 것과 똑같은 수치의 타임스탬프 값 입니다.

let myDate = new Date(2017, 4, 18);
console.log(myDate.getTime() === Number(myDate)); // true

  • 이것은 다시 말해 Date 객체끼리 바로 사칙 연산도 충분히 가능하다는 뜻이기도 한데요.

    let myDate1 = new Date(2017, 4, 18);
    let myDate2 = new Date(2017, 4, 19);
    let timeDiff = myDate2 - myDate1;
    console.log(timeDiff); // 86400000 (ms)
    console.log(timeDiff / 1000); // 86400 (sec)
    console.log(timeDiff / 1000 / 60) // 1440 (min)
    console.log(timeDiff / 1000 / 60 / 60) // 24 (hour)
    console.log(timeDiff / 1000 / 60 / 60 / 24) // 1 (date)

이렇게 하니깐 두 Date객체 사이의 시간차이를 어렵지 않게 확인할 수 있죠?

  • <배열 - Array>

    • 프로퍼티 네임보다 값들의 순서가 중요할때는 객체보다는 배열이라는 것을 활용하는 것이 효율적이다.
    • 중괄호 대신에 대괄호로 만들고 쉼표로 구분한다. 배열안에는 각 요소(element)별로 구분해서 적는다.
    • 그리고 각 요소의 순서를 알려주는 숫자가 매겨지는데 이를 index라고 한다. 이는 객체랑 비교했을때 프로퍼티 네임의 역할을 한다.
    • 배열의 요소를 가져오는 방법은 객체의 대괄호 표기법과 같다. console.log(배열이름[index])
    • 이것을 indexing이라고 하는데 index는 0번 부터 시작한다.
    • 순서에 관계없이 여러 값을 묶을대도 배열을 사용해도 좋다.
  • <배열 다루기>

    • 사실 배열도 객체이다.
    • .length 프로퍼티 -> 배열이 가지고 있는 요소의 갯수를 알려준다. 조건으로 사용할때 많이 쓴다.
    • 점표기나 대괄호 표기 둘다 사용가능하다.
    • 추가하거나 수정하는 것도 객체와 비슷하다.
    • 없는 요소를 추가할때는 할당해주면된다.
    • 근데 index를 하나 건너뛰면 건너뛴 요소는 undefined로 생기고, 배열을 확인해보면 empty로 뜬다.
    • 인덱스의 값은 비어있지만 하나의 요소로 생성이 되었다는 것을 알아두자.
    • 수정은 그냥 재할당해주면 된다.
    • 요소 지울때는 delete로 하면 값은 지워지지만 요소는 empty로 남아있고 length도 유지된다.
    • 완벽하게 삭제하려면 배열이 가지고 있는 메서드를 이용해야한다.
  • <배열 메소드1>

    • 배열을 안정적이고 효과적으로 사용하기위해 배열 메소드를 사용해야한다.
    • 삭제할때는 splice라는 메서드를 이용한다.
    • splice(startIndex, deleteCount, item)
    • 점표기법으로 splice를 호출하고, 삭제하고 싶은 index를 파라미터로 전달한다.
    • 파라미터로 한개의 값만 전달하면 그 인덱스로부터 이후 모든 요소를 삭제한다.
    • 그래서 splice 메서드의 두번째 파라미터로 숫자를 전달해주면 첫번째 파라미터의 숫자 인덱스로부터 요소 두번째 파라미터 값만큼 삭제된다.
    • 세번째 파라미터는 요소를 삭제하고 그 자리에 값을 추가하는 파라미터이다.
    • 만약 삭제할 개수를 0으로 만들어준다면, 쉽게 추가할수도 있겠징? 수정도 마찬가지다.
  • <배열 메소드2>

    • //배열의 첫 요소를 삭제: shift();
      이거는 배열의 첫 요소를 삭제하고 나머지를 순서 당김
    • //배열의 마지막 요소를 삭제: pop();
      이거는 배열의 가장 마지막 요소를 삭제함. 순서가 바뀌지않는다.
    • // 배열의 첫 요소로 값을 추가: unshift();
      매소드를 호출할때 파라미터로 추가할 값을 전달해줘야한다.
      나머지 요소들은 순서가 하나씩 밀린다.
    • //배열의 마지막 요소로 추가: push();
      역시 파라미터로 추가할 값을 전달해줘야하고 배열의 끝부분에 추가된다.
    • splice, shift, pop, unshift, push 말고도 배열에는 다양한 메소드들이 있습니다.
  • 배열에서 특정 값 찾기 (indexOf / lastIndexOf)

    • 배열에서 특정 값을 찾으려면 indexOf 메소드를 사용하면 됩니다. array.indexOf(item)을 하면 array 배열에 item이 포함되어 있는지 확인할 수 있습니다.
    • 만약 포함되어 있다면, item이 있는 인덱스가 리턴됩니다.
    • 포함되어 있지 않다면, -1이 리턴됩니다.
    • 여러 번 포함되어 있으면, 처음 발견된 인덱스가 리턴됩니다.

let brands = ['Google', 'Kakao', 'Naver', 'Kakao'];
console.log(brands.indexOf('Kakao'));
console.log(brands.indexOf('Daum'));
1
-1

  • 그리고 비슷하게 lastIndexOf라는 메소드가 있는데요. indexOf와는 반대로 탐색을 뒤에서 부터 하게 됩니다. 그러니깐 방금과 같은 경우에 'Kakao'를 lastIndexOf 메소드로 찾게 되면 마지막에 있는 인덱스가 리턴되겠죠?

let brands = ['Google', 'Kakao', 'Naver', 'Kakao'];
console.log(brands.lastIndexOf('Kakao'));
console.log(brands.lastIndexOf('Daum'));
3
-1

  • 배열에서 특정 값이 있는지 확인하기 (includes)
    • indexOf / lastIndexOf는 특정 값을 찾아서 해당 값의 index를 알려줍니다.
    • 하지만, 때로는 그냥 그 값이 배열안에 있는지, 그 여부만 확인하고 싶을 수도 있는데요.
    • 그럴때는 includes 라는 메소드를 활용하면 됩니다.
    • array.includes(item)을 하게되면 array배열에 item이 있을 경우 true를, 없을 경우 false를 리턴합니다.

let brands = ['Google', 'Kakao', 'Naver', 'Kakao'];
console.log(brands.includes('Kakao'));
console.log(brands.includes('Daum'));
true
false

  • 배열 뒤집기 (reverse)
    • reverse라는 메소드를 활용하면, 배열의 순서를 뒤집을 수도 있습니다.

      let brands = ['Google', 'Kakao', 'Naver', 'Kakao'];
      console.log(brands);
      brands.reverse();
      console.log(brands);
      (4) "Google", "Kakao", "Naver", "Kakao" ["Kakao", "Naver", "Kakao", "Google"]

사실 이 밖에도 배열이 가지고 있는 유용한 메소드들이 다양하게 있다. 더 많은 내용을 알고싶다면, 이 링크를 참고!
(https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array)

  • <for...of 반복문>

    • 순서대로 인덱스가 있는 배열의 특징과 length 프로퍼티를 잘 활용하면 for문으로도 배열의 길이만큼 반복하면서 각 요소들을 다룰 수 있지만!
    • for of문을 활용하면 단순하게 활용가능하다.
      -> 배열의 각 요소를 대상으로 반복적인 작업을 수행할때 좋아.
    • 구조는 for...in 반복문과 비슷하다.
      -> 그래서 단순한 코드에서는 동일한 동작을 하지만, for...in문은 전체 프로퍼티를 대상으로 동작하기 때문에, 좀 더 복잡한 상황에서는 배열의 메소드나 배열이 가지고 있는 length 프로퍼티들이 변수에 할당될 가능성이 있다.
    • 그래서 일반적인 객체에 좀 더 최적화 되어있으므로 배열에는 활용하지 않는 것을 추천한다.
    • 차이는 객체가 아니라 배열의 요소가 할당된다.

      for(변수 of 배열){
      동작부분;
      }

  • <다차원 배열>

    • 배열 역시 배열의 요소가 될 수 있다.
    • 배열 안에 배열 -> 2차원 배열 / 그안에 또 배열이 들어가면 3차원 배열이다.
    • 이런 배열을 다차원 배열이라고 한다.
    • 2차원 배열의 각 요소에 접근하려면?
      ex) 배열이름[0][1]
    • 언제 활용하면 좋아?
      -> 값들의 의미보다는 값들의 위치나 혹은 순서에 초점이 맞춰질때
      -> 값들의 의미가 중요하다면 프로퍼티를 만드는 객체가 더 좋다.
  • <다양한 숫자 표기법>

    • 지수표기법 - 1e9 => 왼쪽의 1에 오른쪽에 있는 수만큼 10의 거듭제곱을 곱하는 의미이다.
    • 그외 2진, 8진, 16진 등 잘 사용하진않지만 이런 방법들도 있다.
  • <숫자형 메소드>

  • toFixed

    • 소수를 다룰 때 사용하는 메소드인데 파라미터로 숫자값을 전달해주면 그만큼 소수점 아래의 자릿수를 고정해주는 메소드(아래값은 반올림해서), 범위는 0~100
    • 계산되어서 나온 값이 문자열로 나온다.
  • toString

    • 파라미터로 전달하는 숫자의 진법으로 숫자를 변환해주는 메소드, 범위는 2~36
    • 사용할때는 변수에 .toString으로 해야한다. 그냥 숫자에 .toString을 붙여버리면 소수점으로 인식한다. 그래서 정수형에 사용하면 ..(점 2개를 붙여야한다.) or (숫자).toString으로 하는 방식으로 활용한다.
    • Date객체처럼 자바스크립트의 내장 객체 중 다양한 연산들을 유용하게 사용하기 위해 Math라는 객체가 있습니다.
  • 이번에는 Math객체의 다양한 메소드에 대해 살펴봅시다.

    • 절댓값 (Absolute Number)
      학창 시절 수학 시간에서 배운 '절댓값(absolute value)' 기억하시나요? 간단하게 설명하자면, 어떤 값의 '양수(positive number)' 버전이라고 할 수 있습니다. 음수 -5의 절댓값은 양수 5고, 그냥 양수 5의 절댓값은 그대로 양수 5인 거죠.
      - Math.abs(x)를 하면 x의 절댓값이 리턴됩니다.

      console.log(Math.abs(-10));
      console.log(Math.abs(10));
      10
      10

    • 최댓값 (Maximum)
      Math.max 함수에 파라미터로 여러 수를 넘겨주면, 그중 가장 큰 값이 리턴됩니다.

      console.log(Math.max(2, -1, 4, 5, 0));
      5

    • 최솟값 (Minimum)
      Math.min 함수에 파라미터로 여러 수를 넘겨주면, 그중 가장 작은 값이 리턴됩니다.

      console.log(Math.min(2, -1, 4, 5, 0));
      -1

    • 거듭제곱 (Exponentiation)
      '제곱'의 개념 기억하시나요? '2의 3승'(혹은 '2의 세제곱')을 하면, 2를 세 번 곱한다는 뜻입니다. '2 곱하기 2 곱하기 2'를 하면 8이죠? 마찬가지로 '5의 2승'을 하면, '5 곱하기 5'를 해서 25입니다.
      자바스크립트에서 Math.pow(x, y)를 하면 x의 y승의 결괏값이 리턴됩니다.

      console.log(Math.pow(2, 3));
      console.log(Math.pow(5, 2));\
      8
      25

    • 제곱근 (Square Root)
      '제곱근(square root)'은 '제곱'의 반대라고 생각할 수 있습니다. 5의 제곱이 25이기 때문에, 25의 제곱근은 5입니다. 7의 제곱이 49이기 때문에, 49의 제곱근은 7입니다.
      Math.sqrt(x)를 하면 x의 제곱근이 리턴됩니다.

      console.log(Math.sqrt(25));
      console.log(Math.sqrt(49));
      5
      7

    • 반올림 (Round)
      Math.round(x)를 하면 x의 반올림된 값이 리턴됩니다. 소수점 부분이 0.5 이상이면 가장 가까운 정숫값으로 올라가고, 소수점 부분이 0.5 미만이면 가장 가까운 정숫값으로 내려갑니다.

      console.log(Math.round(2.3));
      console.log(Math.round(2.4));
      console.log(Math.round(2.49));
      console.log(Math.round(2.5));
      console.log(Math.round(2.6));
      2
      2
      2
      3
      3

    • 버림과 올림 (Floor and Ceil)
      Math.floor(x)을 하면 x의 버림 값이, Math.ceil(x)을 하면 x의 올림 값이 리턴됩니다. 이 경우, 소수 부분이 얼마 인지와는 상관이 없습니다.

      console.log(Math.floor(2.4));
      console.log(Math.floor(2.49));
      console.log(Math.floor(2.8));
      console.log(Math.ceil(2.4));
      console.log(Math.ceil(2.49));
      console.log(Math.ceil(2.8));
      2
      2
      2
      3
      3
      3

    • 난수 (Random)
      Math.random을 하면 0 이상 1 미만의 값이 랜덤으로 리턴됩니다.

      console.log(Math.random());
      console.log(Math.random());
      console.log(Math.random());
      console.log(Math.random());
      0.21458369059793236
      0.6622040803059857
      0.785172717569619
      0.9056556038884926

    • 이뿐만 아니라 '삼각 함수 계산'이나 '로그'같은 더 깊은 수학 계산도 가능합니다. 더 깊게 알아보고 싶다면 이 링크 를 참고하세요!
      (https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Math)

  • <문자열 심화>

    • 자바스크립트에서는 문자열도 객체처럼 다룰 수 있다.

    • length 프로퍼티를 활용하면 공백포함해서 문자열의 길이를 알 수 있다.

    • 요소에도 접근할 수 있는데

      대괄호 표기법 - myString[3]
      or
      charAt 메소드 - myString.charAt(3)
      or
      indexOf 나 lastIndexOf를 활용할 수 있다.
      (없는 문자열 찾을때는 역시 -1이 출력된다)

    • //대소문자 변환
      myString.toUpperCase() -> 대문자로 바꿔
      myString.toLowerCase() -> 소문자로 바꿔

    • //양 끝 공백 제거
      mySting.trim()

    • //부분 문자열 접근 slice(start, end)
      -> 2개의 파라미터를 가지고 있는데, 시작과 끝 인덱스이다. 끝 인덱스 바로 직전까지의 범위를 가져온다.
      myString.slice(0,2) -> 0번부터 1번까지
      myString.slice(3) -> 시작지점부터 끝까지
      myString.slice() -> 문자열 전체

    • 문자열도 생각해보면 '문자' + '열'이기 때문에 배열과 비슷한 부분들이 많습니다.

    • 비슷한 점
      배열과 문자열 모두 length프로퍼티를 가지고 있고, 대괄호 표기법으로 각 요소에 접근할 수 있다거나..
      꽤 많은 메소드들이 배열과 문자열 모두 동일하게 사용되는 것도 확인할 수 있었다. 심지어 지난 시간에 다루진 못했지만 배열을 다룰 때 유용한 for..of문을 문자열에 활용할 수도 있습니다.

let myString = 'Codeit';
for (let str of myString) {
console.log(str);
}
C
o
d
e
i
t

  • 다른 점
    하지만 비슷하다고 해서 완전히 같다고는 할 수 없습니다.

    let myString = 'Codeit';
    let myArray = ['C', 'o', 'd', 'e', 'i', 't'];
    console.log(typeof myString);
    console.log(typeof myArray);

  • 일단 typeof 연산자를 사용해서 두 값의 자료형을 비교해보면,
    string
    object
    확실히 서로 다른 자료형인 걸 확인할 수 있고,

let myString = 'Codeit';
let myArray = ['C', 'o', 'd', 'e', 'i', 't'];
console.log(myString === myArray);
console.log(myString == myArray);

두 값을 서로 비교해 보아도
false
false
일치 비교뿐만 아니라, 느슨하게 비교하는 동등비교에서도 false가 출력되는걸 확인할 수 있습니다.

  • mutable vs. immutable

    • 가장 중요한 차이는 배열은 'mutable(바뀔 수 있는)' 자료형인 반면
    • 문자열은 'immutable(바뀔 수 없는)' 자료형이라는 것입니다.
  • 배열은 요소에 접근해서 할당연산자를 통해 요소를 수정할 수 있었죠?

  • 문자열은 한 번 할당된 값을 수정할 수 없습니다. 다르게 표현해서, 변수에 할당된 문자열을 바꾸고 싶다면, 일부를 바꾸는 게 아니라 새로운 문자열을 지정해주어야 한다는 것이죠.

// 배열은 mutable
let myArray = ['C', 'o', 'd', 'e', 'i', 't'];
myArray[0] = 'B';
console.log(myArray);
// 문자열은 immutable
let myString = 'Codeit';
myString[0] = 'B';
console.log(myString);
(6) ["B", "o", "d", "e", "i", "t"]
Codeit

  • 다시 한번 되돌아보면, 문자열이 가진 메소드들은 모두 return 값들을 활용하고, 본래의 문자열 값을 수정하지 않습니다.
    같은 의미에서 문자열에 splice 같은 메소드들은 사용할 수 없겠죠?

  • 문자열과 배열은 서로 비슷하지만 엄연히 다른 차이가 있다는 점 꼭 기억하자!

  • <기본형과 참조형>

    • 기본형(or 기본타입, Primitive Type)
    • Number, String, Boolean, Null, Undefined
    • 변수라는 상자에 값이 저장되는 느낌
    • 다른 변수에 기존 변수를 =으로 집어넣으면 같은 값이 상자에 들어가고, 다른 변수 y 상자에만 다른 값으로 변경이된다.
    • 근데 객체는 아니야.
  • 객체(참조형, Reference Type)

    • 객체 x = y로 했을때, 같은 값이 출력되지만, y.property로 하면 y객체 뿐만아니라 x 객체도 수정된다.
    • 자바스크립트에서 변수에 객체 값을 할당한 경우에는 객체 값이 어딘가에서 만들어지고 변수에는 그 객체값으로 가는 주소값이 저장된다. 쉽게 말하면 변수 상자와 객체 값 사이의 길이 열리는 것이다.
    • 그래서 y에는 길이 복사가 되는 것이다. 그래서 x와 y가 같은 객체를 보고 있는 것이고, y만 수정했지만 x도 수정한 것 처럼 보인다.
    • 이렇게 주소값을 참조해서 그 값에 접근하기 때문에 객체와 같은 자료형을 참조형이라고 한다.
    • 이 둘은 다른 언어에서도 적용되는 개념이니깐 알고 있자!
  • <참조형 복사하기>

    • 객체는 slice 라는 메소드가 없어서 복사는 다른 방법으로 해야한다.
    • 물론 let course2 = Object.assign({}, course1)을 통해서 복사 후 course2만 수정 가능하다.
    • 직관적으로 이해하기 위해서는 for...in문으로 객체를 복사해서 하면 된다.

      function 함수(object){
      let temp = {};
      for(let key in object){
      temp[key] = object[key]
      }
      return temp;
      }

    • 근데 객체나 배열안에 중첩해서 객체나 배열이 있는 경우에는 복사할 때 또 주소값이 복사되기때문에 예상치 못한 결과가 발생할 수 있다.
  • <const, 변수와 상수 사이>

    • const와 let의 가장 큰차이는 재할당이 불가능하다.
    • const는 선언한 다음에 새로운 값을 할당하려고하면 오류가 발생한다.
    • 근데 상수를 뜻하는 const로 변수를 선언하라고?
    • 예를 들어 검색사이트에서 검색어는 입력하는 값에 따라 변하지만, 검색하는 순간에는 그 값이 변하지않는 상수이다.
    • 결국 사용자의 입력값에 따라 변하지만 코드가 동작하는 순간에는 상수가 된다.
    • 코드 속에 변하게 되는 값이 많을수록 코드의 일관성을 유지하기 어렵다.
    • 왜? 언제 변하게 될지 모르니까. 그래서 안정적이고 일관되게 코드를 작성하기위해서 const 키워드로 변수를 사용하기 시작
    • 그럼, 진짜 상수와 변수를 구분하는 것은?
    • 이름을 짓는 방식으로 구분할 수 있다.
    • 변수이름은 기본적으로 소문자, 여러단어 조합될때는 두번째오는 단어부터 대문자로 조합한다.
    • 상수이름은 기본적으로 영어 대문자, 여러단어 조합될때는 밑줄로 구분한다.
    • 객체 프로퍼티나 배열의 요소들이 변경되는 경우는 변수가 가진 주소값을 변경하는게 아니기에 const 키워드로 변수를 선언해도 값이 바뀔수있다는 점 기억하자.

참고: https://cwdeveloper.tistory.com/38

질문 2
다음 코드의 실행결과로 올바른 것을 고르세요.
let team1 = ['Drum', 'Bass', 'Saxophone'];
const team2 = team1;
team1.splice(2, 1, 'Trumpet');
team2.splice(2, 1, 'Piano');
console.log(team1);
console.log(team2);

  1. [ 'Drum', 'Bass', 'Trumpet' ][ 'Drum', 'Bass', 'Saxophone' ]
  2. [ 'Drum', 'Bass', 'Piano' ][ 'Drum', 'Bass', 'Piano' ]
  3. [ 'Drum', 'Bass', 'Trumpet' ][ 'Drum', 'Bass', 'Piano' ]
  4. [ 'Drum', 'Bass', 'Piano' ]
    TypeError
  5. TypeError

해설

답: 2

  • 1번 줄에서 let 키워드로 변수 team1을 선언하고, 배열하나를 할당해줬습니다. 그리고 2번 줄에서 const 키워드로 변수 team2를 선언하고, team1을 할당했는데요.
  • 배열은 참조형 값이기 때문에, 이때 주소 값이 복사되어서 일단 team1과 team2는 서로 같은 배열을 가리키게 됩니다.
  • 그리고 4번 줄에서 splice 메소드를 통해 team1의 값을 변경하고 있는데요. 2번 인덱스에 있는 값을 'Trumpet' 으로 수정했습니다.
  • 그러면 현재까지, team1과 team2가 함께 가리키는 배열의 모습은 [ 'Drum', 'Bass', 'Trumpet' ]이 됩니다.
  • 다음으로 5번 줄에서는 const 키워드로 선언한 변수 team2의 값을 변경하고 있는데요.
  • const 키워드로 변수를 선언하게 되면 값을 재할당할 수 없지만, 할당된 값이 객체나 배열일 경우 메소드를 통해서 그 값을 변경할 수가 있는데요.
  • 그렇기 때문에 제시된 코드는 아무런 Error 없이 실행되서, 2번 인덱스 값이 'Piano'로 변경이 됩니다.
  • 그래서 team1, team2가 같은 배열을 가리키고 있기 때문에, 결과적으로 두 변수가 가리키는 배열의 모습은 [ 'Drum', 'Bass', 'Piano' ]이 됩니다. 정답은 (2)번 입니다.
  • 자바스크립트에는 variable의 약자를 따서 var라는 키워드로 변수를 선언할 때가 있었습니다.

    • 자바스크립트에 변수를 선언하는 방식은 처음부터 let과 const가 아니였던 것이죠!
    • 그래서 오래된 프로젝트들이나 혹은 자바스크립트의 정보들을 정리해둔 조금 오랜 시간이 지난 블로그들을 살펴보면 심심찮게 var라는 키워드를 만나볼 수가 있는데요.
    • 이제는 거의 사용되지 않는 var 변수, 그래도 언제 어떻게 만나게 될지 모르니 조금만 살펴봅시다.
  • 변수 선언

    • 일단 아래 코드 처럼 var 변수는 let 이나 const 처럼 똑같이 키워드 다음에 변수 이름을 써서 선언할 수 있고,

      var myVariable;
      myVariable = 'codeit';

    • 혹은 키워드와 변수이름, 그리고 할당연산자와 값으로 선언과 동시에 값을 할당해 줄 수도 있습니다.
      var myVariable = 'codeit';

    • 중복 선언 허용

    • var 키워드로 선언한 변수의 첫 번째 문제는, let과 const와는 다르게 중복 선언이 가능하다는 겁니다.

    • 똑같은 이름으로 변수를 한 번 더 선언하게 되면, 에러가 발생하는 것이 아니라 그냥 기존의 변수를 덮어써 버리는 것이죠.

    • let키워드로 선언한 변수에 값을 재할당하는 것과는 엄연히 다릅니다.

      var myVariable = 'codeit';
      console.log(myVariable);
      var myVariable = 'Codeit!';
      console.log(myVariable);
      codeit
      Codeit!

    • 이렇게 변수가 중복선언이 되면, 길고 복잡한 코드를 작성할 때 실수를 할 가능성이 커지고, 상황에 따라서는 치명적인 오류가 발생할 수 있습니다.

  • 함수 스코프

    • var 키워드 변수가 사라진 두 번째 문제는 Scope의 문제입니다.
    • let과 const 키워드로 선언한 변수는 if, for, function 등등 어떤 키워드와 관계없이 코드 블록, 즉 {} 중괄호로 감싸진 부분을 기준으로 scope를 갖게 되지만, var 키워드로 선언한 변수는 scope가 function에서만 구분되어 있습니다.

      {
         let x = 3;
      }
      function myFunction( )  {
         let y = 4;
      }
      console.log(x);
      console.log(y);
      //Uncaught ReferenceError: x is not defined

  • let이나 const 키워드의 경우에는 중괄호로 감싸진 경우라면 모두 중괄호 밖에서는 지역 변수에 접근할 수 없습니다.

    {
    var x = 3;
    }
    function myFunction() {
    var y = 4;
    }
    console.log(x);
    console.log(y);
    //3
    //Uncaught ReferenceError: y is not defined

    • 하지만 var 변수는 지역변수의 구분이 함수에만 있기 때문에 if, for, while, switch 등 다양한 상황에서 선언한 변수가 자칫, 전역변수의 역할을 하게 될 수도 있는 것이죠.

    • 참고로 이렇게 함수를 기준으로만 적용되는 스코프를 함수 스코프, 코드 블록을 기준으로 적용되는 스코프를 블록 스코프라는 용어를 사용한다는 점도 참고해 주세요!

  • 끌어올림 (Hoisting)

    console.log(myVariable);
    let myVariable;
    //Uncaught ReferenceError: Cannot access 'myVariable' //before initialization

    • let과 const로 선언한 변수는 선언되기 이전에 사용될 수 없습니다. 하지만, var 변수는 함수 스코프를 기준으로 선언되기 이전에도 변수에 접근이 가능한데요.

      console.log(myVariable);
      var myVariable;
      //undefined

    • 변수의 선언이 끌려 올라가서 마치, 2번째 줄과 첫 번째 줄이 바뀐 것처럼 동작하는 데요.

      var myVariable;
      console.log(myVariable);

    • 이렇게 변수가 끌어올려 지는 현상을 '호이스팅(hoisting)'이라고 부른다는 점도 기억하자.

    • 다행히 호이스팅은 선언과 동시에 값을 할당하더라도, 선언문만 올려지기 때문에 값은 그대로 두 번째 줄에 남아있는 데요.

      console.log(myVariable);
      var myVariable = 2;
      console.log(myVariable);
      //undefined
      //2

    • 하지만 이런 식으로 동작하는 방식은 코드의 흐름을 방해하기에 충분해 보이죠? 한 가지 주의해야 될 부분은, 함수를 선언할 때도 이 호이스팅이 적용됩니다.

      sayHi();
      function sayHi() {
      console.log('hi');
      }

    • 이렇게 코드를 작성하더라도 실행해보면,
      // hi
      당연한 듯 함수가 잘 실행되는 모습을 확인할 수 있습니다.

    • 이런 현상은 함수를 한 번 선언하고 나면 어디서든 유연하게 사용할 수 있다는 장점이 있지만, 코드의 흐름에는 부정적인 영향을 끼칠 수 있습니다.

    • 그래서 함수를 선언할 때는 가급적 코드 윗부분에 선언하거나, 호출을 항상 아래쪽에서 한다거나 나름대로 규칙을 세워서 코드를 작성하시기를 권장한다.

profile
중고

0개의 댓글