반응형
Jake Seo
제이크서 위키 블로그
Jake Seo
전체 방문자
오늘
어제
  • 분류 전체보기 (715)
    • 일상, 일기 (0)
    • 백준 문제풀이 (1)
    • 릿코드 문제풀이 (2)
    • 알고리즘 이론 (10)
      • 기본 이론 (2)
      • 배열과 문자열 (8)
    • 데이터베이스 (15)
      • Planet Scale (1)
      • MSSQL (9)
      • 디비 기본 개념 (1)
      • SQLite 직접 만들어보기 (4)
    • 보안 (7)
    • 설계 (1)
    • 네트워크 (17)
      • HTTP (9)
      • OSI Layers (5)
    • 회고 (31)
      • 연간 회고 (2)
      • 주간 회고 (29)
    • 인프라 (52)
      • 도커 (12)
      • AWS (9)
      • 용어 (21)
      • 웹 성능 (1)
      • 대규모 서비스를 지탱하는 기술 (9)
    • 깃 (7)
    • 빌드 도구 (7)
      • 메이븐 (6)
      • 그레이들 (0)
    • Java (135)
      • 이펙티브 자바 (73)
      • 자바 API (4)
      • 자바 잡지식 (30)
      • 자바 디자인 패턴 (21)
      • 톰캣 (Tomcat) (7)
    • 프레임워크 (64)
      • next.js (14)
      • 스프링 프레임워크 (28)
      • 토비의 스프링 (6)
      • 스프링 부트 (3)
      • JPA (Java Persistence API) (5)
      • Nest.js (8)
    • 프론트엔드 (48)
      • 다크모드 (1)
      • 노드 패키지 관리 매니저 (3)
      • CSS (19)
      • Web API (11)
      • tailwind-css (1)
      • React (5)
      • React 새 공식문서 요약 (1)
      • HTML (Markup Language) (5)
    • 자바스크립트 (108)
      • 모던 자바스크립트 (31)
      • 개념 (31)
      • 정규표현식 (5)
      • 코드 스니펫 (1)
      • 라이브러리 (6)
      • 인터뷰 (24)
      • 웹개발자를 위한 자바스크립트의 모든 것 (6)
      • 팁 (2)
    • Typescript (49)
    • 리눅스와 유닉스 (10)
    • Computer Science (1)
      • Compiler (1)
    • IDE (3)
      • VSCODE (1)
      • IntelliJ (2)
    • 세미나 & 컨퍼런스 (1)
    • 용어 (개발용어) (16)
      • 함수형 프로그래밍 용어들 (1)
    • ORM (2)
      • Prisma (2)
    • NODEJS (2)
    • cypress (1)
    • 리액트 네이티브 (React Native) (31)
    • 러스트 (Rust) (15)
    • 코틀린 (Kotlin) (4)
      • 자바에서 코틀린으로 (4)
    • 정규표현식 (3)
    • 구글 애널리틱스 (GA) (1)
    • SEO (2)
    • UML (2)
    • 맛탐험 (2)
    • 리팩토링 (1)
    • 서평 (2)
    • 소프트웨어 공학 (18)
      • 테스팅 (16)
      • 개발 프로세스 (1)
    • 교육학 (1)
    • 삶의 지혜, 통찰 (1)
    • Chat GPT (2)
    • 쉘스크립트 (1)
    • 컴파일 (2)
    • Dart (12)
    • 코드팩토리의 플러터 프로그래밍 (4)
    • 플러터 (17)
    • 안드로이드 스튜디오 (1)
    • 윈도우즈 (1)
    • 잡다한 백엔드 지식 (1)
    • 디자인 패턴 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 프로그래머의 뇌
  • 빈 검증
  • 자바스크립트 인터뷰
  • 도커공식문서
  • 토비의 스프링
  • 자바스크립트 면접
  • 자바스크립트
  • prerendering
  • 싱글턴
  • next js app
  • 자바
  • 싱글톤
  • rust
  • Java
  • 러스트
  • 자바 검증
  • Next.js
  • 자바 디자인패턴
  • item7
  • 객체복사
  • 느린 쿼리
  • 메이븐 페이즈
  • 메이븐 골
  • 플라이웨이트패턴
  • Pre-rendering
  • 팩터리 메서드 패턴
  • 디자인패턴
  • 싱글톤 패턴
  • item8
  • 서버리스 컴퓨팅
  • MSSQL
  • 자료구조
  • bean Validation
  • Javadoc 자바독 자바주석 주석 Comment
  • 이펙티브 자바
  • serverless computing
  • 메이븐 라이프사이클
  • 슬로우 쿼리
  • 참조 해제
  • 이펙티브자바
  • 추상 팩터리 패턴
  • 스프링 검증
  • 알고리즘
  • NEXT JS
  • 작업기억공간
  • try-with-resources
  • item9
  • 외래키 제약조건
  • 이펙티브 자바 item9
  • pnpm

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jake Seo

