Chapter 5: Prototypes - hochan222/Everything-in-JavaScript GitHub Wiki

[Prototype](/hochan222/Everything-in-JavaScript/wiki/Prototype)

ํ”„๋กœํ† ํƒ€์ž…์€ ๋‹จ์ˆœํ•˜๊ฒŒ ๋‹ค๋ฅธ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์ฐธ์กฐ์ด๋‹ค. ๊ฐ์ฒด์—์„œ ์ด ์†์„ฑ์ด ์ƒ์„ฑ๋ ๋•Œ null์ด ์•„๋‹Œ ๊ฐ’์ด ์ฃผ์–ด์ง„๋‹ค.

๊ธฐ๋ณธ Get ์ž‘์—…์€ ๊ฐœ์ฒด์—์„œ ์š”์ฒญ ๋œ ์†์„ฑ์„ ์ง์ ‘ ์ฐพ์„ ์ˆ˜์—†๋Š” ๊ฒฝ์šฐ ๊ฐœ์ฒด์˜ Prototype ๋งํฌ๋ฅผ ๋”ฐ๋ผ ์ง„ํ–‰๋œ๋‹ค.

var anotherObject = {
	a: 2
};

// create an object linked to `anotherObject`
var myObject = Object.create( anotherObject );

myObject.a; // 2

a๊ฐ€ anotherObject์—์„œ ๋ฐœ๊ฒฌ๋˜์ง€ ์•Š์œผ๋ฉด ๋น„์–ด์žˆ์ง€ ์•Š์€ ๊ฒฝ์šฐ Prototype ์ฒด์ธ์„ ๋‹ค์‹œ ์ฐธ์กฐํ•˜๊ณ  ์ถ”์ ํ•œ๋‹ค. ์ด ํ”„๋กœ์„ธ์Šค๋Š” ์ผ์น˜ํ•˜๋Š” ์†์„ฑ ์ด๋ฆ„์„ ์ฐพ๊ฑฐ๋‚˜ Prototype ์ฒด์ธ์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ณ„์†๋œ๋‹ค.

for in ์„ ํ†ตํ•ด์„œ๋„ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๋‹ค.

var anotherObject = {
	a: 2
};

// create an object linked to `anotherObject`
var myObject = Object.create( anotherObject );

for (var k in myObject) {
	console.log("found: " + k);
}
// found: a

("a" in myObject); // true

Object.prototype

Prototype chain์€ ์ •ํ™•ํžˆ ์–ด๋””์„œ ๋๋‚ ๊นŒ?

๋ชจ๋“  prototype์˜ ์ตœ์ƒ์œ„๋Š” Object.prototype์ด๋‹ค.

Setting & Shadowing Properties

myObject.foo = "bar";

myObject ๊ฐœ์ฒด์— ์ด๋ฏธ foo๋ผ๋Š” ์ผ๋ฐ˜ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ์ž ์†์„ฑ์ด ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ ๊ฐ’์˜ ํ• ๋‹น์€ ๊ธฐ์กด ์†์„ฑ์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๊ฐ„๋‹จํ•˜๋‹ค. ํ•˜์ง€๋งŒ, foo๊ฐ€ myObject์— ์ง์ ‘ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ Get ์ž‘์—…๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Prototype ์ฒด์ธ์ด ์ˆœํšŒ๋œ๋‹ค. ๋งŒ์•ฝ ๋ชป์ฐพ์œผ๋ฉด myObject์— ์ง์ ‘ ์ถ”๊ฐ€ํ•œ๋‹ค.

foo๊ฐ€ myObject์—์„œ๋‚˜ ๋†’์€ ๋ ˆ๋ฒจ์˜ Prototype chain์—์„œ ๋๋‚ ๋•Œ ์‰๋„์ž‰์ด๋ผ๊ณ  ํ•œ๋‹ค. ํ•ญ์ƒ ๋‚ฎ์€ ๋ ˆ๋ฒจ์—์„œ ์ฐพ๊ธฐ๋•Œ๋ฌธ์— ๋†’์€ ๋ ˆ๋ฒจ์˜ ๋ชจ๋“  foo ์†์„ฑ์€ ์ˆจ๊ฒจ์ง„๋‹ค.

var anotherObject = {
	a: 2
};

var myObject = Object.create( anotherObject );

anotherObject.a; // 2
myObject.a; // 2

anotherObject.hasOwnProperty( "a" ); // true
myObject.hasOwnProperty( "a" ); // false

myObject.a++; // oops, implicit shadowing!

anotherObject.a; // 2
myObject.a; // 3

myObject.hasOwnProperty( "a" ); // true

"Class"

