[The Modern JavaScript] The JavaScript language - Objects: the basics

둡둡·2022년 12월 28일
0

Modern-JavaScript

목록 보기
6/10

Objects

  • 객체형에는 다양한 데이터를 담을 수 있는 연관 배열(associative array)
  • 키(key)로 구분된 데이터 집합이나 복잡한 개체(entity) 저장 가능
  • {프로퍼티(property) - 키(key): 값(value)}
    • 키는 문자형, 값은 모든 자료형 가능
  • {..} : 객체 리터럴(object literal)

Literals and properties

// 객체 user에 2개의 프로퍼티
let user = {		// 객체
  name: "John", 	// 키: name, 값: John
  age: 30,			// 키: age, 값: 30
  "is admin":true,
};

alert( user.name ); // John
alert( user.age ); // 30
  • 콜론(:)을 기준으로 왼쪽 키, 오른쪽 값
  • 프로퍼티 키는 프로퍼티 이름 또는 식별자라고도 불림
  • 점 표기법(dot notation)으로 프로퍼티 값 읽기
  • 프로퍼티 값 추가/삭제(delete) 가능
  • 여러 단어로 프로퍼티 이름을 생성할 시 따옴표("") 표기
  • trailing, hanging 쉼표 : 마지막 프로퍼티 끝에 쉼표 가능

Square brackets

user.is admin = true // 에러

user["is admin"]; // true
user[key] = value;
  • 대괄호 표기법(square brackets)
    • 여러 단어로 생성한 프로퍼티 이름은 점 표기법 사용 불가하여 대괄호 표기법 사용
    • 프로퍼티 변수 값에 접근 가능
let fruit = prompt("어떤 과일을 구매하시겠습니까?","apple");
let bag = {
  [fruit] : 5 // 변수 fruit에서 이름을 동적으로 가져옴
  [fruit + 'Computers'] : 1 // 'apple' + 'Computers' 
};

alert ( bag.apple ); // 5
alert ( bag.appleComputers ); // 1
  • 계산된 프로퍼티(computed property)
    • 대괄호 [] 와 같이 사용하여 복잡한 표현식 가능

Property value shorthand

function addUser(name, age) {
  return {
    name, // name: name과 같음
    age
  };
}
  • 프로퍼티 값에 변수를 사용해 만드는 경우 프로퍼티 값 단축 구문으로 가능

Property names limitations

  • 객체 프로퍼티 이름에는 특별한 제약이 없음
    • 예약어(for, let, return, etc.)도 문자열로 자동 형 변환 되어 사용 가능
  • _proto_만 제한

Property existence test, "in" operator

let user = {};

alert( user.noSuchProperty === undefined ); // true
alert( "key" in object ); // false
  • 존재하지 않는 프로퍼티에 접근하는 경우 undefined 반환
  • 존재 여부를 확인하는 방법
    • === undefined 비교 : true 존재하지 않음
    • "key" in object : true 존재 / false 존재하지 않음

The "for..in" loop

for (let key in object) {
  
  // 각 프로퍼티 키(key)를 이용하여 본문(body) 실행
  alert( key ); // 프로퍼티 이름 key 조회
  alert( object[key] ); // 키에 해당하는 값 value 조회
  
}
  • 반복 변수(looping variable) 선언하여 사용, 다른 변수명 가능

Ordered like an object

  • 키가 정수 프로퍼티(integer property)인 경우 자동 정렬
  • 그 외의 프로퍼티는 객체에 추가한 순서로 정렬

Object references and copying

  • 객체는 '참조에 의해(by reference)' 저장 및 복사
    • 객체가 저장되어 있는 '메모리 주소 = 참조 값'이 저장됨
    • 원시값(프리미티브 타입)은 값 그대로
  • 동일한 객체에 대해 접근하거나 조작하기 위해 여러 변수를 사용할 수 있음
    • 객체가 하나의 서랍장이라면 변수는 서랍장을 열 수 있는 열쇠

Comparison by reference

  • 두 객체가 같은 객체를 참조하는 경우 true 반환

Cloning and merging, Object.assign

  • 기존 객체와 같으면서 독립적인 새로운 객체를 복제하려는 경우
    • 새로운 객체 생성 후 기존 객체의 프로퍼티들을 순회하여 복사
    • Object.assign 사용
      • 여러 객체를 하나로 병합할 때도 사용
Object.assign(dest, [src1, src2, ...])
  • dest : 대상 객체
  • srcN : 복사하려는 객체
  • 객체 srcN들의 모든 프로퍼티를 dest에 복사하고, dest 반환
    • 동일한 이름의 프로퍼티가 있는 경우 덮어쓰기

Nested cloning

  • 깊은 복사(deep cloning)
    • 프로퍼티 값이 다른 객체에 대한 참조 값인 경우
    • 각 obj[key]의 값을 검사하면서, 그 값이 객체인 경우 구조도 함께 복사하는 반복문 사용

Garbage collection

