Chapter 4: Mixing (Up) "Class" Objects - hochan222/Everything-in-JavaScript GitHub Wiki

์•ž์˜ ๋‹จ์› object๋ฅผ ์ ‘ํ•˜๋ฉด ๊ฐ์ฒด์ง€ํ–ฅํ”„๋กœ๊ทธ๋ž˜๋ฐ(object oriented (OO) programming)์„ ๋– ์˜ฌ๋ฆฌ๋Š”๊ฑด ์ž์—ฐ์Šค๋Ÿฐ ์ผ์ด๋‹ค. class์˜ ์ธ์Šคํ„ด์Šคํ™”(instantiation), ์ƒ์†(inheritance), ๋‹คํ˜•์„ฑ(polymorphism)์„ ๋ณด๊ธฐ์ „์— ๋จผ์ € ๋””์ž์ธ ํŒจํ„ด์œผ๋กœ์จ์˜ "class orientation"์„ ๋ณด์ž.

Class Theory

ํด๋ผ์Šค ์ƒ์†์€ ์†Œํ”„ํŠธ์›จ์–ด์—์„œ ์‹ค์ œ ๋ฌธ์ œ๋“ค์„ ๋ชจ๋ธ๋งํ•˜๋Š” ์•„ํ‚คํ…์ณ์ด๋‹ค. ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๊ธฐ๋ณธ ๋™์ž‘์€ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๊ด€๋ จ ๋™์ž‘์„ ๋ณธ์งˆ์ ์œผ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ์„ ์ž๋ฃŒ ๊ตฌ์กฐ๋ผ๊ณ ๋„ ๋ถ€๋ฅธ๋‹ค. ํด๋ž˜์Šค๋Š” ํŠน์ • ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ๋ถ„๋ฅ˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์˜๋ฏธํ•œ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด ์ž๋™์ฐจ๋Š” ์ฐจ๋Ÿ‰์ด๋ผ๋Š” ์ผ๋ฐ˜์ ์ธ class์˜ ํŠน์ • ๊ตฌํ˜„์œผ๋กœ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค. Vehicle Class(๋น„ํ–‰๊ธฐ, ์ž๋™์ฐจ ํƒˆ๊ฒƒ๋“ฑ๋“ฑ)์™€ Car Class(์—”์ง„, ์˜์ž ๋“ฑ๋“ฑ)๋ฅผ ์ •์˜ํ•˜์—ฌ ์†Œํ”„ํŠธ์›จ์–ด์—์„œ ํด๋ž˜์Šค์™€์ด ๊ด€๊ณ„๋ฅผ ๋ชจ๋ธ๋งํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ณธ์งˆ์„ ๋ฐ˜๋ณตํ•ด์„œ ๋‹ค์‹œ ์ •์˜ํ•˜๋Š” ๊ฒƒ์€ ์†Œํ”„ํŠธ์›จ์–ด์—์„œ ์˜๋ฏธ๊ฐ€ ์—†๋‹ค. ๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” ์ƒ์†์„ ์‚ฌ์šฉํ•œ๋‹ค. Class๋Š” ์ด๊ฒƒ๋“ค์„ ์ง‘ํ•ฉ์ ์œผ๋กœ ์ •์˜ํ•˜์ง€๋งŒ ์ธ์Šคํ„ด์Šคํ™”๋Š” ๊ฐœ๋ณ„ ๊ณ ์œ ์˜ ๊ฒƒ์œผ๋กœ ์ •์˜ํ•œ๋‹ค.

Class์˜ ๋˜ ๋‹ค๋ฅธ ํŠน์„ฑ์€ ๋‹คํ˜•์„ฑ์œผ๋กœ ๋ถ€๋ชจ Class์˜ ์ผ๋ฐ˜์  ๋™์ž‘์„ ์ž์‹ Class์—์„œ ์žฌ์ •์˜ํ•ด์„œ ์ œ๊ณต ํ•  ์ˆ˜ ์žˆ๋‹ค.

"Class" Design Pattern

"Iterator", "Observer", "Factory", "Singleton"์„ "OO Design Patterns"์œผ๋กœ ํ† ๋ก ํ•˜๋Š” ๊ฑฐ์Šฌ ์ฃผ๋กœ ๋ณด์•˜๊ธฐ์— OO class๋“ค์„ ๋””์ž์ธ ํŒจํ„ด์œผ๋กœ ์ƒ๊ฐํ•˜์ง€ ์•Š์•˜์„ ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ class๋Š” ๋ชจ๋“  ๊ฒƒ์„ ํฌํ•จ ํ•  ์ˆ˜ ์žˆ๋Š” ๋‚ฎ์€ ์ˆ˜์ค€์˜ ๋””์ž์ธ ํŒจํ„ด์ด๋‹ค.