์‚ฌ์‹ค ์ž๋ฐ” ์Šคํฌ๋ฆฝํŠธ๋Š” "๊ฐ์ฒด ์ง€ํ–ฅ"์ด๋ผ๋Š” ๋‹จ์–ด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ์•ˆ๋˜๋Š” ์–ธ์–ด์ผ ๊ฒƒ์ด๋‹ค. ์ž๋ฐ” ์Šคํฌ๋ฆฝํŠธ๋Š” ํด๋ž˜์Šค ์—†์ด ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ƒ์„ฑ ํ•  ์ˆ˜์žˆ๋Š” ๋งค์šฐ ์งง์€ ์–ธ์–ด์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

"Class" Functions

๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  ํ•จ์ˆ˜๋Š” ํ”„๋กœํ† ํƒ€์ž…์ด๋ผ๊ณ ํ•˜๋Š” ์—ด๊ฑฐ ํ•  ์ˆ˜์—†๋Š” ๊ณต๊ฐœ ์†์„ฑ์„ ์–ป๋Š”๋ฐ, ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฅดํ‚จ๋‹ค.

function Foo() {
	// ...
}

Foo.prototype; // { }

๊ฐ€์žฅ ์ง์ ‘์ ์ธ ์„ค๋ช…์€ new Few()๋กœ ์ƒ์„ฑํ•œ ๊ฐ์ฒด๊ฐ€ ์ด ํ”„๋กœํ† ํƒ€์ž…์— ์—ฐ๊ฒฐ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

function Foo() {
	// ...
}

var a = new Foo();

Object.getPrototypeOf( a ) === Foo.prototype; // true

new Foo()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ a๊ฐ€ ์ƒ์„ฑ ๋  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ผ ์ค‘ ํ•˜๋‚˜๋Š” Foo.prototype์ด ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฐ์ฒด์— ๋Œ€ํ•œ ๋‚ด๋ถ€ Prototype ๋งํฌ๋ฅผ ์–ป๋Š” ๊ฒƒ์ด๋‹ค.

ํด๋ž˜์Šค ์ง€ํ–ฅ ์–ธ์–ด์—์„œ๋Š” ์ธ์Šคํ„ด์Šคํ™” ๊ณผ์ •์ด 'ํ•ด๋‹น ํด๋ž˜์Šค์˜ ํ–‰๋™ ๊ณ„ํš์„ ๋ฌผ๋ฆฌ์  ๊ฐ์ฒด๋กœ ๋ณต์‚ฌํ•˜๋Š” ๊ฒƒ'์„ ์˜๋ฏธํ•œ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” ์ด๋Ÿฐ ๋ณต์‚ฌ ๊ณผ์ •์ด ์ˆ˜ํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ํด๋ž˜์Šค์˜ ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์ง€๋„ ์•Š๋Š”๋‹ค. ๊ณตํ†ต ๊ฐœ์ฒด์— Prototype ๋งํฌํ•˜๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์ฒด๋ฅผ ๋งŒ๋“ ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ณต์‚ฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ์„ "prototypal inheritance"์ด๋ผ ๋ถ€๋ฅธ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๋‘ ๊ฐ์ฒด์‚ฌ์ด์˜ ๋งํฌ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋Š” ๋ณธ์งˆ์ ์œผ๋กœ ๋‹ค๋ฅธ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์†์„ฑ/๊ธฐ๋Šฅ ์•ก์„ธ์Šค๋ฅผ ์œ„์ž„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

"Constructors"

function Foo() {
	// ...
}

var a = new Foo();

Foo๊ฐ€ class๋ผ๊ณ  ์ƒ๊ฐํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ธ๊ฐ€?

function Foo() {
	// ...
}

Foo.prototype.constructor === Foo; // true

var a = new Foo();
a.constructor === Foo; // true

Constructor Or Call?

function NothingSpecial() {
	console.log( "Don't mind me!" );
}

var a = new NothingSpecial();
// "Don't mind me!"

a; // {}

์ผ๋ฐ˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์•ž์— new ํ‚ค์›Œ๋“œ๋ฅผ ๋„ฃ์œผ๋ฉด ํ•ด๋‹น ํ•จ์ˆ˜ ํ˜ธ์ถœ์ด "์ƒ์„ฑ์ž ํ˜ธ์ถœ"์ด ๋œ๋‹ค.

Mechanics

function Foo(name) {
	this.name = name;
}

Foo.prototype.myName = function() {
	return this.name;
};

var a = new Foo( "a" );
var b = new Foo( "b" );

a.myName(); // "a"
b.myName(); // "b"

"Constructor" Redux

function Foo() { /* .. */ }

Foo.prototype = { /* .. */ }; // create a new prototype object

