avatar
Published on

Javascript Prototype

Author
  • avatar
    Name
    yceffort

Table of Contents

ν”„λ‘œν† νƒ€μž…

μƒμ†μ΄λΌλŠ” κ΄€μ μ—μ„œ 봀을 λ•Œ, μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ μœ μΌν•œ μƒμ„±μžλŠ” 객체 뿐이닀. λͺ¨λ“  κ°μ²΄λŠ” [[prototype]] μ΄λΌλŠ” private 속성을 κ°€μ§€κ³  μžˆλŠ”λ°, μ΄λŠ” μžμ‹ μ˜ ν”„λ‘œν† νƒ€μž…μ΄ λ˜λŠ” λ‹€λ₯Έ 객체λ₯Ό 가리킨닀. μ΄λ ‡κ²Œ μžμ‹ μ˜ ν”„λ‘œν† νƒ€μž…μ˜ ν”„λ‘œν† νƒ€μž…μ˜ ν”„λ‘œν† νƒ€μž…μ„ 따라가닀보면, κ²°κ΅­ null을 ν”„λ‘œν† νƒ€μž…μœΌλ‘œ κ°€μ§€λŠ” μ˜€λΈŒμ νŠΈμ—μ„œ λλ‚œλ‹€. null은 ν”„λ‘œν† νƒ€μž…μ΄ 더 이상 μ—†λ‹€κ³  μ •μ˜λ˜λ©° μ΄λŠ” ν”„λ‘œν† νƒ€μž…μ˜ 쒅점을 λ§ν•œλ‹€.

var obj = {a: 'hello'}
console.dir(obj)

./images/proto1.png

속성 상속

객체의 μ–΄λ– ν•œ 속성에 μ ‘κ·Όν•˜λ €κ³  ν•  λ•Œ, κ·Έ 객체 자체의 속성 뿐만 μ•„λ‹ˆλΌ 객체의 ν”„λ‘œν† νƒ€μž…, κ·Έ ν”„λ‘œν†  νƒ€μž…μ˜ ν”„λ‘œν†  νƒ€μž… λ“±λ“±λ“± μ•žμ„œ λ§ν•œ ν”„λ‘œν† νƒ€μž…μ˜ 쒅단 κΉŒμ§€ 갈 λ•Œκ°€μ§€ κ·Έ 속성을 νƒμƒ‰ν•œλ‹€.

let foo = function () {
  this.a = 1
  this.b = 2
}

let bar = new foo() // {a:1, b:2}

foo.prototype.b = 3
foo.prototype.c = 4

// barκ°€ a 속성을 κ°€μ§€κ³  있기 λ•Œλ¬Έμ— 1
console.log(bar.a)
// barκ°€ a 속성을 κ°€μ§€κ³  있기 λ•Œλ¬Έμ— 2
console.log(bar.b)
// barκ°€ c의 속성을 κ°€μ§€κ³  μžˆμ§€ μ•Šλ‹€. κ·Έλž˜μ„œ ν”„λ‘œν† νƒ€μž…μ„ μ²΄ν¬ν•œλ‹€.
// foo.[[prototype]] 이 cλ₯Ό κ°–κ³  μžˆλŠ”μ§€ ν™•μΈν•˜μž.
// cκ°€ μžˆλ‹€.
// 4
console.log(bar.c)
// ν”„λ‘œν† νƒ€μž…μ„ 뒀져도 dλŠ” λ‚˜μ˜€μ§€ μ•ŠλŠ”λ‹€.
console.log(bar.d)

λ©”μ†Œλ“œ 상속

var foo = {
  a: 2,
  b: function (c) {
    return this.a + 1
  },
}

// 3
// μ—¬κΈ°μ„œ thisλŠ” fooλ₯Ό 가리킨닀
console.log(foo.b())

// barλŠ” ν”„λ‘œν† νƒ€μž…μ„ foo둜 κ°€μ§€λŠ” μ˜€λΈŒμ νŠΈλ‹€.
var bar = Object.create(foo)

bar.a = 4
// bar.b()λ₯Ό 호좜 ν•˜λ©΄, thisλŠ” barλ₯Ό 가리킨닀.
// λ”°λΌμ„œ foo의 ν•¨μˆ˜ bλ₯Ό 상속 λ°›μœΌλ©°,
// aλŠ” foo.aκ°€ μ•„λ‹Œ barμ—μ„œ μƒˆλ‘œμ§€μ •ν•œ aλ₯Ό λ³΄κ²Œλœλ‹€.
// 5
console.log(bar.b())

How to use

function dummy() {}
console.dir(dummy.prototype)

./images/proto2.png

여기에 속성을 μΆ”κ°€ν•΄λ³΄μž.

function dummy() {}
dummy.prototype.foo = 'bar'
console.dir(dummy.prototype)

