Modern Javascript Deep Dive - 17μž₯ μƒμ„±μž ν•¨μˆ˜μ— μ˜ν•œ 객체 생성

1. Object μƒμ„±μž ν•¨μˆ˜

new μ—°μ‚°μžμ™€ ν•¨κ»˜ Object μƒμ„±μž ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ 빈 객체λ₯Ό μƒμ„±ν•˜μ—¬ λ°˜ν™˜ν•œλ‹€.

// 빈 객체의 생성
const person = new Object();

// ν”„λ‘œνΌν‹° μΆ”κ°€
person.name = 'Son';
person.sayHello = function() {
  console.log(`Hi! My name is ${this.name}`);
};

console.log(person);
person.sayHello;

// { name: 'Son', sayHello: [Function (anonymous)] }
// Hi! My name is Son

μƒμ„±μž ν•¨μˆ˜λž€ new μ—°μ‚°μžμ™€ ν•¨κ»˜ ν˜ΈμΆœν•˜μ—¬ 객체λ₯Ό μƒμ„±ν•˜λŠ” ν•¨μˆ˜λ₯Ό λ§ν•œλ‹€. μƒμ„±μž ν•¨μˆ˜λ‘œ μƒμ„±λœ 객체λ₯Ό μΈμŠ€ν„΄μŠ€λΌ ν•œλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” Object μƒμ„±μž ν•¨μˆ˜ 이외에도 빌트인 μƒμ„±μž ν•¨μˆ˜λ₯Ό μ œκ³΅ν•œλ‹€.

// String
const strObj = new String('Son');
console.log(typeof strObj);
console.log(strObj); // [String : "Son"]

// Number
const numObj = new Number(123);
console.log(typeof numObj);
console.log(numObj); // [Number : 123]

// Boolean
const boolObj = new Boolean(true);
console.log(typeof boolObj);
console.log(boolObj); // [Boolean: true]

// Function
const func = new Function('x', 'return x * x');
console.log(typeof func); // function
console.dir(func);

// Array
const arr = new Array(1, 2, 3);
console.log(typeof arr);
console.log(arr); // [1, 2, 3]

// RegExp
const regExp = new RegExp(/ab+c/i);
console.log(typeof regExp);
console.log(regExp); // /ab+c/i

// Date
const date = new Date();
console.log(typeof date);
console.log(date); // 2023-08-03T04:11:37.506Z

객체λ₯Ό μƒμ„±ν•˜λŠ” 방법은 객체 λ¦¬ν„°λŸ΄μ„ μ‚¬μš©ν•˜λŠ” 것이 더 κ°„νŽΈν•˜λ‹€. ꡳ이 Object μƒμ„±μž ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄ 객체λ₯Ό μƒμ„±ν•˜λŠ” 방식은 νŠΉλ³„ν•œ μ΄μœ κ°€ μ—†λ‹€λ©΄ 그닀지 μœ μš©ν•΄ 보이지 μ•ŠλŠ”λ‹€.

2. μƒμ„±μž ν•¨μˆ˜

2.1 객체 λ¦¬ν„°λŸ΄μ— μ˜ν•œ 객체 생성 λ°©μ‹μ˜ 문제점

객체 λ¦¬ν„°λŸ΄μ— μ˜ν•œ 객체 생성 방식은 단 ν•˜λ‚˜μ˜ 객체만 μƒμ„±ν•œλ‹€. λ™μΌν•œ ν”„λ‘œνΌν‹°λ₯Ό 가진 객체λ₯Ό μ—¬λŸ¬ 개 μƒμ„±ν•˜λŠ” 경우 맀번 같은 ν”„λ‘œνΌν‹°λ₯Ό κΈ°μˆ ν•΄μ•Ό ν•œλ‹€.

const circle1 = {
  radius: 5,
  getDiameter() {
    return 2 * this.radius;
  },
};

const circle2 = {
  radius: 10,
  getDiameter() {
    return 2 * this.radius;
  },
};

console.log(circle1.getDiameter()) // 10
console.log(circle2.getDiameter()) // 20

2.2 μƒμ„±μž ν•¨μˆ˜μ— μ˜ν•œ 객체 생성 λ°©μ‹μ˜ μž₯점

μƒμ„±μž ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ ν”„λ‘œνΌν‹° ꡬ쑰가 λ™μΌν•œ 객체 μ—¬λŸ¬ 개λ₯Ό κ°„νŽΈν•˜κ²Œ 생성할 수 μžˆλ‹€.

