πŸ“– μƒμ„±μž ν•¨μˆ˜μ— μ˜ν•œ 객체 생성

κΈ°λ‘μΌκΈ°πŸ“«Β·2020λ…„ 12μ›” 17일
0

Javascript κ°œλ…μ •λ¦¬

λͺ©λ‘ 보기
7/15

μ•žμ„œ μž‘μ„±ν–ˆλ˜ 객체 λ¦¬ν„°λŸ΄ ν¬μŠ€νŒ…μ—μ„œ κ°μ²΄λ¦¬ν„°λŸ΄λ‘œ 객체λ₯Ό μƒμ„±ν•˜λŠ” 방법에 λŒ€ν•΄ μ•Œμ•„λ³΄μ•˜λ‹€.

이번 ν¬μŠ€νŒ…μ—μ„œλŠ” 객체λ₯Ό μƒμ„±ν•˜λŠ” 또 λ‹€λ₯Έ 방식인 μƒμ„±μž ν•¨μˆ˜μ™€ object μƒμ„±μž ν•¨μˆ˜μ— μ˜ν•œ 객체 생성 방식을 μ‚΄νŽ΄λ³΄μž.

이 ν¬μŠ€νŒ…μ€ 이웅λͺ¨ λ‹˜μ˜ Javascript deepdive 17μž₯을 μ°Έκ³ ν•˜μ—¬ μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€.


μƒμ„±μž ν•¨μˆ˜λž€β“

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

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

첫번째둜 μ‚΄νŽ΄λ³Ό μƒμ„±μžλŠ” Object μƒμ„±μž ν•¨μˆ˜μ΄λ‹€. new μ—°μ‚°μžμ™€ ν•¨κ»˜ Object μƒμ„±μžν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄Β λΉˆ 객체λ₯Ό μƒμ„±ν•΄μ„œ λ°˜ν™˜ν•œλ‹€.

// 빈 객체의 생성
const person = new Object(); // {} λ°˜ν™˜

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

console.log(person); // {name: "Lee", sayHello: Ζ’}
person.sayHello(); // Hi! My name is Lee

Object μƒμ„±μžλ₯Ό 직접 μ΄μš©ν•΄μ„œ 객체λ₯Ό μƒμ„±ν•˜λŠ” 것 λ³΄λ‹€λŠ”, 객체 λ¦¬ν„°λŸ΄μ„ μ΄μš©ν•΄ 객체λ₯Ό μƒμ„±ν•˜λŠ” 방식이 더 μΌλ°˜μ μ΄λ‹€.


일반 μƒμ„±μž ν•¨μˆ˜

λ‘λ²ˆμ§Έλ‘œ μ‚΄νŽ΄λ³Ό μƒμ„±μžλŠ” Objectκ°€ μ œκ³΅ν•˜λŠ” μƒμ„±μž ν•¨μˆ˜κ°€ μ•„λ‹Œ, 일반 μƒμ„±μž ν•¨μˆ˜λ₯Ό 톡해 객체λ₯Ό μ •μ˜ν•˜λŠ” 방식이닀.

객체 λ¦¬ν„°λŸ΄μ— μ˜ν•œ 객체 생성 방식은 κ°„νŽΈν•˜μ§€λ§Œ 단점 λ˜ν•œ 가지고 μžˆλ‹€. 단점은 λ°”λ‘œ λ™μΌν•œ 객체λ₯Ό μ—¬λŸ¬κ°œ μƒμ„±ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ™μΌν•œ μ½”λ“œλ₯Ό μƒμ„±ν•˜λ €κ³  ν•˜λŠ” 객체의 개수만큼 μž‘μ„±ν•΄μ•Ό ν•œλ‹€λŠ” 것이닀!

