JavaScript로 OOP를 만드는 방법을 배우고 있습니다. 인터페이스 개념 (예 : Java interface
)이 있습니까?
그래서 나는 청취자를 만들 수있을 것입니다 ...
JavaScript로 OOP를 만드는 방법을 배우고 있습니다. 인터페이스 개념 (예 : Java interface
)이 있습니까?
그래서 나는 청취자를 만들 수있을 것입니다 ...
답변:
"이 클래스에는 이러한 함수가 있어야합니다"라는 개념이 없습니다. 즉, 인터페이스 자체가 없습니다.
대신, JavaScript는 duck typing 이라는 것을 사용합니다 . (Jacks가 관심을 갖는 한, 오리처럼 걷고 오리처럼 cks 거리면 오리입니다.) 객체에 quack (), walk () 및 fly () 메소드가있는 경우 코드는 예상 한 곳에서 사용할 수 있습니다. "Duckable"인터페이스를 구현할 필요없이 걷거나 qua 고 날 수있는 객체. 인터페이스는 코드에서 사용하는 기능 세트 (및 해당 함수의 반환 값)이며 덕 타이핑을 사용하면 무료로 제공됩니다.
이제 전화를 걸려 고해도 코드가 반쯤 실패하지는 않습니다 some_dog.quack()
. TypeError가 발생합니다. 솔직히 말해서 개에게 qua을 지시하면 약간 더 큰 문제가 있습니다. 오리 타자는 모든 오리를 한 줄로 모을 때 가장 잘 작동하며, 말과 함께 일반적인 동물로 취급하지 않는 한 개와 오리가 서로 어울리지 않도록합니다. 다시 말해, 인터페이스가 유동적이지만 여전히 존재합니다. 개를 처음에 pass 고 날아갈 것으로 예상하는 코드로 개를 전달하는 것은 종종 오류입니다.
그러나 당신이 옳은 일을하고 있다고 확신한다면, 그것을 사용하기 전에 특정 방법의 존재를 테스트함으로써 쿼킹 독 문제를 해결할 수 있습니다. 같은 것
if (typeof(someObject.quack) == "function")
{
// This thing can quack
}
따라서 사용하기 전에 사용할 수있는 모든 방법을 확인할 수 있습니다. 그래도 구문은 추악합니다. 약간 더 아름다운 방법이 있습니다.
Object.prototype.can = function(methodName)
{
return ((typeof this[methodName]) == "function");
};
if (someObject.can("quack"))
{
someObject.quack();
}
이것은 표준 JavaScript이므로 사용할 가치가있는 모든 JS 인터프리터에서 작동해야합니다. 영어처럼 읽는다는 이점도 있습니다.
최신 브라우저 (즉, IE 6-8 이외의 거의 모든 브라우저)의 경우 속성이 표시되지 않도록 할 수있는 방법이 있습니다 for...in
.
Object.defineProperty(Object.prototype, 'can', {
enumerable: false,
value: function(method) {
return (typeof this[method] === 'function');
}
}
문제는 IE7 객체가 전혀 없으며 .defineProperty
IE8에서는 호스트 객체 (즉, DOM 요소 등)에서만 작동한다는 것입니다. 호환성이 문제인 경우을 사용할 수 없습니다 .defineProperty
. (IE6에 대해서는 언급하지 않을 것입니다. 중국 이외의 지역에서는 더 이상 관련이 없기 때문입니다.)
또 다른 문제는 일부 코딩 스타일은 모든 사람이 잘못된 코드를 작성한다고 가정하고 Object.prototype
누군가가 맹목적으로 사용하려는 경우 수정 을 금지 한다는 것 for...in
입니다. 관심이 있거나 (IMO broken ) 코드를 사용 하는 경우 약간 다른 버전을 시도하십시오.
function can(obj, methodName)
{
return ((typeof obj[methodName]) == "function");
}
if (can(someObject, "quack"))
{
someObject.quack();
}
for...in
는 그러한 위험에 처해 있으며, 항상 위험에 처해 있으며, 적어도 Object.prototype
기사에 추가 된 사람 (이 기사 자체의 승인에 의해 흔하지 않은 기술은 아님)은 다른 사람의 손에 코드가 깨지는 것을 볼 것입니다.
for...in
문제를 피할 수 있습니다 . developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
for...in
문제는"여전히 항상이 아니라 실수 코드 ...,이 되리라 원인, 어느 정도 존재합니다 Object.defineProperty(obj, 'a', {writable: true, enumerable: false, value: 3});
단지보다 아주 조금 더 많은 작업입니다 obj.a = 3;
. 더 자주하지 않는 사람들을 완전히 이해할 수 있습니다. : P
Dustin Diaz 의 ' JavaScript 디자인 패턴 ' 사본을 선택하십시오 . Duck Typing을 통해 JavaScript 인터페이스를 구현하기위한 장이 몇 가지 있습니다. 잘 읽었습니다. 그러나 아니요, 인터페이스의 언어 기본 구현이 없으므로 Duck Type 해야합니다 .
// example duck typing method
var hasMethods = function(obj /*, method list as strings */){
var i = 1, methodName;
while((methodName = arguments[i++])){
if(typeof obj[methodName] != 'function') {
return false;
}
}
return true;
}
// in your code
if(hasMethods(obj, 'quak', 'flapWings','waggle')) {
// IT'S A DUCK, do your duck thang
}
JavaScript (ECMAScript 버전 3)에는 나중에 사용할 수 있도록implements
예약어가 저장되어 있습니다 . 나는 이것이 정확히이 목적을위한 것이라고 생각하지만, 사양을 문 밖으로 가져 오기 위해 서두르면 그들은 그와 관련하여 무엇을 정의 할 시간이 없었기 때문에 현재 브라우저는 브라우저 외에는 아무것도하지 않습니다. 당신이 무언가에 그것을 사용하려고하면 거기에 앉아 때로는 불평하자.
Object.implement(Interface)
특정 객체에서 특정 속성 / 함수 집합이 구현되지 않을 때마다 로직 을 사용하여 자신 만의 메서드 를 만드는 것이 가능하고 실제로 쉽습니다 .
나는 다음과 같이 내 자신의 표기법을 사용 하는 객체 지향 에 관한 기사를 썼습니다 .
// Create a 'Dog' class that inherits from 'Animal'
// and implements the 'Mammal' interface
var Dog = Object.extend(Animal, {
constructor: function(name) {
Dog.superClass.call(this, name);
},
bark: function() {
alert('woof');
}
}).implement(Mammal);
이 특정 고양이를 스키닝하는 방법에는 여러 가지가 있지만 이것이 내 인터페이스 구현에 사용한 논리입니다. 나는이 접근 방식을 선호하며 읽기 쉽고 사용하기 쉽습니다 (위에서 볼 수 있듯이). 그것은 Function.prototype
일부 사람들에게 문제가있을 수 있는 '구현'방법을 추가하는 것을 의미 하지만 아름답게 작동합니다.
Function.prototype.implement = function() {
// Loop through each interface passed in and then check
// that its members are implemented in the context object (this).
for(var i = 0; i < arguments.length; i++) {
// .. Check member's logic ..
}
// Remember to return the class being tested
return this;
}
var interf = arguments[i]; for (prop in interf) { if (this.prototype[prop] === undefined) { throw 'Member [' + prop + '] missing from class definition.'; }}
합니다. 보다 정교한 예제 는 기사 링크 의 맨 아래를 참조하십시오 .
자바 스크립트는 않지만 하지 이 interface
유형을 종종 필요한 시간이다. JavaScript의 동적 특성 및 프로토 타입 상속의 사용과 관련하여 클래스간에 일관된 인터페이스를 보장하기는 어렵지만 그렇게 할 수는 있습니다. 그리고 자주 모방합니다.
이 시점에서 JavaScript로 인터페이스를 에뮬레이트하는 몇 가지 특별한 방법이 있습니다. 접근 방식의 차이는 일반적으로 일부 요구를 충족시키는 반면 다른 접근 방식은 해결되지 않습니다. 종종 가장 강력한 접근 방식은 지나치게 번거롭고 구현 자 (개발자)를 불쾌하게 만듭니다.
인터페이스 / 추상 클래스에 대한 접근 방식은 매우 성 가시지 않으며 설명 적이며 추상화 내부의 구현을 최소로 유지하며 동적 또는 사용자 정의 방법론을위한 충분한 공간을 남겨 둡니다.
function resolvePrecept(interfaceName) {
var interfaceName = interfaceName;
return function curry(value) {
/* throw new Error(interfaceName + ' requires an implementation for ...'); */
console.warn('%s requires an implementation for ...', interfaceName);
return value;
};
}
var iAbstractClass = function AbstractClass() {
var defaultTo = resolvePrecept('iAbstractClass');
this.datum1 = this.datum1 || defaultTo(new Number());
this.datum2 = this.datum2 || defaultTo(new String());
this.method1 = this.method1 || defaultTo(new Function('return new Boolean();'));
this.method2 = this.method2 || defaultTo(new Function('return new Object();'));
};
var ConcreteImplementation = function ConcreteImplementation() {
this.datum1 = 1;
this.datum2 = 'str';
this.method1 = function method1() {
return true;
};
this.method2 = function method2() {
return {};
};
//Applies Interface (Implement iAbstractClass Interface)
iAbstractClass.apply(this); // .call / .apply after precept definitions
};
계명 분석기
이 resolvePrecept
함수는 추상 클래스 내부에서 사용할 유틸리티 및 도우미 함수 입니다. 그 역할은 캡슐화 된 Precepts (data & behavior) 의 맞춤형 구현 처리를 허용하는 것입니다 . 오류를 발생 시키거나 경고 할 수 있으며-그리고-기본값을 Implementor 클래스에 할당합니다.
iAbstractClass
는 iAbstractClass
사용되는 인터페이스를 정의합니다. 그 접근 방식은 Implementor 클래스와의 암묵적 계약을 수반합니다. 이 인터페이스는 각각의 할당 교훈 또는 - - 무엇이든에 똑같은 교훈 네임 스페이스에 대한 교훈 확인자 함수가 반환을. 그러나 암묵적 합의는 맥락 , 즉 Implementor의 조항으로 해석됩니다 .
구현 자
구현 자는 단순히 인터페이스 ( 이 경우 iAbstractClass )와 '동의'하고 Constructor-Hijacking :을 사용하여 적용합니다 iAbstractClass.apply(this)
. 위의 데이터 및 동작을 정의한 다음 인터페이스 생성자 를 하이재킹 하여 구현 자의 컨텍스트를 인터페이스 생성자에 전달하면 구현 자의 재정의가 추가되고 인터페이스가 경고 및 기본값을 설명 할 수 있습니다.
이것은 시간이 지남에 따라 다른 프로젝트를 수행하는 데 매우 귀찮은 접근법입니다. 그러나 몇 가지 단점과 단점이 있습니다.
단점
이는 소프트웨어 전체의 일관성을 상당 부분 구현하는 데 도움이 되지만 실제 인터페이스를 구현하지는 않지만 에뮬레이션합니다. 정의, 기본값, 경고 또는 오류 가 설명 되어 있지만, 개발자는 JavaScript를 많이 사용하는 것처럼 사용에 대한 예외를 적용하고 주장 합니다.
이것은 "JavaScript의 인터페이스"에 대한 최선의 접근 방법으로 보이지만 다음 사항이 해결 된 것을보고 싶습니다.
delete
동작 에서 객체 고정즉, 이것이 내 팀과 나만큼 도움이되기를 바랍니다.
정적으로 유형이 지정되고 컴파일 중에 클래스 간 계약을 알아야하므로 Java로 인터페이스가 필요합니다. JavaScript에서는 다릅니다. JavaScript는 동적으로 입력됩니다. 그것은 객체를 얻을 때 특정 메소드가 있는지 확인하고 호출 할 수 있음을 의미합니다.
yourMethod
5 번 항목을 추가 할 수 Superclass
있으며, 고유 한을 가진 각 서브 클래스에 대해 yourMethod
단순히 해당 서브 클래스의 항목 # 5를 가리킬 수 있습니다. 적절한 구현에.
Implementation
구현 하는 클래스 SomeInterface
는 전체 인터페이스를 구현한다고 말하는 것이 아닙니다. "I implement SomeInterface.yourMethod
" 이라는 정보가 있으며의 메소드 정의를 가리 킵니다 Implementation.yourMethod
. JVM이 호출 SomeInterface.yourMethod
하면 클래스에서 해당 인터페이스의 메소드 구현에 대한 정보를 찾고 호출해야하는 것을 찾습니다 Implementation.yourMethod
.
여전히 답을 찾고있는 사람이라면 누구나 도움이 되길 바랍니다.
프록시를 사용하여 시도해 볼 수 있습니다 (ECMAScript 2015 이후 표준) : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
latLngLiteral = new Proxy({},{
set: function(obj, prop, val) {
//only these two properties can be set
if(['lng','lat'].indexOf(prop) == -1) {
throw new ReferenceError('Key must be "lat" or "lng"!');
}
//the dec format only accepts numbers
if(typeof val !== 'number') {
throw new TypeError('Value must be numeric');
}
//latitude is in range between 0 and 90
if(prop == 'lat' && !(0 < val && val < 90)) {
throw new RangeError('Position is out of range!');
}
//longitude is in range between 0 and 180
else if(prop == 'lng' && !(0 < val && val < 180)) {
throw new RangeError('Position is out of range!');
}
obj[prop] = val;
return true;
}
});
그러면 다음과 같이 쉽게 말할 수 있습니다.
myMap = {}
myMap.position = latLngLiteral;
트랜스 컴파일러를 사용하려면 TypeScript를 사용해보십시오. 이는 coffeescript 또는 babel과 같은 언어와 유사한 초안 ECMA 기능 (제안서에서 인터페이스를 " 프로토콜 " 이라고 함 )을 지원합니다.
TypeScript에서 인터페이스는 다음과 같습니다.
interface IMyInterface {
id: number; // TypeScript types are lowercase
name: string;
callback: (key: string; value: any; array: string[]) => void;
type: "test" | "notATest"; // so called "union type"
}
할 수없는 것 :
자바 스크립트에는 인터페이스가 없습니다. 그러나 오리 유형일 수 있으며 여기에서 예를 찾을 수 있습니다.
http://reinsbrain.blogspot.com/2008/10/interface-in-javascript.html
나는 이것이 오래된 것임을 알고 있지만 최근에 인터페이스에 대해 객체를 검사하기위한 편리한 API가 필요하다는 것을 알았습니다. 그래서 나는 이것을 썼습니다 : https://github.com/tomhicks/methodical
NPM을 통해서도 이용할 수 있습니다. npm install methodical
그것은 기본적으로 위에서 제안한 모든 것을 수행하며, 조금 더 엄격하고 몇 가지 if (typeof x.method === 'function')
상용구 를 사용하지 않아도됩니다 .
바라건대 누군가가 유용하다고 생각합니다.
이것은 오래된 질문이지만 그럼에도 불구 하고이 주제는 결코 저를 괴롭히지 않습니다.
여기 및 웹 전체의 많은 답변이 인터페이스를 "강화"하는 데 중점을두기 때문에 다른 견해를 제안하고 싶습니다.
비슷한 동작을하는 여러 클래스를 사용할 때 인터페이스가 가장 부족하다고 생각합니다 (예 : 인터페이스 구현 ).
예를 들어, 섹션의 컨텐츠 및 HTML을 생성하는 방법을 "알고있는" 이메일 섹션 팩토리 를 수신 할 이메일 생성기 가 있습니다. 따라서, 그들은 모두 필요가 어떤 종류의가하기 와 방법을.getContent(id)
getHtml(content)
내가 생각할 수있는 인터페이스에 가장 가까운 패턴 (아직 해결 방법은 있지만) 2 개의 인수를 얻는 클래스를 사용하여 2 개의 인터페이스 메소드를 정의합니다.
이 패턴의 주요 과제 static
는 속성에 액세스 하려면 메서드 가 인스턴스 자체이거나 인수로 가져와야한다는 것입니다. 그러나 번거롭지 않은 가치가있는 경우가 있습니다.
class Filterable {
constructor(data, { filter, toString }) {
this.data = data;
this.filter = filter;
this.toString = toString;
// You can also enforce here an Iterable interface, for example,
// which feels much more natural than having an external check
}
}
const evenNumbersList = new Filterable(
[1, 2, 3, 4, 5, 6], {
filter: (lst) => {
const evenElements = lst.data.filter(x => x % 2 === 0);
lst.data = evenElements;
},
toString: lst => `< ${lst.data.toString()} >`,
}
);
console.log('The whole list: ', evenNumbersList.toString(evenNumbersList));
evenNumbersList.filter(evenNumbersList);
console.log('The filtered list: ', evenNumbersList.toString(evenNumbersList));
이와 같은 추상 인터페이스
const MyInterface = {
serialize: () => {throw "must implement serialize for MyInterface types"},
print: () => console.log(this.serialize())
}
인스턴스를 만듭니다.
function MyType() {
this.serialize = () => "serialized "
}
MyType.prototype = MyInterface
그리고 그것을 사용하십시오
let x = new MyType()
x.print()