var a1 = new Foo();
a1.constructor === Foo; // false!
a1.constructor === Object; // true!

Foo.prototype์˜ .constructor ์†์„ฑ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ Foo ํ•จ์ˆ˜๊ฐ€ ์„ ์–ธ ๋  ๋•Œ ์ƒ์„ฑ ๋œ ๊ฐ์ฒด์—๋งŒ ์žˆ๋‹ค. new๋กœ ์ƒ์„ฑํ•œ ๊ฐ์ฒด๋ฅผ ํ•จ์ˆ˜์˜ ๊ธฐ๋ณธ .prototype ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ๋Œ€์ฒดํ•˜๋ฉด ๊ฐ์ฒด๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ .constructor๋ฅผ ์–ป์ง€ ์•Š๋Š”๋‹ค.

Misconception, busted.

function Foo() { /* .. */ }

Foo.prototype = { /* .. */ }; // create a new prototype object

// Need to properly "fix" the missing `.constructor`
// property on the new object serving as `Foo.prototype`.
// See Chapter 3 for `defineProperty(..)`.
Object.defineProperty( Foo.prototype, "constructor" , {
	enumerable: false,
	writable: true,
	configurable: true,
	value: Foo    // point `.constructor` at `Foo`
} );

"(Prototypal) Inheritance"

function Foo(name) {
	this.name = name;
}

Foo.prototype.myName = function() {
	return this.name;
};

function Bar(name,label) {
	Foo.call( this, name );
	this.label = label;
}

// here, we make a new `Bar.prototype`
// linked to `Foo.prototype`
Bar.prototype = Object.create( Foo.prototype );

// Beware! Now `Bar.prototype.constructor` is gone,
// and might need to be manually "fixed" if you're
// in the habit of relying on such properties!

Bar.prototype.myLabel = function() {
	return this.label;
};

var a = new Bar( "a", "obj a" );

a.myName(); // "a"
a.myLabel(); // "obj a"
// doesn't work like you want!
Bar.prototype = Foo.prototype;

// works kinda like you want, but with
// side-effects you probably don't want :(
Bar.prototype = new Foo();
// pre-ES6
// throws away default existing `Bar.prototype`
Bar.prototype = Object.create( Foo.prototype );

// ES6+
// modifies existing `Bar.prototype`
Object.setPrototypeOf( Bar.prototype, Foo.prototype );

Inspecting "Class" Relationships

a์™€ ๊ฐ™์€ ๊ฐ์ฒด๊ฐ€ ์žˆ๊ณ  ์–ด๋–ค ๊ฐ์ฒด (์žˆ๋Š” ๊ฒฝ์šฐ)๋ฅผ ์œ„์ž„ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ•˜๋‚˜?

์ „ํ†ต ํด๋ผ์Šค ์ง€ํ–ฅ ํ™˜๊ฒฝ์—์„œ ์ธ์Šคํ„ด์Šค์ธ์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์„ introspection (or reflection)์ด๋ผ ํ•œ๋‹ค.

function Foo() {
	// ...
}

Foo.prototype.blah = ...;

var a = new Foo();
a instanceof Foo; // true

a์˜ ์ „์ฒด Prototype ์ฒด์ธ์—์„œ Foo.prototype์„ ์ž„์˜๋กœ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฐ์ฒด๊ฐ€ ์žˆ๋Š”์ง€ instanceof๊ฐ€ ์กฐ์‚ฌํ•ด์ค€๋‹ค.

๊ทธ์—๋Œ€ํ•œ ์Šค๋‹ˆํŽซ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

// helper utility to see if `o1` is
// related to (delegates to) `o2`
function isRelatedTo(o1, o2) {
	function F(){}
	F.prototype = o2;
	return o1 instanceof F;
}

var a = {};
var b = Object.create( a );

isRelatedTo( b, a ); // true

๋”ฐ๋ผ์„œ ๋‘๊ฐœ์˜ ์ธ์Šคํ„ด์Šค๊ฐ„์—๋Š” instanceof ๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•  ์ˆ˜ ์žˆ๋‹ค.

Foo.prototype.isPrototypeOf( a ); // true

a์˜ ์ „์ฒด Prototype ์ฒด์ธ์—์„œ Foo.prototype์ด ๋‚˜ํƒ€๋‚˜๋Š”์ง€?
instanceof์™€ ๋™์ผํ•˜๋‹ค ๊ทธ๋Ÿฌ๋‚˜ ์œ ์ผํ•œ ์ฐจ์ด์ ์€ ๋‘ ๋ฒˆ์งธ ์ ‘๊ทผ ๋ฐฉ์‹์—์„œ๋Š” .prototype ์†์„ฑ์ด ์ž๋™์œผ๋กœ ์ฐธ์กฐ๋˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๊ฐ„์ ‘์ ์ธ ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•˜์ง€์•Š๋Š”๋‹ค.