반면 μƒμ„±μž ν•¨μˆ˜μ— μ˜ν•œ 객체생성 방식은 객체λ₯Ό μƒμ„±ν•˜κΈ° μœ„ν•œ ν…œν”Œλ¦Ώμ²˜λŸΌ μƒμ„±μž ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ—, ν•¨μˆ˜ ν•˜λ‚˜λ‘œ ꡬ쑰가 λ™μΌν•œ 객체λ₯Ό μ—¬λŸ¬ 개 생성할 수 μžˆλ‹€!

μ½”λ“œλ₯Ό 톡해 μ‚΄νŽ΄λ³΄μž.

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

// μΈμŠ€ν„΄μŠ€μ˜ 생성
const circle1 = new Circle(5);  // λ°˜μ§€λ¦„μ΄ 5인 Circle 객체λ₯Ό 생성
const circle2 = new Circle(10); // λ°˜μ§€λ¦„μ΄ 10인 Circle 객체λ₯Ό 생성
const circle3 = new Circle(20); // λ°˜μ§€λ¦„μ΄ 20인 Circle 객체λ₯Ό 생성
const circle4 = new Circle(30); // λ°˜μ§€λ¦„μ΄ 30인 Circle 객체λ₯Ό 생성

객체 λ¦¬ν„°λŸ΄λ‘œ μƒμ„±ν•˜λŠ” 것보닀 훨씬 μ½”λ“œκ°€ κΉ”λ”ν•˜κ³  짧은 것을 확인할 수 μžˆλ‹€. 😊😊

사싀 μœ„μ™€ 같이 객체λ₯Ό 생성할 λ•Œ λ§ˆλ‹€ λ©”μ„œλ“œλ₯Ό 계속 λ§Œλ“œλŠ” 것 λ³΄λ‹€λŠ” ν”„λ‘œν† νƒ€μž…μ„ μ΄μš©ν•΄μ„œ 곡톡 λ©”μ„œλ“œλ‘œ μΆ”μΆœ ν•˜λŠ”κ²ƒμ΄ λ°”λžŒμ§ν•˜λ‹€. μžμ„Έν•œ λ‚΄μš©μ€ μΆ”ν›„ ν”„λ‘œν† νƒ€μž… κ΄€λ ¨ ν¬μŠ€νŒ…μ—μ„œ 닀루어 보도둝 ν•˜κ² λ‹€.


μƒμ„±μž ν•¨μˆ˜μ˜ μΈμŠ€ν„΄μŠ€ 생성과정

κ·Έλ ‡λ‹€λ©΄ new ν‚€μ›Œλ“œλ₯Ό μ΄μš©ν•΄μ„œ 객체λ₯Ό μƒμ„±ν•˜λ©΄ μ–΄λ–€ 과정을 거쳐 μƒˆλ‘œμš΄ 객체가 μƒμ„±λ˜λŠ” κ²ƒμΌκΉŒ? μœ„μ˜ μ½”λ“œλ₯Ό μ°Έκ³ ν•΄μ„œ μŠ€ν…λ³„λ‘œ μ‚΄νŽ΄λ³΄μž.

1. μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κ³  thisλ₯Ό λ°”μΈλ”©ν•œλ‹€

new μ—°μ‚°μž 뒀에 μƒμ„±μž ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ μ•”λ¬΅μ μœΌλ‘œ 빈 객체가 μƒμ„±λœλ‹€. 그리고 μ•”λ¬΅μ μœΌλ‘œ μƒμ„±λœ 빈 κ°μ²΄λŠ” this에 λ°”μΈλ”©λœλ‹€.

πŸ’‘ λ°”μΈλ”©μ΄λž€ μ‹λ³„μžκ³Ό 값을 μ—°κ²°ν•˜λŠ” κ³Όμ •μœΌλ‘œ, this λ°”μΈλ”©μ΄λž€ this와 thisκ°€ 가리킬 객체λ₯Ό μ—°κ²°ν•˜λŠ” 것을 μ˜λ―Έν•©λ‹ˆλ‹€.

