


Pemahaman mendalam tentang siri JavaScript (17): Pengenalan terperinci kepada pengaturcaraan berorientasikan objek_Pengetahuan asas
Pengenalan
Dalam artikel ini, kami mempertimbangkan pelbagai aspek pengaturcaraan berorientasikan objek dalam ECMAScript (walaupun topik ini telah dibincangkan dalam banyak artikel sebelum ini). Kami akan melihat isu-isu ini lebih daripada perspektif teori. Khususnya, kami akan mempertimbangkan algoritma penciptaan objek, bagaimana objek berkaitan (termasuk hubungan asas - warisan), yang juga boleh digunakan dalam perbincangan (yang saya harap akan menghilangkan beberapa kekaburan konsep sebelumnya tentang OOP dalam JavaScript).
Teks asal bahasa Inggeris:http://dmitrysoshnikov.com/ecmascript/chapter-7-1-oop-general-theory/
Pengenalan, paradigma dan idea
Sebelum menjalankan analisis teknikal OOP dalam ECMAScript, kami perlu menguasai beberapa ciri asas OOP dan menjelaskan konsep utama dalam pengenalan.
ECMAScript menyokong pelbagai kaedah pengaturcaraan termasuk berstruktur, berorientasikan objek, berfungsi, imperatif, dll. Dalam sesetengah kes, ia juga menyokong pengaturcaraan berorientasikan aspek tetapi artikel ini membincangkan pengaturcaraan berorientasikan objek, jadi di sini ialah objek-; pengaturcaraan berorientasikan dalam ECMAScript Definisi:
ECMAScript ialah bahasa pengaturcaraan berorientasikan objek berdasarkan pelaksanaan prototaip.
Terdapat banyak perbezaan antara OOP berasaskan prototaip dan pendekatan berasaskan kelas statik. Mari kita lihat perbezaan mereka secara terperinci.
Berdasarkan atribut kelas dan berdasarkan prototaip
Perhatikan bahawa satu perkara penting telah dinyatakan dalam ayat sebelumnya - sepenuhnya berdasarkan kelas statik. Dengan perkataan "statik" kami memahami objek statik dan kelas statik, ditaip dengan kuat (walaupun tidak diperlukan).
Mengenai situasi ini, banyak dokumen di forum telah menekankan bahawa ini adalah sebab utama mengapa mereka membantah membandingkan "kelas dengan prototaip" dalam JavaScript, walaupun pelaksanaannya berbeza (seperti berdasarkan kelas dinamik) Python dan Ruby) tidak terlalu menentang fokus (beberapa syarat ditulis, walaupun terdapat perbezaan tertentu dalam pemikiran, JavaScript tidak menjadi begitu alternatif), tetapi tumpuan penentangan mereka adalah kelas statik vs. prototaip dinamik ), tepatnya, mekanisme daripada kelas statik (contohnya: C, JAVA) dan subordinatnya serta takrifan kaedah membolehkan kita melihat perbezaan yang tepat antara kelas itu dan pelaksanaan berasaskan prototaip.
Tetapi mari kita senaraikan satu persatu. Mari kita pertimbangkan prinsip umum dan konsep utama paradigma ini.
Berdasarkan kelas statik
Dalam model berasaskan kelas, terdapat konsep kelas dan kejadian. Contoh kelas juga sering dinamakan objek atau kejadian.
Kelas dan Objek
Kelas mewakili abstraksi suatu contoh (iaitu, objek). Ia agak seperti matematik dalam hal ini, tetapi kami memanggilnya jenis atau klasifikasi.
Sebagai contoh (contoh di sini dan di bawah ialah pseudokod):
C = Kelas {a, b, c} // Kelas C, termasuk ciri a, b, c
Ciri-ciri kejadian ialah: sifat (huraian objek) dan kaedah (aktiviti objek). Sifat itu sendiri juga boleh dianggap sebagai objek: iaitu, sama ada sifat boleh ditulis, boleh dikonfigurasikan, boleh ditetapkan (getter/setter), dsb. Oleh itu, keadaan menyimpan objek (iaitu, nilai khusus semua sifat yang diterangkan dalam kelas), dan kelas mentakrifkan struktur tidak berubah (sifat) dan tingkah laku (kaedah) yang tidak boleh diubah secara ketat untuk kejadiannya.
C = Kelas {a, b, c, kaedah1, kaedah2}
c1 = {a: 10, b: 20, c: 30} // Kelas C ialah contoh: objek с1
c2 = {a: 50, b: 60, c: 70} // Kelas C ialah contoh: objek с2, yang mempunyai keadaan sendiri (iaitu, nilai atribut)
Warisan berhierarki
Untuk meningkatkan penggunaan semula kod, kelas boleh dilanjutkan dari satu ke satu sama lain, menambah maklumat tambahan. Mekanisme ini dipanggil warisan (hierarki).
D = Kelas melanjutkan C = {d, e} // {a, b, c, d, e}
d1 = {a: 10, b: 20, c: 30, d: 40, e: 50}
Apabila memanggil kaedah pada contoh kelas, anda biasanya akan mencari kaedah dalam kelas asli Jika tidak ditemui, pergi ke kelas induk langsung untuk mencari Jika belum ditemui, pergi ke kelas induk kelas induk untuk mencari (Contohnya, dalam rantaian warisan yang ketat), jika bahagian atas warisan ditemui tetapi belum ditemui, hasilnya ialah: objek tidak mempunyai kelakuan yang serupa dan tiada cara untuk mendapatkan hasilnya.
d1.method1() // D.method1 (no) -> C.method1 (ya)
d1.method5() // D.method5 (no) -> C.method5 (no) ->
Konsep utama berdasarkan kelas
Oleh itu, kami mempunyai konsep utama berikut:1. Sebelum mencipta objek, kelas mesti diisytiharkan terlebih dahulu, adalah perlu untuk menentukan kelasnya
2. Oleh itu, objek akan dicipta daripada kelas yang diabstraksikan ke dalam "ikonogram dan persamaan" (struktur dan kelakuan)nya sendiri
3. Kaedah diproses melalui rantaian warisan yang ketat, langsung dan tidak berubah
4. Subkelas mengandungi semua atribut dalam rantaian warisan (walaupun beberapa atribut tidak diperlukan oleh subkelas itu);
5. Buat contoh kelas. Kelas tidak boleh (kerana model statik) mengubah ciri (sifat atau kaedah) contohnya;
6. Kejadian (kerana model statik yang ketat) tidak boleh mempunyai gelagat atau atribut tambahan selain daripada yang diisytiharkan dalam kelas yang sepadan dengan contoh.
Mari lihat cara menggantikan model OOP dalam JavaScript, yang kami cadangkan berdasarkan prototaip OOP.
Konsep asas di sini ialah objek boleh ubah dinamik. Transformasi (transformasi lengkap, termasuk bukan sahaja nilai tetapi juga atribut) berkaitan secara langsung dengan bahasa dinamik. Objek seperti berikut boleh menyimpan semua sifatnya (sifat, kaedah) secara bebas tanpa memerlukan kelas.
object.method();
Tambahan pula, kerana dinamik, mereka boleh menukar (menambah, memadam, mengubah suai) ciri mereka dengan mudah:
object.a = 100; // Ubah suai atribut "а"
// Hasilnya ialah: objek: {a: 100, b: 20, d: 40, kaedah: fn, kaedah5: fn};
Iaitu, pada masa tugasan, jika beberapa atribut tidak wujud, buatnya dan mulakan tugasan dengannya, dan jika wujud, kemas kini sahaja.
Prototaip ialah objek yang digunakan sebagai salinan primitif objek lain, atau jika sesetengah objek tidak mempunyai sifat yang diperlukan sendiri, prototaip boleh digunakan sebagai perwakilan untuk objek ini dan berfungsi sebagai objek tambahan .
Berasaskan perwakilan
Sebarang objek boleh digunakan sebagai objek prototaip untuk objek lain, kerana objek boleh menukar prototaipnya secara dinamik pada masa jalan dengan mudah.
Perhatikan bahawa kami sedang mempertimbangkan gambaran keseluruhan dan bukannya pelaksanaan khusus Apabila kami membincangkan pelaksanaan khusus dalam ECMAScript, kami akan melihat beberapa ciri mereka sendiri.Contoh (pseudokod):
y.a; // 40, ciri sendiri
y.c; // 50, juga ciri-cirinya sendiri
y.b; // 20 – diperolehi daripada prototaip: y.b (tidak) -> y.[[Prototaip]].b (ya): 20
padam y.a; // Padam "а"nya sendiri
y.a; // 10 – Dapatkan
daripada prototaip
z = {a: 100, e: 50}
y.[[Prototaip]] = z; // Ubah suai prototaip y kepada z
y.a; // 100 – Dapatkan
daripada prototaip z y.e // 50, juga diperoleh daripada prototaip z
z.q = 200 // Tambah sifat baharu pada prototaip
y.q // Pengubahsuaian juga digunakan pada y
Contoh ini menunjukkan fungsi dan mekanisme penting prototaip sebagai atribut objek tambahan, sama seperti meminta atributnya sendiri Berbanding dengan atributnya sendiri, atribut ini adalah atribut perwakilan. Mekanisme ini dipanggil perwakilan, dan model prototaip berdasarkannya ialah prototaip perwakilan (atau prototaip berasaskan perwakilan). Mekanisme rujukan di sini dipanggil menghantar mesej kepada objek Jika objek tidak mendapat respons, ia akan diwakilkan kepada prototaip untuk mencarinya (memerlukannya untuk cuba membalas mesej tersebut).
Penggunaan semula kod dalam kes ini dipanggil warisan berasaskan perwakilan atau warisan berasaskan prototaip. Memandangkan sebarang objek boleh digunakan sebagai prototaip, ini bermakna prototaip juga boleh mempunyai prototaip sendiri. Prototaip ini dihubungkan bersama untuk membentuk rantai prototaip yang dipanggil. Rantaian juga berhierarki seperti kelas statik, tetapi ia boleh disusun semula dengan mudah untuk menukar hierarki dan struktur.
x = {a: 10}
y = {b: 20}
y.[[Prototaip]] = x
z = {c: 30}
z.[[Prototaip]] = y
z.a // 10
// z.a ditemui dalam rantaian prototaip:
// z.a (tidak) ->
// z.[[Prototaip]].a (tidak) ->
// z.[[Prototaip]].[[Prototaip]].a (ya): 10
Jika objek dan rantai prototaipnya tidak dapat bertindak balas kepada penghantaran mesej, objek boleh mengaktifkan isyarat sistem yang sepadan, mungkin dikendalikan oleh perwakilan lain pada rantai prototaip.
Isyarat sistem ini tersedia dalam banyak pelaksanaan, termasuk sistem berdasarkan kelas dinamik kurungan: #doesNotUnderstand dalam Smalltalk, method_missing dalam Ruby, __call dalam PHP dan __noSuchMethod__ pelaksanaan dalam ECMAScript, dsb.
Contoh (pelaksanaan ECMAScript SpiderMonkey):
objek var = {
//Tangkap isyarat sistem yang tidak dapat membalas mesej
__noSuchMethod__: fungsi (nama, args) {
makluman([nama, args]);
Jika (nama == 'ujian') {
pulangkan kaedah '.test() dikendalikan';
}
Kembalikan perwakilan[nama].apply(this, args);
}
};
var perwakilan = {
segi empat sama: fungsi (a) {
Kembalikan * a;
}
};
alert(object.square(10)); // 100
alert(object.test()); // kaedah .test() dikendalikan
Dalam erti kata lain, jika pelaksanaan berdasarkan kelas statik tidak dapat bertindak balas kepada mesej, kesimpulannya ialah objek semasa tidak mempunyai ciri yang diperlukan, tetapi jika anda cuba mendapatkannya daripada rantai prototaip, anda mungkin masih dapatkan hasilnya , atau objek mempunyai ciri ini selepas beberapa siri perubahan.
Mengenai ECMAScript, pelaksanaan khusus ialah: menggunakan prototaip berasaskan perwakilan. Walau bagaimanapun, seperti yang akan kita lihat dari spesifikasi dan pelaksanaan, mereka juga mempunyai ciri-ciri mereka sendiri.
Model Gabungan
Sejujurnya, adalah perlu untuk mengatakan sesuatu tentang situasi lain (sebaik sahaja ia tidak digunakan dalam ECMASCript): keadaan apabila prototaip menggantikan objek asli daripada objek lain. Penggunaan semula kod dalam kes ini ialah salinan sebenar (klon) objek semasa fasa penciptaan objek dan bukannya perwakilan. Prototaip jenis ini dipanggil prototaip concatenative. Menyalin semua sifat prototaip objek boleh mengubah sepenuhnya sifat dan kaedahnya, dan prototaip juga boleh mengubah dirinya sendiri (dalam model berasaskan perwakilan, perubahan ini tidak akan mengubah kelakuan objek sedia ada, tetapi mengubah sifat prototaipnya) . Kelebihan kaedah ini ialah ia dapat mengurangkan masa penjadualan dan delegasi, tetapi kelemahannya ialah penggunaan memori adalah tinggi.
Jenis Itik
Memulangkan objek yang menukar jenis lemah secara dinamik Berbanding dengan model berdasarkan kelas statik, menguji sama ada ia boleh melakukan perkara ini tiada kaitan dengan jenis (kelas) objek, tetapi sama ada ia boleh bertindak balas kepada mesej (yang. ialah, selepas menyemak sama ada Keupayaan untuk melakukannya adalah satu kemestian).
Contohnya:
// Dalam model berasaskan statik
if (objek instanceof SomeClass) {
// Beberapa tindakan dijalankan
}
// Dalam pelaksanaan dinamik
// Tidak kira jenis objek pada ketika ini
// Kerana mutasi, jenis dan sifat boleh diubah secara bebas dan berulang kali.
// Sama ada objek penting boleh membalas mesej ujian
if (isFunction(object.test)) // ECMAScript
if object.respond_to?(:test) // Ruby
if hasattr(objek, 'test'): // Python
Inilah yang dipanggil jenis Dok. Iaitu, objek boleh dikenal pasti dengan ciri-ciri mereka sendiri apabila menyemak, bukannya kedudukan objek dalam hierarki atau kepunyaan mereka kepada mana-mana jenis tertentu.
Konsep utama berdasarkan prototaip
Mari kita lihat ciri utama pendekatan ini:
1. Konsep asas ialah objek
2. Objek benar-benar dinamik dan berubah-ubah (secara teorinya ia boleh ditukar dari satu jenis ke jenis yang lain)
3. Objek tidak mempunyai kelas ketat yang menerangkan struktur dan tingkah lakunya sendiri. Objek tidak memerlukan kelas
4. Objek tidak mempunyai kelas tetapi boleh mempunyai prototaip Jika mereka tidak dapat membalas mesej, mereka boleh diwakilkan kepada prototaip
5. Prototaip objek boleh ditukar pada bila-bila masa semasa runtime;
6. Dalam model berasaskan perwakilan, mengubah ciri prototaip akan menjejaskan semua objek yang berkaitan dengan prototaip;
7. Dalam model prototaip gabungan, prototaip ialah salinan asal yang diklon daripada objek lain, dan seterusnya menjadi salinan asal yang bebas sepenuhnya Perubahan ciri prototaip tidak akan menjejaskan objek yang diklon daripadanya
8. Jika mesej tidak dapat dibalas, pemanggilnya boleh mengambil langkah tambahan (cth., menukar penjadualan)
9. Kegagalan objek tidak boleh ditentukan oleh tahap mereka dan kelas mana mereka tergolong, tetapi oleh ciri semasa
Walau bagaimanapun, terdapat model lain yang juga harus kita pertimbangkan.
Berdasarkan kelas dinamik
Kami percaya bahawa perbezaan "prototaip kelas VS" yang ditunjukkan dalam contoh di atas tidak begitu penting dalam model ini berdasarkan kelas dinamik, (terutamanya jika rantaian prototaip tidak boleh diubah, untuk perbezaan yang lebih tepat, ia masih perlu pertimbangkan kelas statik). Sebagai contoh, ia juga boleh menggunakan Python atau Ruby (atau bahasa lain yang serupa). Bahasa-bahasa ini semuanya menggunakan paradigma berasaskan kelas dinamik. Walau bagaimanapun, dalam beberapa aspek kita boleh melihat beberapa fungsi dilaksanakan berdasarkan prototaip.
Dalam contoh berikut, kita dapat melihat bahawa hanya berdasarkan delegasi, kita boleh membesarkan kelas (prototaip), dengan itu mempengaruhi semua objek yang berkaitan dengan kelas ini Kita juga boleh menukar objek ini secara dinamik pada masa runtime objek untuk perwakilan) dan sebagainya.
#Python
kelas A(objek):
Def __init__(self, a):
diri.a = a
Def persegi(diri):
kembalikan diri.a * diri.a
a = A(10) # Cipta contoh
cetak(a.a) # 10
A.b = 20 # Sediakan atribut baharu untuk kelas
print(a.b) # 20 –
boleh diakses dalam contoh "a"
a.b = 30 # Cipta atribut a sendiri
cetak(a.b) # 30
del a.b # Padamkan atributnya sendiri
print(a.b) # 20 - Dapatkan (prototaip) dari kelas sekali lagi
# Sama seperti model berasaskan prototaip
# Anda boleh menukar prototaip objek pada masa jalan
kelas B(objek): # Kosongkan kelas B
Lulus
b = B() # Contoh B
b.__class__ = A # Tukar kelas (prototaip) secara dinamik
b.a = 10 # Cipta atribut baharu
print(b.square()) # 100 - Kaedah Kelas A tersedia pada masa ini
# Anda boleh memaparkan rujukan kepada kelas yang dipadam
del A
del B
# Tetapi objek masih mempunyai rujukan tersirat, dan kaedah ini masih tersedia
print(b.square()) # 100
# Tetapi kelas tidak boleh ditukar pada masa ini
# Ini ialah ciri yang dilaksanakan
b.__class__ = dict # error
Pelaksanaan dalam Ruby adalah serupa: kelas dinamik sepenuhnya juga digunakan (dengan cara dalam versi Python semasa, berbeza dengan Ruby dan ECMAScript, pembesaran kelas (prototaip) tidak berfungsi), kita boleh menukar objek sepenuhnya (atau kelas) ciri (menambah kaedah/sifat pada kelas, dan perubahan ini akan menjejaskan objek sedia ada), namun, ia tidak boleh menukar kelas objek secara dinamik.
Walau bagaimanapun, artikel ini tidak khusus mengenai Python dan Ruby, jadi kami tidak akan bercakap lebih lanjut dan mari terus membincangkan ECMAScript itu sendiri.
Tetapi sebelum itu, kita perlu melihat sekali lagi tentang "gula sintaksis" yang terdapat dalam beberapa OOP, kerana banyak artikel terdahulu tentang JavaScript sering merangkumi isu ini.
Satu-satunya ayat yang salah untuk diperhatikan dalam bahagian ini ialah: "JavaScript bukan kelas, ia mempunyai prototaip, yang boleh menggantikan kelas." Adalah penting untuk mengetahui bahawa tidak semua pelaksanaan berasaskan kelas adalah sama sekali berbeza Walaupun kita mungkin mengatakan "JavaScript adalah berbeza", ia juga perlu mempertimbangkan bahawa (sebagai tambahan kepada konsep "kelas") terdapat ciri lain yang berkaitan. .
Ciri lain pelbagai pelaksanaan OOP
Dalam bahagian ini kami memperkenalkan secara ringkas ciri dan kaedah penggunaan semula kod lain dalam pelbagai pelaksanaan OOP, termasuk pelaksanaan OOP dalam ECMAScript. Sebabnya ialah terdapat beberapa sekatan pemikiran lazim pada pelaksanaan OOP dalam JavaScript Satu-satunya keperluan utama ialah ia harus dibuktikan secara teknikal dan ideologi. Ia tidak boleh dikatakan bahawa kami tidak menemui fungsi gula sintaksis dalam pelaksanaan OOP lain, dan kami dengan tergesa-gesa menganggap bahawa JavaScript bukan bahasa OOP tulen Ini adalah salah.
Polimorfik
Objek mempunyai beberapa makna polimorfisme dalam ECMAScript.
Sebagai contoh, fungsi boleh digunakan pada objek yang berbeza, sama seperti sifat objek asli (kerana nilai ditentukan apabila memasuki konteks pelaksanaan):
ujian fungsi() {
makluman([ini.a, ini.b]);
}
test.call({a: 10, b: 20}); // 10, 20
test.call({a: 100, b: 200}); // 100, 200
var a = 1;
var b = 2;
ujian(); // 1, 2
Walau bagaimanapun, terdapat pengecualian: kaedah Date.prototype.getTime(), yang mengikut piawaian harus sentiasa mempunyai objek tarikh, jika tidak pengecualian akan dilemparkan.
alert(Date.prototype.getTime.call(new Date())); // masa
alert(Date.prototype.getTime.call(new String(''))); // TypeError
Apa yang dipanggil polimorfisme parameter apabila mentakrifkan fungsi adalah setara dengan semua jenis data, kecuali ia menerima parameter polimorfik (seperti kaedah pengisihan .sort tatasusunan dan parameternya - fungsi pengisihan polimorfik). Dengan cara ini, contoh di atas juga boleh dianggap sejenis polimorfisme parametrik.
Kaedah dalam prototaip boleh ditakrifkan sebagai kosong dan semua objek yang dicipta harus mentakrifkan semula (melaksanakan) kaedah ini (iaitu "satu antara muka (tandatangan), berbilang pelaksanaan").
Polymorphism berkaitan dengan jenis Itik yang kami nyatakan di atas: iaitu jenis dan kedudukan objek dalam hierarki tidak begitu penting, tetapi jika ia mempunyai semua ciri yang diperlukan, ia boleh diterima dengan mudah (iaitu antara muka biasa adalah penting , pelaksanaan boleh menjadi pelbagai).
Ekapsulasi
Selalunya terdapat salah tanggapan tentang pengkapsulan. Dalam bahagian ini kita membincangkan beberapa gula sintaksis dalam pelaksanaan OOP - juga dikenali sebagai pengubah: Dalam kes ini, kita akan membincangkan beberapa kemudahan "gula" dalam pelaksanaan OOP - pengubah yang terkenal: peribadi, dilindungi dan awam ( Atau dikenali sebagai akses objek tahap atau pengubah suai akses).
Di sini saya ingin mengingatkan anda tentang tujuan utama enkapsulasi: enkapsulasi ialah penambahan abstrak, bukan "penggodam berniat jahat" tersembunyi yang menulis sesuatu terus ke dalam kelas anda.
Ini adalah kesilapan besar: gunakan sorok demi menyembunyikan.
Tahap akses (peribadi, dilindungi dan awam) telah dilaksanakan dalam banyak program berorientasikan objek untuk memudahkan pengaturcaraan (gula sintaks yang sangat mudah), menerangkan dan membina sistem dengan lebih abstrak.
Ini boleh dilihat dalam beberapa pelaksanaan (seperti Python dan Ruby yang telah disebutkan). Di satu pihak (dalam Python), atribut __private_protected ini (dinamakan melalui konvensyen garis bawah) tidak boleh diakses dari luar. Python, sebaliknya, boleh diakses dari luar dengan peraturan khas (_ClassName__field_name).
kelas A(objek):
Def __init__(self):
self.public = 10
diri.__swasta = 20
Def get_private(self):
pulangkan sendiri.__peribadi
# di luar:
a = A() # Contoh A
print(a.public) # OK, 30
print(a.get_private()) # OK, 20
print(a.__private) # Gagal kerana ia hanya boleh digunakan dalam A
# Tetapi dalam Python, ia boleh diakses melalui peraturan khas
print(a._A__private) # OK, 20
Dalam Ruby: Di satu pihak, ia mempunyai keupayaan untuk menentukan ciri peribadi dan dilindungi Sebaliknya, terdapat juga kaedah khas (seperti instance_variable_get, instance_variable_set, send, dll.) untuk mendapatkan data terkapsul.
kelas A
def mulakan
@a = 10
tamat
def public_method
kaedah_peribadi(20)
tamat
persendirian
def private_method(b)
Kembalikan @a b
tamat
tamat
a = A.baru # Contoh baharu
a.kaedah_awam # OK, 30
a.a # Kegagalan, @a - ialah pembolehubah contoh peribadi
# "private_method" adalah peribadi dan hanya boleh diakses dalam kelas A
a.kaedah_pribadi # Ralat
# Tetapi ada nama kaedah metadata khas yang boleh mendapatkan data
a.send(:private_method, 20) # OK, 30
a.instance_variable_get(:@a) # OK, 10
Sebab utamanya ialah pengaturcara sendiri ingin mendapatkan data yang dikapsulkan (perhatikan bahawa saya secara khusus tidak menggunakan data "tersembunyi"). Jika data ini berubah secara tidak betul dalam beberapa cara atau mempunyai sebarang ralat, tanggungjawab penuh terletak pada pengaturcara, tetapi bukan hanya "ralat menaip" atau "hanya menukar beberapa medan". Tetapi jika ini berlaku dengan kerap, ia adalah tabiat dan gaya pengaturcaraan yang sangat buruk, kerana ia biasanya bernilai menggunakan API awam untuk "bercakap" dengan objek.
Untuk mengulangi, tujuan asas enkapsulasi adalah untuk mengabstrak pengguna data tambahan, bukan untuk menghalang penggodam daripada menyembunyikan data. Lebih serius, enkapsulasi tidak menggunakan persendirian untuk mengubah suai data untuk mencapai keselamatan perisian.
Mengenkapsulkan objek tambahan (sebahagian) Kami menggunakan kos minimum, penyetempatan dan perubahan ramalan untuk memberikan kebolehlaksanaan untuk perubahan tingkah laku dalam antara muka awam.
Selain itu, tujuan penting kaedah penetap adalah untuk mengabstraksi pengiraan kompleks. Sebagai contoh, penetap element.innerHTML - pernyataan abstrak - "HTML di dalam elemen ini sekarang ialah kandungan berikut", dan fungsi penetap dalam sifat innerHTML akan menjadi sukar untuk dikira dan diperiksa. Dalam kes ini, masalah kebanyakannya melibatkan abstraksi, tetapi enkapsulasi juga berlaku.
Konsep enkapsulasi bukan sahaja berkaitan dengan OOP. Sebagai contoh, ia boleh menjadi fungsi mudah yang hanya merangkum pelbagai pengiraan, menjadikannya abstrak (tidak perlu untuk pengguna mengetahui, sebagai contoh, bagaimana fungsi Math.round(...) dilaksanakan, pengguna hanya memanggil ia). Ia adalah sejenis enkapsulasi. Perhatikan bahawa saya tidak mengatakan ia adalah "peribadi, dilindungi dan awam".
Versi semasa spesifikasi ECMAScript tidak mentakrifkan pengubah suai peribadi, dilindungi dan awam.
Walau bagaimanapun, dalam amalan adalah mungkin untuk melihat sesuatu yang dinamakan "Mock JS Encapsulation". Secara amnya konteks ini bertujuan untuk digunakan (sebagai peraturan, pembina itu sendiri). Malangnya, "mimikri" ini sering dilaksanakan dan pengaturcara boleh menghasilkan tetapan entiti pseudo-benar-benar bukan abstrak "kaedah getter/setter" (saya ulangi, ia adalah salah):
fungsi A() {
var _a; // "peribadi" a
this.getA = fungsi _getA() {
Kembali _a;
};
this.setA = fungsi _setA(a) {
_a = a;
};
}
var a = new A();
a.setA(10);
alert(a._a); // undefined, "pribadi"
makluman(a.getA()); // 10
Jadi, semua orang faham bahawa untuk setiap objek yang dicipta, kaedah getA/setA juga dicipta, yang juga merupakan sebab peningkatan dalam ingatan (berbanding dengan definisi prototaip). Walaupun, secara teori objek boleh dioptimumkan dalam kes pertama.
Selain itu, beberapa artikel JavaScript sering menyebut konsep "kaedah peribadi".
Walau bagaimanapun, dalam beberapa kes ia boleh dibuat dalam pembina, kerana JS ialah bahasa ideologi - objek boleh berubah sepenuhnya dan mempunyai ciri unik (di bawah keadaan tertentu dalam pembina, sesetengah objek boleh mendapatkan kaedah tambahan manakala yang lain tidak ).
Selain itu, dalam JavaScript, jika enkapsulasi masih disalahtafsirkan sebagai pemahaman yang menghalang penggodam berniat jahat daripada menulis nilai tertentu secara automatik dan bukannya menggunakan kaedah penetap, maka apa yang dipanggil "tersembunyi" dan " "peribadi" sebenarnya tidak terlalu "tersembunyi", sesetengah pelaksanaan boleh mendapatkan nilai pada rantai skop yang berkaitan (dan sepadan dengan semua objek pembolehubah) dengan memanggil konteks ke fungsi eval (boleh diuji pada SpiderMonkey1.7).
eval('_a = 100', a.getA); // atau a.setA, kerana dua kaedah "_a" berada pada [[Skop]]
a.getA(); // 100
Sebagai alternatif, pelaksanaan membenarkan akses terus kepada objek aktif (seperti Rhino), dan nilai pembolehubah dalaman boleh diubah dengan mengakses sifat objek yang sepadan:
// Badak
var foo = (fungsi () {
var x = 10; // "peribadi"
kembalikan fungsi () {
Cetak(x);
};
})();
foo(); // 10
foo.__ibu bapa__.x = 20;
foo(); // 20
Kadangkala data "peribadi" dan "dilindungi" dicapai dalam JavaScript dengan memberi awalan pembolehubah dengan garis bawah (tetapi berbanding Python, ini hanyalah konvensyen penamaan):
var _myPrivateData = 'testString';
Ia sering digunakan untuk menyertakan konteks pelaksanaan dalam kurungan, tetapi untuk data tambahan sebenar, ia tidak berkaitan secara langsung dengan objek, tetapi hanya mudah untuk mengabstrakkan daripada API luaran:
(fungsi () {
// Mulakan konteks
})();
Warisan berbilang
Pewarisan berbilang ialah gula sintaksis yang sangat mudah untuk meningkatkan penggunaan semula kod (jika kita boleh mewarisi satu kelas pada satu masa, mengapa kita tidak boleh mewarisi 10 pada satu masa?). Walau bagaimanapun, disebabkan oleh beberapa kekurangan warisan berbilang, ia tidak menjadi popular dalam pelaksanaan.
ECMAScript tidak menyokong berbilang warisan (iaitu hanya satu objek boleh digunakan sebagai prototaip langsung), walaupun bahasa pengaturcaraan nenek moyangnya mempunyai keupayaan sedemikian. Tetapi dalam beberapa pelaksanaan (seperti SpiderMonkey) menggunakan __noSuchMethod__ boleh digunakan untuk mengurus penjadualan dan perwakilan dan bukannya rantai prototaip.
Bancuhan
Mixin ialah cara yang mudah untuk menggunakan semula kod. Mixin telah dicadangkan sebagai alternatif kepada pelbagai warisan. Setiap elemen individu ini boleh dicampur dengan mana-mana objek untuk melanjutkan fungsinya (jadi objek juga boleh dicampur dengan berbilang Mixin). Spesifikasi ECMA-262-3 tidak mentakrifkan konsep "Mixins", tetapi mengikut takrifan Mixins dan ECMAScript mempunyai objek boleh ubah secara dinamik, tiada halangan untuk hanya memanjangkan ciri menggunakan Mixins.
Contoh biasa:
// pembantu untuk pembesaran
Object.extend = fungsi (destinasi, sumber) {
untuk (harta dalam sumber) jika (sumber.hasOwnProperty(harta)) {
destinasi[harta] = sumber[harta];
}
Destinasi pulang;
};
var X = {a: 10, b: 20};
var Y = {c: 30, d: 40};
Object.extend(X, Y); // campurkan Y menjadi X
makluman([X.a, X.b, X.c, X.d]); 10, 20, 30, 40
Sila ambil perhatian bahawa saya menggunakan takrifan ini ("mixin", "mix") dalam tanda petikan yang disebut dalam ECMA-262-3 Tiada konsep sedemikian dalam spesifikasi, dan ia bukan campuran tetapi biasa digunakan untuk memanjangkan objek dengan ciri-ciri baharu. (Konsep mixin dalam Ruby ditakrifkan secara rasmi. Mixins mencipta rujukan kepada modul yang mengandungi dan bukannya menyalin semua sifat modul ke modul lain - sebenarnya: mencipta objek tambahan (prototaip) untuk perwakilan. ).
Sifat
Sifat adalah serupa dalam konsep kepada campuran, tetapi ia mempunyai banyak fungsi (mengikut definisi, kerana campuran boleh digunakan, ia tidak boleh mengandungi keadaan, kerana ia boleh menyebabkan konflik penamaan). Menurut ECMAScript, Traits dan mixin mengikut prinsip yang sama, jadi spesifikasi tidak mentakrifkan konsep "Traits".
Antara Muka
Antara muka yang dilaksanakan dalam beberapa OOP adalah serupa dengan campuran dan sifat. Walau bagaimanapun, berbeza dengan campuran dan sifat, antara muka memaksa kelas pelaksana untuk melaksanakan gelagat tandatangan kaedah mereka.
Antara muka boleh dianggap sepenuhnya sebagai kelas abstrak. Walau bagaimanapun, berbanding dengan kelas abstrak (kaedah dalam kelas abstrak hanya boleh melaksanakan sebahagian daripada kaedah, dan bahagian lain masih ditakrifkan sebagai tandatangan), warisan hanya boleh mewarisi kelas asas tunggal, tetapi boleh mewarisi berbilang antara muka. antara muka (pelbagai Campuran) boleh dilihat sebagai alternatif kepada pewarisan berbilang.
Piawaian ECMA-262-3 tidak mentakrifkan konsep "antara muka" mahupun konsep "kelas abstrak". Walau bagaimanapun, sebagai tiruan, adalah mungkin untuk melaksanakan objek dengan kaedah "kosong" (atau pengecualian yang dilemparkan dalam kaedah kosong untuk memberitahu pembangun bahawa kaedah ini perlu dilaksanakan).
Kombinasi objek
Komposisi objek juga merupakan salah satu teknologi penggunaan semula kod dinamik. Komposisi objek berbeza daripada warisan yang sangat fleksibel kerana ia melaksanakan perwakilan yang boleh berubah secara dinamik. Dan ini juga berdasarkan asas prototaip yang ditugaskan. Selain prototaip boleh ubah secara dinamik, objek boleh mengagregat objek untuk perwakilan (mencipta gabungan sebagai hasil - pengagregatan) dan seterusnya menghantar mesej kepada objek yang mewakilkan kepada perwakilan. Ini boleh dilakukan dengan lebih daripada dua perwakilan, kerana sifat dinamiknya bermakna ia boleh berubah semasa masa jalan.
Contoh __noSuchMethod__ yang telah disebutkan melakukan ini, tetapi mari kita tunjukkan juga cara menggunakan perwakilan secara eksplisit:
Contohnya:
var _delegate = {
foo: fungsi () {
alert('_delegate.foo');
}
};
var agregat = {
perwakilan: _delegate,
foo: fungsi () {
Kembalikan this.delegate.foo.call(this);
}
};
aggregate.foo(); // delegate.foo
aggregate.delegate = {
foo: fungsi () {
alert('foo daripada perwakilan baharu');
}
};
aggregate.foo(); // foo daripada wakil baharu
Hubungan objek ini dipanggil "mempunyai-a", dan penyepaduan ialah hubungan "adalah-a".
Disebabkan kekurangan komposisi eksplisit (fleksibiliti berbanding warisan), menambah kod perantaraan juga tidak mengapa.
Ciri AOP
Sebagai fungsi berorientasikan aspek, anda boleh menggunakan penghias fungsi. Spesifikasi ECMA-262-3 tidak mentakrifkan dengan jelas konsep "penghias fungsi" (berbanding dengan Python, di mana istilah ini ditakrifkan secara rasmi). Walau bagaimanapun, fungsi dengan parameter berfungsi boleh dihiasi dan diaktifkan dalam aspek tertentu (dengan menggunakan apa yang dipanggil cadangan):
Contoh penghias paling mudah:
function checkDecorator(originalFunction) {
kembalikan fungsi () {
Jika (fooBar != 'ujian') {
alert('parameter salah');
Kembalikan palsu;
}
Kembalikan originalFunction();
};
}
ujian fungsi() {
alert('fungsi ujian');
}
var testWithCheck = checkDecorator(ujian);
var fooBar = palsu;
test(); // 'fungsi ujian'
testWithCheck(); // 'parameter salah'
fooBar = 'ujian';
test(); // 'fungsi ujian'
testWithCheck(); // 'fungsi ujian'
Kesimpulan
Dalam artikel ini, kami telah menjelaskan pengenalan OOP (saya harap maklumat ini berguna kepada anda), dan dalam bab seterusnya kami akan meneruskan pelaksanaan ECMAScript untuk pengaturcaraan berorientasikan objek.

