Saya sedang belajar cara membuat OOP dengan JavaScript . Apakah ia memiliki konsep antarmuka (seperti Java interface
)?
Jadi saya bisa membuat pendengar ...
Saya sedang belajar cara membuat OOP dengan JavaScript . Apakah ia memiliki konsep antarmuka (seperti Java interface
)?
Jadi saya bisa membuat pendengar ...
Jawaban:
Tidak ada anggapan "kelas ini harus memiliki fungsi-fungsi ini" (yaitu, tidak ada antarmuka per se), karena:
Sebaliknya, JavaScript menggunakan apa yang disebut bebek mengetik . (Jika berjalan seperti bebek, dan dukun seperti bebek, sejauh JS peduli, itu bebek.) Jika objek Anda memiliki duri (), metode berjalan (), dan terbang (), kode dapat menggunakannya di mana pun ia harapkan sebuah objek yang dapat berjalan, dukun, dan terbang, tanpa memerlukan implementasi beberapa antarmuka "Duckable". Antarmuka persis set fungsi yang menggunakan kode (dan nilai pengembalian dari fungsi-fungsi), dan dengan mengetik bebek, Anda mendapatkannya secara gratis.
Sekarang, bukan berarti kode Anda tidak akan gagal setengah jalan, jika Anda mencoba menelepon some_dog.quack()
; Anda akan mendapatkan TypeError. Terus terang, jika Anda memberitahu anjing untuk dukun, Anda memiliki masalah yang sedikit lebih besar; Mengetik bebek bekerja paling baik ketika Anda menjaga semua bebek Anda berturut-turut, sehingga untuk berbicara, dan tidak membiarkan anjing dan bebek berbaur bersama kecuali Anda memperlakukan mereka sebagai hewan generik. Dengan kata lain, meskipun antarmuka itu cair, masih ada di sana; sering kali kesalahan menularkan anjing ke kode yang mengharapkannya untuk dukun dan terbang di tempat pertama.
Tetapi jika Anda yakin telah melakukan hal yang benar, Anda dapat mengatasi masalah anjing dukun dengan menguji keberadaan metode tertentu sebelum mencoba menggunakannya. Sesuatu seperti
if (typeof(someObject.quack) == "function")
{
// This thing can quack
}
Jadi, Anda dapat memeriksa semua metode yang dapat Anda gunakan sebelum menggunakannya. Sintaksnya agak jelek. Ada cara yang sedikit lebih cantik:
Object.prototype.can = function(methodName)
{
return ((typeof this[methodName]) == "function");
};
if (someObject.can("quack"))
{
someObject.quack();
}
Ini adalah JavaScript standar, jadi itu harus bekerja pada penerjemah JS apa pun yang layak digunakan. Ini memiliki manfaat tambahan membaca seperti bahasa Inggris.
Untuk peramban modern (artinya, hampir semua peramban selain IE 6-8), bahkan ada cara untuk menjaga agar properti tidak muncul for...in
:
Object.defineProperty(Object.prototype, 'can', {
enumerable: false,
value: function(method) {
return (typeof this[method] === 'function');
}
}
Masalahnya adalah bahwa objek IE7 tidak memiliki .defineProperty
sama sekali, dan di IE8, itu diduga hanya bekerja pada objek host (yaitu, elemen DOM dan semacamnya). Jika kompatibilitas merupakan masalah, Anda tidak dapat menggunakan .defineProperty
. (Saya bahkan tidak akan menyebutkan IE6, karena agak tidak relevan lagi di luar China.)
Masalah lain adalah bahwa beberapa gaya pengkodean suka berasumsi bahwa setiap orang menulis kode yang buruk, dan melarang memodifikasi Object.prototype
jika seseorang ingin menggunakan secara membabi buta for...in
. Jika Anda peduli tentang itu, atau menggunakan kode ( rusak IMO ), coba versi yang sedikit berbeda:
function can(obj, methodName)
{
return ((typeof obj[methodName]) == "function");
}
if (can(someObject, "quack"))
{
someObject.quack();
}
for...in
adalah - dan selalu - penuh dengan bahaya seperti itu, dan siapa pun yang melakukannya tanpa setidaknya mempertimbangkan bahwa seseorang ditambahkan ke Object.prototype
(teknik yang tidak biasa, dengan pengakuan artikel itu sendiri) akan melihat kode mereka rusak di tangan orang lain.
for...in
masalah. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
for...in
masalah" masih akan ada sampai taraf tertentu, karena akan selalu ada kode ceroboh ... baik, itu, dan Object.defineProperty(obj, 'a', {writable: true, enumerable: false, value: 3});
sedikit lebih banyak pekerjaan daripada sekadar obj.a = 3;
. Saya benar-benar dapat memahami orang yang tidak berusaha melakukannya lebih sering. : P
Ambil salinan ' pola desain JavaScript ' oleh Dustin Diaz . Ada beberapa bab yang didedikasikan untuk mengimplementasikan antarmuka JavaScript melalui Duck Typing. Ini bacaan yang bagus juga. Tapi tidak, tidak ada implementasi bahasa asli antarmuka, Anda harus 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 edition 3) implements
menyimpan kata yang disimpan untuk digunakan di masa mendatang . Saya pikir ini dimaksudkan persis untuk tujuan ini, namun, dengan tergesa-gesa untuk mendapatkan spesifikasi keluar dari pintu mereka tidak punya waktu untuk menentukan apa yang harus dilakukan dengan itu, jadi, pada saat ini, browser tidak melakukan apa pun selain biarkan duduk di sana dan sesekali mengeluh jika Anda mencoba menggunakannya untuk sesuatu.
Adalah mungkin dan memang cukup mudah untuk membuat Object.implement(Interface)
metode Anda sendiri dengan logika yang muncul setiap kali set properti / fungsi tertentu tidak diimplementasikan dalam objek tertentu.
Saya menulis artikel tentang orientasi objek di mana menggunakan notasi saya sendiri sebagai berikut :
// 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);
Ada banyak cara untuk menguliti kucing khusus ini, tetapi ini adalah logika yang saya gunakan untuk implementasi Antarmuka saya sendiri. Saya menemukan saya lebih suka pendekatan ini, dan mudah dibaca dan digunakan (seperti yang Anda lihat di atas). Itu berarti menambahkan metode 'implement' di Function.prototype
mana beberapa orang mungkin memiliki masalah dengan, tetapi saya menemukan itu berfungsi dengan baik.
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.'; }}
. Lihat bagian bawah tautan artikel untuk contoh yang lebih rumit.
Meskipun JavaScript tidak memiliki interface
tipe, seringkali diperlukan. Untuk alasan yang berkaitan dengan sifat dinamis JavaScript dan penggunaan Prototypical-Inheritance, sulit untuk memastikan antarmuka yang konsisten di seluruh kelas - namun, dimungkinkan untuk melakukannya; dan sering ditiru.
Pada titik ini, ada beberapa cara tertentu untuk meniru Antarmuka dalam JavaScript; varians pada pendekatan biasanya memuaskan beberapa kebutuhan, sementara yang lain tidak tersentuh. Seringkali, pendekatan yang paling kuat terlalu rumit dan menghalangi implementor (pengembang).
Berikut adalah pendekatan untuk Kelas Antarmuka / Abstrak yang tidak terlalu rumit, eksplikatif, menjaga implementasi dalam Abstraksi seminimal mungkin, dan menyisakan ruang yang cukup untuk metodologi dinamis atau khusus:
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
};
Penyelesai Sila
The resolvePrecept
fungsi adalah fungsi utilitas & pembantu untuk menggunakan dalam Anda Kelas Abstrak . Tugasnya adalah untuk memungkinkan implementasi khusus - penanganan Sila yang dienkapsulasi (data & perilaku) . Itu bisa melempar kesalahan atau memperingatkan - DAN - menetapkan nilai default ke kelas Implementor.
iAbstractClass
The iAbstractClass
mendefinisikan interface yang akan digunakan. Pendekatannya memerlukan perjanjian diam-diam dengan kelas implementornya. Antarmuka ini menetapkan setiap sila ke namespace sila persis yang sama - ATAU - untuk apa pun yang dihasilkan fungsi Precept Resolver . Namun, perjanjian diam-diam memutuskan untuk konteks - ketentuan Pelaksana.
Pelaksana
The Pelaksana hanya 'setuju' dengan Interface ( iAbstractClass dalam kasus ini) dan menerapkannya dengan menggunakan Constructor-Pembajakan : iAbstractClass.apply(this)
. Dengan mendefinisikan data & perilaku di atas, dan kemudian membajak konstruktor Antarmuka - meneruskan konteks Implementor ke konstruktor Antarmuka - kami dapat memastikan bahwa penggantian Implementor akan ditambahkan, dan bahwa Antarmuka akan menjelaskan peringatan dan nilai default.
Ini adalah pendekatan yang sangat tidak rumit yang telah melayani tim saya & saya sangat baik selama perjalanan waktu dan proyek yang berbeda. Namun, itu memang memiliki beberapa peringatan & kekurangan.
Kekurangannya
Meskipun ini membantu menerapkan konsistensi di seluruh perangkat lunak Anda ke tingkat yang signifikan, ini tidak mengimplementasikan antarmuka yang sebenarnya - tetapi mengemulasi mereka. Meskipun definisi, default, dan peringatan atau kesalahan yang explicated, yang penjelasan penggunaan yang ditegakkan & menegaskan oleh pengembang (seperti dengan banyak pengembangan JavaScript).
Tampaknya ini adalah pendekatan terbaik untuk "Antarmuka dalam JavaScript" , namun, saya ingin melihat yang berikut ini terselesaikan:
delete
tindakanYang mengatakan, saya harap ini membantu Anda sebanyak tim saya dan saya.
Anda memerlukan antarmuka di Java karena diketik secara statis dan kontrak antara kelas harus diketahui selama kompilasi. Dalam JavaScript berbeda. JavaScript diketik secara dinamis; itu berarti bahwa ketika Anda mendapatkan objek Anda hanya dapat memeriksa apakah ia memiliki metode tertentu dan menyebutnya.
yourMethod
pada entri # 5 di Superclass
vtable, dan untuk setiap subkelas yang memiliki sendiri yourMethod
, cukup tunjukkan bahwa entri subkelas # 5 pada implementasi yang sesuai.
Implementation
yang mengimplementasikan SomeInterface
tidak hanya mengatakan itu mengimplementasikan seluruh antarmuka. Ini memiliki info yang mengatakan "Saya mengimplementasikan SomeInterface.yourMethod
" dan menunjuk pada definisi metode untuk Implementation.yourMethod
. Ketika panggilan JVM SomeInterface.yourMethod
, itu terlihat di kelas untuk info tentang implementasi metode antarmuka itu, dan menemukan itu perlu untuk memanggil Implementation.yourMethod
.
Harapan, bahwa siapa pun yang masih mencari jawaban merasa terbantu.
Anda dapat mencoba menggunakan Proksi (Ini adalah standar sejak 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;
}
});
Maka Anda dapat dengan mudah mengatakan:
myMap = {}
myMap.position = latLngLiteral;
Ketika Anda ingin menggunakan transcompiler, maka Anda bisa mencoba TypeScript. Ini mendukung rancangan fitur ECMA (dalam proposal, antarmuka disebut " protokol ") mirip dengan apa yang dilakukan bahasa seperti coffeescript atau babel.
Di TypeScript antarmuka Anda dapat terlihat seperti:
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"
}
Apa yang tidak bisa Anda lakukan:
tidak ada antarmuka asli dalam JavaScript, ada beberapa cara untuk mensimulasikan antarmuka. saya telah menulis paket yang melakukannya
Anda dapat melihat implantasi di sini
Javascript tidak memiliki antarmuka. Tapi itu bisa diketik bebek, contohnya bisa ditemukan di sini:
http://reinsbrain.blogspot.com/2008/10/interface-in-javascript.html
Saya tahu ini adalah yang lama, tetapi saya baru-baru ini menemukan diri saya membutuhkan lebih dan lebih untuk memiliki API yang berguna untuk memeriksa objek terhadap antarmuka. Jadi saya menulis ini: https://github.com/tomhicks/methodical
Ini juga tersedia melalui NPM: npm install methodical
Ini pada dasarnya melakukan semua yang disarankan di atas, dengan beberapa opsi untuk menjadi sedikit lebih ketat, dan semua tanpa harus melakukan banyak if (typeof x.method === 'function')
boilerplate.
Semoga ada yang merasa bermanfaat.
Ini adalah pertanyaan lama, namun topik ini tidak pernah berhenti menggangguku.
Karena banyak jawaban di sini dan di seluruh web fokus pada "menegakkan" antarmuka, saya ingin menyarankan pandangan alternatif:
Saya merasakan kurangnya antarmuka yang paling ketika saya menggunakan beberapa kelas yang berperilaku sama (yaitu mengimplementasikan antarmuka ).
Sebagai contoh, saya memiliki Generator Email yang mengharapkan untuk menerima Pabrik Bagian Email , yang "tahu" bagaimana menghasilkan konten bagian dan HTML. Karenanya, mereka semua perlu memiliki semacam getContent(id)
dan getHtml(content)
metode.
Pola terdekat ke antarmuka (meskipun masih merupakan solusi) yang bisa saya pikirkan adalah menggunakan kelas yang akan mendapatkan 2 argumen, yang akan menentukan 2 metode antarmuka.
Tantangan utama dengan pola ini adalah bahwa metode harus static
, atau untuk mendapatkan sebagai argumen instance itu sendiri, untuk mengakses propertinya. Namun ada beberapa kasus di mana saya menemukan pertukaran ini sepadan dengan kerumitan.
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));
antarmuka abstrak seperti ini
const MyInterface = {
serialize: () => {throw "must implement serialize for MyInterface types"},
print: () => console.log(this.serialize())
}
buat sebuah instance:
function MyType() {
this.serialize = () => "serialized "
}
MyType.prototype = MyInterface
dan gunakan itu
let x = new MyType()
x.print()