JavaScript "Classes"

Javascript๊ฐ€ class ๋ฌธ๋ฒ•์„ ์‹ค์ œ์ ์œผ๋กœ ๊ฐ–๊ณ ์žˆ๋Š”๊ฐ€? ์•„๋‹ˆ๋‹ค.

JS๋Š” ํด๋ž˜์Šค์™€ ์œ ์‚ฌํ•œ ๊ตฌ๋ฌธ์„ ์ œ๊ณตํ•˜์—ฌ Class๋ฅผ ๋””์ž์ธํ•˜๋ ค๋Š” ์š•๊ตฌ๋ฅผ ์ถฉ์กฑ์‹œ์ผœ์ค€๋‹ค. ํ•œ๋งˆ๋””๋กœ JS์—์„œ ํด๋ผ์Šค๋Š” ๊ฐ€์งœ๋‹ค.

Class Mechanics

๋งŽ์€ ํด๋ผ์Šค ์ง€ํ–ฅ ์–ธ์–ด์—์„œ ์ž๋ฃŒ๊ตฌ์กฐ์ธ ์Šคํƒ์„ ํด๋ผ์Šค๋กœ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์–ธ์–ด์—์„œ ์‹ค์ œ๋กœ ์Šคํƒ์—์„œ ์ง์ ‘ ์ž‘์—…ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์Šคํƒ ํด๋ผ์Šค๋Š” ์ถ”์ƒ์ ์ธ ์„ค๋ช…์— ๋ถˆ๊ณผํ•˜๊ณ  ๊ตฌ์ฒด์ ์ธ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ๊ฐ–์œผ๋ ค๋ฉด ์ธ์Šคํ„ด์Šคํ™” ํ•ด์•ผํ•œ๋‹ค.

Building

์šฐ๋ฆฌ๋Š” ์‹ค์ œ๋กœ ์ƒํ˜ธ์ž‘์šฉ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ธฐ์œ„ํ•ด ํด๋ผ์Šค๋ฅผ ๋นŒ๋“œ(์ธ์Šคํ„ด์Šคํ™”)ํ•˜๋ฉฐ ์ธ์Šคํ„ด์Šค(๊ฐ์ฒด)๋ฅผ ์–ป๋Š”๋‹ค. ๋˜ํ•œ, ํ•„์š”์—๋”ฐ๋ผ ์ด๋ฅผ ํ†ตํ•ด ๋ฉ”์†Œ๋“œ์™€ ๊ณต๊ฐœ ๋ฐ์ดํ„ฐ ์†์„ฑ์— ์ ‘๊ทผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

Constructor

ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋Š” ์ƒ์„ฑ์ž๋ผ๊ณ  ํ•˜๋Š” ํด๋ผ์Šค์™€ ๋™์ผํ•œ ์ด๋ฆ„์˜ ํด๋ผ์Šค์˜ ํŠน์ˆ˜ ๋ฉ”์†Œ๋“œ์™€ ํ•จ๊ป˜ ๊ตฌ์„ฑ๋œ๋‹ค. ์ด ๋ฉ”์„œ๋“œ์˜ ๋ช…์‹œ์  ์ž‘์—…์€ ์ธ์Šคํ„ด์Šค์˜ ํ•„์š”ํ•œ ์ •๋ณด(์ƒํƒœ)๋ฅผ ์ดˆ๊ธฐํ™” ํ•˜๋Š”๊ฒƒ์ด๋‹ค.

๋‹ค์Œ ์˜์‚ฌ์ฝ”๋“œ๋ฅผ ๋ณด์ž.

class CoolGuy {
	specialTrick = nothing

	CoolGuy( trick ) {
		specialTrick = trick
	}

	showOff() {
		output( "Here's my trick: ", specialTrick )
	}
}

Joe = new CoolGuy( "jumping rope" )

Joe.showOff() // Here's my trick: jumping rope

Class Inheritance

์ƒ์† ์˜์‚ฌ์ฝ”๋“œ

class Vehicle {
	engines = 1

	ignition() {
		output( "Turning on my engine." )
	}

	drive() {
		ignition()
		output( "Steering and moving forward!" )
	}
}

class Car inherits Vehicle {
	wheels = 4

	drive() {
		inherited:drive()
		output( "Rolling on all ", wheels, " wheels!" )
	}
}

class SpeedBoat inherits Vehicle {
	engines = 2

	ignition() {
		output( "Turning on my ", engines, " engines." )
	}

	pilot() {
		inherited:drive()
		output( "Speeding through the water with ease!" )
	}
}