Memahami bagaimana enjin JavaScript berfungsi secara dalaman adalah penting kepada pemaju kerana ia membantu menulis kod yang lebih cekap dan memahami kesesakan prestasi dan strategi pengoptimuman. 1) aliran kerja enjin termasuk tiga peringkat: parsing, penyusun dan pelaksanaan; 2) Semasa proses pelaksanaan, enjin akan melakukan pengoptimuman dinamik, seperti cache dalam talian dan kelas tersembunyi; 3) Amalan terbaik termasuk mengelakkan pembolehubah global, mengoptimumkan gelung, menggunakan const dan membiarkan, dan mengelakkan penggunaan penutupan yang berlebihan.

Python lebih sesuai untuk pemula, dengan lengkung pembelajaran yang lancar dan sintaks ringkas; JavaScript sesuai untuk pembangunan front-end, dengan lengkung pembelajaran yang curam dan sintaks yang fleksibel. 1. Sintaks Python adalah intuitif dan sesuai untuk sains data dan pembangunan back-end. 2. JavaScript adalah fleksibel dan digunakan secara meluas dalam pengaturcaraan depan dan pelayan.

Python dan JavaScript mempunyai kelebihan dan kekurangan mereka sendiri dari segi komuniti, perpustakaan dan sumber. 1) Komuniti Python mesra dan sesuai untuk pemula, tetapi sumber pembangunan depan tidak kaya dengan JavaScript. 2) Python berkuasa dalam bidang sains data dan perpustakaan pembelajaran mesin, sementara JavaScript lebih baik dalam perpustakaan pembangunan dan kerangka pembangunan depan. 3) Kedua -duanya mempunyai sumber pembelajaran yang kaya, tetapi Python sesuai untuk memulakan dengan dokumen rasmi, sementara JavaScript lebih baik dengan MDNWebDocs. Pilihan harus berdasarkan keperluan projek dan kepentingan peribadi.