// μƒμ„±μž ν•¨μˆ˜
function Circle(radius) {
  // μƒμ„±μž ν•¨μˆ˜ λ‚΄λΆ€μ˜ thisλŠ” μƒμ„±μž ν•¨μˆ˜κ°€ 생성할 μΈμŠ€ν„΄μŠ€λ₯Ό 가리킨닀.
  this.radius = radius;
  this.getDiameter = function() {
    return 2 * this.radius;
  }
}

const circle1 = new Circle(5);
const circle2 = new Circle(10);

console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20

μƒμ„±μž ν•¨μˆ˜λŠ” 이름 κ·ΈλŒ€λ‘œ 객체(μΈμŠ€ν„΄μŠ€)λ₯Ό μƒμ„±ν•˜λŠ” ν•¨μˆ˜λ‹€.

new μ—°μ‚°μžμ™€ ν•¨κ»˜ ν˜ΈμΆœν•˜λ©΄ ν•΄λ‹Ή ν•¨μˆ˜λŠ” μƒμ„±μž ν•¨μˆ˜λ‘œ λ™μž‘ν•œλ‹€.

// new μ—°μ‚°μžμ™€ ν•¨κ»˜ ν˜ΈμΆœν•˜μ§€ μ•ŠμœΌλ©΄ μƒμ„±μž ν•¨μˆ˜λ‘œ λ™μž‘ν•˜μ§€ μ•ŠλŠ”λ‹€.
// 일반 ν•¨μˆ˜λ‘œ 호좜됨
const circle3 = Circle(15);

console.log(circle3); // undefined

// 일반 ν•¨μˆ˜λ‘œμ†Œ 호좜된 Circle λ‚΄μ˜ thisλŠ” μ „μ—­ 객체λ₯Ό 가리킨닀.
console.log(radius); // 15

2.3 μƒμ„±μž ν•¨μˆ˜μ˜ μΈμŠ€ν„΄μŠ€ 생성 κ³Όμ •

μƒμ„±μž ν•¨μˆ˜μ˜ 역할은 ν”„λ‘œνΌν‹° ꡬ쑰가 λ™μΌν•œ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κΈ° μœ„ν•œ ν…œν”Œλ¦Ώ(클래슀)μœΌλ‘œμ„œ λ™μž‘ν•˜μ—¬ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜λŠ” 것과 μƒμ„±λœ μΈμŠ€ν„΄μŠ€λ₯Ό μ΄ˆκΈ°ν™”(μΈμŠ€ν„΄μŠ€ ν”„λ‘œνΌν‹° μΆ”κ°€ 및 μ΄ˆκΈ°κ°’ ν• λ‹Ή)ν•˜λŠ” 것이닀.

function Circle(radius) {
  this.radius = radius;
  this.getDiameter = function() {
    return 2 * this.radius;
  }
}

const circle1 = new Circle(5);

μžλ°”μŠ€ν¬λ¦½νŠΈ 엔진은 암묡적인 처리λ₯Ό 톡해 μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κ³  λ°˜ν™˜ν•œλ‹€. new μ—°μ‚°μžμ™€ ν•¨κ»˜ μƒμ„±μž ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ μžλ°”μŠ€ν¬λ¦½νŠΈ 엔진은 μ•”λ¬΅μ μœΌλ‘œ μΈμŠ€ν„΄μŠ€λ₯Ό μ΄ˆκΈ°ν™”ν•œ ν›„ μ•”λ¬΅μ μœΌλ‘œ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•œλ‹€.

β†’ μƒμ„±μž ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ return 문은 λ°˜λ“œμ‹œ μƒλž΅ν•΄μ•Ό ν•œλ‹€.

2.4 λ‚΄λΆ€ λ©”μ„œλ“œ [[Call]] κ³Ό [[Construct]]

// ν•¨μˆ˜λŠ” 객체닀.
function foo() {};

// ν•¨μˆ˜λŠ” κ°μ²΄μ΄λ―€λ‘œ ν”„λ‘œνΌν‹°λ₯Ό μ†Œμœ ν•  수 μžˆλ‹€.
foo.prop = 10;

// ν•¨μˆ˜λŠ” κ°μ²΄μ΄λ―€λ‘œ λ©”μ„œλ“œλ₯Ό μ†Œμœ ν•  수 μžˆλ‹€.
foo.method = function() {
  console.log(this.prop);
}