일단은 new μ—°μ‚°μž 뒀에 μƒμ„±μž ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ μƒμ„±λœ κ°μ²΄λŠ” μžλ™μœΌλ‘œ this에 바인딩 λœλ‹€λŠ” μ‚¬μ‹€λ§Œ κΈ°μ–΅ν•˜μž!

2. μΈμŠ€ν„΄μŠ€λ₯Ό μ΄ˆκΈ°ν™” ν•œλ‹€

μΈμŠ€ν„΄μŠ€ 생성 ν›„ μƒμ„±μž ν•¨μˆ˜λ‚΄λΆ€μ˜ μ½”λ“œκ°€ ν•œμ€„μ”© μ‹€ν–‰λ˜μ–΄ this에 λ°”μΈλ”©λ˜μ–΄ μžˆλŠ” μΈμŠ€ν„΄μŠ€λ₯Ό μ΄ˆκΈ°ν™”ν•˜κ±°λ‚˜ 값을 ν• λ‹Ήν•œλ‹€.

3. μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•œλ‹€

μƒμ„±μž ν•¨μˆ˜ λ‚΄λΆ€μ˜ λͺ¨λ“ μ²˜λ¦¬κ°€ λλ‚˜λ©΄ μ™„μ„±λœΒ μΈμŠ€ν„΄μŠ€κ°€ λ°”μΈλ”©λœ thisκ°€ μ•”λ¬΅μ μœΌλ‘œ λ°˜ν™˜λœλ‹€.

function Circle(radius) {
  // 1. μ•”λ¬΅μ μœΌλ‘œ μΈμŠ€ν„΄μŠ€κ°€ μƒμ„±λ˜κ³  this에 λ°”μΈλ”©λœλ‹€.
  console.log(this); // Circle {}
  
  // 2. this에 λ°”μΈλ”©λ˜μ–΄ μžˆλŠ” μΈμŠ€ν„΄μŠ€λ₯Ό μ΄ˆκΈ°ν™”ν•œλ‹€.
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };

  // 3. return ν•˜μ§€ μ•Šμ•„λ„ μžλ™μœΌλ‘œ thisλ₯Ό returnν•œλ‹€!
  return this;
}

// μΈμŠ€ν„΄μŠ€ 생성. Circle μƒμ„±μž ν•¨μˆ˜λŠ” λͺ…μ‹œμ μœΌλ‘œ λ°˜ν™˜ν•œ 객체λ₯Ό λ°˜ν™˜ν•œλ‹€.
const circle = new Circle(1);
console.log(circle); // Circle {radius: 1, getDiameter: Ζ’}

μΌλ°˜ν•¨μˆ˜ vs μƒμ„±μžν•¨μˆ˜

ν•¨μˆ˜λ„ 객체이기 λ•Œλ¬Έμ—, 기본적으둜 객체가 가지고 μžˆλŠ” λ‚΄λΆ€ μŠ¬λ‘―μ€ λͺ¨λ‘ 가지고 μžˆλ‹€. (λ‚΄λΆ€ λ©”μ„œλ“œκ°€ 무엇인지 λͺ¨λ₯΄μ‹œλŠ” 뢄듀은 λ‚΄λΆ€ λ©”μ„œλ“œ ν¬μŠ€νŒ…μ„ μ°Έμ‘°ν•˜μ„Έμš”!)

여기에 더해 ν•¨μˆ˜κ°μ²΄λŠ” ν•¨μˆ˜λ‘œ λ™μž‘ν•˜κΈ° μœ„ν•΄ [[Environment]], [[Formal Parameters]]λ“±μ˜ λ‚΄λΆ€ 슬둯과, [[Call]], [[Construct]] 같은 λ‚΄λΆ€λ©”μ„œλ“œλ₯Ό μΆ”κ°€λ‘œ 가지고 μžˆλ‹€.