./images/proto3.png

fooκ°€ barκ°’μœΌλ‘œ μΆ”κ°€λœ 것을 λ³Ό 수 μžˆλ‹€.

function dummy() {}
dummy.prototype.foo = 'bar'
var d = new dummy()
d.hello = 'world'
console.log(d)

./images/proto4.png

  1. d 의 속성에 μ ‘κ·Όν•  λ•Œ, λΈŒλΌμš°μ €λŠ” μš°μ„  dκ°€ κ·Έ 속성을 κ°€μ§€κ³  μžˆλŠ”μ§€ ν™•μΈν•œλ‹€.
  2. λ§Œμ•½ dκ°€ ν•΄λ‹Ή 속성을 κ°€μ§€κ³  μžˆμ§€ μ•Šλ‹€λ©΄, d.__proto__ (dummy.prototype)이 κ·Έ 속성을 κ°€μ§€κ³  μžˆλŠ”μ§€ ν™•μΈν•œλ‹€.
  3. λ§Œμ•½ dummy.__proto__κ°€ κ·Έ 속성을 κ°€μ§€κ³  μžˆλ‹€λ©΄, dummy.__proto__κ°€ κ°–κ³  μžˆλŠ” 속성을 μ‚¬μš©ν•œλ‹€.
  4. λ§Œμ•½ dummy.__proto__λ§ˆμ € κ·Έ 속성을 κ°€μ§€κ³  μžˆμ§€ μ•ŠμœΌλ©΄, dummy.__proto__.__proto__κ°€ κ°€μ§€κ³  μžˆλŠ”μ§€ ν™•μΈν•œλ‹€. 기본적으둜 μ—¬κΈ°μ„œ ν•¨μˆ˜μ˜ prototype의 __proto__λŠ” window.Object.prototype이닀.
  5. κ·Έλž˜μ„œ 이제 dummy.__proto__.__proto__ (dummy.prototype의 __proto__) (Object.prototype)μ—μ„œ κ·Έ 속성을 μ°ΎλŠ”λ‹€.
  6. 근데 이제 κ·Έ μœ„μ˜ dummy.__proto__.__proto__.__proto__λ₯Ό 찾으렀고 ν•˜μ§€λ§Œ, 더 이상은 μ—†μœΌλ―€λ‘œ
  7. undefined둜 κ²°λ‘  μ§“λŠ”λ‹€.

μœ„μ˜ μ§€λ…ν•œ 과정을 (....) μ½”λ“œλ‘œ μ‚΄νŽ΄λ³΄μž.

function dummy() {}
dummy.prototype.foo = 'bar'
var d = new dummy()
d.prop = 'some value'
console.log('d.prop:      ' + d.prop)
console.log('d.foo:       ' + d.foo)
console.log('dummy.prop:           ' + dummy.prop)
console.log('dummy.foo:            ' + dummy.foo)
console.log('dummy.prototype.prop: ' + dummy.prototype.prop)
console.log('dummy.prototype.foo:  ' + dummy.prototype.foo)
d.prop:      some value
d.foo:       bar
dummy.prop:           undefined
dummy.foo:            undefined
dummy.prototype.prop: undefined
dummy.prototype.foo:  bar

μ—¬λŸ¬κ°€μ§€ λ°©λ²•μœΌλ‘œ 객체λ₯Ό μƒμ„±ν•˜κ³  ν”„λ‘œν† νƒ€μž… 체인 κ²°κ³Όλ₯Ό 보자

문법 μƒμ„±μž

var foo = {bar: 1}
// foo의 ν”„λ‘œν† νƒ€μž…μ€ Object.prototype

var arrayFoo = ['please', 'go', 'home']
// arrayFoo의 ν”„λ‘œν† νƒ€μž…μ€ Array.prototype
// κ·Έλž˜μ„œ map, index 등을 μ“Έ 수 μžˆλ‹€.

function functionFoo() {
  return 'fuck'
}
// Function.prototype을 μƒμ†λ°›μ•„μ„œ
// call, bind λ“±μœΌλ₯΄ μ“Έ 수 μžˆλ‹€.

μƒμ„±μžλ₯Ό 이용

function hello() {
  this.name = ''
  this.age = 0
}

hello.prototype = {
  setName: function (name) {
    this.name = name
  },
}

var h = new hello()
// hλŠ” nameκ³Ό ageλ₯Ό μ†μ„±μœΌλ‘œ κ°–λŠ” 객체닀.
// 생성이 h.[[prototype]]은 Hello.prototypeκ³Ό 같은 값을 κ°€μ§„λ‹€.

Object.create ν™œμš©

var a = {a: 1}
// a --> Object.prototype --> null

var b = Object.create(a)
// b --> a --> Object.prototype --> null

var c = Object.create(b)
// c --> b --> a --> Object.prototype --> null