제이크서 위키 블로그

자바스크립트/웹개발자를 위한 자바스크립트의 모든 것

웹 개발자를 위한 자바스크립트의 모든 것 5장 새로운 객체 기능

2023. 1. 2. 00:31

계산된 속성 이름 (computed property name)

  • 계산된 속성 이름 (computed property name) 은 표현식의 결과를 프로퍼티의 이름으로 쓸 수 있게 해준다.

예제1

const name = "answer";
const obj = {
  [name]: 100,
};
console.log(obj);

예제2: 작동 순서 알아보기

let counter = 0;
const obj = {
  [counter++]: counter++,
  [counter++]: counter++,
};

console.log(obj); // {"0": 1, "2": 3}

단축 속성 (shorthand property)

단축 속성 (shorthand property) 은 프로퍼티의 키와 값이 동일할 때 아래와 같이 표현할 수 있도록 해준다.

예제

function getMinMax(nums) {
  const min, max;
  return {min, max};
}

객체 프로토타입 얻기 및 설정하기

  • Object.getPrototypeOf(o) 를 통해 객체의 프로토타입을 얻을 수 있다.
  • Object.setPrototypeOf(o) 를 통해 객체의 프로토타입을 설정할 수 있다.

기존의 __proto__ 는 deprecated 되었으므로, 더이상 사용하지 않는 것이 좋다.

객체를 만든 후 프로토타입을 변경하면, 객체를 최적화 해제하여 속성 조회가 훨씬 느려질 수 있으니 주의해야 한다.

메서드 문법과 super 외부 클래스

메서드 문법은 더 간결하며, super 에 접근도 가능하다.

예제1: 메서드 문법을 사용하지 않음

const obj1 = {
  name: "Joe",
  say: function () {
    console.log(this.name);
  },
};

obj1.say(); // "Joe"

picture 1

  • say() 메서드를 호출했다.
  • [[HomeObject]] 는 null 을 가리키고 있다.

예제2: 메서드 문법을 사용함

const obj1 = {
  name: "Joe",
  say() {
    console.log(this.name);
  },
};

obj1.say(); // "Joe"

picture 2

  • say() 메서드를 호출했다.
  • [[HomeObject]] 가 Object 를 가리키고 있어서, super 키워드를 통해 상위 메서드를 불러올 수 있다.

예제3: 메서드 문법을 사용하지 않은 채로 상위 클래스의 메서드 이용하기

const obj = {
  toString: function () {
    return Object.prototype.toString.call(this).toUpperCase();
  },
};

console.log(obj.toString()); // "[OBJECT OBJECT]"
  • 프로토타입에 직접 접근하여 this 를 바인딩했다.

예제4: 메서드 문법을 사용하여 상위 클래스의 메서드 이용하기

const obj = {
  toString() {
    return super.toString().toUpperCase();
  },
};

console.log(obj.toSring()); // "[OBJECT OBJECT]"
  • super 를 이용하면, [[HomeObject]] 에 접근할 수 있다.
  • 메서드의 이름 역시 computed property 로 표현될 수 있다.
    • ex) [variableA + variableB]() { ... }

예제5: 원리 살펴보기

const obj = {
  toString() {
    return super.toString().toUpperCase();
  },
};

Object.setPrototypeOf(obj, {
  toString() {
    return "a different string";
  },
});

console.log(obj.toString()); // "A DIFFERENT STRING"
  • 프로토타입이 실제로 [[HomeObject]] 가 들어오는 곳이다.
  • super 는 코드가 실행될 때, 현재 프로토타입을 가져온다.
  • 프로토타입 객체의 속성을 조회하고 사용한다.

