[K.O Javascript] 객체 : 기본

진형욱·2022년 8월 8일
0

TIL - JavaScript

목록 보기
3/6
post-thumbnail

ko.javascript에서 Javascript를 공부를 하며 배운 내용을 개인적으로 블로그에 정리하고 학습하는 글입니다.

객체:기본

객체

객체는 몇 가지 특수한 기능을 가진 연관 배열이며, 즉 프로퍼티 값을 저장한다.

  • 프로퍼티 => 서랍장 안 파일

    • 프로퍼티 키는 문자열이나 심볼이어야 한다. (보통은 문자열)

    • 값은 어떤 자료형이나 가능하다.

  • 객체의 키 => 파일 각각에 붙어있는 이름표

복잡한 서랍장 안에서 이름표를 보고 원하는 파일을 쉽게 찾듯이,
객체에서 키를 이용해 프로퍼티를 쉽게 찾을 수 있다. (추가, 삭제도 마찬가지)


빈 객체를 만드는 방법

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

  • 중괄호를 이용해 객체를 선언하는 것을 객체 리터럴이라고 한다.

    - 점 표기법 : obj.property

    - 대괄호 표기법 : obj["property"] : 텍스트 대괄호 표기법을 사용하면 obj[varWithKey]와 같이 변수에서 키를 가져올 수 있다.


객체-연산자

delete obj.prop : 프로퍼티를 삭제하고 싶을때

"key" in obj : 해당 key를 가진 프로퍼티가 객체 내에 있는지 확인할 때

for (let key in obj) : 프로퍼티를 나열할 때


const 상수 객체는 수정될 수 있다.

const user = {
name : "john"
};
user.name = "Pete";
alert(user.name) // Pete

constuser의 값을 고정하지만, 그 내용은 고정하지 않는다.
constuser를 전체적으로 설정하려고 할 때만 오류가 발생한다.

지금까지는 '순수 객체'라 불리는 일반 객체에 대해 학습했다.
객체 이외에도 다양한 종류의 객체가 있다.

Array - 정렬된 데이터 컬렉션을 저장할 때 쓰임 (자주씀)
Date - 날짜와 시간 정보를 저장할 때 (자주씀)
Error - 에러 정보를 저장할 때


정수 프로퍼티

  • 변형 없이 정수에서 왔다 갔다 할 수 있는 문자열을 의미한다.
    • 문자열 "49"는 정수로 변환하거나 변환한 정수를 다시 문자열로 바꿔도 변형이 없어서 정수 프로퍼티이다.
    • 하지만 '+49+', '1.2'는 정수 프로퍼티가 아니다.

참조에 의한 객체 복사

객체는 참조에 의한 객체 복사된다. 변수에는 객체 자체가 아닌 참조가 저장이 된다.
(객체가 할당된 변수를 복사할 땐 객체의 참조 값이 복사되고 객체는 복사되지 않는다.)

따라서 객체가 할당된 변수를 복사하거나 함수의 인자로 넘길 땐 객체가 아닌 객체의 참조 값이 저장된다.

변수엔 객체가 그대로 저장되는 것이 아니라, 객체가 저장되어있는 '메모리 주소'인 객체에 대한 '참조 값'이 저장된다.

let user = { name: 'John' };

let admin = user;

admin.name = 'Pete'; // 'admin' 참조 값에 의해 변경됨

alert(user.name); // 'Pete'가 출력됨. 'user' 참조 값을 이용해 변경사항을 확인함

객체의 '진짜 복사본'을 만들기 위한 두가지의 종류

1. '얕은 복사' Object.assign

Object.assign(dest, [src1, src2, src3...])

  • dest는 목표로 하는 객체

  • src1 ~ ... 는 복사하고자 하는 객체

  • src1 ~ ...의 프로퍼티를 dest에 복사한다.

  • dest를 제외한 객체의 프로퍼티 전부가 첫 번재 객체로 복사된다.

  • 마지막으로 dest를 반환한다.

let user = { name: "John" };

Object.assign(user, { name: "Pete" });

alert(user.name); // user = { name: "Pete" }

2. '깊은 복사' _.cloneDeep(obj)로 나뉜다.


가비지 컬렉션(GC)