[[prototype]] vs prototype

λͺ¨λ“  κ°μ²΄λŠ” μžμ‹ μ˜ ν”„λ‘œν† νƒ€μž…μ„ κ°€λ¦¬ν‚€λŠ” [[prototype]]μ΄λΌλŠ” 인터널 μŠ¬λ‘―μ„ κ°€μ§€λ©°, 상속을 μœ„ν•΄ μ‚¬μš©λœλ‹€. ν•¨μˆ˜λ„ 객체이기 λ•Œλ¬Έμ—, [[prototype]]λ₯Ό κ°–λŠ”λ‹€. 그런데 ν•¨μˆ˜λŠ” 일반 κ°μ²΄μ™€λŠ” 달리 prototype ν”„λ‘œν† νƒ€μž…λ„ κ°–κ²Œ λœλ‹€. μ€‘μš”ν•œ 것은 [[prototype]]κ³Ό prototype은 λ‹€λ₯΄λ‹€λŠ” 것이닀.

function Person(name) {
  this.name = name
}

var foo = new Person('Lee')

console.dir(Person) // prototype ν”„λ‘œνΌν‹°κ°€ μžˆλ‹€.
console.dir(foo) // prototype ν”„λ‘œνΌν‹°κ°€ μ—†λ‹€.

function P(name) {
  return name
}

var bar = P('Lee')
console.dir(bar)

./images/proto5.png

[[prototype]]

  • ν•¨μˆ˜λ₯Ό ν¬ν•¨ν•œ λͺ¨λ“  객체가 κ°€μ§€κ³  μžˆλŠ” 인터널 슬둯
  • 객체 μž…μž₯μ—μ„œ λ΄€μ„λ•Œ, μžμ‹ μ˜ λΆ€λͺ¨μ—­ν• μ„ ν•˜λŠ” ν”„λ‘œν† νƒ€μž… 객체λ₯Ό 가리킀며, ν•¨μˆ˜μ˜ 경우 Function.prototype을 가리킨닀.

prototype ν”„λ‘œνΌν‹°

  • 였직 ν•¨μˆ˜λ§Œ κ°€μ§€κ³  μžˆλŠ” ν”„λ‘œνΌν‹°λ‹€.
  • ν•¨μˆ˜ 객체가 μƒμ„±μžλ‘œ μ΄μš©λ λ•Œ, 이 ν•¨μˆ˜λ₯Ό 톡해 생성될 객체의 λΆ€λͺ¨μ—­ν• μ„ ν•˜λŠ” 객체λ₯Ό 가리킨닀.
  • μœ„ μ˜ˆμ œμ—μ„œ new Person('Lee')λŠ” μ΄ν•¨μˆ˜λ₯Ό 톡해 fooκ°€ λ‚˜μ™”λ‹€. 이 foo의 __proto__λ₯Ό 가리킨닀.

차이비ꡐ

function Person(name) {
  this.name = name
}

var foo = new Person('Kim')

console.log(Person.__proto__ === Function.prototype)

console.log(Person.prototype === foo.__proto__)

Constructor

ν”„λ‘œν† νƒ€μž… κ°μ²΄λŠ” constructor ν”„λ‘œνΌν‹°λ₯Ό κ°–λŠ”λ‹€. 이 constructor ν”„λ‘œνΌν‹°λŠ” κ°μ²΄μž…μž₯μ—μ„œ μžμ‹ μ„ μƒμ„±ν•œ 객체λ₯Ό 가리킨닀.

function Person(name) {
  this.name = name
}

var foo = new Person('Lee')

// Person() μƒμ„±μž ν•¨μˆ˜μ— μ˜ν•΄ μƒμ„±λœ 객체λ₯Ό μƒμ„±ν•œ κ°μ²΄λŠ” Person() μƒμ„±μž ν•¨μˆ˜μ΄λ‹€.
console.log(Person.prototype.constructor === Person)

// foo 객체λ₯Ό μƒμ„±ν•œ κ°μ²΄λŠ” Person() μƒμ„±μž ν•¨μˆ˜μ΄λ‹€.
console.log(foo.constructor === Person)

// Person() μƒμ„±μž ν•¨μˆ˜λ₯Ό μƒμ„±ν•œ κ°μ²΄λŠ” Function() μƒμ„±μž ν•¨μˆ˜μ΄λ‹€.
console.log(Person.constructor === Function)