자바스크립트 엔진은 super 키워드를 사용하지 않는다면, 링크를 최적화할 가능성이 높다.

심볼

  • 자바스크립트 프로퍼티의 키로 사용된다.
    • 키는 문자열 혹은 심볼이다.
  • 심볼은 고유한 키가 된다. 유일하다는 것이 주요 목적과 특징이다.

심볼이 탄생하게된 배경

  • 내장 오브젝트를 toString() 메서드를 이용해 출력하면, [object TypeName] 과 같이 출력된다.
    • ex) [object Array], [object, Object], [object XYZ]
  • toString() 의 출력 내용을 [object CustomType] 으로 바꾸고 싶다면?
    • toString() 메서드만 새로 오버라이드 해도 되겠지만, 여러 군데에 TypeName 이 쓰였다면 난감한 일이다.
    • 객체 자체의 이름을 바꿔 TypeName 을 바꾼다면, 해당 객체를 사용하던 모든 코드가 망가진다.

심볼은 위의 문제를 해결한다.

class Example1 {}
class Example2 {
  get [Symbol.toStringTag]() {
    return "Example2";
  }
}
console.log(new Example1().toString()); // [object Object]
console.log(new Example2().toString()); // [object Example2]
  • [Symbol.toStringTag]() 의 내용을 바꿈으로써, 해당 패턴이 포함된 모든 메서드의 동작을 일괄적으로 변경할 수 있다.

심볼 생성법 1: Symbol() 함수 이용하기

const symbol = Symbol("description");
  • 매번 다른 Symbol 이 생성된다.
  • Symbol() 함수에 넘긴 인자는 디버깅을 위한 설명일뿐, 설명이 같아도 Symbol 은 동등하지 않다.

심볼 생성법 2: 전역 심볼 사용하기

const BarkCounter = (() => {
  const barks = Symbol("nifty-barks");

  return class BarkCounter {
    constructor(other = null) {
      this[barks] = other && barks in other ? other[barks] : 0;
    }

    bark() {
      return ++this[barks];
    }

    showBarks(label) {
      console.log(label + ": Barks = " + this[barks]);
    }
  };
})();

const b1 = new BarkCounter();
b1.bark(); // 1
b1.bark(); // 2
b1.showBarks("b1"); // b1: Barks = 2
const b2 = new BarkCounter(b1);
b2.showBarks("b2"); // b2: Barks = 2
  • 생성자에서 barks 프로퍼티가 있는 객체를 받으면, 해당 객체의 barks 프로퍼티를 this[barks] 에 할당하여, 숫자가 유지된다.
  • IIFE 를 이용한 스코프 가두기를 통해 단 하나의 "nifty-barks" 심볼만 생성하였으므로, 숫자를 이어받는 것이 가능하다.
  • 위의 코드를 내부 iframe 과 같이 별개의 영역에서 실행한다면, 독립된 Symbol 이 생성된다.

barkcounter-version-1.js

const BarkCounter = (() => {
  const barks = Symbol("nifty-barks");

  return class BarkCounter {
    constructor(other = null) {
      this[barks] = other && barks in other ? other[barks] : 0;
    }

    bark() {
      return ++this[barks];
    }

    showBarks(label) {
      console.log(label + ": Barks = " + this[barks]);
    }
  };
})();

1-barkcounter-main-1.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>BarkCounter Across Realms - Main - Version 1</title>
  </head>
  <body>
    <script src="barkcounter-version-1.js"></script>
    <script>
      var barkCounterFrame = document.createElement("iframe");
      barkCounterFrame.addEventListener("load", function () {
        const b1 = new BarkCounter();
        b1.bark();
        b1.bark();
        b1.showBarks("main"); // main: Barks = 2
        barkCounterFrame.contentWindow.useBarkCounter(b1);
      });
      barkCounterFrame.src = "1-barkcounter-frame-1.html";
      document.body.appendChild(barkCounterFrame);
    </script>
  </body>
</html>

1-barkcounter-frame-1.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>BarkCounter Across Realms - Frame - Version 1</title>
  </head>
  <body>
    <script src="barkcounter-version-1.js"></script>
    <script>
      function useBarkCounter(b1) {
        b1.showBarks("frame-b1");
        const b2 = new BarkCounter(b1);
        b2.showBarks("frame-b2");
      }
    </script>
  </body>
