소개하다
이 기사에서는 ECMAScript에서 객체 지향 프로그래밍의 다양한 측면을 고려합니다 (이 주제는 이전에 많은 기사에서 논의되었지만). 우리는 이러한 문제를 이론적 관점에서 더 많이 살펴볼 것입니다. 특히, 우리는 객체의 생성 알고리즘을 고려할 것입니다. 토론에서 객체 (기본 관계 - 상속 포함) 간의 관계가 어떻게 사용할 수 있는지 고려할 것입니다 (JavaScript에서 OOP의 개념적 모호성이 제거되기를 바랍니다).
원본 영어 텍스트 : http://dmitrysoshnikov.com/ecmascript/chapter-7-1-op-general-theory/
소개, 패러다임 및 생각
ECMAScript에서 OOP 기술 분석을 수행하기 전에 OOP의 기본 특성을 마스터하고 소개의 주요 개념을 명확히해야합니다.
ECMAScript는 구조적, 객체 지향, 기능적, 명령 등을 포함한 다양한 프로그래밍 방법을 지원하며 경우에 따라 측면 지향 프로그래밍도 지원합니다. 그러나이 기사는 객체 지향 프로그래밍에 대해 설명하므로 ECMAScript에서 객체 지향 프로그래밍의 정의를 제공 할 것입니다.
ECMAScript는 프로토 타입 구현을 기반으로 한 객체 지향 프로그래밍 언어입니다.
프로토 타입 기반 OOP와 정적 클래스 기반 방법에는 많은 차이가 있습니다. 직접적인 상세한 차이점을 살펴 보겠습니다.
클래스 기반 및 프로토 타입 기반
이전 문장의 중요한 점은 이미 지적되었습니다. 전적으로 정적 클래스에 근거합니다. "정적"이라는 단어를 사용하면 정적 객체와 정적 클래스를 이해하고 강하게 입력했습니다 (필수는 아니지만).
이러한 상황과 관련하여 많은 포럼 문서는 이것이 JavaScript에서 "클래스를 프로토 타입과 비교하는 것을 반대하는 주된 이유라고 강조합니다. 비록 그들의 구현 차이 (동적 클래스를 기반으로하는 Python 및 Ruby와 같은)는 요점에 너무 반대하지 않지만 (일부 조건은 아이디어에 약간의 차이가 있지만, JavaScript는 대안은 아니지만, 반대는 정적 클래스 및 동적 프로토 타입 (Statics + Classs vs. Dynamics + Prototypes)입니다. 정확하게 말하면, 정적 클래스 (예 : C ++, Java) 및 그 부하 직원 및 방법 정의 메커니즘을 사용하면 IT와 프로토 타입 구현의 정확한 차이를 볼 수 있습니다.
그러나 그들을 하나씩 나열하자. 이러한 패러다임의 일반적인 원칙과 주요 개념을 고려해 봅시다.
정적 클래스를 기반으로합니다
클래스 기반 모델에는 클래스와 인스턴스에 대한 개념이 있습니다. 클래스 사례는 종종 대상 또는 패러다임이라고합니다.
수업과 대상
클래스는 인스턴스의 추상화 (즉, 객체)를 나타냅니다. 이것은 수학과 비슷하지만 유형 또는 분류라고합니다.
예를 들어 (여기 및 아래의 예는 의사 코드입니다) :
코드 사본은 다음과 같습니다.
C = 클래스 {A, B, C} // 기능 A, B, C를 포함한 Class C
인스턴스의 특성은 다음과 같습니다. 속성 (객체 설명) 및 메소드 (객체 활동). 특성 자체는 또한 객체로 간주 될 수 있습니다. 즉, 속성이 쓰기 가능, 구성 가능, 정착식 (getter/setter) 등이든, 객체 저장 상태 (즉, 클래스에 설명 된 모든 속성의 특정 값) 및 클래스는 인스턴스에 대한 엄격하게 무적 구조 (속성) 및 엄격하게 무적의 행동 (방법)을 정의합니다.
코드 사본은 다음과 같습니다.
C = 클래스 {a, b, c, method1, method2}
c1 = {a : 10, b : 20, c : 30} // 클래스 C는 인스턴스입니다 : Object с1
c2 = {a : 50, b : 60, c : 70} // 클래스 C는 인스턴스입니다. 객체 с2, 자체 상태가 있습니다 (즉, 속성 값)
계층 적 상속
코드 재사용을 개선하기 위해 클래스를 서로 연장하여 추가 정보를 추가 할 수 있습니다. 이 메커니즘을 (계층 적) 상속이라고합니다.
코드 사본은 다음과 같습니다.
d = 클래스는 c = {d, e} // {a, b, c, d, e}를 확장합니다.
d1 = {a : 10, b : 20, c : 30, d : 40, e : 50}
수업의 인스턴스에서 파티에 전화 할 때, 원시 수업은 일반적으로 지금이 방법을 찾습니다. 찾을 수없는 경우 부모 수업으로 이동하여 직접 검색하십시오. 찾을 수없는 경우 부모 클래스의 상위 클래스로 이동하여 검색하십시오 (예 : 엄격한 상속 체인에서). 상속 재산의 상단이 발견되지 않은 것으로 밝혀지면 결과는 다음과 같습니다. 객체는 비슷한 동작이 없으며 결과를 얻을 수 없습니다.
코드 사본은 다음과 같습니다.
d1.method1 () // d.method1 (no) -> c.method1 (예)
d1.method5 () // d.method5 (no) -> c.method5 (no) -> 결과 없음
서브 클래스에 복사되지 않은 상속 방법과 비교하여 속성은 항상 서브 클래스로 복잡합니다. 서브 클래스 D가 상위 클래스 C : 속성 A, B, C가 복사되고 d의 구조가 {a, b, c, d, e}}임을 알 수 있습니다. 그러나 {method1, method2} 방법은 과거를 복사하지 않고 과거를 상속합니다. 따라서, 매우 깊은 클래스에 객체에 전혀 필요하지 않은 속성이 있다면 서브 클래스에는 이러한 속성도 있습니다.
수업을 기반으로 한 주요 개념
따라서 다음과 같은 주요 개념이 있습니다.
1. 객체를 만들기 전에 클래스를 선언해야합니다. 첫째, 클래스를 정의해야합니다.
2. 그러므로 대상은 자신의 "그림과 유사성"(구조 및 행동)으로 추상화 된 수업에서 생성됩니다.
3.이 방법은 엄격하고 직접적이며 변하지 않는 상속 체인을 통해 처리됩니다.
4. 서브 클래스는 상속 체인의 모든 속성을 포함합니다 (일부 속성이 서브 클래스에 의해 요구되지 않더라도);
5. 클래스 인스턴스를 만듭니다. 클래스는 (정적 모델로 인해) 인스턴스의 특성 (속성 또는 방법)을 변경할 수 없습니다.
6. 인스턴스 (엄격한 정적 모델로 인해)는 해당 클래스에서 인스턴스에 선언 된 동작 및 속성을 제외하고는 추가 동작이나 속성을 가질 수 없습니다.
OOP 모델을 JavaScript로 바꾸는 방법을 살펴 보겠습니다.이 프로토 타입 기반 OOP입니다.
프로토 타입을 기반으로합니다
여기서 기본 개념은 동적 변이성 객체입니다. 변환 (값뿐만 아니라 기능을 포함한 전체 변환)은 동적 언어와 직접 관련이 있습니다. 다음과 같은 객체는 클래스없이 모든 속성 (속성, 방법)을 독립적으로 저장할 수 있습니다.
코드 사본은 다음과 같습니다.
개체 = {a : 10, b : 20, c : 30, 메소드 : fn};
대상 .a; // 10
Object.c; // 30
Object.Method ();
또한 동적으로 인해 자체 기능을 쉽게 변경 (추가, 삭제, 수정) 할 수 있습니다.
코드 사본은 다음과 같습니다.
object.method5 = function () {...}; // 새 메소드를 추가합니다
Object.d = 40; // 새 속성 추가 "d"
Object.c 삭제; // 속성 삭제 "с"
대상 .a = 100; // 속성 "r"수정
// 결과는 다음과 같습니다. 객체 : {a : 100, b : 20, d : 40, method : fn, method5 : fn};
즉, 할당 할 때 일부 기능이 존재하지 않으면 생성되고 할당이 초기화되며 존재하면 업데이트됩니다.
이 경우 클래스를 확장하여 코드 재사용이 구현되지 않습니다 (여기에는 클래스 개념이 없기 때문에 클래스가 변경 될 수 없다고 말하지는 않았지만 프로토 타입으로 구현됩니다.
프로토 타입은 다른 객체의 원시 사본으로 사용되는 객체이거나 일부 객체에 필요한 특성이없는 경우 프로토 타입은 이러한 객체의 대의원으로 사용될 수 있으며 보조 객체 역할을 할 수 있습니다.
대표단을 기반으로합니다
객체는 런타임에 프로토 타입 역학을 쉽게 변경할 수 있기 때문에 모든 객체는 다른 객체의 프로토 타입 객체로 사용할 수 있습니다.
우리는 현재 구체적인 구현보다는 소개를 고려하고 있으며 ECMAScript의 구체적인 구현에 대해 논의 할 때 그 자체의 특성 중 일부를 볼 수 있습니다.
예제 (의사 코드) :
코드 사본은 다음과 같습니다.
x = {a : 10, b : 20};
y = {a : 40, c : 50};
y. [[프로토 타입]] = x; // x는 y의 프로토 타입입니다
당신; // 40, 고유 한 특성
YC; // 50, 그것은 또한 고유 한 특성입니다
YB; // 20 프로토 타입에서 얻는다 : yb (no) -> y. [[프로토 타입]]. b (예) : 20
ya 삭제; // 자신의 "r"삭제
당신; // 10 프로토 타입에서 가져옵니다
z = {a : 100, e : 50}
y. [[프로토 타입]] = z; // y의 프로토 타입을 z로 수정하십시오
당신; // 100 프로토 타입에서 얻는다 z
Ye // 50, 프로토 타입 z에서도 얻었습니다
ZQ = 200 // 프로토 타입에 새 속성 추가
YQ // 수정은 Y에도 적용됩니다
이 예제는 자신의 속성이 필요한 것처럼 보조 객체 속성으로서 프로토 타입의 중요한 기능과 메커니즘을 보여줍니다. 자신의 속성과 비교하여 이러한 속성은 대의원 속성입니다. 이 메커니즘을 대의원이라고하며,이를 기반으로 한 프로토 타입 모델은 대의원 프로토 타입 (또는 대의원 기반 프로토 타입)입니다. 참조 메커니즘을 객체에 보내는 정보를 전달합니다. 객체가 응답을 얻지 못하면 프로토 타입에 위임되어 (메시지에 응답해야 함).
이 경우 코드 재사용을 대의원 기반 상속 또는 프로토 타입 기반 상속이라고합니다. 모든 객체는 프로토 타입으로 간주 될 수 있으므로 프로토 타입에는 자체 프로토 타입이있을 수도 있습니다. 이 프로토 타입은 함께 연결되어 소위 프로토 타입 체인을 형성합니다. 체인은 정적 클래스에서도 계층 적이지만 쉽게 재 배열되어 계층 구조와 구조가 변경 될 수 있습니다.
코드 사본은 다음과 같습니다.
x = {a : 10}
y = {b : 20}
y. [[프로토 타입]] = x
z = {c : 30}
Z. [[프로토 타입]] = y
ZA // 10
// 프로토 타입 체인에서 발견 된 ZA :
// ZA (아니오) ->
// z. [[[프로토 타입]]. a (no) ->
// z. [[프로토 타입]]. [[프로토 타입]]. a (예) : 10
객체와 프로토 타입 체인이 메시지 전송에 응답 할 수없는 경우, 객체는 해당 시스템 신호를 활성화 할 수 있으며, 이는 프로토 타입 체인의 다른 대표단이 처리 할 수 있습니다.
이 시스템 신호는 동적 클래스를 기반으로 한 시스템을 포함하여 많은 구현에서 사용할 수 있습니다. #doesnotunderstand, smalltalk, method_missing in ruby; __getattr__의 Python, __call php; 및 ecmascript에서 __nosuchmethod__ 구현 등
예제 (Spidermonkey의 ecmascript 구현) :
코드 사본은 다음과 같습니다.
var 객체 = {
// 메시지에 응답 할 수없는 시스템 신호를 포착합니다.
__nosuchmethod__ : 함수 (이름, args) {
경고 ([이름, args]);
if (name == 'test') {
return '.test () 메소드가 처리됩니다.;
}
반환 대의원 [이름] .Apply (this, args);
}
};
var delegate = {
제곱 : 기능 (a) {
a * a를 반환합니다.
}
};
경고 (Object.Square (10)); // 100
alert (object.test ()); // .test () 메소드가 처리됩니다
즉, 정적 클래스의 구현에 기초하여 메시지를 응답 할 수없는 경우, 현재 객체에 필요한 특성이 없다는 결론은 프로토 타입 체인에서 얻으려고해도 결과를 얻을 수 있거나 일련의 변경 후에도 특성을 가질 수 있습니다.
ECMAScript와 관련하여 특정 구현은 다음과 같습니다. 대의원 기반 프로토 타입 사용. 그러나 사양 및 구현에서 볼 수 있듯이 고유 한 특성도 있습니다.
연결 모델
솔직히, 다른 상황에 대해 말해야합니다 (가능한 한 빨리 ECMAScript에서 사용되지 않음) :이 상황은 프로토 타입이 다른 개체에서 원래 대상의 원래 대체로 복잡 할 때. 이 경우 코드 재사용은 객체 생성 단계에서 대의원이 아닌 객체의 실제 사본 (클론)입니다. 이 프로토 타입을 연결 프로토 타입이라고합니다. 객체의 모든 프로토 타입의 특성을 복사하면 특성과 방법을 완전히 변경할 수 있으며 프로토 타입으로서 자신을 변경할 수도 있습니다 (대의원 기반 모델에서는이 변경 사항이 기존 객체의 동작을 변경하지는 않지만 프로토 타입 특성을 변경합니다). 이 방법의 장점은 예약 및 위임 시간을 줄일 수 있지만 단점은 메모리를 사용한다는 것입니다.
오리 타입
정적 클래스 기반 모델과 비교하여 동적으로 약한 유형 변경 객체로 돌아가서, 이러한 작업을 수행 할 수 있는지 여부와 객체가 어떤 유형 (클래스) 할 수 있지만 해당 메시지와 관련 될 수 있는지 (즉, 점검 후 수행 할 수 있는지 여부)를 확인해야합니다.
예를 들어:
코드 사본은 다음과 같습니다.
// 정적 기반 모델에서
if (객체 인스턴스의 someclass) {
// 일부 동작이 실행 중입니다
}
// 동적 구현에서
// 현재 객체의 유형이 중요하지 않습니다.
// 돌연변이, 유형 및 특성을 자유롭게 변경할 수 있기 때문입니다.
// 중요한 객체가 메시지 테스트에 응답 할 수 있는지 여부
if (isfunction (object.test)) // ecmascript
객체. response? (: test) // ruby
hasattr (object, 'test') : // python
이것을 도크 유형이라고합니다. 즉, 객체는 계층 구조에서 객체의 위치가 아니라 특정 유형에 속하는 것이 아니라 확인할 때 자체 특성으로 식별 할 수 있습니다.
프로토 타입을 기반으로하는 주요 개념
이 방법의 주요 기능을 살펴 보겠습니다.
1. 기본 개념은 객체입니다
2. 객체는 완전히 역동적이고 변이 가능합니다 (이론적으로는 한 유형에서 다른 유형으로 완전히 변환 할 수 있습니다).
3. 객체에는 자신의 구조와 행동을 설명하는 엄격한 클래스가 없으며 객체에는 클래스가 필요하지 않습니다.
4. 객체에는 클래스 클래스가 없지만 프로토 타입이있을 수 있습니다. 메시지에 응답 할 수없는 경우 프로토 타입을 위임 할 수 있습니다.
5. 객체의 프로토 타입은 런타임 동안 언제든지 변경할 수 있습니다.
6. 대의원 기반 모델에서 프로토 타입의 특성을 변경하면 프로토 타입과 관련된 모든 객체에 영향을 미칩니다.
7. 연결 프로토 타입 모델에서 프로토 타입은 다른 객체에서 복제 된 원래 사본이며 완전히 독립적 인 사본이됩니다. 프로토 타입의 특성의 변환은 복제 된 물체에 영향을 미치지 않습니다.
8. 메시지에 응답 할 수없는 경우 발신자는 추가 조치를 취할 수 있습니다 (예 : 예약 변경)
9. 객체 실패는 레벨과 그들이 속한 클래스에 의해가 아니라 현재 특성에 의해 결정될 수 있습니다.
그러나 우리가 고려해야 할 또 다른 모델도 있습니다.
동적 클래스를 기반으로합니다
위의 예에 표시된 차이 "클래스 대 프로토 타입"은이 동적 클래스 기반 모델에서 그다지 중요하지 않다고 생각합니다 (특히 프로토 타입 체인이 변경되지 않은 경우에도보다 정확한 구별을 위해 정적 클래스를 고려해야합니다). 예를 들어, 파이썬 또는 루비 (또는 다른 유사한 언어)에서도 사용할 수 있습니다. 이 언어는 모두 동적 클래스 기반 패러다임을 사용합니다. 그러나 일부 측면에서는 프로토 타입을 기반으로 특정 기능이 구현되는 것을 볼 수 있습니다.
다음 예에서는 대의원 프로토 타입을 기반으로 클래스 (프로토 타입)를 증폭 시켜이 클래스와 관련된 모든 객체에 영향을 미칠 수 있으므로 런타임 에서이 객체의 클래스를 동적으로 변경할 수 있습니다 (대표를위한 새 개체 제공) 등.
코드 사본은 다음과 같습니다.
# 파이썬
클래스 A (객체) :
def __init __ (self, a) :
self.a = a
def square (self) :
Self.a * self.a를 반환합니다
a = a (10) # 인스턴스를 만듭니다
인쇄 (AA) # 10
AB = 20 # 클래스에 새로운 속성을 제공합니다.
Print (AB) # 20 "A"인스턴스에서 액세스 할 수 있습니다.
a = 30 # 자체의 속성을 만듭니다.
인쇄 (AB) # 30
del ab # 자체 속성을 삭제합니다
print (ab) # 20- 수업에서 다시 가져 오기 (프로토 타입)
# 프로토 타입 기반 모델처럼
# 런타임에 객체의 프로토 타입을 변경할 수 있습니다.
클래스 B (객체) : # 빈 클래스 b
통과하다
b = b () # b 인스턴스
b. __ class__ = a # 동적으로 변경 클래스 (프로토 타입)
BA = 10 # 새 속성을 만듭니다
print (b.square ()) # 100- 클래스 A 메소드가 현재 사용할 수 있습니다.
# 삭제 된 클래스에 참조를 표시 할 수 있습니다
델 a
델 b
# 그러나 객체에는 여전히 암시 적 참조가 있으며 이러한 방법은 여전히 사용 가능합니다.
print (b.square ()) # 100
#하지만 지금은 클래스를 변경할 수 없습니다.
# 이것은 구현의 기능입니다
b. __ class__ = dict # 오류
Ruby의 구현은 비슷합니다. 또한 완전히 역동적 인 클래스를 사용합니다 (현재 Python의 현재 버전에서 Ruby 및 ECMAScript와 비교하여 클래스를 증폭 할 수 없음)를 수행 할 수 없음) 객체의 특성 (또는 클래스에 메소드/속성을 추가 할 수 있음)을 완전히 변경할 수 있지만 이러한 변경 사항은 기존 대상에 영향을 줄 수는 없지만 대상을 동적으로 변경할 수는 없습니다.
그러나이 기사는 Python과 Ruby를위한 것이 아니므로 많은 말을하지 않을 것입니다. ECMAScript 자체에 대해 계속 논의합시다.
그러나 그 전에는 JavaScript에 관한 많은 이전 기사가 종종 이러한 문제를 다루기 때문에 일부 OOP에서 발견되는 "합성 설탕"을 살펴 봐야합니다.
이 섹션에서 언급해야 할 유일한 잘못된 문장은 "JavaScript는 클래스가 아니며 프로토 타입을 가지고 있으며 클래스를 대체 할 수 있습니다." 모든 클래스 기반 구현이 완전히 다르지는 않다는 것을 알아야합니다. "JavaScript가 다르다"고 말할 수 있더라도 "클래스"의 개념 외에 다른 관련 기능이 있음을 고려해야합니다.
다양한 OOP 구현의 다른 기능
이 섹션에서는 ECMAScript의 OOP 구현을 포함하여 코드 재사용에 대한 다른 기능과 다양한 OOP 구현을 간단히 소개합니다. 그 이유는 JavaScript에서 OOP 구현의 이전 모양이 습관적인 사고 제한을 가지고 있기 때문입니다. 유일한 주요 요구 사항은 기술적으로나 이념적으로 입증되어야한다는 것입니다. 다른 OOP 구현에서 구문 설탕 기능을 발견하지 못했고 JavaScript가 순수한 OOP 언어가 아니라는 것을 알 수 없습니다.
다형성
ECMAScript에는 물체가 의미하는 몇 가지 다형성이 있습니다.
예를 들어, 기본 객체의 속성과 마찬가지로 함수는 다른 객체에 적용될 수 있습니다 (실행 컨텍스트를 입력 할 때이 값이 결정되기 때문에).
코드 사본은 다음과 같습니다.
기능 test () {
경고 ([this.a, this.b]);
}
test.call ({a : 10, b : 20}); // 10, 20
test.call ({a : 100, b : 200}); // 100, 200
var a = 1;
var b = 2;
시험(); // 1, 2
그러나 예외는 다음과 같습니다. date.prototype.gettime () 메소드 표준에 따라 항상 날짜 개체가 있어야합니다. 그렇지 않으면 예외가 발생합니다.
코드 사본은 다음과 같습니다.
alert (date.prototype.gettime.call (new date ())); // 시간
alert (date.prototype.gettime.call (new String ( ''))); // TypeError
함수를 정의 할 때 소위 매개 변수 다형성은 모든 데이터 유형과 동일하지만 다형성 매개 변수 (예 : 배열의 .SORT 정렬 방법 - 다형성의 분류 함수) 만 허용합니다. 그건 그렇고, 위의 예는 또한 파라미터 다형성으로 간주 될 수있다.
프로토 타입의 메소드는 공허한 것으로 정의 될 수 있으며, 생성 된 모든 객체는 메소드 (예 : 하나의 인터페이스 (서명), 다중 구현 ")를 재정의 (구현)해야합니다.
다형성은 위에서 언급 한 오리 유형과 관련이 있습니다. 즉, 물체의 유형과 계층 구조에서의 위치는 그다지 중요하지 않지만 필요한 모든 기능이있는 경우 쉽게 받아 들일 수 있습니다 (즉, 일반적인 인터페이스가 중요하며 구현이 다양 할 수 있습니다).
패키지
캡슐화에 대한 잘못된 견해가 종종 있습니다. 이 섹션에서는 OOP 구현의 일부 구문 설탕 (즉, 잘 알려진 수정 자)에 대해 논의 할 것입니다.이 경우 우리는 일부 OOP 구현에 대해 편리한 "설탕" - 잘 알려진 수정 자 : 개인, 보호 및 공개 (또는 객체의 액세스 레벨 또는 액세스 수정 자)에 대해 논의 할 것입니다.
여기서 캡슐화의 주요 목적에 대해 상기시키고 싶습니다. 캡슐화는 수업에 직접 무언가를 쓰는 숨겨진 "악의적 인 해커"를 선택하는 대신 추상 추가입니다.
이것은 큰 실수입니다. 숨기기 위해 숨겨진 사용.
프로그래밍의 편의성을 위해 액세스 레벨 (개인, 보호 및 공개)은 많은 객체 지향 (실제로 매우 편리한 구문 설탕)에서 구현되었으며 시스템을보다 추상적으로 설명하고 구축했습니다.
이것들은 일부 구현 (예 : 이미 언급 된 Python 및 Ruby)에서 볼 수 있습니다. 한편으로 (파이썬에서), 이러한 __ -private _protected 속성 (밑줄 이름 사양을 통해)은 외부에서 액세스 할 수 없습니다. 반면에 Python은 특수 규칙 (_classname__field_name)을 통해 외부에서 액세스 할 수 있습니다.
코드 사본은 다음과 같습니다.
클래스 A (객체) :
def __init __ (self) :
self.public = 10
self .__ private = 20
def get_private (self) :
자체를 반환합니다 .__ private
# 밖의:
a = a () # a의 예
인쇄 (A.public) # OK, 30
print (a.get_private ()) # OK, 20
print (a .__ private) # 실패는
# 그러나 파이썬에서는 특별 규칙에 액세스 할 수 있습니다
인쇄 (A._A__ -Private) # OK, 20
루비 : 한편으로는 개인 및 보호의 특성을 정의 할 수있는 능력이 있으며, 다른 한편으로는 캡슐화 된 데이터를 얻기위한 특별한 방법 (예 : instance_variable_get, instance_variable_set, send 등)도 있습니다.
코드 사본은 다음과 같습니다.
클래스 A
def 초기화
@A = 10
끝
def public_method
private_method (20)
끝
사적인
def private_method (b)
@A + b를 반환합니다
끝
끝
A = A.New # 새 인스턴스
A.public_method # OK, 30
AA # 실패, @A-는 개인 인스턴스 변수입니다.
# "private_method"는 개인이며 클래스 A에서만 액세스 할 수 있습니다.
A.private_method # 오류
#하지만 데이터를 얻을 수있는 특수 메타 데이터 메소드 이름이 있습니다.
A.Send (: private_method, 20) # OK, 30
a.instance_variable_get (:@a) # ok, 10
주된 이유는 프로그래머가 얻고 자하는 캡슐화 ( "숨겨진"을 사용하지 않음) 데이터입니다. 이 데이터가 어떤 식 으로든 잘못 변경되거나 오류가 있으면 전체 책임은 프로그래머이지만 단순히 "Spellow"또는 "특정 필드 변경"은 아닙니다. 그러나 이것이 자주 발생한다면, 그것은 일반적으로 물체와 "대화"하는 일반적인 API이기 때문에 나쁜 프로그래밍 습관과 스타일입니다.
캡슐화의 기본 목적은 해커가 데이터를 숨기지 않고 보조 데이터 사용자로부터 추상화하는 것입니다. 더 심각한 캡슐화는 소프트웨어 보안의 목적을 달성하기 위해 데이터를 수정하기 위해 개인을 사용하지 않는 것입니다.
도우미 물체 (로컬)의 캡슐화, 우리는 최소한의 비용, 현지화 및 예측 변화로 공공 인터페이스의 행동 변화에 대한 타당성을 제공합니다. 이는 정확히 캡슐화의 목적입니다.
또한 세터 방법의 중요한 목적은 복잡한 계산을 추상화하는 것입니다. 예를 들어, element.innerhtml setter- 초록 문 - "이 요소 내부의 html은 이제 다음과 같습니다."그리고 innerhtml 속성의 setter 함수는 계산 및 확인하기가 어렵습니다. 이 경우 문제는 대부분 추상화와 관련이 있지만 캡슐화가 발생할 수 있습니다.
캡슐화의 개념은 OOP와 관련이있는 것이 아닙니다. 예를 들어, 모든 종류의 계산을 캡슐화하여 추상적이되도록 간단한 기능이 될 수 있습니다 (예 : 기능 Math.round (......)가 구현되는 방법과 같은 사용자는 간단히 호출). 그것은 캡슐화입니다. 나는 그것이 "개인, 보호 및 공개"라고 말하지 않았습니다.
ECMAScript 사양의 현재 버전은 개인, 보호 및 공개 수정자를 정의하지 않습니다.
그러나 실제로 "Mimitate JS 캡슐화"라는 이름을 볼 수 있습니다. 일반적으로 이러한 맥락의 목적은 (원칙적으로, 생성자 자체) 사용되어야한다. 불행히도, 종종이 "모방"을 구현할 수 있으며, 프로그래머는 유문 의식이 아닌 비 임금 엔티티를 생성하여 "getter/setter 방법"을 설정할 수 있습니다 (다시 말하면 잘못입니다).
코드 사본은 다음과 같습니다.
기능 a () {
var _a; // "개인"a
this.geta = function _geta () {
반환 _a;
};
this.seta = function _seta (a) {
_a = a;
};
}
var a = 새로운 a ();
A. 세트 (10);
경고 (A._A); // 정의되지 않은, "개인"
경고 (a.geta ()); // 10
따라서 모든 사람은 생성 된 각 객체에 대해 geta/seta 메소드가 생성되며, 이는 메모리 증가의 이유 (프로토 타입 정의와 비교)를 이해합니다. 이론적으로는 첫 번째 경우에 객체를 최적화 할 수 있습니다.
또한 일부 JavaScript 기사는 종종 "개인 방법"이라는 개념을 언급합니다. 참고 : ECMA-262-3 표준은 "개인 방법"의 개념을 정의하지 않습니다.
그러나 JS는 이데올로기 언어이기 때문에 생성자에서 생성 될 수있는 경우에 따라 물체는 완전히 변한적이고 고유 한 특성을 가질 수 있습니다 (생성자의 특정 조건 하에서 일부 객체는 추가 방법을 얻을 수 있지만 다른 객체는 할 수 없습니다).
또한 JavaScript에서 캡슐화가 여전히 악의적 인 해커가 세터 메소드를 사용하는 대신 특정 값을 자동으로 작성하는 것을 방지하는 방법으로 여전히 잘못 해석되는 경우 소위 "숨겨진"및 "개인"은 "숨겨진"것이 아닙니다. 일부 구현은 컨텍스트를 평가 함수 (SpiderMonkey 1.7에서 테스트 할 수 있음)를 호출하여 관련 스코프 체인 (및 해당 변수 개체)에서 값을 얻을 수 있습니다.
코드 사본은 다음과 같습니다.
평가 ( '_ a = 100', a.geta); // 또는 a.seta, [[scope]에서 "_a"메소드 때문에]
a.geta (); // 100
또는 구현에서 객체의 해당 속성에 액세스함으로써 활성 객체 (예 : Rhino)에 직접 액세스 할 수있게되면 내부 변수의 값이 변경 될 수 있습니다.
코드 사본은 다음과 같습니다.
// 코뿔소
var foo = (function () {
var x = 10; // "사적인"
return function () {
인쇄 (x);
};
}) ();
foo (); // 10
foo .__ 부모 __. x = 20;
foo (); // 20
때로는 JavaScript에서 "개인"및 "보호 된"데이터의 목적은 변수를 묘사하여 달성됩니다 (그러나 Python과 비교할 때 이것은 단지 이름 지정 사양입니다).
var _myPrivatedata = 'testString';
종종 실행 컨텍스트를 브래킷으로 동봉하는 데 사용되지만 실제 보조 데이터의 경우 객체와 직접적인 연관성이 없으며 외부 API에서 초록이 편리합니다.
코드 사본은 다음과 같습니다.
(기능 () {
// 컨텍스트를 초기화합니다
}) ();
다중 상속
다중 상점은 코드 재사용 개선을위한 매우 편리한 구문 설탕입니다 (한 번에 한 클래스를 상속받을 수 있다면 한 번에 10을 물려받을 수없는 이유는 무엇입니까?). 그러나 여러 상속의 일부 단점으로 인해 구현에서 인기가 없어졌습니다.
ECMAScript는 여러 상속을 지원하지 않습니다 (즉, 조상의 자체 프로그래밍 언어에는 그러한 기능이 있지만 하나의 객체 만 직접 프로토 타입으로 사용할 수 있습니다). 그러나 __nosuchmethod__를 사용하는 일부 구현 (예 : Spidermonkey)에서는 스케줄링을 관리하고 프로토 타입 체인을 대체하기위한 대표단을 사용할 수 있습니다.
믹스 인
Mixins는 코드를 재사용하는 편리한 방법입니다. Mixins는 다중 상속을 대체하는 것이 좋습니다. 이러한 독립적 인 요소는 모든 객체와 혼합되어 기능을 확장 할 수 있습니다 (따라서 물체는 여러 믹스 인을 혼합 할 수 있습니다). ECMA-262-3 사양은 "Mixins"의 개념을 정의하지는 않지만 Mixins 정의에 따르면 ECMAScript에는 동적 변이 가능한 객체가 있으므로 Mixins를 사용하여 기능을 확장하는 데 장애가 없습니다.
일반적인 예 :
코드 사본은 다음과 같습니다.
// 증강을위한 도우미
Object.extend = 함수 (대상, 소스) {
for (소스의 속성) if (source.hasownproperty (property)) {
대상 [속성] = 소스 [속성];
}
반환 목적지;
};
var x = {a : 10, b : 20};
var y = {c : 30, d : 40};
Object.extend (x, y); // y를 x에 섞습니다
경고 ([XA, XB, XC, XD]); 10, 20, 30, 40
ECMA-262-3에 언급 된 인용문으로 이러한 정의 ( "Mixin", "Mix")를 취했습니다. 사양에는 그러한 개념이 없으며 혼합이 아니라 새로운 기능을 통해 객체를 확장하는 데 일반적으로 사용되는 방법입니다. (Ruby의 Mixins의 개념은 공식적으로 정의됩니다. Mixin은 모듈의 모든 속성을 다른 모듈에 간단히 복사하는 대신 모듈에 대한 참조를 만듭니다.
특성
특성과 믹스 인은 비슷한 개념을 가지고 있지만 많은 기능이 있습니다 (정의에 따라 믹스 인을 적용 할 수 있으므로 명명 충돌을 일으킬 가능성이 있기 때문에 상태를 포함 할 수 없기 때문입니다). ECMAScript에 따르면, 특성과 믹스 인은 동일한 원칙을 따르므로 사양은 "특성"의 개념을 정의하지 않습니다.
인터페이스
일부 oop에서 구현 된 인터페이스는 믹스 인 및 특성과 유사합니다. 그러나 Mixins 및 특성과 비교하여 인터페이스는 구현 클래스를 시행하여 메소드 서명 동작을 구현합니다.
인터페이스는 추상 클래스로 간주 될 수 있습니다. 그러나 추상 클래스 (추상 클래스의 메소드는 그 부분 만 구현할 수 있고 다른 부분은 여전히 서명으로 정의 됨)와 비교할 때 상속은 단일 상속 기본 클래스 일 수 있지만 여러 인터페이스를 상속받을 수 있습니다. 그렇기 때문에 인터페이스 (혼합 다중)가 다중 상속에 대한 대안으로 간주 될 수 있습니다.
ECMA-262-3 표준은 "인터페이스"의 개념이나 "추상 클래스"의 개념을 정의하지 않습니다. 그러나 모방으로서 "빈"방법 (또는 빈 메소드에 예외가 발생하여 개발자 에게이 방법을 구현해야한다는 것을 알리는 객체에 의해 구현 될 수 있습니다.
객체 조합
객체 조합은 또한 동적 코드 재사용 기술 중 하나입니다. 객체 조합은 높은 유연성 상속과 다르며, 이는 동적 및 가변 대의원을 구현합니다. 그리고 이것은 주요 프로토 타입을 기반으로합니다. 동적 돌연변이 가능한 프로토 타입 외에도 객체는 대의원 집계 객체 (결과로 조합 생성 - 집계) 일 수 있으며 추가 메시지를 객체에 보내고 대의원에게 대의원합니다. 동적 특성이 런타임에 변경 될 수 있다고 결정하기 때문에 이것은 두 대의 대의원이 될 수 있습니다.
언급 된 __nosuchmethod__ 예는 다음과 같습니다. 또한 대의원을 명시 적으로 사용하는 방법을 보여 드리겠습니다.
예를 들어:
코드 사본은 다음과 같습니다.
var _delegate = {
foo : function () {
경고 ( '_ delegate.foo');
}
};
var Aggregate = {
대의원 : _delegate,
foo : function () {
react this.delegate.foo.call (this);
}
};
Aggregate.foo (); // delegate.foo
Aggregate.delegate = {
foo : function () {
Alert ( 'New Delegate의 Foo');
}
};
Aggregate.foo (); // New Delegate의 foo
이 객체 관계를 "Has-A"라고하며 통합은 "IS-A"의 관계입니다.
디스플레이 조합이 없기 때문에 (상속에 비해 유연성) 중간 코드를 추가해도됩니다.
AOP 기능
측면 지향적 기능으로 기능 데코레이터를 사용할 수 있습니다. ECMA-262-3 사양은 "기능 데코레이터"라는 명확하게 정의 된 개념을 가지고 있지 않습니다 (파이썬과 반대로 단어는 공식적으로 파이썬으로 정의됩니다). 그러나 기능적 매개 변수를 가진 기능은 장식적이고 어떤 방식으로 소위 제안을 적용함으로써) 활성화됩니다.
데코레이터의 가장 간단한 예 :
코드 사본은 다음과 같습니다.
함수 checkDecorator (OriginalFunction) {
return function () {
if (foobar! = 'test') {
경고 ( '잘못된 매개 변수');
거짓을 반환합니다.
}
Return OriginalFunction ();
};
}
기능 test () {
경고 ( '테스트 기능');
}
var testwithCheck = CheckDecorator (테스트);
var foobar = false;
시험(); // '테스트 기능'
testwithCheck (); // '잘못된 매개 변수'
foobar = '테스트';
시험(); // '테스트 기능'
testwithCheck (); // '테스트 기능'
결론적으로
이 기사에서는 OOP에 대한 소개를 분류했습니다 (이 정보가 귀하에게 유용했으면 좋겠다). 다음 장에서는 객체 지향 프로그래밍에서 ECMAScript를 계속 구현할 것입니다.