Saya bertanya-tanya tentang apa cara terbaik untuk membuat objek JavaScript yang memiliki properti dan metode.
Saya telah melihat contoh di mana orang tersebut digunakan var self = this
dan kemudian digunakan self.
di semua fungsi untuk memastikan ruang lingkup selalu benar.
Lalu saya telah melihat contoh menggunakan .prototype
untuk menambahkan properti, sementara yang lain melakukannya inline.
Dapatkah seseorang memberi saya contoh yang tepat dari objek JavaScript dengan beberapa properti dan metode?
javascript
Michael Stum
sumber
sumber
self
kata yang khusus? Jika tidak, seharusnya; karenaself
merupakan variabel yang ditentukan sebelumnya yang merujuk ke jendela saat ini.self === window
window
di dalam Browser Object Model sepertidocument
atauframes
; Anda tentu dapat menggunakan kembali pengenal sebagai nama variabel. Meskipun, ya, secara gaya saya lebih sukavar that= this
menghindari kemungkinan kebingungan. Meskipunwindow.self
pada akhirnya tidak ada gunanya sehingga jarang ada alasan untuk menyentuhnya.this
ke variabel lokal (misalnyaself
) mengurangi ukuran file.Jawaban:
Ada dua model untuk mengimplementasikan kelas dan instance dalam JavaScript: cara prototyping, dan cara penutupan. Keduanya memiliki kelebihan dan kekurangan, dan ada banyak variasi tambahan. Banyak pemrogram dan perpustakaan memiliki pendekatan dan fungsi utilitas penanganan kelas yang berbeda untuk meliput beberapa bagian bahasa yang lebih buruk.
Hasilnya adalah bahwa di perusahaan campuran Anda akan memiliki mishmash of metaclasses, semua berperilaku sedikit berbeda. Yang lebih buruk, sebagian besar materi tutorial JavaScript mengerikan dan menyajikan semacam kompromi di sela-sela untuk mencakup semua pangkalan, membuat Anda sangat bingung. (Mungkin penulis juga bingung. Model objek JavaScript sangat berbeda dengan kebanyakan bahasa pemrograman, dan di banyak tempat langsung dirancang dengan buruk.)
Mari kita mulai dengan cara prototipe . Ini adalah JavaScript-asli yang paling banyak Anda dapatkan: ada minimum kode overhead dan instanceof akan bekerja dengan instance objek semacam ini.
Kita bisa menambahkan metode ke instance yang dibuat
new Shape
dengan menulisnya keprototype
pencarian fungsi konstruktor ini:Sekarang untuk subkelasnya, sebanyak yang Anda bisa sebut apa yang JavaScript lakukan subklasifikasi. Kami melakukannya dengan sepenuhnya mengganti
prototype
properti sihir aneh itu :sebelum menambahkan metode ke dalamnya:
Contoh ini akan berfungsi dan Anda akan melihat kode seperti itu di banyak tutorial. Tapi man, itu
new Shape()
jelek: kami membuat instance kelas dasar meskipun tidak ada Shape yang sebenarnya yang harus dibuat. Itu terjadi untuk bekerja dalam kasus sederhana ini karena JavaScript sangat ceroboh: ini memungkinkan nol argumen untuk diteruskan, dalam hal inix
dany
menjadiundefined
dan ditugaskan ke prototipethis.x
danthis.y
. Jika fungsi konstruktor melakukan sesuatu yang lebih rumit, itu akan jatuh datar di wajahnya.Jadi yang perlu kita lakukan adalah menemukan cara untuk membuat objek prototipe yang berisi metode dan anggota lain yang kita inginkan di tingkat kelas, tanpa memanggil fungsi konstruktor kelas dasar. Untuk melakukan ini, kita harus mulai menulis kode pembantu. Ini adalah pendekatan paling sederhana yang saya tahu:
Ini mentransfer anggota kelas dasar dalam prototipe ke fungsi konstruktor baru yang tidak melakukan apa-apa, kemudian menggunakan konstruktor itu. Sekarang kita dapat menulis secara sederhana:
bukannya
new Shape()
kesalahan. Kami sekarang memiliki satu set primitif yang dapat diterima untuk membangun kelas.Ada beberapa penyempurnaan dan ekstensi yang dapat kita pertimbangkan dalam model ini. Sebagai contoh di sini adalah versi sintaksis-gula:
Versi mana pun memiliki kelemahan bahwa fungsi konstruktor tidak dapat diwarisi, seperti dalam banyak bahasa. Jadi, bahkan jika subkelas Anda tidak menambahkan apa pun pada proses konstruksi, ia harus ingat untuk memanggil konstruktor dasar dengan argumen apa pun yang diinginkan dasar. Ini bisa sedikit otomatis menggunakan
apply
, tetapi Anda masih harus menulis:Jadi ekstensi yang umum adalah untuk membagi hal-hal inisialisasi ke dalam fungsinya sendiri daripada konstruktor itu sendiri. Fungsi ini kemudian dapat mewarisi dari basis dengan baik:
Sekarang kita baru saja mendapatkan fungsi konstruktor boilerplate yang sama untuk setiap kelas. Mungkin kita bisa memindahkannya ke dalam fungsi penolongnya sendiri sehingga kita tidak harus terus mengetiknya, misalnya alih-alih
Function.prototype.subclass
, memutarnya dan membiarkan Function kelas dasar mengeluarkan subclass:... yang mulai sedikit lebih mirip dengan bahasa lain, walaupun dengan sintaksis yang sedikit klumsier. Anda dapat menaburkan beberapa fitur tambahan jika suka. Mungkin Anda ingin
makeSubclass
mengambil dan mengingat nama kelas dan memberikan defaulttoString
menggunakannya. Mungkin Anda ingin membuat konstruktor mendeteksi ketika secara tidak sengaja dipanggil tanpanew
operator (yang sebaliknya akan sering mengakibatkan debugging yang sangat menjengkelkan):Mungkin Anda ingin memasukkan semua anggota baru dan
makeSubclass
menambahkannya ke prototipe, agar Anda tidak perlu menulisClass.prototype...
terlalu banyak. Banyak sistem kelas melakukan itu, misalnya:Ada banyak fitur potensial yang mungkin Anda anggap diinginkan dalam sistem objek dan tidak ada yang benar-benar setuju pada satu formula tertentu.
Cara penutupan , kalau begitu. Ini menghindari masalah warisan berbasis prototipe JavaScript, dengan tidak menggunakan warisan sama sekali. Sebagai gantinya:
Sekarang setiap instance
Shape
akan memiliki salinantoString
metode sendiri (dan metode lain atau anggota kelas lain yang kami tambahkan).Hal buruk dari setiap instance memiliki salinannya sendiri dari setiap anggota kelas adalah bahwa itu kurang efisien. Jika Anda berurusan dengan banyak contoh subclass, pewarisan prototipikal dapat membantu Anda dengan lebih baik. Juga memanggil metode kelas dasar sedikit mengganggu seperti yang Anda lihat: kita harus ingat apa metode itu sebelum konstruktor subkelas menimpa, atau hilang.
[Juga karena tidak ada warisan di sini,
instanceof
operator tidak akan bekerja; Anda harus menyediakan mekanisme Anda sendiri untuk mengendus kelas jika Anda membutuhkannya. Sementara Anda bisa mengutak-atik objek prototipe dengan cara yang sama seperti dengan warisan prototipe, ini agak rumit dan tidak terlalu layak hanya untukinstanceof
bekerja.]Hal yang baik tentang setiap instance yang memiliki metode sendiri adalah bahwa metode tersebut kemudian dapat terikat pada instance spesifik yang memilikinya. Ini berguna karena cara aneh JavaScript
this
dalam mengikat pemanggilan metode, yang memiliki kesimpulan bahwa jika Anda melepaskan metode dari pemiliknya:kemudian
this
di dalam metode tidak akan menjadi contoh Circle seperti yang diharapkan (itu sebenarnya akan menjadiwindow
objek global , menyebabkan celaka debugging luas). Pada kenyataannya ini biasanya terjadi ketika suatu metode diambil dan ditugaskan kesetTimeout
,onclick
atauEventListener
secara umum.Dengan cara prototipe, Anda harus menyertakan penutupan untuk setiap tugas seperti:
atau, di masa depan (atau sekarang jika Anda meretas Function.prototype) Anda juga dapat melakukannya dengan
function.bind()
:jika instans Anda dilakukan dengan cara penutupan, pengikatan dilakukan secara gratis oleh penutupan atas variabel instan (biasanya disebut
that
atauself
, meskipun secara pribadi saya akan menyarankan agar yang terakhir tidakself
memiliki arti lain yang berbeda dalam JavaScript). Anda tidak mendapatkan argumen1, 1
dalam cuplikan di atas secara gratis, jadi Anda masih perlu penutupan lain ataubind()
jika Anda perlu melakukannya.Ada banyak varian pada metode penutupan juga. Anda mungkin lebih suka menghilangkan
this
sepenuhnya, membuat yang baruthat
dan mengembalikannya daripada menggunakannew
operator:Jalan mana yang “tepat”? Kedua. Mana yang "terbaik"? Itu tergantung situasi Anda. FWIW Saya cenderung membuat prototipe untuk warisan JavaScript nyata ketika saya melakukan banyak hal OO, dan penutupan untuk efek halaman sekali pakai yang sederhana.
Namun kedua cara tersebut cukup kontra-intuitif bagi kebanyakan programmer. Keduanya memiliki banyak variasi yang berpotensi berantakan. Anda akan bertemu keduanya (dan juga banyak skema di antara dan umumnya rusak) jika Anda menggunakan kode / pustaka orang lain. Tidak ada satu jawaban yang diterima secara umum. Selamat datang di dunia objek JavaScript yang luar biasa.
[Ini telah menjadi bagian 94 dari Mengapa JavaScript Bukan Bahasa Pemrograman Favorit Saya.]
sumber
new
.Saya sering menggunakan pola ini - saya menemukan bahwa ini memberi saya fleksibilitas yang cukup besar ketika saya membutuhkannya. Dalam penggunaannya agak mirip dengan kelas gaya Jawa.
Ini menggunakan fungsi anonim yang dipanggil pada penciptaan, mengembalikan fungsi konstruktor baru. Karena fungsi anonim dipanggil hanya sekali, Anda dapat membuat variabel statis pribadi di dalamnya (mereka berada di dalam penutupan, dapat dilihat oleh anggota kelas lainnya). Fungsi konstruktor pada dasarnya adalah objek Javascript standar - Anda mendefinisikan atribut pribadi di dalamnya, dan atribut publik dilampirkan ke
this
variabel.Pada dasarnya, pendekatan ini menggabungkan pendekatan Crockfordian dengan objek Javascript standar untuk membuat kelas yang lebih kuat.
Anda dapat menggunakannya seperti halnya pada objek Javascript lainnya:
sumber
this
apakah masih perlu dipakainew
?this
tidak digunakan dalamconstructor
fungsi, yang menjadiFoo
dalam contoh.constructor.prototype.myMethod = function () { ... }
.Douglas Crockford membahas topik itu secara luas di The Good Parts . Dia merekomendasikan untuk menghindari operator baru untuk membuat objek baru. Alih-alih ia mengusulkan untuk membuat konstruktor yang disesuaikan. Contohnya:
Dalam Javascript, fungsi adalah objek, dan dapat digunakan untuk membuat objek bersama-sama dengan operator baru . Dengan konvensi, fungsi yang dimaksudkan untuk digunakan sebagai konstruktor dimulai dengan huruf kapital. Anda sering melihat hal-hal seperti:
Jika Anda lupa menggunakan operator baru saat membuat objek baru, apa yang Anda dapatkan adalah panggilan fungsi biasa, dan ini terikat ke objek global, bukan ke objek baru.
sumber
new
divar that = {};
pula.Untuk melanjutkan jawaban bobince
Di ES6 Anda sekarang dapat benar-benar membuat
class
Jadi sekarang Anda bisa melakukannya:
Jadi perluas ke lingkaran (seperti pada jawaban lain) yang dapat Anda lakukan:
Berakhir sedikit lebih bersih di es6 dan sedikit lebih mudah dibaca.
Berikut adalah contoh yang baik dari itu dalam aksi:
Tampilkan cuplikan kode
sumber
Anda juga dapat melakukannya dengan menggunakan struktur:
Kemudian :
sumber
Cara lain adalah http://jsfiddle.net/nnUY4/ (saya tidak tahu apakah penanganan objek semacam ini dan fungsi pengungkapannya mengikuti pola tertentu)
sumber
Ketika seseorang menggunakan trik menutup "ini" selama doa konstruktor, itu untuk menulis fungsi yang dapat digunakan sebagai panggilan balik oleh beberapa objek lain yang tidak ingin memanggil metode pada objek. Ini tidak terkait dengan "membuat ruang lingkup menjadi benar".
Berikut ini adalah objek JavaScript vanilla:
Anda mungkin bisa keluar banyak dari membaca apa yang dikatakan Douglas Crockford tentang JavaScript. John Resig juga brilian. Semoga berhasil!
sumber
this
hubungannya dengan "membuat ruang lingkup yang benar".self=this
meskipun tidak memaksathis
untuk bertahan, dengan mudah memungkinkan pelingkupan "benar" melalui penutupan.Closure
serbaguna. bobince telah meringkas baik prototipe vs pendekatan penutupan saat membuat objek. Namun Anda dapat meniru beberapa aspekOOP
menggunakan penutupan dengan cara pemrograman fungsional. Ingat fungsi adalah objek dalam JavaScript ; jadi gunakan fungsi sebagai objek dengan cara yang berbeda.Berikut adalah contoh penutupannya:
Beberapa waktu yang lalu saya menemukan artikel Mozilla tentang Penutupan. Inilah yang melompat di mata saya: "Penutupan memungkinkan Anda mengaitkan beberapa data (lingkungan) dengan fungsi yang beroperasi pada data itu. Ini memiliki paralel yang jelas dengan pemrograman berorientasi objek, di mana objek memungkinkan kita untuk mengaitkan beberapa data (properti objek ) dengan satu atau lebih metode ". Itu adalah pertama kalinya saya membaca paralelisme antara penutupan dan OOP klasik tanpa referensi prototipe.
Bagaimana?
Misalkan Anda ingin menghitung PPN beberapa item. PPN kemungkinan akan tetap stabil selama masa aplikasi. Salah satu cara untuk melakukannya dalam OOP (kode semu):
Pada dasarnya Anda memasukkan nilai PPN ke konstruktor dan metode penghitungan Anda dapat beroperasi dengannya melalui penutupan . Sekarang alih-alih menggunakan kelas / konstruktor, berikan PPN sebagai argumen ke fungsi. Karena satu-satunya hal yang Anda minati adalah perhitungan itu sendiri, mengembalikan fungsi baru, yang merupakan metode penghitungan:
Dalam proyek Anda, identifikasi nilai-nilai tingkat atas yang merupakan kandidat yang baik untuk perhitungan PPN. Sebagai aturan praktis setiap kali Anda menyampaikan argumen yang sama terus-menerus, ada cara untuk memperbaikinya menggunakan penutupan. Tidak perlu membuat objek tradisional.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
sumber
Membuat objek
Cara termudah untuk membuat objek dalam JavaScript adalah dengan menggunakan sintaks berikut:
Ini berfungsi baik untuk menyimpan data dengan cara yang terstruktur.
Namun, untuk kasus penggunaan yang lebih kompleks, sering kali lebih baik membuat instance fungsi:
Ini memungkinkan Anda untuk membuat beberapa objek yang memiliki "cetak biru" yang sama, mirip dengan cara Anda menggunakan kelas misalnya. Jawa.
Ini masih bisa dilakukan dengan lebih efisien, dengan menggunakan prototipe.
Setiap kali contoh fungsi yang berbeda berbagi metode atau properti yang sama, Anda bisa memindahkannya ke prototipe objek itu. Dengan begitu, setiap instance dari fungsi memiliki akses ke metode atau properti itu, tetapi itu tidak perlu diduplikasi untuk setiap instance.
Dalam kasus kami, masuk akal untuk memindahkan metode
f
ke prototipe:Warisan
Cara sederhana namun efektif untuk melakukan pewarisan dalam JavaScript, adalah dengan menggunakan dua-baris berikut:
Itu mirip dengan melakukan ini:
Perbedaan utama antara keduanya adalah bahwa konstruktor
A
tidak berjalan saat menggunakanObject.create
, yang lebih intuitif dan lebih mirip dengan warisan berbasis kelas.Anda selalu dapat memilih untuk menjalankan konstruktor opsional
A
saat membuat instance baruB
dengan menambahkannya ke konstruktorB
:Jika Anda ingin meneruskan semua argumen
B
untukA
, Anda juga dapat menggunakanFunction.prototype.apply()
:Jika Anda ingin mencampur objek lain ke dalam rantai konstruktor
B
, Anda dapat menggabungkanObject.create
denganObject.assign
:Demo
Catatan
Object.create
dapat digunakan dengan aman di setiap browser modern, termasuk IE9 +.Object.assign
tidak berfungsi di versi IE apa pun atau beberapa peramban seluler. Disarankan untuk mengisi polyfillObject.create
dan / atauObject.assign
jika Anda ingin menggunakannya dan mendukung browser yang tidak mengimplementasikannya.Anda dapat menemukan polyfill untuk di
Object.create
sini dan satu untuk diObject.assign
sini .sumber
sumber
Selain jawaban yang diterima dari 2009. Jika Anda dapat menargetkan browser modern, Anda dapat menggunakan Object.defineProperty .
Untuk melihat daftar kompatibilitas desktop dan seluler, lihat daftar Kompatibilitas Browser Mozilla . Ya, IE9 + mendukungnya serta Safari mobile.
sumber
Anda juga dapat mencoba ini
sumber
Pertama, Anda dapat mengubah preferensi Anda menambahkan metode untuk contoh bukan konstruktor
prototype
objek . Saya hampir selalu mendeklarasikan metode di dalam konstruktor karena saya sering menggunakan Constructor Hijacking untuk keperluan mengenai Warisan & Dekorator.Inilah cara saya memutuskan di mana deklarasi ditulis:
this
)var
deklarasi didahulukan darifunction
deklarasi{}
dan[]
)public
deklarasi didahulukan dariprivate
deklarasiFunction.prototype.bind
lebihthus
,self
,vm
,etc
this
dari dalam Lingkup Leksikal Ruang Penutupan.Inilah sebabnya mengapa ini membantu:
Pembajakan konstruktor Desain Model Pola desainSeperti yang Anda lihat, saya benar-benar tidak perlu
thus
karena saya lebih sukaFunction.prototype.bind
(.call
atau.apply
)thus
. DiSingleton
kelas kami , kami bahkan tidak menyebutkannyathus
karenaINSTANCE
menyampaikan lebih banyak informasi. KarenaModel
, kami kembalithis
sehingga kami dapat memanggil Constructor menggunakan.call
untuk mengembalikan contoh yang kami lewati. Secara berlebihan, kami menugaskannya ke variabelupdated
, meskipun berguna dalam skenario lain.Di samping itu, saya lebih suka membangun objek-literal menggunakan
Lebih disukai Tidak disukainew
kata kunci di atas {kurung}:Seperti yang Anda lihat, yang terakhir tidak memiliki kemampuan untuk menimpa properti "menimpa" Superclass.
Jika saya menambahkan metode ke objek Class
Lebih disukai Tidak disukaiprototype
, saya lebih suka objek literal - dengan atau tanpa menggunakannew
kata kunci:sumber
Saya ingin menyebutkan bahwa kita dapat menggunakan Judul atau String untuk mendeklarasikan Objek.
Ada berbagai cara memanggil masing-masing jenis. Lihat di bawah:
sumber
Pada dasarnya tidak ada konsep kelas di JS jadi kami menggunakan fungsi sebagai konstruktor kelas yang relevan dengan pola desain yang ada.
Sampai sekarang JS tidak memiliki petunjuk bahwa Anda ingin membuat objek jadi inilah kata kunci baru.
Ref: JS profesional untuk pengembang web - Nik Z
sumber
class
di JS, seperti yang Anda sebutkan di pos Anda menggunakanfunction
kata kunci. Ini bukan Pola Desain tetapi fitur bahasa yang disengaja. Saya tidak meremehkan Anda dalam hal ini, tetapi sepertinya orang lain melakukannya karena kesederhanaan dan hampir tidak ada hubungannya dengan pertanyaan. Semoga umpan balik ini membantu.