κΈ°μ–΅ν•΄μ•Ό ν•  사싀은 μΌλ°˜ν•¨μˆ˜λ‘œ 호좜될 λ•Œμ™€ new ν‚€μ›Œλ“œλ₯Ό 톡해 μƒμ„±μž ν•¨μˆ˜λ‘œ 호좜될 λ•Œ ν˜ΈμΆœλ˜λŠ” λ‚΄λΆ€ λ©”μ„œλ“œκ°€ λ‹€λ₯΄λ‹¨ 것이닀.

μΌλ°˜ν•¨μˆ˜λ‘œ 호좜되면 λ‚΄λΆ€λ©”μ„œλ“œ [[Call]]이 호좜되고, new μ—°μ‚°μžμ™€ ν•¨κ»˜ μƒμ„±μž ν•¨μˆ˜λ‘œ 호좜되면 λ‚΄λΆ€λ©”μ„œλ“œ [[Construct]]κ°€ ν˜ΈμΆœλœλ‹€.

κ·Έλ ‡λ‹€λ©΄ μ•žμ„œ λ³Έ Circle ν•¨μˆ˜λ‘œ newλ₯Ό μ΄μš©ν•΄μ„œ μƒμ„±μž ν•¨μˆ˜λ‘œ ν˜ΈμΆœν•˜μ§€ μ•Šκ³  일반 ν•¨μˆ˜λ‘œ ν˜ΈμΆœν•˜κ²Œ 되면 μ–΄λ–€ 일이 λ°œμƒν• κΉŒ?

// μƒμ„±μž ν•¨μˆ˜
function Circle(radius) {
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };
}

const circle = Circle(5); // newλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  μΌλ°˜ν•¨μˆ˜λ‘œ 호좜
console.log(circle); // undefined(ν•¨μˆ˜ 내뢀에 returnν•˜λŠ” 값이 μ—†μŒ)

// 일반 ν•¨μˆ˜ λ‚΄λΆ€μ˜ thisλŠ” μ „μ—­ 객체 windowλ₯Ό κ°€λ¦¬ν‚€λ―€λ‘œ, window객체의 property둜 μΆ”κ°€λœλ‹€.
console.log(radius); // 5
console.log(getDiameter()); // 10

circle.getDiameter();
// TypeError: Cannot read property 'getDiameter' of undefined

newλ₯Ό 뢙이지 μ•Šκ³  호좜 ν•  μ‹œ μƒˆλ‘œμš΄ 객체가 μƒμ„±λ˜μ§€ μ•Šκ³ (암묡적인 return이 μΌμ–΄λ‚˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— circle은 undefined이닀),

일반 ν•¨μˆ˜ λ‚΄λΆ€μ˜ thisλŠ” μ „μ—­ 객체인 windowμ΄λ―€λ‘œ window 객체의 ν”„λ‘œνΌν‹°λ‘œ μΆ”κ°€λ˜λŠ” 것을 λ³Ό 수 μžˆλ‹€.

μ΄λŠ” μœ„μ—μ„œ μ„€λͺ…ν–ˆλ“― new ν‚€μ›Œλ“œλ₯Ό 뢙이지 μ•Šκ³  ν•¨μˆ˜ 객체λ₯Ό μƒμ„±ν•˜κ²Œ 되면 λ‚΄λΆ€λ©”μ„œλ“œ [[Construct]] λŒ€μ‹  [[Call]]이 μ‹€ν–‰λ˜κΈ° λ•Œλ¬Έμ΄λ‹€.


[[construct]] vs [[call]]

정리해보면, μœ„μ™€ 같이 [[construct]] λ‚΄λΆ€ λ©”μ†Œλ“œλ₯Ό 가지고 μžˆλŠ” ν•¨μˆ˜λ§Œ μƒμ„±μžλ‘œμ„œ λ™μž‘ν•  수 μžˆλ‹€λŠ” 사싀을 μ•Œ 수 μžˆλ‹€.

