Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

heyday2024 님의 블로그

[모던 자바스크립트 스터디]10~12장(객체 리터럴, 원시 값과 객체의 비교, 함수) 정리 본문

스터디

[모던 자바스크립트 스터디]10~12장(객체 리터럴, 원시 값과 객체의 비교, 함수) 정리

heyday2024 2024. 10. 22. 01:09

10장: 객체 리터럴

자바스크립트는 객체 기반의 프로그래밍 언어

객체: 원시값을 제외한 나머지(함수, 배열, 정규 표현식 등) 값은 모두 객체.

 

원시타입 vs 객체 타입

 

원시타입은 단 하나의 값만 나타내지만, 

객체 타입(object/reference type)은 다양한 타입의 값을 하나의 단위로 구성한 복합적인 자료구조(data structure)임

 

원시타입의 값은 변경 불가능한 값(immutable value)이지만,

객체타입의 값, 즉 객체는 변경 가능한 값(mutable value)임


 

<객체의 프로퍼티>

 

객체는 0개 이상의 property(속성)로 구성된 집합,

property는 key 와 value로 구성됨

  • property의 key는 일반적으로 문자열 사용(symbol도 사용가능 그러나 많이 안쓰임)
    • 식별자 네이밍 규칙(카멜케이스, 파스칼케이스, 스네이크케이스 등...)을 따르지 않는 이름에는 반드시 따옴표 사용해야함
    • 프로퍼티 키를 중복으로 사용하면 최근에 선언된 것이 사용됨(덮어씀)
    • 문자열, 문자열로 평가할 수 있는 표현식을 사용해서 프로퍼티 키 동적으로 생성 가능.
      • obj[key] ="새로운 프로퍼티에 할당된 값"
    • 프로퍼티 키에 문자열이나 symbol 외의 값을 사용하면 암묵적 타입 변환을 통해 문자열로 변경됨
let foo = {
0 : 1,
1 : 2,
2 : 3,
};

console.log(foo) //{0 : 1, 1 : 2, 2 : 3}

프로퍼티 키를 숫자로 사용했지만, 암묵적 타입 변환으로 문자열로 변환되어 사용됨.

 

자바스크립트의 함수는 일급객체이기 떄문에 함수도 속성의 값으로 넣을 수 있음.

----> property의 값이 함수면 그것은 메서드.


<객체 생성>

 

JS는 프로토타입 기반 객체지향 언어로 클래스 기반 객체지향 언어와 달리 다양한 객체 생성 방법 지원함

  • 객체 리터럴
  • Object 생성자 함수
  • 생성자 함수
  • Object.create 메서드
  • 클래스(ES6)

*Literal : 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용하여 값을 생성하는 표기법.

 

객체 리터럴은 중괄호({}) 안에 0개 이상의 property 정의함.

- 객체 리터럴의 중괄호는 코드 블록을 의미하지 않음. 그럼으로 코드 블록 뒤에 세미콜론 붙이는 것 안해도 됨.

 

객체 리터럴 은 자바스크립트의 유연함과 강렬함을 대표함. 다른 언어들과 갈리 객체 생성을 위해 클래스를 먼저 정의하고 new 연산자와 함께 생성자를 호출할 필요가 없음. (숫자나 문자열 값 할당할 떄처럼 걍 생성하면 됨.)

  • 객체 리터럴에 객체 생성과 동시에 프로퍼티를 만들수 있음
  • 객체 생성 이후에 속성을 동적으로 추가할수도 있음.

<프로퍼티 접근법>

  1. 마침표 표기법 dot notation  . 
  2. 대괄호 표기법 bracket notation []
let person = {
'last-name': 'Lee',
1: 10
};

person.'last-name'; //SyntaxError: Unexpected String

person.last-name; 
// 브라우저 환경: NaN
//Node.js 환경: ReferenceError: name is not defined

person[last-name]; //ReferenceError: name is not defined
person['last-name']; //Lee

person.1; // SyntaxError: Unexpected number
person.'1'; //SyntaxError: Unexpected string 
//프로퍼티 키가 숫자로 이뤄진 문자열의 경우 따옴표를 생략할 수도 있음.
person[1]; //10: person[1] -> person ['1']
person['1']; 10

- 점 표기법에서는 유효한 JavaScript 식별자만 사용 가능. 'last-name'은 하이픈(-)이 포함되어 있으므로 유효한 식별자가 아니기 때문에 점 표기법으로는 접근할 수 없으며, 구문 오류(SyntaxError)가 발생