</html>
  • 위의 코드를 작성하고, 웹서버를 켠 뒤 1-barkcounter-main-1.html 에 접근하면, frame-b2: Barks = 0 이 되는 것을 볼 수 있다.
  • iframe 에서 독립적인 BarkCounter 스코프를 또 만들어서 그렇다.
  • 심볼을 생성하는 부분을 Symbol.for("nifty-barks") 로 코드를 약간 바꾸면, 전역 레지스트리에서 고유한 키를 가진 심볼을 가져올 수 있는데, 이를 이용하면 frame-b2: Barks = 2 가 되는 것을 볼 수 있다.

심볼은 은닉을 위한 것이 아니다.

  • Object.getOwnPropertySymbols() 메서드를 통해 해당 오브젝트가 사용하는 심볼을 가져올 수 있다.
  • 비공개 속성은 WeakMap 을 통해 사용할 수 있다.

초기의 심볼은 "프라이빗 이름 객체" 로 시작했는데, 시간이 지남에 따라 단순히 고유 식별자를 갖는 용도로 쓰이게 됐다.

잘 알려진 심볼

picture 1

  • MDN Symbols 페이지에 가면, Properties 에 잘 알려진 심볼의 목록들이 있다.
  • 잘 알려진 심볼들은 전역으로 취급되며, 특정한 목적을 가지고 만들어져있다.

새로운 객체 함수

  • ES2015, ES2017 업데이트에서 Object 에 몇가지 새로운 기능이 추가되었다.

Object.assign()

Object.assign() 메서드 설명 블로그 포스팅 에 잘 설명되어 있다.

Object.is()

=== 연산자와 같으나, 약간의 차이가 있다.

  • NaN 은 자신과 동일하다. (반면, NaN === NaN 은 거짓이다.)
  • 양의 0(+0) 과 음의 0(-0) 은 서로 같지 않다. (반면, +0 === -0 은 참이다.)
console.log(Object.is(+0, -0)); // false
console.log(Object.is(NaN, NaN)); // true
console.log(+0 === -0); // true
console.log(NaN === NaN); // false

Object.values()

  • enumerable 한 값을 제공한다.
const obj = {
  a: 1,
  b: 2,
  c: 3,
};

console.log(Object.values(obj)); // [1, 2, 3]

Object.entries()

  • enumerable 한 엔트리를 배열로 제공한다.
const obj = {
  a: 1,
  b: 2,
  c: 3,
};

console.log(Object.entries(obj)); // [["a", 1], ["b", 2], ["c", 3]]

Object.fromEntries()

  • 엔트리를 다시 오브젝트로 돌려준다.
  • 맵을 객체로 변환하는 데도 편리하다.
console.log(
  Object.fromEntries([
    ["a", 1],
    ["b", 2],
    ["c", 3],
  ])
); // {a: 1, b: 2, c: 3}

Object.getOwnPropertySymbols()

  • 심볼을 키로 이용한 프로퍼티가 있는 경우, 키로 이용된 심볼을 반환해준다.
  • ES5 메서드 Object.getOwnPropertyNames() 와 이름이 매우 흡사하다.
const mySymbol = Symbol("mySymbol");
const obj = {
  [mySymbol]: "hello",
};

console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(mySymbol)]

Object.getOwnPropertyDescriptors()

  • 열거할 수 없는 프로퍼티와 심볼을 키로 하는 모든 프로퍼티를 포함한 객체를 반환한다.
  • Object.defineProperties()
const s = Symbol("example");

const o1 = {
  // SYMBOL and not enumerable
  [s]: "one",
  // Getter
  get example() {
    return this[s];
  },
  // Setter
  set example(value) {
    this[s] = value;
  },
  // 일반 프로퍼티
  data: "value",
};

Object.defineProperty(o1, "nonEnum", {
  value: 42,
  writable: true,
  configurable: true,
});

