클래스 전체에 메소드를 할당할 수도 있습니다. 이러한 메소드를 static 이라고 합니다.
클래스 선언에서는 다음과 같이 static
키워드가 앞에 붙습니다.
클래스 사용자 { 정적 정적메소드() { 경고(이 === 사용자); } } 사용자.정적메소드(); // 진실
이는 실제로 이를 속성으로 직접 할당하는 것과 동일합니다.
클래스 사용자 { } User.staticMethod = 함수() { 경고(이 === 사용자); }; 사용자.정적메소드(); // 진실
User.staticMethod()
호출에서 this
값은 클래스 생성자 User
자체입니다("점 앞의 개체" 규칙).
일반적으로 정적 메서드는 클래스 전체에 속하지만 특정 개체에는 속하지 않는 함수를 구현하는 데 사용됩니다.
예를 들어, Article
개체가 있고 이를 비교하는 함수가 필요합니다.
자연스러운 해결책은 Article.compare
정적 메소드를 추가하는 것입니다.
수업 기사 { 생성자(제목, 날짜) { this.제목 = 제목; this.date = 날짜; } 정적 비교(기사A, 기사B) { 기사A.날짜 반환 - 기사B.날짜; } } // 용법 기사를 보자 = [ 새 기사("HTML", 새 날짜(2019, 1, 1)), new Article("CSS", new Date(2019, 0, 1)), new Article("JavaScript", new Date(2019, 11, 1)) ]; Articles.sort(Article.compare); 경고( 기사[0].제목 ); // CSS
여기서 Article.compare
메소드는 기사를 비교하는 수단으로 기사 "위"를 의미합니다. 기사의 방법이 아니라 전체 수업의 방법입니다.
또 다른 예는 소위 "팩토리" 방법입니다.
기사를 작성하려면 여러 가지 방법이 필요하다고 가정해 보겠습니다.
주어진 매개변수( title
, date
등)로 생성합니다.
오늘 날짜로 빈 기사를 만듭니다.
… 아니면 어떻게든.
첫 번째 방법은 생성자를 통해 구현할 수 있습니다. 두 번째로는 클래스의 정적 메서드를 만들 수 있습니다.
여기에는 Article.createTodays()
가 포함됩니다.
수업 기사 { 생성자(제목, 날짜) { this.제목 = 제목; this.date = 날짜; } 정적 createTodays() { // 기억하세요. 이것은 = 기사입니다. return new this("오늘의 다이제스트", new Date()); } } 기사 = Article.createTodays()를 보자; 경고(기사.제목); //오늘의 다이제스트
이제 오늘의 다이제스트를 생성해야 할 때마다 Article.createTodays()
호출할 수 있습니다. 다시 한번 말씀드리지만, 그것은 기사의 방법이 아니라 학급 전체의 방법입니다.
정적 메소드는 데이터베이스 관련 클래스에서도 다음과 같이 데이터베이스에서 항목을 검색/저장/제거하는 데 사용됩니다.
// Article이 기사 관리를 위한 특수 클래스라고 가정합니다. // ID별로 기사를 제거하는 정적 메소드: Article.remove({id: 12345});
개별 객체에는 정적 메서드를 사용할 수 없습니다.
정적 메서드는 개별 개체가 아닌 클래스에서 호출 가능합니다.
예를 들어 다음과 같은 코드는 작동하지 않습니다.
// ... 기사.createTodays(); /// 오류: Article.createTodays는 함수가 아닙니다.
최근 추가된 내용
이것은 최근에 언어에 추가된 것입니다. 예제는 최신 Chrome에서 작동합니다.
정적 속성도 가능합니다. 일반 클래스 속성처럼 보이지만 앞에 static
붙습니다.
수업 기사 { 정적 게시자 = "일리아 칸토르"; } 경고( Article.publisher ); // 일리아 칸토르
이는 Article
에 직접 할당하는 것과 같습니다.
Article.publisher = "일리아 칸토르";
정적 속성과 메서드는 상속됩니다.
예를 들어 아래 코드의 Animal.compare
및 Animal.planet
Rabbit.compare
및 Rabbit.planet
으로 상속되고 액세스 가능합니다.
클래스 동물 { 정적 행성 = "지구"; 생성자(이름, 속도) { this.speed = 속도; this.name = 이름; } 실행(속도 = 0) { this.speed += 속도; Alert(`${this.name}은(는) ${this.speed} 속도로 실행됩니다.`); } 정적 비교(동물A, 동물B) { returnanimalA.speed -animalB.speed; } } // 동물로부터 상속 Rabbit 클래스는 Animal {를 확장합니다. 숨다() { Alert(`${this.name}이(가) 숨겨졌습니다!`); } } 토끼 = [ new Rabbit("흰 토끼", 10), new Rabbit("검은 토끼", 5) ]; Rabbits.sort(Rabbit.compare); 토끼[0].run(); // 검은 토끼는 속도 5로 달립니다. 경고(토끼.행성); // 지구
이제 Rabbit.compare
호출하면 상속된 Animal.compare
호출됩니다.
어떻게 작동하나요? 이번에도 프로토타입을 사용합니다. 이미 짐작하셨겠지만, extends
Rabbit
Animal
에 대한 [[Prototype]]
참조를 제공합니다.
따라서 Rabbit extends Animal
두 개의 [[Prototype]]
참조를 생성합니다.
Rabbit
함수는 프로토타입적으로 Animal
함수를 상속받습니다.
Rabbit.prototype
프로토타입적으로 Animal.prototype
상속받습니다.
결과적으로 상속은 일반 메서드와 정적 메서드 모두에서 작동합니다.
여기서는 코드로 확인해 보겠습니다.
클래스 동물 {} Rabbit 클래스는 Animal {}를 확장합니다. // 정적용 Alert(Rabbit.__proto__ === 동물); // 진실 // 일반 메소드의 경우 Alert(Rabbit.prototype.__proto__ === Animal.prototype); // 진실
정적 메서드는 "전체적으로" 클래스에 속하는 기능에 사용됩니다. 구체적인 클래스 인스턴스와 관련이 없습니다.
예를 들어, Article.compare(article1, article2)
비교 메서드 또는 Article.createTodays()
팩토리 메서드가 있습니다.
클래스 선언에서 static
단어로 레이블이 지정됩니다.
정적 속성은 인스턴스에 바인딩되지 않은 클래스 수준 데이터를 저장하려고 할 때 사용됩니다.
구문은 다음과 같습니다.
클래스 MyClass { 정적 속성 = ...; 정적 메소드() { ... } }
기술적으로 정적 선언은 클래스 자체에 할당하는 것과 동일합니다.
MyClass.property = ... MyClass.방법 = ...
정적 속성과 메서드는 상속됩니다.
class B extends A
경우 클래스 B
자체의 프로토타입은 A
: B.[[Prototype]] = A
가리킵니다. 따라서 B
에서 필드를 찾을 수 없으면 A
에서 검색이 계속됩니다.
중요도: 3
우리가 알고 있듯이 모든 개체는 일반적으로 Object.prototype
에서 상속되며 hasOwnProperty
등과 같은 "일반" 개체 메서드에 액세스할 수 있습니다.
예를 들어:
클래스 토끼 { 생성자(이름) { this.name = 이름; } } let Rabbit = new Rabbit("Rab"); // hasOwnProperty 메소드는 Object.prototype에서 온 것입니다. 경고(rabbit.hasOwnProperty('이름') ); // 진실
그러나 "class Rabbit extends Object"
와 같이 명시적으로 철자를 사용하면 결과는 단순한 "class Rabbit"
과 달라집니다.
차이점은 무엇입니까?
다음은 그러한 코드의 예입니다(작동하지 않습니다. 왜 고치나요?).
클래스 Rabbit은 Object {를 확장합니다. 생성자(이름) { this.name = 이름; } } let Rabbit = new Rabbit("Rab"); 경고(rabbit.hasOwnProperty('이름') ); // 오류
먼저 후자의 코드가 작동하지 않는 이유를 살펴보겠습니다.
그 이유는 실행해 보면 분명해집니다. 상속 클래스 생성자는 super()
호출해야 합니다. 그렇지 않으면 "this"
가 "정의"되지 않습니다.
수정 사항은 다음과 같습니다.
클래스 Rabbit은 Object {를 확장합니다. 생성자(이름) { 감독자(); // 상속할 때 부모 생성자를 호출해야 합니다. this.name = 이름; } } let Rabbit = new Rabbit("Rab"); 경고(rabbit.hasOwnProperty('이름') ); // 진실
하지만 아직 그게 다가 아닙니다.
수정 후에도 "class Rabbit extends Object"
과 class Rabbit
사이에는 여전히 중요한 차이점이 있습니다.
우리가 알고 있듯이 "extends" 구문은 두 가지 프로토타입을 설정합니다.
생성자 함수의 "prototype"
사이(메서드용)
생성자 함수 자체 사이(정적 메서드의 경우)
class Rabbit extends Object
경우 이는 다음을 의미합니다.
Rabbit 클래스는 Object {}를 확장합니다. 경고( Rabbit.prototype.__proto__ === Object.prototype ); // (1) 참 경고( Rabbit.__proto__ === 객체 ); // (2) 참
따라서 Rabbit
이제 다음과 같이 Rabbit
통해 Object
의 정적 메서드에 대한 액세스를 제공합니다.
Rabbit 클래스는 Object {}를 확장합니다. // 일반적으로 Object.getOwnPropertyNames를 호출합니다. 경고( Rabbit.getOwnPropertyNames({a: 1, b: 2})); //a,b
그러나 extends Object
없으면 Rabbit.__proto__
Object
로 설정되지 않습니다.
데모는 다음과 같습니다.
클래스 토끼 {} 경고( Rabbit.prototype.__proto__ === Object.prototype ); // (1) 참 경고( Rabbit.__proto__ === 객체 ); // (2) 거짓(!) 경고( Rabbit.__proto__ === Function.prototype ); // 기본적으로 모든 함수로 // 오류입니다. Rabbit에는 해당 기능이 없습니다. 경고( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // 오류
따라서 Rabbit
이 경우 Object
의 정적 메서드에 대한 액세스를 제공하지 않습니다.
그건 그렇고, Function.prototype
call
, bind
등과 같은 "일반" 함수 메서드도 있습니다. 내장 Object
생성자의 경우 Object.__proto__ === Function.prototype
때문에 궁극적으로 두 경우 모두 사용할 수 있습니다.
사진은 다음과 같습니다.
간단히 말해서 두 가지 차이점이 있습니다.
클래스 토끼 | 클래스 Rabbit은 객체를 확장합니다. |
---|---|
– | 생성자에서 super() 호출해야 합니다. |
Rabbit.__proto__ === Function.prototype | Rabbit.__proto__ === Object |