// Simply: does `b` appear anywhere in
// `c`s [Prototype](/hochan222/Everything-in-JavaScript/wiki/Prototype) chain?
b.isPrototypeOf( c );
Object.getPrototypeOf( a ) === Foo.prototype; // true

๋Œ€๋ถ€๋ถ„์˜ ๋ธŒ๋ผ์šฐ์ €์˜ ๋‚ด๋ถ€ Prototype์— ์•ก์„ธ์Šคํ•˜๋Š” ๋น„ํ‘œ์ค€ ๋Œ€์ฒด ๋ฐฉ๋ฒ•๋„ ์˜ค๋žซ๋™์•ˆ ์ง€์›ํ•ด ์™”๋‹ค.

a.__proto__ === Foo.prototype; // true

proto(ES6๊นŒ์ง€ ํ‘œ์ค€ํ™”๋˜์ง€ ์•Š์Œ)๋Š” ๊ฐ์ฒด์˜ ๋‚ด๋ถ€ Prototype์„ ์ฐธ์กฐ๋กœ ๊ฒ€์ƒ‰ํ•œ๋‹ค.

Object.defineProperty( Object.prototype, "__proto__", {
	get: function() {
		return Object.getPrototypeOf( this );
	},
	set: function(o) {
		// setPrototypeOf(..) as of ES6
		Object.setPrototypeOf( this, o );
		return o;
	}
} );

Object Links

Create()ing Links

var foo = {
	something: function() {
		console.log( "Tell me something good..." );
	}
};

var bar = Object.create( foo );

bar.something(); // Tell me something good...

Object.create (..)๋Š” ์ง€์ •ํ•œ ๊ฐ์ฒด foo์— ์—ฐ๊ฒฐ๋œ ์ƒˆ ๊ฐ์ฒด bar๋ฅผ ์ƒ์„ฑํ•˜์—ฌ Prototype ๋ฉ”์ปค๋‹ˆ์ฆ˜์˜ ๋ชจ๋“  ๊ถŒํ•œ (์œ„์ž„)์„ ์ œ๊ณตํ•œ๋‹ค.

Object.create() Polyfilled

Object.create()๋Š” ES5์— ์ถ”๊ฐ€๋˜์—ˆ๋‹ค.

if (!Object.create) {
	Object.create = function(o) {
		function F(){}
		F.prototype = o;
		return new F();
	};
}
var anotherObject = {
	a: 2
};

var myObject = Object.create( anotherObject, {
	b: {
		enumerable: false,
		writable: true,
		configurable: false,
		value: 3
	},
	c: {
		enumerable: true,
		writable: false,
		configurable: false,
		value: 4
	}
} );

myObject.hasOwnProperty( "a" ); // false
myObject.hasOwnProperty( "b" ); // true
myObject.hasOwnProperty( "c" ); // true

myObject.a; // 2
myObject.b; // 3
myObject.c; // 4

Links As Fallbacks?

var anotherObject = {
	cool: function() {
		console.log( "cool!" );
	}
};

var myObject = Object.create( anotherObject );

myObject.cool(); // "cool!"

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ฉด ํ˜ผ๋ž€์„ ๊ฐ€์ค‘์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

ES6์—์„œ๋Š” "๋ฉ”์„œ๋“œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ" ์œ ํ˜•์˜ ๋™์ž‘์„ ์ œ๊ณตํ•  ์ˆ˜์žˆ๋Š” ํ”„๋ก์‹œ๋ผ๋Š” ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์ด ๋„์ž…๋˜์—ˆ๋‹ค. (๋’ค์—์„œ ๋‹ค๋ฃฐ๊ฒƒ)

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜์ž.

var anotherObject = {
	cool: function() {
		console.log( "cool!" );
	}
};

var myObject = Object.create( anotherObject );

myObject.doCool = function() {
	this.cool(); // internal delegation!
};

myObject.doCool(); // "cool!"

Review (TL;DR)

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์ƒ์† ๋ฐ OOP ์šฉ์–ด๋“ค์€ ๋ฌด์˜๋ฏธํ•˜๋‹ค. ๊ด€๊ณ„๋“ค์ด ๋ณต์‚ฌ๋ณธ์ด์•„๋‹ˆ๋ผ ์œ„์ž„๋งํฌ์ด๊ธฐ ๋•Œ๋ฌธ์— ์œ„์ž„์ด๋ผ๊ณ  ๋ถ€๋ฅด๋Š”๊ฒŒ ์ ์ ˆํ•˜๋‹ค.