JavaScript ile OOP yapmayı öğreniyorum . Arayüz konseptine sahip mi (Java gibi interface
)?
Böylece bir dinleyici oluşturabilirdim ...
JavaScript ile OOP yapmayı öğreniyorum . Arayüz konseptine sahip mi (Java gibi interface
)?
Böylece bir dinleyici oluşturabilirdim ...
Yanıtlar:
"Bu sınıfın bu işlevlere sahip olması gerekir" (yani kendi başına arabirim yok) diye bir kavram yoktur, çünkü:
Bunun yerine, JavaScript ördek yazma denen şeyi kullanır . (Ördek gibi yürürse ve ördek gibi quack yaparsa, JS'nin umduğu kadarıyla bir ördek.) Nesnenizde quack (), walk () ve fly () yöntemleri varsa, kod beklediği her yerde kullanabilir bazı "Ördeklenebilir" arayüzlerin uygulanmasına gerek kalmadan yürüyebilen, vaktinden kaçabilen ve uçabilen bir nesne. Arayüz, kodun kullandığı işlevler kümesidir (ve bu işlevlerden gelen dönüş değerleri) ve ördek yazarken, bunu ücretsiz olarak alırsınız.
Şimdi, aramaya çalışırsanız, kodunuzun yarısında başarısız olmayacağı anlamına gelmez some_dog.quack()
; bir TypeError alırsınız. Açıkçası, köpeklere quack yapmalarını söylüyorsanız, biraz daha büyük sorunlarınız var; Ördek yazmak, tüm ördeklerinizi üst üste tuttuğunuzda en iyi sonucu verir, tabiri caizse, genel hayvan olarak muamele etmedikçe köpeklerin ve ördeklerin birbirine karışmasına izin vermeyin. Diğer bir deyişle, arayüz akışkan olmasına rağmen hala orada; genellikle bir köpeği ilk etapta quack ve uçmayı bekleyen kodlara geçirmek bir hatadır.
Ancak doğru şeyi yaptığınızdan eminseniz, kullanmaya başlamadan önce belirli bir yöntemin varlığını test ederek quacking-dog problemi üzerinde çalışabilirsiniz. Gibi bir şey
if (typeof(someObject.quack) == "function")
{
// This thing can quack
}
Böylece, kullanmadan önce kullanabileceğiniz tüm yöntemleri kontrol edebilirsiniz. Ancak sözdizimi biraz çirkin. Biraz daha güzel bir yol var:
Object.prototype.can = function(methodName)
{
return ((typeof this[methodName]) == "function");
};
if (someObject.can("quack"))
{
someObject.quack();
}
Bu standart JavaScript, bu yüzden kullanmaya değer herhangi bir JS yorumlayıcıda çalışması gerekir. İngilizce gibi okuma ek bir yararı vardır.
Modern tarayıcılar için (yani, IE 6-8 dışında hemen hemen tüm tarayıcılar), mülkün görünmesini engellemenin bir yolu bile vardır for...in
:
Object.defineProperty(Object.prototype, 'can', {
enumerable: false,
value: function(method) {
return (typeof this[method] === 'function');
}
}
Sorun, IE7 nesnelerinin hiç olmaması .defineProperty
ve IE8'de, yalnızca ana nesnelerde (yani DOM öğeleri ve benzeri) çalıştığı iddia ediliyor. Uyumluluk bir sorunsa kullanamazsınız .defineProperty
. (IE6'dan bile bahsetmeyeceğim, çünkü artık Çin dışında oldukça alakasız.)
Başka bir sorun, bazı kodlama stillerinin herkesin kötü kod yazdığını varsaymak ve Object.prototype
birinin körü körüne kullanmak istemesi durumunda değiştirilmesini yasaklamaktır for...in
. Bunu önemsiyorsanız veya (IMO bozuk ) kodunu kullanıyorsanız, biraz farklı bir sürüm deneyin:
function can(obj, methodName)
{
return ((typeof obj[methodName]) == "function");
}
if (can(someObject, "quack"))
{
someObject.quack();
}
for...in
bu tür tehlikelerle doludur - ve her zaman böyle olmuştur ve bunu en azından birisinin Object.prototype
(bu makalenin kendi kabulü ile nadir olmayan bir teknik) eklediğini düşünmeden yapan herkes , kodlarının başka birinin eline geçeceğini görecektir.
for...in
sorunu önleyebilirsiniz . developer.mozilla.org/tr-TR/docs/Web/JavaScript/Reference/…
for...in
sorun" hala bir dereceye kadar var olacak, çünkü her zaman özensiz kod olacak ... şey, bu ve Object.defineProperty(obj, 'a', {writable: true, enumerable: false, value: 3});
sadece biraz daha fazla iş obj.a = 3;
. İnsanları daha sık yapmaya çalışmayanları tamamen anlayabiliyorum. : P
Dustin Diaz'ın ' JavaScript tasarım modellerinin ' bir kopyasını alın . Duck Typing ile JavaScript arayüzleri uygulamaya adanmış birkaç bölüm var. Ayrıca güzel bir okuma. Ama hayır, bir arayüzün yerel dil uygulaması yok, Duck Type'a ihtiyacınız var .
// 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 edition 3), ileride kullanılmak üzere kaydedilmiş , implements
ayrılmış bir kelimeye sahiptir . Bunun tam olarak bu amaç için tasarlandığını düşünüyorum, ancak spesifikasyonu kapıdan çıkarmak için acele ederek, onunla ne yapacağını tanımlamak için zamanları yoktu, bu yüzden şu anda tarayıcılar başka bir şey yapmıyor orada oturmasına izin verin ve ara sıra bir şey için kullanmaya çalışırsanız şikayet edin.
Object.implement(Interface)
Belirli bir özellik / işlev kümesi belirli bir nesneye uygulanmadığında şaşırtıcı olan kendi yönteminizi oluşturmak mümkün ve gerçekten kolaydır .
Kendi gösterimi aşağıdaki gibi kullandığım nesne yönelimi üzerine bir makale yazdım :
// 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);
Bu kediyi kaplamanın birçok yolu var, ama bu benim kendi Arayüz uygulamam için kullandığım mantık. Bu yaklaşımı tercih ettiğimi görüyorum ve okumak ve kullanmak kolaydır (yukarıda gördüğünüz gibi). Bu, Function.prototype
bazı insanların sorun yaşayabileceği bir 'uygulama' yöntemi eklemek anlamına gelir , ancak bunun güzel çalıştığını düşünüyorum.
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.'; }}
. Daha ayrıntılı bir örnek için makale bağlantısının altına bakın .
JavaScript belirtildiyse de değil sahip interface
tipini, genellikle gerekli katıdır. JavaScript'in dinamik doğası ve Prototip-Kalıtım kullanımı ile ilgili nedenlerden ötürü, sınıflar arasında tutarlı arabirimler sağlamak zordur - ancak bunu yapmak mümkündür; ve sıklıkla taklit edilir.
Bu noktada, JavaScript'te Arayüzleri taklit etmenin birkaç yolu vardır; yaklaşımlardaki varyans genellikle bazı ihtiyaçları karşılarken, diğerleri unutulmadan bırakılır. Çoğu zaman, en sağlam yaklaşım aşırı derecede zahmetlidir ve uygulayıcıyı (geliştirici) sabitler.
İşte arayüzler / Soyut Sınıflar için çok hantal olmayan, açıklayıcı, Soyutlamalar içindeki uygulamaları minimumda tutan ve dinamik veya özel metodolojiler için yeterli alan bırakan bir yaklaşım:
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 Çözücü
resolvePrecept
İşlevi içinde kullanmak için bir yardımcı program ve yardımcı fonksiyonudur Özet Class . Görevi, kapsüllenmiş kuralların (veri ve davranış) özel olarak uygulanmasına izin vermektir . Hatalar atabilir veya Uygulayıcı sınıfına varsayılan bir değer atayabilir - VE -.
iAbstractClass
Kullanılacak iAbstractClass
arabirimi tanımlar. Yaklaşımı, Uygulayıcı sınıfıyla örtük bir anlaşma gerektirir. Bu arabirim, her bir precept'i Precept Resolver işlevinin döndürdüğü her ne olursa olsun aynı precept ad alanına - VEYA - atar . Ancak zımni anlaşma, bir bağlam - Uygulayıcı hükmü ile sonuçlanır.
Uygulatıcı
Uygulayıcı sadece bir Arabirim ( bu durumda iAbstractClass ) ile 'hemfikirdir' ve Yapıcı-Hijacking : kullanarak uygular iAbstractClass.apply(this)
. Yukarıdaki verileri ve davranışı tanımlayıp ardından Arabirim yapıcısını (Uygulayıcı'nın bağlamını Arabirim yapıcısına geçirerek) ele geçirerek, Uygulayıcının geçersiz kılmalarının eklenmesini ve Arabirimin uyarıları ve varsayılan değerleri açıklamasını sağlayabiliriz.
Bu, ekibime ve zamana ve farklı projelere çok iyi hizmet eden çok hantal bir yaklaşım. Ancak, bazı uyarılar ve dezavantajları vardır.
Dezavantajları
Bu, yazılımınız boyunca tutarlılığın önemli ölçüde uygulanmasına yardımcı olsa da , gerçek arayüzleri uygulamaz - ancak bunları taklit eder. Tanımlar, varsayılan ve uyarı veya hata olsa edilir irdelenmiş, kullanım izahati edilir zorlanan & iddia (JavaScript gelişme çok olduğu gibi) geliştirici tarafından.
Bu görünüşe göre "JavaScript Arayüzleri" için en iyi yaklaşımdır , ancak aşağıdakilerin çözüldüğünü görmek isterim:
delete
eylemlerden dondurmaBununla birlikte, umarım bu, ekibim ve ben kadar size yardımcı olur.
Statik olarak yazıldığından ve sınıflar arasındaki sözleşmenin derleme sırasında bilinmesi gerektiğinden Java'da arabirimlere ihtiyacınız vardır. JavaScript'te farklıdır. JavaScript dinamik olarak yazılmıştır; bu, nesneyi aldığınızda belirli bir yöntemin olup olmadığını kontrol edip çağırabileceğiniz anlamına gelir.
yourMethod
, Superclass
's vtable' daki # 5 girişine ve kendine ait her alt sınıf için yourMethod
, sadece alt sınıfın # 5 girişine işaret edebilir uygun uygulamada.
Implementation
uygular o SomeInterface
sadece bu bütün arabirimini uygulayan söylemez. "Ben uyguluyorum SomeInterface.yourMethod
" diyen ve yöntem tanımına işaret eden bilgiler var Implementation.yourMethod
. JVM aradığında SomeInterface.yourMethod
, bu arabirimin yönteminin uygulamaları hakkında bilgi arar ve çağırması gerektiğini bulur Implementation.yourMethod
.
Umarım, hala bir cevap arayan herkes yardımcı olur.
Proxy (ECMAScript 2015'ten beri standarttır) kullanmayı deneyebilirsiniz: 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;
}
});
Sonra kolayca şunu söyleyebilirsiniz:
myMap = {}
myMap.position = latLngLiteral;
Bir transcompiler kullanmak istediğinizde, TypeScript'i deneyebilirsiniz. Kahve ya da babil gibi dillerin yaptığı taslak ECMA özelliklerini (teklifte arayüzlere " protokoller " denir ) destekler.
TypeScript'te arayüzünüz aşağıdaki gibi görünebilir:
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"
}
Ne yapamazsın:
JavaScript'te yerel arayüzler yoktur, bir arayüzü simüle etmenin birkaç yolu vardır. bunu yapan bir paket yazdım
implantasyonu burada görebilirsiniz
Javascript'in arayüzü yok. Ancak ördek tipi olabilir, burada bir örnek bulabilirsiniz:
http://reinsbrain.blogspot.com/2008/10/interface-in-javascript.html
Bunun eski bir şey olduğunu biliyorum, ancak son zamanlarda kendimi arayüzlere karşı nesneleri kontrol etmek için kullanışlı bir API'ye ihtiyacım olduğunu gördüm. Bu yüzden şunu yazdım: https://github.com/tomhicks/methodical
NPM üzerinden de kullanılabilir: npm install methodical
Temel olarak yukarıda önerilen her şeyi yapar, biraz daha katı olmak için bazı seçenekler ve hepsi bir sürü if (typeof x.method === 'function')
kazan plakası yapmak zorunda kalmadan .
Umarım birisi faydalı bulur.
Bu eski bir soru, yine de bu konu beni rahatsız etmekten asla vazgeçmiyor.
Buradaki ve web'deki yanıtların birçoğu arayüzün "uygulanmasına" odaklandığından, alternatif bir görünüm önermek isterim:
Benzer şekilde davranan birden fazla sınıf kullandığımda (yani bir arabirim uygulamak ) arayüzlerin eksikliğini en çok hissediyorum .
Örneğin , bölümlerin içeriğini ve HTML nasıl "bilmek" e-posta bölümleri fabrikaları almak için bekleyen bir e- posta oluşturucu var. Bu nedenle, hepsinin bir çeşit getContent(id)
ve getHtml(content)
yöntemlere sahip olması gerekir .
Arayüzlere en yakın desen (yine de bir çözüm olsa da) 2 arabirim yöntemini tanımlayacak 2 bağımsız değişken alacak bir sınıf kullanmaktır.
Bu kalıpla ilgili temel zorluk, yöntemlerin static
özelliklerine erişmek için ya örnek olması ya da örneğin argümanı olarak alınması gerektiğidir . Ancak bu değiş tokuşu zahmete değdiğim durumlar var.
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));
böyle soyut bir arayüz
const MyInterface = {
serialize: () => {throw "must implement serialize for MyInterface types"},
print: () => console.log(this.serialize())
}
örnek oluştur:
function MyType() {
this.serialize = () => "serialized "
}
MyType.prototype = MyInterface
ve kullan
let x = new MyType()
x.print()