메모리 관리 기법 중의 하나로, 프로그램이 동적으로 할당했던 메모리 영역 중 필요없게 된 영역을 해제하는 기능

  • 가비지 컬렉션은 엔진이 자동으로 수행하므로 개발자는 이를 억지로 실행하거나 막을 수 없다.

  • 객체는 도달 가능한 상태일 때 메모리에 남는다.

  • 참조된다고 해서 도달 가능한 것은 아니며, 서로 연결된 객체들도 도달 불가능할 수 있다.


메서드와 this

메서드 만들기

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

user.sayHi = function() {
  alert("안녕하세요!");
};

user.sayHi(); // 안녕하세요!

함수 표현식으로 함수를 만들고, 객체 프로퍼티 user.sayHi에 함수를 할당했다.
이렇게 함수를 호출하면 호출값은 안녕하세요!가 나온다.

이렇게 객체 프로퍼티에 할당된 함수를 메서드(method) 라고 부른다.

위 예시에서는 user에 할당된 sayHi메서드이다.

객체 지향 프로그래밍(OOP)
객체를 사용하여 객체를 표현하는 방식을 객체 지향 프로그래밍(OOP)라고 부른다.

  • 올바른 개체를 선택하는 방법
  • 개체 사이의 상호작용을 나타내는 방법
  • 위에 관한 의사결정은 객체 지향 설계를 기반으로 이뤄진다.

메서드와 this

메서드는 객체에 저장된 정보에 접근할 수 있어야 제 역할을 할 수 있다.
대부분의 메서드가 개체 프로퍼티의 값을 활용한다.

메서드 내부에서 this 키워드를 사용하면 객체에 접근할 수 있다.

이때 '점 앞'의 this는 객체를 나타낸다. 정확히는 메서드를 호출할때 사용된 객체.

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

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

};

user.sayHi(); // John

user.sayHi()가 실행되는 동안 thisuser를 나타낸다.

this를 사용하지 않고 외부 변수를 참조해 객체에 접근하는 것도 가능하다.

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

  sayHi() {
    alert(user.name); // 'this' 대신 'user'를 이용함
  }

};

하지만 외부 변수를 사용해 객체를 참조하면 예상치 못한 에러가 발생할 수 있다.

user를 복사해 다른 변수에 할당하고 (admin = user) 하고
user는 전혀 다른 값으로 덮어썼을때, sayHi()원치 않는 값(null)을 참조한다.

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

  sayHi() {
    alert( user.name ); // Error: Cannot read property 'name' of null
  }

};


let admin = user;
user = null; // user를 null로 덮어씁니다.

admin.sayHi(); // sayHi()가 엉뚱한 객체를 참고하면서 에러가 발생했습니다.

alert함수가 user.name대신 this.name을 인수로 받았으면 에러가 발생하지 않았을것.

자유로운 this

  • 자바스크립트에선 모든 함수에 this를 사용할 수 있다.
function sayHi() {
  alert( this.name );
}

동일한 함수라도, 다른 객체에서 호출하면 this가 참조하는 값이 달라진다.

let user = { name: "John" };
let admin = { name: "Admin" };

function sayHi() {
  alert( this.name );
}

// 별개의 객체에서 동일한 함수를 사용함
user.f = sayHi;
admin.f = sayHi;

// 'this'는 '점(.) 앞의' 객체를 참조하기 때문에
// this 값이 달라짐
user.f(); // John  (this == user)
admin.f(); // Admin  (this == admin)

admin['f'](); // Admin (점과 대괄호는 동일하게 동작함)
  • this는 '점(.)앞의 객체를 참조하기 때문에 this가 참조하는 값이 달라진다.

  • '점'과 '대괄호'는 동일하게 동작한다.

객체 없이 호출하기(this)

  • this는 객체가 없어도 함수를 호출할 수 있다.

  • 엄격 모드가 아닐 때(지정된 값이 없을때) this전역 객체를 참조한다. 브라우저 환경에서는 window라는 전역 객체를 참조한다.

this가 없는 화살표 함수

  • 화살표 함수는 일반 함수와 달리 this를 가지지 않는다.
    • 만약 화살표 함수에서 this를 참조한다면, 화살표 함수가 아닌 평범한 외부 함수에서 this 값을 가져오는 것이다.

new 연산자와 생성자 함수

  • 객체 리터럴( {...} )을 사용하면 객체를 쉽게 만들 수 있다. 하지만 개발을 하다 보면 유사한 객체를 여러개 만들어야 할 때가 생기는데, new연산자와 생성자 함수를 사용하면 유사 객체를 여러개 만들 수 있다.

