JavaScriptでOOPを作成する方法を学んでいます。インターフェースの概念(Javaなどinterface
)はありますか?
だから私はリスナーを作成することができるでしょう...
JavaScriptでOOPを作成する方法を学んでいます。インターフェースの概念(Javaなどinterface
)はありますか?
だから私はリスナーを作成することができるでしょう...
回答:
「このクラスはこれらの関数を持たなければならない」という概念はありません(つまり、インターフェース自体はありません)。
代わりに、JavaScriptはダックタイピングと呼ばれるものを使用します。(アヒルのように歩き、アヒルのように鳴く場合、JSが気にする限り、それはアヒルです。)オブジェクトにquack()、walk()、fly()メソッドがある場合、コードはそれが期待するところならどこでも使用できますいくつかの「Duckable」インターフェースの実装を必要とせずに、ウォーク、クワッ、フライを実行できるオブジェクト。インターフェイスは、コードが使用する一連の関数(およびそれらの関数からの戻り値)であり、ダックタイピングを使用すると、無料でそれを取得できます。
ここで、を呼び出そうとしても、コードが途中で失敗しないわけではありませんsome_dog.quack()
。TypeErrorが発生します。率直に言って、犬にイタズラをするように言っている場合、少し大きな問題があります。アヒルのタイピングは、いわばすべてのアヒルを一列に並べ、いわば一般的な動物として扱わない限り、犬とアヒルを一緒にさせない場合に最適です。つまり、インターフェースは流動的ですが、まだ存在しています。犬がいちゃつくと最初から飛ぶことを期待するコードに犬を渡すことはしばしばエラーです。
しかし、あなたが正しいことをしていると確信しているなら、それを使用する前に特定のメソッドの存在をテストすることによって、いんちき犬の問題を回避することができます。何かのようなもの
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については、中国以外では関係がないため、ここでは触れません。)
もう1つの問題は、一部のコーディングスタイルでは、誰もが不正なコードを書くと想定し、Object.prototype
誰かが盲目的に使用したい場合に備えて変更を禁止することfor...in
です。それが気になる場合、またはそうする(IMOが壊れた)コードを使用している場合は、少し異なるバージョンを試してください。
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
ダスティン・ディアスによる「JavaScriptデザインパターン」のコピーを入手してください。Duck Typingを介してJavaScriptインターフェースを実装するための章がいくつかあります。それもいい読みです。しかし、いや、インタフェースのない言語のネイティブ実装はありません、あなたがしなければならないダックタイプ。
// 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.'; }}
。より複雑な例については、記事のリンクの下部をご覧ください。
JavaScriptにはタイプがありませんが、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
};
Precept Resolver
resolvePrecept
この関数は、あなたの内側に使用するユーティリティ&ヘルパー関数である抽象クラス。その仕事は、カプセル化されたPrecepts(データと動作)のカスタマイズされた実装処理を可能にすることです。エラーをスローしたり、警告したり、デフォルト値をImplementorクラスに割り当てたりできます。
iAbstractClass
iAbstractClass
使用するインターフェイスを定義します。そのアプローチには、実装者クラスとの暗黙の合意が伴います。このインターフェースは、各Preceptを同じ正確なPrecept名前空間に割り当てます-または-Precept Resolver関数が返すものに割り当てます。ただし、暗黙の合意は、実装者の規定というコンテキストに解決されます。
実施者
インプリメンターは単にインターフェース(この場合はiAbstractClass)に同意し、それをConstructor-Hijackingを使用して適用しますiAbstractClass.apply(this)
。上記のデータと動作を定義し、ハイジャックする、インターフェイスのコンストラクターを、実装者のコンテキストをインターフェイスコンストラクターに渡すことで、実装者のオーバーライドが確実に追加され、インターフェイスが警告とデフォルト値を明示するようになります。
これは非常に面倒なアプローチではなく、時間の経過やさまざまなプロジェクトで私のチームと私に非常に役立ちました。ただし、いくつかの注意点と欠点があります。
欠点
これは、ソフトウェア全体の一貫性を大幅に実装するのに役立ちますが、真のインターフェースを実装するのではなく、それらをエミュレートします。定義、デフォルト、警告またはエラーが説明されているが、使用の説明が強制され、主張されている、(JavaScript開発の多くと同様に)開発者によってされます。
これは「JavaScriptのインターフェース」への最良のアプローチのようですが、次の問題が解決されることを期待しています。
delete
アクションからオブジェクトをフリーズとはいえ、これが私のチームと私と同じくらいあなたに役立つことを願っています。
静的に型付けされているため、Javaのインターフェースが必要であり、クラス間の規約はコンパイル時に認識される必要があります。JavaScriptでは異なります。JavaScriptは動的に型付けされます。つまり、オブジェクトを取得したときに、特定のメソッドがあるかどうかを確認して呼び出すだけです。
yourMethod
エントリ#5に配置しSuperclass
、独自のを持つサブクラスごとにyourMethod
、そのサブクラスのエントリ#5をポイントするだけです。適切な実装で。
Implementation
実装するという名前のクラスSomeInterface
は、インターフェース全体を実装していると言うだけではありません。「実装する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を試してみることができます。それはドラフトECMA機能をサポートします(提案では、インターフェースはプロトコルと呼ばれます) coffeescriptやbabelのような言語が行うのと同様の」)をします。
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"
}
できないこと:
JavaScriptにはインターフェースがありません。しかし、それはアヒル型であることができます、例はここで見つけることができます:
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を生成する方法を「知っている」電子メールセクションファクトリーを受け取ることを期待している電子メールジェネレーターがあります。したがって、それらすべてに何らかの種類のand メソッドが必要です。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()