Reachability

  • 자바스크립트 엔진에서는 모든 객체를 모니터링하고 도달할 수 없는 객체는 삭제하는 가비지 컬렉터(garbage collector)가 동작함
  • 자바스크립트는 도달 가능성(reachability)을 통해 메모리 관리 수행
    • '도달 가능한 값'은 어떻게든 접근하거나 사용할 수 있는 값을 의미
    • 현재 함수의 지역 변수와 파라미터
    • 중첩 함수의 체인에 있는 함수에서 사용되는 변수와 파라미터
    • 전역 변수 등
    • 루트가 참조하는 값이나 참조할 수 있는 값

A simple example

// 전역 변수 "user" -> Object("name:'John'") 참조함
let user = {
  name: "John"
};

// user를 다른 값으로 덮어쓰면 참조가 사라지고,
// object(John)는 접근도 참조도 되지 않기 때문에 삭제됨
user = null;

Two references

// Object(John)를 참조하는 값이 user, clone 2개
let user = {
  name: "John"
};
let clone = user;

// user를 다른 값으로 덮어쓰더라도
// clone을 통해 Object(John)에 접근할 수 있기 때문에 삭제되지 않음
user = null;

Interlinked objects

function marry(man, woman) {
  woman.husband = man;
  man.wife = woman;
  
  return {
    father: man,
    mother: woman
  }
}

let family = marry({
  name: "John"
},{
  name: "Ann"
});

  • 전역 객체 family는 father(John) / mother(Ann)로 연결됨
  • father(John)과 mother(Ann)는 wife, husband로 연결됨
delete family.father;
delete family.mother.husband;
  • 만약 두 개의 참조를 삭제하면 John으로 들어오는 참조가 모두 삭제되어 Object(John)는 메모리에서 삭제됨
    • John에서 외부로 나가는 참조는 영향을 주지 않음

Unreachable island

  • 객체들이 연결되어 섬과 같은 구조를 만드는데, 이 섬에 도달할 방법이 없는 경우 객체 전부가 모두 삭제됨
family = null;

  • 만약 family의 참조를 삭제하면, father(John)와 mother(Ann)는 도달할 수 없는 섬이 되어 전체가 메모리에서 삭제됨

Internal algorithms

  • 'mark-and-sweep' : 가비지 컬렉션의 기본 알고리즘
    • 가비지 컬렉터는 루트(root) 정보를 수집하고 기억(mark)
    • 루트가 참조하고 있는 모든 객체를 방문하고 mark
    • 모든 객체들이 참조하고 있는 객체들도 모두 mark
    • 루트에서 도달 가능한 모든 객체를 방문할 때까지 반복
    • mark 되지 않은 모든 객체를 메모리에서 삭제
  • 다양한 최적화 기법
    • generational collection(세대별 수집)
      • 새로운 객체와 오래된 객체로 구분하여 관리
    • incremental collection(점진적 수집)
      • 작업을 여러 부분으로 분리하여 별도 관리
    • idle-itme collection(유휴 시간 수집)
      • CPU가 유휴 상태일 때에만 가비지 컬렉션 실행

Object methods, "this"

Method examples

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

user.sayHi = function(){
  alert("Hello");
}
/* 
  미리 선언된 함수를 메서드 등록도 가능
  function sayHi() {
    alert("Hello");
  };
  user.sayHi = sayHi; 
  
  메서드 단축 구문
  user = {
    sayHi() { // sayHi: function() 과 동일
      alert("Hello");
    }
  }
*/
  • 메서드(method) : 객체 프로퍼티에 저장된 함수
  • object.doSomething()

"this" in methods

sayHi(){
  // this = user, user.name도 가능
  alert(this.name); // John
}
  • 대부분의 메서드는 객체 프로퍼트의 값을 활용
  • 메서드 내부에서 this 키워드를 사용하면 객체에 접근할 수 있음
  • this는 런타임 시 결정됨, 메서드를 호출할 때 사용된 객체를 의미

"this" is not bound

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

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

user.fn = sayHi;
admin.fn = sayHi;

user.fn(); // John
admin.fn(); // Admin
// admin['fn'](); 동일
  • 동일한 함수라도 다른 객체에서 호출한다면 'this' 값이 달라짐
  • '.' 앞의 객체가 무엇인가에 따라 '자유롭게' 결정됨
  • cf. 객체 없이 this 호출 시 == undefined

Arrow functions have no "this"

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

user.sayHi(); // John
  • 화살표 함수는 자신만의 this를 갖지 않음
  • 화살표 함수에서 this를 참조하면 외부 함수의 this를 참조
  • 외부 컨텍스트에 있는 this를 사용하려는 경우 화살표 함수 권장

Constructor, operator "new"

  • 같은 타입의 (유사한) 객체를 여러 개 만들 때, 'new' 연산자와 생성자 함수 사용

Constructor function

