웹사이트 검색

JavaScript의 클래스 이해


소개

JavaScript는 프로토타입 기반 언어이며 JavaScript의 모든 개체에는 개체 속성 및 메서드를 확장하는 데 사용할 수 있는 [[Prototype]]라는 숨겨진 내부 속성이 있습니다. JavaScript 자습서의 프로토타입 및 상속 이해에서 프로토타입에 대한 자세한 내용을 읽을 수 있습니다.

최근까지 부지런한 개발자들은 생성자 함수를 사용하여 JavaScript에서 객체 지향 디자인 패턴을 모방했습니다. ES6이라고도 하는 언어 사양 ECMAScript 2015는 JavaScript 언어에 클래스를 도입했습니다. JavaScript의 클래스는 실제로 추가 기능을 제공하지 않으며 더 깨끗하고 우아한 구문을 제공한다는 점에서 프로토타입 및 상속보다 "구문적 설탕\을 제공하는 것으로 종종 설명됩니다. 다른 프로그래밍 언어는 클래스를 사용하기 때문에 JavaScript의 클래스 구문은 개발자가 언어 간에 이동하는 것이 더 간단합니다.

클래스는 함수다

JavaScript 클래스는 함수의 한 유형입니다. 클래스는 class 키워드로 선언됩니다. 함수 표현식 구문을 사용하여 함수를 초기화하고 클래스 표현식 구문을 사용하여 클래스를 초기화합니다.

// Initializing a function with a function expression
const x = function() {}
// Initializing a class with a class expression
const y = class {}

Object.getPrototypeOf() 메서드를 사용하여 개체의 [[Prototype]]에 액세스할 수 있습니다. 이를 사용하여 우리가 만든 빈 함수를 테스트해 봅시다.

Object.getPrototypeOf(x);
Output
ƒ () { [native code] }

방금 만든 클래스에서 해당 메서드를 사용할 수도 있습니다.

Object.getPrototypeOf(y);
Output
ƒ () { [native code] }

functionclass로 선언된 코드는 모두 [[Prototype]] 함수를 반환합니다. 프로토타입을 사용하면 모든 함수가 new 키워드를 사용하여 생성자 인스턴스가 될 수 있습니다.

const x = function() {}

// Initialize a constructor from a function
const constructorFromFunction = new x();

console.log(constructorFromFunction);
Output
x {} constructor: ƒ ()

이것은 수업에도 적용됩니다.

const y = class {}

// Initialize a constructor from a class
const constructorFromClass = new y();

console.log(constructorFromClass);
Output
y {} constructor: class

이 프로토타입 생성자 예제는 그렇지 않으면 비어 있지만 구문 아래에서 두 메서드가 동일한 최종 결과를 달성하는 방법을 볼 수 있습니다.

클래스 정의

프로토타입 및 상속 자습서에서는 텍스트 기반 롤플레잉 게임에서 캐릭터 생성을 기반으로 한 예제를 만들었습니다. 여기서 해당 예제를 계속 사용하여 함수에서 클래스로 구문을 업데이트하겠습니다.

생성자 함수는 함수 자체를 참조하는 this의 속성으로 할당되는 여러 매개 변수로 초기화됩니다. 식별자의 첫 글자는 규칙에 따라 대문자로 표시됩니다.

// Initializing a constructor function
function Hero(name, level) {
	this.name = name;
	this.level = level;
}

이것을 아래에 표시된 클래스 구문으로 변환하면 구조가 매우 유사하다는 것을 알 수 있습니다.

// Initializing a class definition
class Hero {
	constructor(name, level) {
		this.name = name;
		this.level = level;
	}
}

우리는 생성자 함수가 이니셜라이저(선택 사항)의 첫 글자를 대문자로 표시하고 구문에 익숙함을 통해 개체 청사진을 의미한다는 것을 알고 있습니다. class 키워드는 우리 함수의 목적을 보다 직접적인 방식으로 전달합니다.

초기화 구문의 유일한 차이점은 function 대신 class 키워드를 사용하고 constructor() 메서드 내에서 속성을 할당하는 것입니다.

방법 정의

생성자 함수의 일반적인 방법은 아래의 greet() 메서드에서 볼 수 있듯이 초기화 대신 prototype에 직접 메서드를 할당하는 것입니다.

function Hero(name, level) {
	this.name = name;
	this.level = level;
}

// Adding a method to the constructor
Hero.prototype.greet = function() {
	return `${this.name} says hello.`;
}

클래스를 사용하면 이 구문이 단순화되고 메서드를 클래스에 직접 추가할 수 있습니다. ES6에 도입된 메서드 정의 속기를 사용하면 메서드를 정의하는 것이 훨씬 더 간결한 프로세스가 됩니다.

class Hero {
	constructor(name, level) {
		this.name = name;
		this.level = level;
	}

	// Adding a method to the constructor
	greet() {
		return `${this.name} says hello.`;
    }
}

이러한 속성과 메서드를 실제로 살펴보겠습니다. new 키워드를 사용하여 Hero의 새 인스턴스를 만들고 일부 값을 할당합니다.

const hero1 = new Hero('Varg', 1);

console.log(hero1)를 사용하여 새 개체에 대한 자세한 정보를 출력하면 클래스 초기화에서 발생하는 상황에 대한 자세한 내용을 볼 수 있습니다.

Output
Hero {name: "Varg", level: 1} __proto__: ▶ constructor: class Hero ▶ greet: ƒ greet()

출력에서 constructor()greet() 함수가 __proto__ 또는 [[Prototype ]]hero1이며 hero1 개체의 메서드로 직접 사용되지 않습니다. 이는 생성자 함수를 만들 때는 명확하지만 클래스를 만들 때는 명확하지 않습니다. 클래스는 보다 단순하고 간결한 구문을 허용하지만 프로세스에서 일부 명확성을 희생합니다.

클래스 확장

생성자 함수 및 클래스의 유리한 기능은 부모를 기반으로 하는 새 개체 청사진으로 확장할 수 있다는 것입니다. 이렇게 하면 유사하지만 일부 추가 기능이나 보다 구체적인 기능이 필요한 개체에 대한 코드의 반복을 방지할 수 있습니다.

새 생성자 함수는 call() 메서드를 사용하여 부모에서 만들 수 있습니다. 아래 예에서는 Mage라는 보다 구체적인 캐릭터 클래스를 만들고 call()을 사용하여 Hero의 속성을 할당합니다. 뿐만 아니라 추가 속성을 추가합니다.

// Creating a new constructor from the parent
function Mage(name, level, spell) {
	// Chain constructor with call
	Hero.call(this, name, level);

	this.spell = spell;
}

이 시점에서 Hero와 동일한 속성 및 추가한 새 속성을 사용하여 Mage의 새 인스턴스를 만들 수 있습니다.

const hero2 = new Mage('Lejon', 2, 'Magic Missile');

콘솔에 hero2를 보내면 생성자를 기반으로 새로운 Mage가 생성된 것을 볼 수 있습니다.

Output
Mage {name: "Lejon", level: 2, spell: "Magic Missile"} __proto__: ▶ constructor: ƒ Mage(name, level, spell)

ES6 클래스에서는 부모 함수에 액세스하기 위해 call 대신 super 키워드가 사용됩니다. extends를 사용하여 부모 클래스를 참조합니다.

// Creating a new class from the parent
class Mage extends Hero {
	constructor(name, level, spell) {
		// Chain constructor with super
		super(name, level);

		// Add a new property
		this.spell = spell;
	}
}

이제 동일한 방식으로 새 Mage 인스턴스를 만들 수 있습니다.

const hero2 = new Mage('Lejon', 2, 'Magic Missile');

hero2를 콘솔에 출력하고 출력을 봅니다.

Output
Mage {name: "Lejon", level: 2, spell: "Magic Missile"} __proto__: Hero ▶ constructor: class Mage

출력은 클래스 구성에서 [[Prototype]]이 부모(이 경우 Hero)에 연결된다는 점을 제외하면 거의 동일합니다.

다음은 초기화, 메서드 추가, 생성자 함수 및 클래스 상속의 전체 프로세스를 나란히 비교한 것입니다.

function Hero(name, level) {
	this.name = name;
	this.level = level;
}

// Adding a method to the constructor
Hero.prototype.greet = function() {
	return `${this.name} says hello.`;
}

// Creating a new constructor from the parent
function Mage(name, level, spell) {
	// Chain constructor with call
	Hero.call(this, name, level);

	this.spell = spell;
}
// Initializing a class
class Hero {
	constructor(name, level) {
		this.name = name;
		this.level = level;
	}

	// Adding a method to the constructor
	greet() {
		return `${this.name} says hello.`;
    }
}

// Creating a new class from the parent
class Mage extends Hero {
	constructor(name, level, spell) {
		// Chain constructor with super
		super(name, level);

		// Add a new property
		this.spell = spell;
	}
}

구문은 상당히 다르지만 기본 결과는 두 메서드 간에 거의 동일합니다. 클래스는 객체 청사진을 생성하는 보다 간결한 방법을 제공하고 생성자 함수는 내부에서 발생하는 일을 보다 정확하게 설명합니다.

결론

이 튜토리얼에서는 JavaScript 생성자 함수와 ES6 클래스의 유사점과 차이점에 대해 배웠습니다. 클래스와 생성자 모두 객체 지향 상속 모델을 프로토타입 기반 상속 언어인 JavaScript로 모방합니다.

프로토타입 상속을 이해하는 것은 효과적인 JavaScript 개발자가 되는 데 가장 중요합니다. 클래스에 익숙해지면 React와 같은 인기 있는 JavaScript 라이브러리가 class 구문을 자주 사용하므로 매우 유용합니다.