Polymorphism

JS์—์„œ ์ž์‹๊ณผ ๋ถ€๋ชจ ์‚ฌ์ด์˜ ๊ด€๊ณ„๋Š” ๊ฐ ์ƒ์„ฑ์ž์˜ ๋‘ prototype ๊ฐ์ฒด ์‚ฌ์ด์—๋งŒ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์„ฑ์ž ์ž์ฒด๋Š” ์ง์ ‘ ๊ด€๋ จ์ด ์—†๋‹ค.

ํด๋ž˜์Šค๊ฐ€ ์ƒ์† ๋  ๋•Œ ํด๋ž˜์Šค ์ž์ฒด(ํด๋ž˜์Šค์—์„œ ์ƒ์„ฑ ๋œ ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค๊ฐ€ ์•„๋‹ˆ๋‹ค.)๊ฐ€ ์ƒ์† ๋œ ํด๋ž˜์Šค๋ฅผ ์ƒ๋Œ€์ ์œผ๋กœ ์ฐธ์กฐํ•  ์ˆ˜์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์œผ๋ฉฐ ์ด ์ƒ๋Œ€ ์ฐธ์กฐ๋ฅผ ์ผ๋ฐ˜์ ์œผ๋กœ super๋ผ๊ณ  ํ•œ๋‹ค.

๋‹คํ˜•์„ฑ์€ ๋ถ€๋ชจ์™€ ์ž์‹์ด ์—ฐ๊ฒฐ๋ผ์žˆ๋Š”๊ฒŒ ์•„๋‹ˆ๊ณ  ์ž์‹ ํด๋ž˜์Šค์—์„œ ๋ถ€๋ชจ ํด๋ž˜์Šค๋กœ๋ถ€ํ„ฐ ๋ณต์‚ฌ๋ณธ์„ ๊ฐ€์ ธ์˜จ๋‹ค. ํด๋ž˜์Šค ์ƒ์†์€ ๋ณต์‚ฌ๋ณธ์„ ์˜๋ฏธํ•œ๋‹ค.

Multiple Inheritance

Javascript๋Š” "๋‹ค์ค‘ ์ƒ์†"์„ ์œ„ํ•œ ๊ธฐ๋ณธ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค.

Mixins

JavaScript์˜ ๊ฐ์ฒด ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ "์ƒ์†"๋˜๋Š” "์ธ์Šคํ„ด์Šคํ™”"ํ•  ๋•Œ ์ž๋™์œผ๋กœ ๋ณต์‚ฌ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ž๋ฐ” ์Šคํฌ๋ฆฝํŠธ์—๋Š” ๊ฐ์ฒด๋ฅผ ์ธ์Šคํ„ด์Šคํ™”ํ•˜๋Š” ํด๋ž˜์Šค๊ฐ€ ์—†๋‹ค. ๊ฐœ์ฒด๋Š” ๋‹ค๋ฅธ ๊ฐœ์ฒด์— ๋ณต์‚ฌ๋˜์ง€ ์•Š๊ณ  ์„œ๋กœ ์—ฐ๊ฒฐ๋œ๋‹ค.

๋‹ค๋ฅธ ์–ธ์–ด์—์„œ ํด๋ž˜์Šค ๋™์ž‘์€ ๋ณต์‚ฌ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ๋”ฐ๋ผ์„œ, JS ๊ฐœ๋ฐœ์ž๊ฐ€ JavaScript์—์„œ ํด๋ž˜์Šค์˜ ๋ˆ„๋ฝ ๋œ ๋ณต์‚ฌ ๋™์ž‘์„ mixins์„ ํ†ตํ•ด ์œ ์‚ฌ ๋™์ž‘์„ ๊ตฌํ˜„ํ•œ๋‹ค.

๋ช…์‹œ์ , ์•”์‹œ์  mixin์„ ์‚ดํŽด๋ณด์ž.

Explicit Mixins

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๋ณต์‚ฌ๋ฅผ ํ•˜์ง€ ์•Š์Œ์œผ๋กœ ๊ฐ™์€ ๋™์ž‘์œผ๋กœ extend()๋ผ๊ณ  ๋ถˆ๋ฆฌ๊ณ ์žˆ๋Š” mixin์„ ๋งŒ๋“ค์–ด๋ณด์ž.

// vastly simplified `mixin(..)` example:
function mixin( sourceObj, targetObj ) {
	for (var key in sourceObj) {
		// only copy if not already present
		if (!(key in targetObj)) {
			targetObj[key] = sourceObj[key];
		}
	}

	return targetObj;
}