foo.method(); // 10

ν•¨μˆ˜λŠ” κ°μ²΄μ΄μ§€λ§Œ 일반 κ°μ²΄μ™€λŠ” λ‹€λ₯΄λ‹€. 일반 κ°μ²΄λŠ” ν˜ΈμΆœν•  수 μ—†μ§€λ§Œ ν•¨μˆ˜λŠ” ν˜ΈμΆœν•  수 μžˆλ‹€.

ν•¨μˆ˜ κ°μ²΄λŠ” 일반 객체가 가지고 μžˆλŠ” λ‚΄λΆ€ 슬둯과 λ‚΄λΆ€ λ©”μ„œλ“œλŠ” λ¬Όλ‘ , ν•¨μˆ˜λ‘œμ„œ λ™μž‘ν•˜κΈ° μœ„ν•΄ ν•¨μˆ˜ 객체 λ§Œμ„ μœ„ν•œ Environment, FormalParameter λ“±μ˜ λ‚΄λΆ€ 슬둯과 Call, Construct 같은 λ‚΄λΆ€ λ©”μ„œλ“œλ₯Ό μΆ”κ°€λ‘œ 가지고 μžˆλ‹€.

function foo() {};

// 일반적인 ν•¨μˆ˜λ‘œμ„œ 호좜: Call 호좜
foo();

// μƒμ„±μž ν•¨μˆ˜λ‘œμ„œ 호좜: Construct 호좜
new foo();

λ‚΄λΆ€ λ©”μ„œλ“œ Call 을 κ°–λŠ” ν•¨μˆ˜ 객체λ₯Ό callable 이라 ν•˜λ©°, λ‚΄λΆ€ λ©”μ„œλ“œ Construct λ₯Ό κ°–λŠ” ν•¨μˆ˜ 객체λ₯Ό constructor, Construct λ₯Ό 갖지 μ•Šμ€ ν•¨μˆ˜ 객체λ₯Ό non-constructor라고 λΆ€λ₯Έλ‹€.

callable 은 ν˜ΈμΆœν•  수 μžˆλŠ” 객체, 즉 ν•¨μˆ˜λ₯Ό λ§ν•˜λ©°

  • constructor λŠ” μƒμ„±μž ν•¨μˆ˜λ‘œμ„œ ν˜ΈμΆœν•  수 μžˆλŠ” ν•¨μˆ˜
  • non-constructor λŠ” 객체λ₯Ό μƒμ„±μž ν•¨μˆ˜λ‘œμ„œ ν˜ΈμΆœν•  수 μ—†λŠ” ν•¨μˆ˜

ν˜ΈμΆœν•  수 μ—†λŠ” κ°μ²΄λŠ” ν•¨μˆ˜ 객체가 μ•„λ‹ˆλ―€λ‘œ ν•¨μˆ˜λ‘œμ„œ κ°€λŠ₯ν•˜λŠ” 객체, 즉 ν•¨μˆ˜ κ°μ²΄λŠ” λ°˜λ“œμ‹œ callable 이어야 ν•œλ‹€. β†’ λͺ¨λ“  ν•¨μˆ˜ κ°μ²΄λŠ” λ‚΄λΆ€ λ©”μ„œλ“œ Call을 κ°–κ³  μžˆμœΌλ―€λ‘œ ν˜ΈμΆœν•  수 μžˆλ‹€.

2.5 constructor와 non-constructor의 ꡬ뢄

  • constructor: ν•¨μˆ˜ μ„ μ–Έλ¬Έ, ν•¨μˆ˜ ν‘œν˜„μ‹, 클래슀(ν΄λž˜μŠ€λ„ ν•¨μˆ˜)
  • non-constructor: λ©”μ„œλ“œ, ν™”μ‚΄ν‘œ ν•¨μˆ˜
// 일반 ν•¨μˆ˜ μ •μ˜: ν•¨μˆ˜ μ„ μ–Έλ¬Έ, ν•¨μˆ˜ ν‘œν˜„μ‹
function foo() {};
const bar = function() {};
// ν”„λ‘œνΌν‹° x의 κ°’μœΌλ‘œ ν• λ‹Ήλœ 것은 일반 ν•¨μˆ˜λ‘œ μ •μ˜λœ ν•¨μˆ˜λ‹€. μ΄λŠ” λ©”μ„œλ“œλ‘œ μΈμ •ν•˜μ§€ μ•ŠμŒ
const baz = {
  x: function() {},
};