정리!!!

  • [[Prototype]]은 μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ λͺ¨λ“  객체가 κ°€μ§„ 값이며, __proto__둜 μ ‘κ·Όν•  수 μžˆλ‹€. (ν˜Ήμ€ Object.getPrototypeOf(obj))도 κ°€λŠ₯ν•˜λ‹€. 사싀 λ˜‘κ°™λ‹€.) 이것을 μ΄μš©ν•΄ 상속을 κ΅¬ν˜„ν•˜λ©°, 타고타고 μ˜¬λΌκ°€λ©΄ Object.prototype을 λ§Œλ‚˜κ³  Object.__proto__λŠ” null이닀.
  • prototype은 new둜 μƒˆλ‘œμš΄ objectλ₯Ό λ§Œλ“€μ—ˆμ„λ•Œ (μƒμ„±μžλ‘œ μ‚¬μš©λ λ•Œ), 이 ν•¨μˆ˜λ‘œ 생성될 객체의 λΆ€λͺ¨ 역할을 ν•  객체λ₯Ό 가리킨닀. (XXX.prototype)
  • XXX.prototype κ°μ²΄λŠ” constructorλ₯Ό κ°–λŠ”λ°, constructorλŠ” μžμ‹ μ˜ μž…μž₯μ—μ„œ μžμ‹ μ„ μƒμ„±ν•œ 객체λ₯Ό 가리킨닀. λ”°λΌμ„œ XXXλ₯Ό 가리킨닀.

https://i.stack.imgur.com/UfXRZ.png

Fooλ₯Ό μ€‘μ‹¬μœΌλ‘œ μ„€λͺ…ν•΄λ³΄μž.

  • FooλŠ” ν•¨μˆ˜λ‹€. λ”°λΌμ„œ 이 ν•¨μˆ˜μ˜ ν”„λ‘œν† νƒ€μž…μ€ Function.prototype이닀.
  • Fooλ₯Ό μƒμ„±μž ν•¨μˆ˜λ‘œ μ‚¬μš©ν–ˆμ„λ•Œ, prototypeν”„λ‘œνΌν‹°λ₯Ό κ°–κ²Œλ˜λ©°, Foo.prototype이 생긴닀.
  • Foo.prototype의 ν”„λ‘œν† νƒ€μž…μ€ Object이닀. (ν•¨μˆ˜κ°€ μ•„λ‹Œ μƒˆλ‘œμš΄ κ°μ²΄μ΄λ―€λ‘œ
  • Fooλ₯Ό μƒμ„±μžν•¨μˆ˜λ‘œ, b와 cλ₯Ό newλ₯Ό μ΄μš©ν•˜μ—¬ λ§Œλ“€μ—ˆλ‹€. b와 c의 ν”„λ‘œν† νƒ€μž…μ€ Foo.prototype이닀.
// μƒμ„±μž ν•¨μˆ˜
function Foo(y) {
  // Objectλ₯Ό μƒμ„±ν•œ 뒀에, y ν”„λ‘œνΌν‹°μ— y값을 κ°–κ²Œ λœλ‹€.
  this.y = y
}

// λ˜ν•œ 'Foo.prototype'은 Foo의 prototype에 ν• λ‹Ήλ˜λ©°,
// 이λ₯Ό ν™œμš©ν•΄μ„œ ν”„λ‘œνΌν‹°λ‚˜ λ©”μ†Œλ“œλ₯Ό μƒμ†λ°›κ±°λ‚˜ κ³΅μœ ν•  수 있으며,
// μœ„μ—μ„œ μ œμ‹œν•œ μ˜ˆμ‹œμ™€ 같이 ν™œμš©ν•  수 μžˆλ‹€.
Foo.prototype.x = 10

// calculate λ©”μ†Œλ“œλ₯Ό μƒμ†λ°›λŠ”λ‹€
Foo.prototype.calculate = function (z) {
  return this.x + this.y + z
}

// Foo νŒ¨ν„΄μ„ ν™œμš©ν•˜μ—¬ b와 c 였브젝트λ₯Ό μƒμ„±ν•œλ‹€.
var b = new Foo(20)
var c = new Foo(30)

// 상속받은 λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•œλ‹€.
b.calculate(30) // 60
c.calculate(40) // 80

// ν•œλ²ˆ ν™•μΈν•΄λ³΄μž.
console.log(
  b.__proto__ === Foo.prototype, // true
  c.__proto__ === Foo.prototype, // true

  // Foo.prototype은 constructorλΌλŠ” μƒˆλ‘œμš΄ ν”„λ‘œνΌν‹°λ₯Ό λ§Œλ“œλŠ”λ°,
  // μ΄λŠ” ν•¨μˆ˜ κ·Έ 자체의 μƒμ„±μžλ₯Ό μ°Έμ‘°ν•˜κ²Œ λœλ‹€.
  // b, c μƒμ„±μžλŠ” Foo μžμ²΄μž„μ„ μ•Œ 수 μžˆλ‹€.

  b.constructor === Foo, // true
  c.constructor === Foo, // true
  Foo.prototype.constructor === Foo, // true

  b.calculate === b.__proto__.calculate, // true
  b.__proto__.calculate === Foo.prototype.calculate, // true
)

./images/proto.001.jpeg