私は学んでいます JavaScriptを使ってOOPを作る方法 。それはインターフェースの概念(Javaのinterface
など)を持っていますか?
だから私はリスナーを作成することができるでしょう...
「このクラスはこれらの関数を持つ必要がある」という概念はありません(つまり、インタフェース自体はありません)。
代わりに、JavaScriptは ダックタイピング と呼ばれるものを使用します。オブジェクトが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();
}
Dustin Diaz による ' JavaScript design patterns 'のコピーを入手してください。 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)
メソッドを作成することは可能であり、本当に簡単です。
私は object-orientationここでは次のように独自の表記法を使用しますの記事を書きました。
// 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
に 'implements'メソッドを追加することを意味します。
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;
}
JavaScriptはinterface
型を持っていませんしていませんが、それはしばしば必要とされます。 JavaScriptの動的な性質とPrototypical-Inheritanceの使用に関連する理由から、クラス間で一貫したインターフェースを確保することは困難です - しかし、そうすることは可能です。そして頻繁にエミュレートされます。
現時点では、JavaScriptでインターフェースをエミュレートする方法はいくつかあります。アプローチの違いは通常いくつかのニーズを満たしますが、他のものは対処されていません。多くの場合、最も堅牢なアプローチは過度に面倒で、実装者(開発者)に迷惑をかけます。
これは、あまり面倒ではなく、わかりやすく、Abstractions内での実装を最小限に抑え、動的またはカスタムの方法論に十分な余地を残す、Interface/Abstract Classesへのアプローチです。
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のカスタマイズされた実装処理を可能にすることです。エラーをスローしたり、デフォルト値をImplementorクラスに代入したりすることができます。
iAbstractClass
iAbstractClass
は使用するインターフェースを定義します。そのアプローチはそのImplementorクラスとの暗黙の合意を伴います。このインタフェースはそれぞれのpreceptを同じ正確なprecept名前空間 - OR - に割り当てますリゾルバを受け付ける関数が戻ります。しかしながら、暗黙の合意は文脈 - Implementorの規定に解決します。
実装者
実装者は単にインターフェース(この場合はiAbstractClass)に '同意'し、コンストラクタを使用してそれを適用します。 - ハイジャック:iAbstractClass.apply(this)
。上記のデータと振る舞いを定義してから、インタフェースのコンストラクタを乗っ取る追加すると、そのインタフェースは警告とデフォルト値を表示します。
これは私のチームに貢献してきた非常に面倒なアプローチであり、私は時間の経過とさまざまなプロジェクトに非常に役立ちました。ただし、いくつか注意点と欠点があります。
欠点
これにより、ソフトウェア全体の一貫性を大幅に向上させることができますが、は真のインターフェースを実装しません - それらをエミュレートします。定義、デフォルト、および警告またはエラーはで説明されていますが、使用の説明は実施および主張( - )されています開発者による(JavaScript開発の多くの場合と同様に)。
これは、「JavaScriptのインターフェース」に対する最善の方法ですが、次のことを解決したいと思います。
delete
アクションからオブジェクトをフリーズしますそれは私のチームと私が持っているのと同じくらいあなたがこれがあなたを助けることを願っています、と述べた.
まだ答えを探している人なら誰でも役に立つと思います。
プロキシを使用して試すことができます(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;
Javaでは静的に型定義されており、コンパイル時にクラス間の規約がわかっている必要があるため、Javaでインターフェースが必要です。 JavaScriptでは違います。 JavaScriptは動的に型付けされています。つまり、オブジェクトを取得したら、そのオブジェクトに特定のメソッドがあるかどうかを確認して呼び出すだけで済みます。
トランスコンパイラを使いたいときは、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"
}
できないこと
Javascriptにはインターフェースがありません。しかし、それはアヒルタイプであることができます、例はここで見つけることができます:
http://reinsbrain.blogspot.com/2008/10/interface-in-javascript.html
javaScriptにはネイティブインタフェースはありません。インタフェースをシミュレートする方法はいくつかあります。私はそれをするパッケージを書きました
あなたは着床を見ることができます ここ
私はこれが古いものであることを知っています、しかし私は最近私自身がインターフェースに対してオブジェクトをチェックするための便利なAPIを持っていることをもっともっと必要としているのを見つけました。だから私はこれを書いた: https://github.com/tomhicks/methodical
NPMからも入手できます。npm install methodical
それは基本的に上で提案されたすべてをします、もう少し厳密であることのためのいくつかのオプションで、そしてif (typeof x.method === 'function')
ボイラープレートのロードをする必要なしですべて。
うまくいけば、誰かがそれが役に立つと思う。