// 일반 ν•¨μˆ˜λ‘œ μ •μ˜λœ ν•¨μˆ˜λ§Œμ΄ constructor.
new foo(); // foo {}
new bar(); // bar {}
new bar.x(); // x {}

// ν™”μ‚΄ν‘œ ν•¨μˆ˜ μ •μ˜
const arrow = () => {};

new arrow(); // TypeError

// λ©”μ„œλ“œ μ •μ˜: ES6 의 λ©”μ„œλ“œ μΆ•μ•½ ν‘œν˜„λ§Œ λ©”μ„œλ“œλ‘œ 인정
const obj = {
  x() {},
};

new obj.x(); // TypeError:

μƒμ„±μž ν•¨μˆ˜λ‘œμ„œ 호좜될 것을 κΈ°λŒ€ν•˜κ³  μ •μ˜ν•˜μ§€ μ•Šμ€ 일반 ν•¨μˆ˜(callble / constructor)에 new μ—°μ‚°μžλ₯Ό λΆ™μ—¬ ν˜ΈμΆœν•˜λ©΄ μƒμ„±μž ν•¨μˆ˜μ²˜λŸΌ λ™μž‘ν•  수 μžˆλ‹€.

2.6 new μ—°μ‚°μž

new μ—°μ‚°μžμ™€ ν•¨κ»˜ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ ν•΄λ‹Ή ν•¨μˆ˜λŠ” μƒμ„±μž ν•¨μˆ˜λ‘œ λ™μž‘ν•œλ‹€.

// μƒμ„±μž ν•¨μˆ˜λ‘œμ„œ μ •μ˜ν•˜μ§€ μ•Šμ€ 일반 ν•¨μˆ˜
function add(x, y) {
  return x + y;
}

// μƒμ„±μž ν•¨μˆ˜λ‘œμ„œ μ •μ˜ν•˜μ§€ μ•Šμ€ 일반 ν•¨μˆ˜λ₯Ό new μ—°μ‚°μžμ™€ ν•¨κ»˜ 호좜
let inst = new add();

// ν•¨μˆ˜κ°€ 객체λ₯Ό λ°˜ν™˜ν•˜μ§€ μ•Šμ•˜μœΌλ―€λ‘œ λ°˜ν™˜λ¬Έμ΄ λ¬΄μ‹œλœλ‹€. -> 빈 객체가 생성
console.log(inst); // {}

// 객체λ₯Ό λ°˜ν™˜ν•˜λŠ” 일반 ν•¨μˆ˜
function createUser(name, role) {
  return { name, role };
}

// 일반 ν•¨μˆ˜λ₯Ό new μ—°μ‚°μžμ™€ ν•¨κ»˜ 호좜
inst = new createUser('Son', 'admin');
// ν•¨μˆ˜κ°€ μƒμ„±λœ 객체 λ°˜ν™˜
console.log(inst); // { name: 'Son', role: 'admin' }

λ°˜λŒ€λ‘œ new μ—°μ‚°μž 없이 μƒμ„±μž ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ 일반 ν•¨μˆ˜λ‘œ ν˜ΈμΆœλœλ‹€.

function Circle(radius) {
  this.radius = radius
  this.getDiameter = function() {
    return 2 * this.radius
  }
}

const circle1 = Circle(5);

console.log(circle1); // undefined

// 일반 ν•¨μˆ˜ λ‚΄λΆ€μ˜ thisλŠ” μ „μ—­ 객체 windowλ₯Ό 가리킨닀.
console.log(radius); // 5
console.log(getDiameter()); // 10

circle1.getDiameter(); // TypeError

μœ„ 예제의 Circle ν•¨μˆ˜λŠ” 일반 ν•¨μˆ˜λ‘œμ„œ ν˜ΈμΆœλ˜μ—ˆκΈ° λ•Œλ¬Έμ— Circle ν•¨μˆ˜ λ‚΄λΆ€μ˜ thisλŠ” μ „μ—­ 객체 windowλ₯Ό 가리킨닀. β†’ radius ν”„λ‘œνΌν‹°, getDiameter λ©”μ„œλ“œλŠ” μ „μ—­ 객체의 ν”„λ‘œνΌν‹°μ™€ λ©”μ„œλ“œκ°€ λœλ‹€.