생성자 함수

- 생성자 함수는 일반함수이다.
다만 일반함수와 구분을 짓기 위해 함수 이름 첫 글자를 대문자로 쓴다.

- 반드시 new연산자와 함께 호출한다.
new와 함게 호출하면 내부에서 this가 암시적으로 만들어지고, 마지막엔 this가 반환된다

  1. 함수 이름의 첫 글자는 대문자로 시작한다.
  2. 반드시 new연산자를 붙여 실행한다.

예시:

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

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

alert(user.name); // 보라
alert(user.isAdmin); // false
  1. 빈 객체를 만들어 this에 할당
  2. this에 새로운 프로퍼티를 추가해 this를 수정
  3. this를 반환
function User(name) {
  // this = {};  (빈 객체가 암시적으로 만들어짐)

  // 새로운 프로퍼티를 this에 추가함
  this.name = name;
  this.isAdmin = false;

  // return this;  (this가 암시적으로 반환됨)
}

즉 생성자의 의의는 재사용할 수 있는 객체 생성 코드를 구현하는것이다.

모든 함수는 생성자 함수가 될 수 있다는 점을 잊지 말자.
new를 붙여 실행하면 어떤 함수라도 위에 언급된 알고리즘이 실행된다.
이름이 '첫 글자가 대문자'인 함수는 new를 실행해야 한다는것도 잊지말자.


생성자와 return문

생성자 함수에는 return문이 없다. 반환해야 할 것들은 모두 this에 저장되고, this는 자동으로 반환되기 때문에 반환문을 명시적으로 사용할 필요가 없다.

만약 return문이 있다면?

  • 객체를 return한다면 this대신 객체가 반환.
  • 원시형을 return한다면 return문이 무시.

return뒤에 객체가 오면 생성자 함수는 해당 객체를 반환, 이외 경우는 this가 반환된다.

예시:

function BigUser() {

  this.name = "원숭이";

  return { name: "고릴라" };  // <-- this가 아닌 새로운 객체를 반환함
}

alert( new BigUser().name );  // 고릴라

예시:_(아무것도 return하지 않았을때)

function SmallUser() {

  this.name = "원숭이";

  return; // <-- this를 반환함
}

alert( new SmallUser().name );  // 원숭이

옵셔널 체이닝

옵셔널 체이닝의 등장

?.?.'앞'의 평가 대상이 'undefined'이나 'null'이면 평가를 멈추고 'undefined'를 반환한다.

예시:

let user = {}; // 주소 정보가 없는 사용자

alert( user?.address?.street ); // undefined, 에러가 발생하지 않습니다.
  • user?.address로 주소를 읽으면 아래와 같이 user 객체가 존재하지 않더라도 에러가 발생하지 않는다.

예시:

let user = null;

alert( user?.address ); // undefined
alert( user?.address.street ); // undefined

위 예시를 통해 ?.?. '앞 '평가 대상에만 동작되고, 확장은 되지 않는걸 알 수 있다.

?.앞의 변수는 꼭 선언되어 있어야 한다.
변수 user가 선언되어있지 않으면 user?.@ 평가시 에러가 발생한다.


단락 평가

?.는 왼쪽 평가대상에 값이 없으면 즉시 평가를 멈춘다 이런 평가 방법을 단락 평가라고 부른다.

let user = null;
let x = 0;

user?.sayHi(x++); // 아무 일도 일어나지 않습니다.

alert(x); // 0, x는 증가하지 않습니다.

?.()와 ?.[]

  • .대신 대괄호 []를 사용해 객체 프로퍼티에 접근하는 경우엔 ?.[]를 사용할 수도 있다.

?.와 delete의 조합

delete user?.name; // user가 존재하면 user.name을 삭제합니다.

?.은 읽기나 삭제하기에는 사용할 수 있지만 쓰기에는 사용할 수 없다.


요약

  • obj?.prop – obj가 존재하면 obj.prop을 반환하고, 그렇지 않으면 undefined를 반환함

  • obj?.[prop] – obj가 존재하면 obj[prop]을 반환하고, 그렇지 않으면 undefined를 반환함

  • obj?.method() – obj가 존재하면 obj.method()를 호출하고, 그렇지 않으면 undefined를 반환함


심볼형