function User(name) {
  // this = {}; 빈 객체가 암시적으로 생성
  
  // 새로운 프로퍼티를 this에 추가
  this.name = name;
  this.isAdmin = false;
  
  // return this; 암시적으로 this 반환
}

let user = new User("John"); // 생성자 함수 호출

alert(user.name); // John
alert(user.isAdmin); // false
  • 생성자 함수
    • 함수 이름의 첫 글자는 대문자로 시작
    • 반드시 'new' 연산자를 붙여 실행
    • 객체 생성 코드의 재사용성
  • new Object(..) 키워드
    • 빈 객체를 만들어 this에 할당
    • 함수 본문 실행, this에 새로운 프로퍼티 추가하여 수정
    • this 반환

Return from constructors

  • 객체를 return 하면 this 대신 객체를 반환
  • 원시형을 return 하면 retur문이 무시됨

Methods in constructors

  • 생성자 함수를 사용하면 객체 내부를 유연성 있게 구성할 수 있음
    • 프로퍼티에 메서드를 추가하는 것도 가능

Optional chaining '?.'

The "non-existing property" problem

  • 특정 객체나 프로퍼티가 실제로 있는지 확인하기 위해 AND(&&) 사용
    • 코드가 길고 복잡해지는 단점

Optional chaining

let user = {}; // 주소 정보가 없는 사용자
alert( user?.address?.street ); // undefined
  • ?. 은 왼쪽(앞)의 평가 대상이 undefined나 null이면 멈추고 undefined 반환 (에러 발생하지 않음)

Short-circuiting

  • 단락 평가(short-circuit) : 평가 대상에 값이 없으면 즉시 평가를 멈춤
    • 오른쪽에 있는 부가 동작은 실행되지 않음

Other variants: ?.(), ?.[]

  • ?. 연산자가 아닌 특수한 문법 구조체(syntax construct)
// user1은 메서드 존재, user2는 없음
let user1 = {
  admin() {
    alert("관리자 계정입니다.");
  }
}
let user2 = {};

user1.admin?.(); // 관리자 계정입니다.
user2.admin?.();
  • ?.() : 메소드 평가
let user1 = {
  firstName: "John"
};
let user2 = null; 

let key = "firstName";

alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined
  • ?.[] : 객체 프로퍼티 접근
delete user?.name; // user가 존재하면 user.name 삭제
  • delete와 조합하여 삭제 가능
    • 읽기/삭제는 가능하지만, 추가/수정은 불가

Symbol type

Symbols

  • 객체 프로퍼티 키는 문자형과 심볼형만 허용
  • 심볼(symbol)은 원시형 데이터로, 유일한 식별자(unique identifier)를 만들고자 할 때 사용
let id1 = Symbol("id");
let id2 = Symbol("id");

alert(id1 == id2); // false
  • Symbol() 로 생성
  • "" 설명 추가 가능
  • 이름과 설명이 동일한 여러 심볼을 만들어도 모두 다른 값
  • cf. 심볼은 문자형으로 자동 형 변환 되지 않음
    • .toString() 또는 symbol.description 사용

"Hidden" properties

  • 숨김(hidden) 프로퍼티는 외부 코드에서 접근 및 수정 불가
  • 심볼을 이용하여 숨김 프로퍼티를 만들 수 있음
  • Symbols in a literal
    • 대괄호를 사용해 심볼형 키를 만들 수 있음
    • [id] : 123
  • for...in에서 심볼 배제
  • Object.assign은 심볼 포함

Global symbols

  • 전역 심볼 레지스트리(global symbol registry)
  • Symbol.for(key)를 사용하여 동일한 심볼 개체에 접근하거나 생성할 수 있음 (전역 심볼)
  • Symbol.keyFor(sym)를 사용하면 전역 심볼 레지스트리 내에서 전역 심볼의 이름을 얻어냄 (없을 경우 undefined)

System symbols

  • 자바스크립트 내부에서 사용되는 심볼
  • 잘 알려진 심볼(well-known symbols)
    • Symbol.hasInstance
    • Symbol.isConcatSpreadable
    • Symbol.iterator
    • Symbol.toPrimitive

Object to primitive conversion

  • 객체의 형 변환 (hint)
    • 객체 논리 평가 시 true, 숫자형이나 문자형으로만 형 변환
    • string
      • alert 같이 객체를 출력할 때 문자형으로의 형 변환
    • number
      • 객체끼리 연산이나 수학 관련 함수를 적용할 때 숫자형으로의 형 변환
    • default
    • 연산자가 기대하는 피연산자를 확신할 수 없을 때
  • 형 변환 알고리즘
    • 객체에 objSymbol.toPrimitive 메서드를 찾아 호출
    • 없는 경우 "string"이라면
      • obj.toString()이나 obj.valueOf() 호출
    • "number"나 "default"라면
      • obj.valueOf()나 obj.toString() 호출

"Objects: the basics", by Ilya Kantor, 2007-2022, https://javascript.info/object-basics

profile
괴발개발라이프

0개의 댓글