- 대괄호 표기법은 점 표기법과 달리 하이픈(-), 공백, 숫자로 시작하는 등 유효하지 않은 식별자를 포함한 속성에도 접근가능.

- 이때 속성 이름을 반드시 문자열로 지정해야 하며, 따라서 'last-name' 속성에 접근하려면 대괄호 표기법과 문자열을 함께 사용하여 접근해야 함.

 

<프로퍼티 삭제>

delete 객체.속성;

 

<ES6에서 추가됨 객체 리터럴의 확장 기능>

프로퍼티 축약 표현:

프로퍼티 값으로 변수를 사용하는 경우,

변수 이름과 프로퍼티 키가 동일한 이름일 때 프로퍼티 키를 생략 property shorthand 할 수 있음.

 

let x = 1, y = 2;

const obj = {x, y};

console.log(obj); //{x:1, y:2}

- 이런 식으로 변수와 값 먼저 선언, 할당 하고, 변수를 객체에 나중에 넣어서 객체 생성 가능.

 

const prefix = 'prop';
let i = 0;

const obj = {
	[`${prefix}-${++i}`]:i,
    [`${prefix}-${++i}`]:i,
    [`${prefix}-${++i}`]:i,
};
console.log(obj); //{prop-1: 1, prop-2: 2, prop-3: 3}

- 이런 식으로 객체 리터럴 내부에서도 계산된 프로퍼티 이름으로 프로퍼티 키 동적 생성 가능.

 


11장: 원시 값과 객체의 비교

원시타입의 값: 원시 값은 변경 불가능한 값 immutable value

  • 원시 값을 변수에 할당하면 변수(확보된 메모리 공간)에는 실제 값이 저장됨.

- 여기서 주의해야될 점은 변경이 불가능하다는 뜻은 변수가 아니라 값에 대한 진술임(메모리에 저장된 값 자체를 바꿀 수 없음!!! 새로운 메모리에 새로운 값을 할당시키고 그 값을 가리키는 참조로 변경! 즉, 원래 저장되어있었던 값 자체를 바꾼 것은 아님.)

 

- 그리고 copy를 하던 새로운 값을 생성/변경하든 그 값이 아닌 메모리 주소가 전달/변경되는 것임을 생각하자!!!!

 

 ===> 두 변수의 원시 값은 서로 다른 메모리 공간에 저장된 별개의 값이 되어 어느 한쪽에서 재할당을 통해 값을 변경하더라도 서로 간섭할 수 없음(서로 다른 값의 메모리 주소를 갖음!!)

 

 

객체타입의 값: 객체 타입의 값은 변경 가능한 값 mutable value

  • 객체를 변수에 할당하면 변수(확보된 메모리 공간)에는 참조 값이 저장됨.

- 원시 값은 변경 불가능한 값으로 원시 값을 갖는 변수의 값을 변경하려면 재할당 외에는 방법이 없음.

- 하지만, 객체는 변경 가능한 값임. 따라서 객체를 할당한 변수는 재할당 없이 객체를 직접 변경할 수 있음. 즉, 재할당 없이 프로퍼티를 동적으로 추가하고, 프로퍼티 값을 갱신할 수 있으며 프로퍼티 자체를 삭제하는 것도 가능!!

- 근데 여러개의 식별자가 하나의 객체를 공유할 수 있으므로 복사할 때 주의해야함. (얕은 복사, 깊은 복사 <---이건 많이 다뤄서 pass~~)

 

Q. 왜 이렇게 다른거야??? 

A. 참조타입들은 원시타입보다 상대적으로 많은 양의 데이터를 갖기 떄문에 매번 복사, 변경 시 생성하는데 비용이 많이 든다. 그리고 얼마만큼의 많은 데이터 양을 갖게 될 지 모르기 때문에 따로 메모리 구역을 주는 것임...


12장: 함수

입력 ----> 출력 까지 일련의 과정을 문statement로 구현하고, 코드 블록을 감싸서 하나의 실행단위로 정의한 것!

 

function 함수이름 (매개변수 parameter){

return 반환값;

}

함수이름(인수 argument); //함수 호출 function call/invoke

- function definition해서 생성 (함수 선언문)

 