그러면 λͺ¨λ“  ν•¨μˆ˜λŠ” new ν‚€μ›Œλ“œμ™€ ν•¨κ»˜ ν˜ΈμΆœν•  수 μžˆμ„κΉŒ?
κ²°λ‘ λΆ€ν„° λ§ν•˜λ©΄, new ν‚€μ›Œλ“œλ‘œ ν˜ΈμΆœν•  수 μžˆλŠ” ν•¨μˆ˜μ™€ μ—†λŠ” ν•¨μˆ˜κ°€ μ‘΄μž¬ν•œλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈ 엔진은 ν•¨μˆ˜ μ •μ˜ 방식에 따라 ν•¨μˆ˜λ₯Ό constructor와 non-constructor둜 κ΅¬λΆ„ν•œλ‹€.

  • constructor : ν•¨μˆ˜ μ„ μ–Έλ¬Έ, ν•¨μˆ˜ ν‘œν˜„μ‹, 클래슀
  • non-constructor : λ©”μ„œλ“œ(ES6 λ©”μ„œλ“œ μΆ•μ•½ ν‘œν˜„), ν™”μ‚΄ν‘œ ν•¨μˆ˜

μ½”λ“œλ₯Ό 보며 직접 μ‚΄νŽ΄λ³΄μž.

function foo(){};
const bar = function(){};
const baz = {
  x: function (){}
}
const bzy = {
  x(){}
}
const arrow = () => {};

new foo(); // foo {}
new bar(); // bar {}
new baz.x(); // x {}
new bzy.x(); // TypeError : bay.y is not a constructor
new arrow(); // TypeError : arrow is not a constructor

κ²°κ³Όλ₯Ό 보면 foo(ν•¨μˆ˜ μ„ μ–Έλ¬Έ), bar(ν•¨μˆ˜ ν‘œν˜„μ‹), baz.x(일반 λ©”μ„œλ“œ)λŠ” μƒμ„±μž ν•¨μˆ˜λ‘œ μ‚¬μš©ν•  수 μžˆμ§€λ§Œ,
bzy(λ©”μ„œλ“œ μΆ•μ•½ν‘œν˜„)와 arrow(ν™”μ‚΄ν‘œ ν•¨μˆ˜)λŠ” μƒμ„±μž ν•¨μˆ˜λ‘œμ¨ μ‚¬μš©ν•  수 μ—†λŠ”κ²ƒμ„ 확인할 수 μžˆλ‹€.

new.target

ES6μ—μ„œ μΆ”κ°€λœ κΈ°λŠ₯으둜, new μ—°μ‚°μžμ™€ ν•¨κ»˜ ν˜ΈμΆœλ˜μ—ˆλŠ”μ§€ μ—¬λΆ€λ₯Ό νŒλ‹¨ν•  수 μžˆλŠ” λ©”μ„œλ“œμ΄λ‹€. new.target을 μ΄μš©ν•˜λ©΄ μƒμ„±μž ν•¨μˆ˜κ°€ new μ—°μ‚°μž 없이 ν˜ΈμΆœν–ˆμ„λ•Œ μƒμ„±λ˜λŠ” 문제λ₯Ό 미연에 방지할 수 μžˆλ‹€.

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

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

const circle = Circle(5);
console.log(circle.getDiameter()); // 10

μœ μš©ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆμ„ 것 κ°™λ‹€. ν•˜μ§€λ§Œ μ—­μ‹œ IEμ—μ„œλŠ” μ§€μ›ν•˜μ§€ μ•ŠλŠ”λ‹€.

https://caniuse.com/ μ—μ„œ νŠΉμ • κΈ°λŠ₯의 λΈŒλΌμš°μ € 별 μ‚¬μš© κ°€λŠ₯ μ—¬λΆ€λ₯Ό 체크할 수 μžˆλ‹€.

0개의 λŒ“κΈ€