const descriptors = Object.getOwnPropertyDescriptors(o1);
console.log(descriptors);
/*
{example: {…}, data: {…}, nonEnum: {…}, Symbol(example): {…}}

data: {value: 'value', writable: true, enumerable: true, configurable: true}
example: {enumerable: true, configurable: true, get: ƒ, set: ƒ}
nonEnum: {value: 42, writable: true, enumerable: false, configurable: true}
Symbol(example): {value: 'one', writable: true, enumerable: true, configurable: true}
*/

const o2 = Object.defineProperties(o1, descriptors);
console.log(o2);
// {data: 'value', nonEnum: 42, Symbol(example): 'one'}

getOwnPropertyDescriptors() 와 defineProperties() 를 합치면, 모든 속성을 그대로 복사하여 다른 객체에 적용할 수 있다.

Symbol.toPrimitive

이 게시글 에 자세히 정리해두었다.

오브젝트의 속성 순서

이 게시글 에 자세히 정리해두었다.

속성 스프레드 구문

전개 구문, 속성 스프레드 구문이란? 게시글 에 자세히 정리해두었다.

액션 플랜

동적 이름으로 속성 만든다면, computed property 를 활용하자.

과거

let name = "answer";
let obj = {};
obj[name] = 42;

console.log(obj[name]);

개선

let name = "answer";
let obj = {
  [name]: 42,
};

console.log(obj[name]);

이름이 같은 변수에서 속성 초기화 시에 단축 구문을 사용하자.

과거

function getMinMax() {
  let min, max;
  // ...
  return { min: min, max: max };
}

개선

function getMinMax() {
  let min, max;
  // ...
  return { min, max };
}

커스텀 확장함수를 사용하기보다 Object.assign() 을 사용하자.

기존 객체의 프로퍼티를 확장하고 싶다면, 함수를 따로 작성하기보다 Object.assign() 을 사용하자.

기존 객체의 속성을 기반으로 새 객체를 만들 때는 Spread Syntax 를 사용하자.

스프레드 구문 을 사용하자.

프로퍼티 이름 충돌이 싫다면, 심볼을 사용하자.

모호한 문자열 말고 심볼을 사용하자.

__proto__ 는 이제 그만쓰고 Object.getPrototypeOf() 와 Obhect.setPrototypeOf() 를 사용하자.

__proto__ 는 deprecated 되었다.

객체 메서드에는 메서드 구문을 사용하자

  • 코드도 더욱 간결해지며, super 를 통해 [[HomeObject]] 에 접근할 수도 있다.
  • super 를 사용하지 않더라도, 자바스크립트 엔진 최적화에 의해 성능 저하는 없다.

기존

const o = {
  f: function () {},
};

개선

const o = {
  f() {},
};
반응형
저작자표시 비영리 (새창열림)

'자바스크립트 > 웹개발자를 위한 자바스크립트의 모든 것' 카테고리의 다른 글

웹 개발자를 위한 자바스크립트의 모든 것 6장 이터러블, 이터레이터, 제너레이터  (0) 2023.01.16
웹 개발자를 위한 자바스크립트의 모든 것 4장 클래스  (0) 2022.12.29
웹 개발자를 위한 자바스크립트의 모든 것 3장 새로운 함수 기능  (0) 2022.12.22
웹 개발자를 위한 자바스크립트의 모든 것 2장 let, const 정리  (0) 2022.12.18
웹 개발자를 위한 자바스크립트의 모든 것 1장 ES2015 - ES2020 정리  (0) 2022.12.17
    '자바스크립트/웹개발자를 위한 자바스크립트의 모든 것' 카테고리의 다른 글
    • 웹 개발자를 위한 자바스크립트의 모든 것 6장 이터러블, 이터레이터, 제너레이터
    • 웹 개발자를 위한 자바스크립트의 모든 것 4장 클래스
    • 웹 개발자를 위한 자바스크립트의 모든 것 3장 새로운 함수 기능
    • 웹 개발자를 위한 자바스크립트의 모든 것 2장 let, const 정리
    Jake Seo
    Jake Seo
    ✔ 잘 보셨다면 광고 한번 클릭해주시면 큰 힘이 됩니다. ✔ 댓글로 틀린 부분을 지적해주시면 기분 나빠하지 않고 수정합니다. ✔ 많은 퇴고를 거친 글이 좋은 글이 된다고 생각합니다. ✔ 간결하고 명료하게 사람들을 이해 시키는 것을 목표로 합니다.

    티스토리툴바