cF. 자바스크립트는 객체 프로퍼티 키로 오직 문자형과 심볼형만을 허용한다.
숫자형, 불린형 모두 불가능하고 오직 문자형과 심볼형만 가능하다.

심볼유일한 식별자를 만들고 싶을때 사용한다. Symbol()을 사용하면 심볼값을 만들 수 있다.

// id는 새로운 심볼이 됩니다.
let id = Symbol();

심볼 이름이라 불리는 설명을 붙일 수도 있다. 심볼 이름은 디버깅 시 아주 유용하다.

// 심볼 id에는 "id"라는 설명이 붙습니다.
let id = Symbol("id");
  • 심볼은 유일성이 보장되는 자료형이기 때문에, 설명이 동일한 심볼을 여러 개 만들어도 각 심볼값은 다르다.

  • 심볼에 붙이는 설명(심볼 이름)은 어떤 것에도 영향을 주지 않는 이름표 역할만을 한다.


전역 심볼

심볼은 이름이 같더라도 모두 별개로 취급된다. 하지만 이름이 같은 심볼이 같은 개체를 가리키길 원하는 경우에는 심볼 "id"를 이용해 특정 프로퍼티에 접근해야 한다고 가정하자.

전역 심볼 레지스트리 는 이런 경우를 위해 만들어졌다. 전역 심볼 레지스트리 안에 심볼을 만들고, 해당 심볼에 접근하면, 이름이 같은 경우 항상 동일한 심볼을 반환해준다.

Symbol.for(key)

  • key라는 이름을 가진 전역 심볼을 반환한다.

  • key라는 이름을 가진 전역 심볼이 없으면 새로운 전역 심볼을 만들어준다.

  • key가 같다면 Symbol.for은 어디서 호출하든 상관없이 같은 심볼을 반환한다.

새로운 심볼을 생성하거나, 레지스트리 안에 있는 심볼을 읽으려면 Symbol.for(key)를 사용하면 된다.

// 전역 레지스트리에서 심볼을 읽습니다.
let id = Symbol.for("id"); // 심볼이 존재하지 않으면 새로운 심볼을 만듭니다.

// 동일한 이름을 이용해 심볼을 다시 읽습니다(좀 더 멀리 떨어진 코드에서도 가능합니다).
let idAgain = Symbol.for("id");

// 두 심볼은 같습니다.
alert( id === idAgain ); // true

전역 심볼 레지스트리 안에 있는 심볼은 전역 심볼이라고 불린다.
광범위하게 사용해야 하는 심볼이라면 전역 심볼을 사용하자.

Symbol.keyFor(sym)

Symbol.for(key)에 반대되는 메서드. Symbol.keyFor(sym)를 사용하면 이름을 얻을 수 있다.

// 이름을 이용해 심볼을 찾음
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");

// 심볼을 이용해 이름을 얻음
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id
  • Symbol.keyFor전역 심볼 레지스트리를 뒤져서 해당 심볼의 이름을 얻어낸다.

  • 검색 범위가 전역 심볼 레지스트리이기 때문에 전역 심볼이 아닌 심볼에는 사용할 수 없다.

  • 전역 심볼이 아닌 인자가 넘어오면 Symbol.keyFor는 undefined를 반환합니다.


객체를 원시형으로 변환하기

원시값을 기대하는 내장 함수나 연산자를 사용할 때 객체-원시형으로의 형 변환이 자동으로 일어난다.

객체-원시형으로 형 변환은 hint를 기준으로 세 종류로 구분한다.

1. "string"

  • alert 함수같이 문자열을 기대하는 연산을 수행할 때는(객체-문자형 변환), hint가 string이 된다.

2. "number"

  • 수학 연산을 적용하려 할 때(객체-숫자형 변환), hint는 number가 된다.

3. "default"

  • 연산자가 기대하는 자료형이 ‘확실치 않을 때’ hint는 default가 된다.(드물게 발생)

따라서 실무에선 hint가 "default"인 경우와 "number"인 경우를 합쳐서 처리하는 경우가 많다.

obj.toString()

  • obj.toString()만 사용해도 '모든 변환’을 다 다룰 수 있기 때문에, 실무에선 obj.toString()만 구현해도 충분한 경우가 많다.

  • obj.tostring()로깅이나 디버깅 목적으로 자주 사용된다.

profile
90% of my problems magically disappeared when I slept well, ate well and went on regular walks

0개의 댓글