함수 리터럴

  • 이름있는 함수: 기명함수 named function// 이름 없는 함수: 익명함수 anonymous function

함수 필요 이유?? 몇번이든 호출할 수 있으니까 같은 작업을 여러번 실행하고자 할때 좋음(코드의 재사용)

  • 유지보수의 편의성
  • 코드의 신뢰성
  • 코드의 가독성

<함수 정의>

  • 함수 선언문
  • 함수 표현식(함수를 변수에 할당하는 것...일급객체 특성)
  • 화살표 함수 () =>
    • 생성자 함수로 사용할 수 없음
    • this 바인딩 안함(이미 바인딩된 것 따름: 상위 스코프...)
    • prototype 프로퍼티 없음
    • arguments 객체 생성 안함
  • Function 생성자 함수(사용 안할 듯....)
let add = new Function('x', 'y', 'return x+y');

- 일반적이지도 바람직하지도 않은 방식

- Function 생성자는 클로저 생성 안하고, 함수 선언문/표현식과 다르게 동작함.

 

 

*  당연하지만, 함수 선언문은 함수 이름을 무조건 붙여줘야하고(나중에 호출해야하니까...)

함수 표현식은 굳이 이름을 붙일 필요 없음...

 


 

<함수 호이스팅>

함수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작

 

- 함수 표현식으로 함수 정의하면 함수 호이스팅이 발생하는 것이 아니라 변수 호이스팅이 발생됨

  • 변수 할당문의 값은 할당문이 실행되는 시점(런타임)에 평가되기 때문(함수표현식은 함수를 변수에 할당하는 거니까...)

 

<arguments 객체>

함수를 정의할 때 매개변수 개수를 확정할 수 없는 가변 인자 함수를 구현할 때 유용하게 사용

- 일반 함수에서 사용되는 특별한 객체로, 함수 호출 시 전달된 모든 인자를 배열 형태로 보관함

function sum() {
    let total = 0;
    for (let i = 0; i < arguments.length; i++) {
        total += arguments[i];
    }
    return total;
}

console.log(sum(1, 2, 3)); // 6
console.log(sum(4, 5, 6, 7)); // 22

굳이 sum 에 매개변수를 정의하지 않아도 arguments 사용으로 손쉽게 처리 가능

- arguments 객체는 진짜 배열이 아니라 유사 배열 객체이기 때문에 배열의 메소드를 바로 사용할 수 없음!!!!

 ===> 이를 배열로 변환하려면 Array.from(arguments)와 같은 방법을 사용해야 함!!!!

 

  • 화살표 함수(arrow function)에서는 arguments 객체가 사용되지 않으므로, 대신 rest 파라미터(...)를 사용하여 가변 인자를 처리함
    • 화살표 함수에서 arguments 대신 rest 파라미터 사용: 
const sum = (...numbers) => {
    return numbers.reduce((total, num) => total + num, 0);
};

console.log(sum(1, 2, 3)); // 6
console.log(sum(4, 5, 6, 7)); // 22

매개변수를 rest 파라미터를 사용해서 유연하게 인자를 받아들이고 처리함.

 


 

참고로!!!!!

rest parameter(...)  vs spread operator(...) 차이

 

rest 파라미터:

  • 함수 정의에서 사용.
  • 여러 인자를 하나의 배열로 수집.
  • 함수 매개변수로 수집되는 값을 처리.
function multiply(...numbers) {
    return numbers.reduce((total, num) => total * num);
}
multiply(1, 2, 3); // 6

 

 

spread 연산자:

  • 함수 호출 또는 배열/객체에서 사용.
  • 배열이나 객체를 개별 요소로 확장.
  • 배열, 객체의 요소를 펼쳐서 전달
const array1 = [1, 2];
const array2 = [3, 4];
const mergedArray = [...array1, ...array2]; // [1, 2, 3, 4]

 

 


<인수 확인>

function add(x, y){
return x+y;
}

console.log(add(2)); // NaN
console.log(add("a", "b")); //ab

- 두번째는 스트링끼리 +연산자로 결합됨,

- 근데 첫번째는 인수가 한개 밖에 전달이 안 되었는데 왜 에러 없이 NaN이 출력된걸까????why????

  • 1. 자바스크립트 함수는 매개변수와 인수의 개수가 일치하는 확인하지 않음!
  • 2. 자바스크립트는 동적타입언어라서 자바스크립트 함수는 매개변수의 타입을 사전에 지정할 수 없음(C++와 완전 다름.....)