Peralihan dari C/C ke JavaScript memerlukan menyesuaikan diri dengan menaip dinamik, pengumpulan sampah dan pengaturcaraan asynchronous. 1) C/C adalah bahasa yang ditaip secara statik yang memerlukan pengurusan memori manual, manakala JavaScript ditaip secara dinamik dan pengumpulan sampah diproses secara automatik. 2) C/C perlu dikumpulkan ke dalam kod mesin, manakala JavaScript adalah bahasa yang ditafsirkan. 3) JavaScript memperkenalkan konsep seperti penutupan, rantaian prototaip dan janji, yang meningkatkan keupayaan pengaturcaraan fleksibiliti dan asynchronous.

Enjin JavaScript yang berbeza mempunyai kesan yang berbeza apabila menguraikan dan melaksanakan kod JavaScript, kerana prinsip pelaksanaan dan strategi pengoptimuman setiap enjin berbeza. 1. Analisis leksikal: Menukar kod sumber ke dalam unit leksikal. 2. Analisis Tatabahasa: Menjana pokok sintaks abstrak. 3. Pengoptimuman dan Penyusunan: Menjana kod mesin melalui pengkompil JIT. 4. Jalankan: Jalankan kod mesin. Enjin V8 mengoptimumkan melalui kompilasi segera dan kelas tersembunyi, Spidermonkey menggunakan sistem kesimpulan jenis, menghasilkan prestasi prestasi yang berbeza pada kod yang sama.