var Vehicle = {
	engines: 1,

	ignition: function() {
		console.log( "Turning on my engine." );
	},

	drive: function() {
		this.ignition();
		console.log( "Steering and moving forward!" );
	}
};

var Car = mixin( Vehicle, {
	wheels: 4,

	drive: function() {
		Vehicle.drive.call( this );
		console.log( "Rolling on all " + this.wheels + " wheels!" );
	}
} );

๊ธฐ์ˆ ์ ์œผ๋กœ ํ•จ์ˆ˜๋Š” ์‹ค์ œ๋กœ ๋ณต์‚ฌ๊ฐ€ ๋˜์ง€์•Š๊ณ  ์ฐธ์กฐ๊ฐ€ ๋ณต์‚ฌ๋œ๋‹ค.

"Polymorphism" Revisited

JavaScript์—๋Š” ์ƒ๋Œ€ ๋‹คํ˜•์„ฑ์„ ์œ„ํ•œ ๊ธฐ๋Šฅ์ด ์—†๋‹ค. ๋”ฐ๋ผ์„œ Car์™€ Vehicle ๋ชจ๋‘ ๊ฐ™์€ ์ด๋ฆ„์˜ ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋‚˜ ๋˜๋Š” ๋‹ค๋ฅธ ํ˜ธ์ถœ์„ ๊ตฌ๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด ์ ˆ๋Œ€ ์ฐธ์กฐ๋ฅผ ๋งŒ๋“ค์–ด์•ผํ•œ๋‹ค.

Parasitic Inheritance

์ด ๋ฐฉ๋ฒ•์€ ์–ด๋–ค ๋ฉด์—์„œ๋Š” ๋ช…์‹œ์ ์ด๊ณ  ์–ด๋–ค ๋ฉด์—์„œ๋Š” ์•”์‹œ์ ์ด๋‹ค. Douglas Crockford์— ์˜ํ•ด ๋Œ€์ค‘ํ™”๋๋‹ค.

// "Traditional JS Class" `Vehicle`
function Vehicle() {
	this.engines = 1;
}
Vehicle.prototype.ignition = function() {
	console.log( "Turning on my engine." );
};
Vehicle.prototype.drive = function() {
	this.ignition();
	console.log( "Steering and moving forward!" );
};

// "Parasitic Class" `Car`
function Car() {
	// first, `car` is a `Vehicle`
	var car = new Vehicle();

	// now, let's modify our `car` to specialize it
	car.wheels = 4;

	// save a privileged reference to `Vehicle::drive()`
	var vehDrive = car.drive;

	// override `Vehicle::drive()`
	car.drive = function() {
		vehDrive.call( this );
		console.log( "Rolling on all " + this.wheels + " wheels!" );
	};

	return car;
}

var myCar = new Car();

myCar.drive();
// Turning on my engine.
// Steering and moving forward!
// Rolling on all 4 wheels!

Implicit Mixins

var Something = {
	cool: function() {
		this.greeting = "Hello World";
		this.count = this.count ? this.count + 1 : 1;
	}
};

Something.cool();
Something.greeting; // "Hello World"
Something.count; // 1

var Another = {
	cool: function() {
		// implicit mixin of `Something` to `Another`
		Something.cool.call( this );
	}
};

Another.cool();
Another.greeting; // "Hello World"
Another.count; // 1 (not shared state with `Something`

Review (TL;DR)

ํด๋ผ์Šค๋Š” ๋””์ž์ธ ํŒจํ„ด์ด๋‹ค.

ํด๋ผ์Šค๋Š” ๋ณต์‚ฌ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

์ „ํ†ต์ ์ธ ํด๋ž˜์Šค๊ฐ€ ์ธ์Šคํ„ด์Šคํ™”๋˜๋ฉด ํด๋ž˜์Šค์—์„œ ์ธ์Šคํ„ด์Šค๋กœ ๋™์ž‘์˜ ๋ณต์‚ฌ๋ณธ์ด ๋ฐœ์ƒํ•˜๊ณ , ํด๋ž˜์Šค๊ฐ€ ์ƒ์†๋˜๋ฉด ๋ถ€๋ชจ์—์„œ ์ž์‹์œผ๋กœ์˜ ๋™์ž‘ ๋ณต์‚ฌ๋ณธ๋„ ๋ฐœ์ƒํ•œ๋‹ค.

๋‹คํ˜•์„ฑ๋„ ๋ณต์‚ฌ๋™์ž‘์˜ ๊ฒฐ๊ณผ์ด๋‹ค.

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๊ฐ์ฒด์‚ฌ์ด์—์„œ ์ž๋™์ ์œผ๋กœ ๋ณต์‚ฌ๋ณธ์„ ์ƒ์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค.