객체 지향 프로그래밍에서 클래스는 객체 생성, 상태(멤버 변수) 및 동작 구현(멤버 함수 또는 메서드)에 대한 초기 값을 제공하기 위한 확장 가능한 프로그램 코드 템플릿입니다.
실제로 우리는 사용자, 상품 등 동일한 종류의 객체를 많이 생성해야 하는 경우가 많습니다.
생성자, 연산자 "new" 장에서 이미 알고 있듯이 new function
이에 도움이 될 수 있습니다.
그러나 최신 JavaScript에는 객체 지향 프로그래밍에 유용한 뛰어난 새 기능을 도입하는 보다 발전된 "클래스" 구조가 있습니다.
기본 구문은 다음과 같습니다.
클래스 MyClass { // 클래스 메소드 생성자() { ... } 메소드1() { ... } 메소드2() { ... } 메소드3() { ... } ... }
그런 다음 new MyClass()
사용하여 나열된 모든 메서드로 새 객체를 만듭니다.
constructor()
메소드는 new
에 의해 자동으로 호출되므로 여기서 객체를 초기화할 수 있습니다.
예를 들어:
클래스 사용자 { 생성자(이름) { this.name = 이름; } 안녕하세요() { 경고(this.name); } } // 용법: user = new User("John"); user.sayHi();
new User("John")
호출될 때:
새로운 객체가 생성됩니다.
constructor
주어진 인수로 실행되고 이를 this.name
에 할당합니다.
… 그런 다음 user.sayHi()
와 같은 객체 메서드를 호출할 수 있습니다.
클래스 메소드 사이에 쉼표가 없습니다.
초보 개발자가 흔히 저지르는 함정은 클래스 메서드 사이에 쉼표를 넣는 것인데, 이로 인해 구문 오류가 발생합니다.
여기서의 표기법을 객체 리터럴과 혼동해서는 안 됩니다. 클래스 내에서는 쉼표가 필요하지 않습니다.
그렇다면 class
정확히 무엇입니까? 생각하는 것처럼 그것은 완전히 새로운 언어 수준의 개체는 아닙니다.
마법을 풀고 클래스가 실제로 무엇인지 살펴보겠습니다. 이는 많은 복잡한 측면을 이해하는 데 도움이 될 것입니다.
JavaScript에서 클래스는 일종의 함수입니다.
여기를 살펴보세요:
클래스 사용자 { 생성자(이름) { this.name = 이름; } sayHi() { 경고(this.name); } } // 증명: 사용자는 함수입니다 경고(사용자 유형); // 기능
class User {...}
실제로 수행하는 작업은 다음과 같습니다.
클래스 선언의 결과가 되는 User
라는 함수를 만듭니다. 함수 코드는 constructor
메서드에서 가져옵니다(해당 메서드를 작성하지 않으면 비어 있다고 가정).
sayHi
와 같은 클래스 메서드를 User.prototype
에 저장합니다.
new User
객체가 생성된 후 해당 메소드를 호출하면 F.prototype 장에 설명된 대로 프로토타입에서 가져옵니다. 따라서 객체는 클래스 메서드에 액세스할 수 있습니다.
class User
선언의 결과를 다음과 같이 설명할 수 있습니다.
이를 조사하는 코드는 다음과 같습니다.
클래스 사용자 { 생성자(이름) { this.name = 이름; } sayHi() { 경고(this.name); } } // 클래스는 함수이다 경고(사용자 유형); // 기능 // ...또는 더 정확하게는 생성자 메서드입니다. 경고(사용자 === User.prototype.constructor); // 진실 // 메소드는 User.prototype에 있습니다. 예: 경고(User.prototype.sayHi); //sayHi 메소드의 코드 // 프로토타입에는 정확히 두 가지 메서드가 있습니다. 경고(Object.getOwnPropertyNames(User.prototype)); // 생성자, sayHi
때때로 사람들은 class
가 "구문적 설탕"(읽기 쉽게 하기 위해 고안되었지만 새로운 것을 도입하지 않는 구문)이라고 말합니다. 왜냐하면 실제로 class
키워드를 전혀 사용하지 않고도 동일한 것을 선언할 수 있기 때문입니다.
// 순수 함수로 클래스 User 다시 작성 // 1. 생성자 함수 생성 함수 사용자(이름) { this.name = 이름; } // 함수 프로토타입에는 기본적으로 "constructor" 속성이 있습니다. // 그래서 우리는 그것을 만들 필요가 없습니다 // 2. 프로토타입에 메소드 추가 User.prototype.sayHi = 함수() { 경고(this.name); }; // 용법: user = new User("John"); user.sayHi();
이 정의의 결과는 거의 동일합니다. 따라서 class
프로토타입 메서드와 함께 생성자를 정의하는 구문적 설탕으로 간주될 수 있는 이유가 있습니다.
그러나 중요한 차이점이 있습니다.
먼저, class
에 의해 생성된 함수는 특수 내부 속성 [[IsClassConstructor]]: true
로 레이블이 지정됩니다. 따라서 수동으로 만드는 것과 완전히 동일하지는 않습니다.
언어는 다양한 위치에서 해당 속성을 확인합니다. 예를 들어 일반 함수와 달리 new
사용하여 호출해야 합니다.
클래스 사용자 { 생성자() {} } 경고(사용자 유형); // 기능 사용자(); // 오류: 클래스 생성자 사용자는 'new' 없이 호출할 수 없습니다.
또한 대부분의 JavaScript 엔진에서 클래스 생성자의 문자열 표현은 "class…"로 시작합니다.
클래스 사용자 { 생성자() {} } 경고(사용자); // 클래스 사용자 { ... }
다른 차이점도 있습니다. 곧 살펴보겠습니다.
클래스 메소드는 열거 불가능합니다. 클래스 정의는 "prototype"
의 모든 메서드에 대해 enumerable
플래그를 false
로 설정합니다.
좋습니다. 왜냐하면 객체에 대해 for..in
사용한다면 일반적으로 객체의 클래스 메서드를 원하지 않기 때문입니다.
클래스는 항상 use strict
. 클래스 구성 내부의 모든 코드는 자동으로 엄격 모드에 있습니다.
게다가 class
구문은 나중에 살펴볼 다른 많은 기능을 제공합니다.
함수와 마찬가지로 클래스도 다른 표현식 내에서 정의하고, 전달하고, 반환하고, 할당하는 등의 작업을 할 수 있습니다.
다음은 클래스 표현식의 예입니다.
사용자 = 클래스 { 안녕하세요() { Alert("안녕하세요"); } };
명명된 함수 표현식과 마찬가지로 클래스 표현식에도 이름이 있을 수 있습니다.
클래스 표현식에 이름이 있으면 클래스 내부에서만 표시됩니다.
// "명명된 클래스 표현식" // (사양에는 그러한 용어가 없지만 명명된 함수 표현식과 유사합니다) 사용자 = 클래스 MyClass { 안녕하세요() { 경고(MyClass); // MyClass 이름은 클래스 내부에서만 표시됩니다. } }; 새로운 사용자().sayHi(); // 작동하고 MyClass 정의를 표시합니다. 경고(MyClass); // 오류, MyClass 이름은 클래스 외부에 표시되지 않습니다.
다음과 같이 동적으로 "주문형" 클래스를 만들 수도 있습니다.
함수 makeClass(문구) { // 클래스를 선언하고 반환 반환 클래스 { 안녕하세요() { 경고(문구); } }; } // 새 클래스 생성 let User = makeClass("안녕하세요"); 새로운 사용자().sayHi(); // 안녕하세요
리터럴 객체와 마찬가지로 클래스에는 getter/setter, 계산된 속성 등이 포함될 수 있습니다.
다음은 get/set
사용하여 구현된 user.name
의 예입니다.
클래스 사용자 { 생성자(이름) { // 세터를 호출합니다. this.name = 이름; } 이름 가져오기() { this._name을 반환합니다. } 이름(값) 설정 { if (value.length < 4) { Alert("이름이 너무 짧습니다."); 반품; } this._name = 값; } } user = new User("John"); 경고(사용자.이름); // 존 사용자 = 새로운 사용자(""); // 이름이 너무 짧습니다.
기술적으로 이러한 클래스 선언은 User.prototype
에서 getter 및 setter를 생성하여 작동합니다.
다음은 대괄호 [...]
사용하여 계산된 메서드 이름을 사용한 예입니다.
클래스 사용자 { ['말하다' + '안녕하세요']() { Alert("안녕하세요"); } } 새로운 사용자().sayHi();
이러한 기능은 리터럴 객체의 기능과 유사하므로 기억하기 쉽습니다.
오래된 브라우저에는 폴리필이 필요할 수 있습니다.
클래스 필드는 최근 언어에 추가되었습니다.
이전에는 클래스에 메서드만 있었습니다.
"클래스 필드"는 모든 속성을 추가할 수 있는 구문입니다.
예를 들어 class User
에 name
속성을 추가해 보겠습니다.
클래스 사용자 { 이름 = "존"; 안녕하세요() { Alert(`안녕하세요, ${this.name}!`); } } 새로운 사용자().sayHi(); // 안녕, 존!
그래서 그냥 "라고 씁니다.
클래스 필드의 중요한 차이점은 User.prototype
이 아닌 개별 객체에 설정된다는 것입니다.
클래스 사용자 { 이름 = "존"; } 사용자 = 새로운 사용자()를 보자; 경고(사용자.이름); // 존 경고(User.prototype.name); // 한정되지 않은
더 복잡한 표현식과 함수 호출을 사용하여 값을 할당할 수도 있습니다.
클래스 사용자 { name = 프롬프트("이름을 알려주세요.", "John"); } 사용자 = 새로운 사용자()를 보자; 경고(사용자.이름); // 존
JavaScript의 함수 바인딩 함수 장에서 설명했듯이 동적 this
있습니다. 통화 상황에 따라 다릅니다.
따라서 객체 메서드가 전달되어 다른 컨텍스트에서 호출되면 더 이상 this
객체에 대한 참조가 아닙니다.
예를 들어, 이 코드는 undefined
표시됩니다.
클래스 버튼 { 생성자(값) { this.value = 값; } 클릭() { 경고(this.value); } } let 버튼 = new Button("안녕하세요"); setTimeout(button.click, 1000); // 한정되지 않은
문제는 " this
잃어버리는 것"이라고 합니다.
함수 바인딩 장에서 설명한 대로 이 문제를 해결하는 두 가지 접근 방식이 있습니다.
setTimeout(() => button.click(), 1000)
과 같은 래퍼 함수를 전달합니다.
메소드를 객체(예: 생성자)에 바인딩합니다.
클래스 필드는 또 다른 매우 우아한 구문을 제공합니다.
클래스 버튼 { 생성자(값) { this.value = 값; } 클릭 = () => { 경고(this.value); } } let 버튼 = new Button("안녕하세요"); setTimeout(button.click, 1000); // 안녕하세요
클래스 필드 click = () => {...}
객체별로 생성되며, 각 Button
객체에 대해 별도의 함수가 있으며, this
함수는 해당 객체를 참조합니다. 어느 곳에서나 button.click
전달할 수 있으며 this
값은 항상 정확합니다.
이는 이벤트 리스너의 경우 브라우저 환경에서 특히 유용합니다.
기본 클래스 구문은 다음과 같습니다.
클래스 MyClass { 소품 = 가치; // 재산 생성자(...) { // 생성자 // ... } 메소드(...) {} // 메소드 get Something(...) {} // getter 메서드 set Something(...) {} // setter 메서드 [Symbol.iterator]() {} // 계산된 이름이 있는 메서드(여기서는 기호) // ... }
MyClass
는 기술적으로 함수(우리가 constructor
로 제공하는 함수)인 반면, 메서드, getter 및 setter는 MyClass.prototype
에 기록됩니다.
다음 장에서는 상속 및 기타 기능을 포함하여 클래스에 대해 자세히 알아봅니다.
중요도: 5
Clock
클래스(샌드박스 참조)는 함수형 스타일로 작성되었습니다. "class" 구문으로 다시 작성하세요.
PS 콘솔에서 시계가 똑딱거립니다. 콘솔을 열어 확인하세요.
작업에 대한 샌드박스를 엽니다.
클래스 시계 { 생성자({ 템플릿 }) { this.template = 템플릿; } 렌더링() { 날짜 = 새 날짜()를 설정합니다. 시간 = date.getHours(); if (시간 < 10) 시간 = '0' + 시간; mins = date.getMinutes(); if (분 < 10) mins = '0' + mins; 초 = date.getSeconds(); if (초 < 10) 초 = '0' + 초; 출력 = this.template .replace('h', 시간) .replace('m', 분) .replace('s', 초); console.log(출력); } 멈추다() { ClearInterval(this.timer); } 시작() { this.render(); this.timer = setInterval(() => this.render(), 1000); } } let clock = new Clock({template: 'h:m:s'}); 시계.시작();
샌드박스에서 솔루션을 엽니다.