Aplikasi JavaScript di dunia nyata termasuk pengaturcaraan sisi pelayan, pembangunan aplikasi mudah alih dan Internet of Things Control: 1. Pengaturcaraan sisi pelayan direalisasikan melalui node.js, sesuai untuk pemprosesan permintaan serentak yang tinggi. 2. Pembangunan aplikasi mudah alih dijalankan melalui reaktnatif dan menyokong penggunaan silang platform. 3. Digunakan untuk kawalan peranti IoT melalui Perpustakaan Johnny-Five, sesuai untuk interaksi perkakasan.

Saya membina aplikasi SaaS multi-penyewa berfungsi (aplikasi edTech) dengan alat teknologi harian anda dan anda boleh melakukan perkara yang sama. Pertama, apakah aplikasi SaaS multi-penyewa? Aplikasi SaaS Multi-penyewa membolehkan anda melayani beberapa pelanggan dari Sing

Artikel ini menunjukkan integrasi frontend dengan backend yang dijamin oleh permit, membina aplikasi edtech SaaS yang berfungsi menggunakan Next.Js. Frontend mengambil kebenaran pengguna untuk mengawal penglihatan UI dan memastikan permintaan API mematuhi dasar peranan


Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

Pelayar Peperiksaan Selamat
Pelayar Peperiksaan Selamat ialah persekitaran pelayar selamat untuk mengambil peperiksaan dalam talian dengan selamat. Perisian ini menukar mana-mana komputer menjadi stesen kerja yang selamat. Ia mengawal akses kepada mana-mana utiliti dan menghalang pelajar daripada menggunakan sumber yang tidak dibenarkan.

Versi Mac WebStorm
Alat pembangunan JavaScript yang berguna

mPDF
mPDF ialah perpustakaan PHP yang boleh menjana fail PDF daripada HTML yang dikodkan UTF-8. Pengarang asal, Ian Back, menulis mPDF untuk mengeluarkan fail PDF "dengan cepat" dari tapak webnya dan mengendalikan bahasa yang berbeza. Ia lebih perlahan dan menghasilkan fail yang lebih besar apabila menggunakan fon Unicode daripada skrip asal seperti HTML2FPDF, tetapi menyokong gaya CSS dsb. dan mempunyai banyak peningkatan. Menyokong hampir semua bahasa, termasuk RTL (Arab dan Ibrani) dan CJK (Cina, Jepun dan Korea). Menyokong elemen peringkat blok bersarang (seperti P, DIV),