2.7 new.target

μƒμ„±μž ν•¨μˆ˜κ°€ new μ—°μ‚°μž 없이 ν˜ΈμΆœλ˜λŠ” 것을 λ°©μ§€ν•˜κΈ° μœ„ν•΄ 파슀칼 μΌ€μ΄μŠ€ μ»¨λ²€μ…˜μ„ μ‚¬μš©ν•œλ‹€ ν•˜λ”λΌλ„ μ‹€μˆ˜κ°€ λ°œμƒν•  수 μžˆλ‹€. β†’ ES6μ—μ„œλŠ” new.target 을 μ§€μ›ν•œλ‹€.

new.target 은 this와 μœ μ‚¬ν•˜κ²Œ constructor인 λͺ¨λ“  ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ 암묡적인 지역 λ³€μˆ˜μ™€ 같이 μ‚¬μš©λ˜λ©° 메타 ν”„λ‘œνΌν‹°λΌκ³  λΆ€λ₯Έλ‹€.

new μ—°μ‚°μžμ™€ ν•¨κ»˜ μƒμ„±μž ν•¨μˆ˜λ‘œμ„œ 호좜되면 ν•¨μˆ˜ λ‚΄λΆ€μ˜ new.target 은 ν•¨μˆ˜ μžμ‹ μ„ 가리킨닀. new μ—°μ‚°μžμ—†μ΄ 일반 ν•¨μˆ˜λ‘œμ„œ 호좜된 ν•¨μˆ˜ λ‚΄λΆ€μ˜ new.target 은 undefinedλ‹€.

// μƒμ„±μž ν•¨μˆ˜
function Circle(radius) {
  // 이 ν•¨μˆ˜κ°€ new μ—°μ‚°μžμ™€ ν•¨κ»˜ ν˜ΈμΆœλ˜μ§€ μ•Šμ•˜λ‹€λ©΄ new.target은 undefinedλ‹€.
  if (!new.target) {
    // new μ—°μ‚°μžμ™€ ν•¨κ»˜ μƒμ„±μž ν•¨μˆ˜λ₯Ό μž¬κ·€ ν˜ΈμΆœν•˜μ—¬ μƒμ„±λœ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•œλ‹€.
    return new Circle(radius);
  }
  this.radius = radius;
  this.getDiameter = function() {
    return 2 * this.radius;
  }
}

// new μ—°μ‚°μž 없이 μƒμ„±μž ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜μ—¬λ„ new.target을 톡해 μƒμ„±μž ν•¨μˆ˜λ‘œμ„œ ν˜ΈμΆœλœλ‹€.
const circle = Circle(5);
console.log(circle.getDiameter()); // 10

참고둜 λŒ€λΆ€λΆ„μ˜ 빌트인 μƒμ„±μž ν•¨μˆ˜λŠ” new μ—°μ‚°μžμ™€ ν•¨κ»˜ ν˜ΈμΆœλ˜μ—ˆλŠ”μ§€λ₯Ό ν™•μΈν•œ ν›„ μ μ ˆν•œ 값을 λ°˜ν™˜ν•œλ‹€.

let obj = new Object();
console.log(obj); // {}

obj = Object();
console.log(obj); // {}

let f = new Function('x', 'return x ** x');
console.log(f); // [Function: anonymous]

f = Function('x', 'return x ** x');
console.log(f); // [Function: anonymous]

ν•˜μ§€λ§Œ String, Number, Boolean μƒμ„±μž ν•¨μˆ˜λŠ” new μ—°μ‚°μžμ™€ ν•¨κ»˜ ν˜ΈμΆœν–ˆμ„ λ•Œ 객체λ₯Ό μƒμ„±ν•˜μ—¬ λ°˜ν™˜ν•˜μ§€λ§Œ new μ—°μ‚°μž 없이 ν˜ΈμΆœν•˜λ©΄ λ¬Έμžμ—΄, 숫자, λΆˆλ¦¬μ–Έ 값을 λ°˜ν™˜ν•œλ‹€.

const str = String(123);
console.log(str, typeof str); // 123 string

const num = Number('123');
console.log(num, typeof num); // 123 number

const bool = Boolean(1);
console.log(bool, typeof bool); // true boolean

Written by@Sunny Son
κ°œλ°œμžλŠ” μ˜€λŠ˜λ„ 뚠뚠

GitHubFacebook