---> 그래서 자바스크립트의 경우 함수를 정의할 떄 적절한 인수가 잘 전달되었는지 확인해줄 필요가 있음!!!

(따라서 타입스크립트와 같은 정적 타입을 선언할 수 있는 자바스크립트의 상위 확장을 도입해서 런타임이 아닌 컴파일 시점에 부적절한 호출/ 오류 방지하는 것임.)

 

  • 인수가 전달되지 않은 경우, 단축평가 사용해서 매개변수에 기본값 할당 가능
function greet(name) {
    name = name || 'Guest';  // 인수가 없으면 'Guest' 할당
    console.log(`Hello, ${name}!`);
}

greet();         // Hello, Guest!
greet('John');   // Hello, John!
  • arguments 객체를 통해 인수 개수 확인 가능
function greet() {
    let name = arguments[0] !== undefined ? arguments[0] : 'Guest';
    console.log(`Hello, ${name}!`);
}

greet();         // Hello, Guest!
greet('John');   // Hello, John!
  • ES6에 도입된 매개변수 기본값을 사용해서 함수 내에서 수행하던 인수 체크, 초기화를 간소화할 수 있음.
function greet(name = 'Guest') {
    console.log(`Hello, ${name}!`);
}

greet();         // Hello, Guest!
greet('John');   // Hello, John!

참고로 매개변수 a, b, c 이렇게 여러개 있으면 기본값은 뒤에서부터(c부터) 넣어주는 것을 권장함.

 

* 매개변수는 가급적 적은 갯수를 사용하자.(3개 이상 넘기지 않는 것을 권장)

 

<즉시 실행 함수 immediately incoked function expression>

단 한번만 호출됨. 다시 호출할 수 없음.

(함수{}());

 

<재귀 함수 recursive function>

함수 내용/내부에서 자기 자신을 호출하는 것.

  • factorial 구현할 때 주로 사용
  • 주의: 재귀함수는 탈출조건이 반드시 필요 ---> 아니면 무한으로 호출되어서 stack overflow 문제 생김.

<중첩함수 rested function>

- 중첩 함수를 포함하는 함수 = 외부함수 outer function

- 일반적으로 중첩함수는 자신을 포함하는 외부함수를 돕는 helper function 역할함

 

<콜백 함수>

함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수

--> 매개변수를 통해 함수의 외부에서 콜백함수를 전달받은 함수 = 고차함수(higher-order function)

 

<순수함수 vs 비순수 함수>

1. pure function: 어떤 외부 상태에 의존하지도 변경하지도 않는 side effect이 없는 함수

 

  • 동일한 입력에 대해 항상 동일한 결과를 반환. (참조 투명성)
  • 부작용이 없기 때문에 외부 상태를 변경하거나 영향을 주지 않음
  • 함수가 예측 가능하고 테스트가 용이.
function add(a, b) {
    return a + b;
}

console.log(add(2, 3)); // 5
console.log(add(2, 3)); // 항상 5 (동일한 입력에 대해 동일한 결과)

 

 

2. impure function: 외부 상태에 의존하거나 외부 상태를 변경하는 side effect 있는 함수

 

 

  • 동일한 입력에 대해 항상 동일한 결과를 반환하지 않을 수 있음. 외부 상태에 의존하기 때문.
  • 외부 상태를 변경하기 때문에 예측하기 어렵고, 테스트가 어려워짐.
  • 부작용을 일으킬 수 있어 코드의 버그를 유발할 가능성이 높음.

 

let count = 0;

function increment() {
    return ++count; // 외부 상태(count)를 변경
}

console.log(increment()); // 1
console.log(increment()); // 2 (외부 상태가 변경되었기 때문에 결과가 다름)

 


확실히 다른 프로그래밍에 비해 자바스크립트는 유연성이 큰 장점인 것 같다.

하지만 그런 유연성 때문에 오류를 방지하기 위해 코드를 예리하게 짜는 것이 중요하다고 생각한다.

항상 코드짤 때 신중하게 변수 이름부터 각 데이터 타입을 지정하고 함수를 다루는 것까지 조심 또 조심하자.

 

많은 내용에 조금 지치지만 그 래 도!!!

이 악물고 스펀지처럼 자바스크립트를 모두 빨아들여보자구영!!.......... 다른 분들도 모두 화이팅!!!!!