The API Halaman Lingkup Referensi mengatakan:
Lingkup dapat mewarisi dari lingkup induk.
The Pengembang Halaman Lingkup Panduan mengatakan:
Lingkup (prototipikal) mewarisi properti dari lingkup induknya.
- Jadi, apakah ruang lingkup anak selalu mewarisi secara prototipik dari ruang lingkup orang tuanya?
- Apakah ada pengecualian?
- Ketika memang mewarisi, apakah itu selalu normal warisan prototypal JavaScript?
javascript
angularjs
inheritance
prototype
prototypal-inheritance
Mark Rajcok
sumber
sumber
Jawaban:
Jawaban cepat :
Lingkup anak biasanya secara bawaan mewarisi dari lingkup induknya, tetapi tidak selalu. Satu pengecualian untuk aturan ini adalah direktif dengan
scope: { ... }
- ini menciptakan ruang lingkup "mengisolasi" yang tidak mewarisi secara prototipe. Konstruk ini sering digunakan ketika membuat arahan "komponen yang dapat digunakan kembali".Sedangkan untuk nuansa, pewarisan lingkup biasanya langsung ... sampai Anda membutuhkan pengikatan data 2 arah (yaitu, elemen bentuk, model-ng) di lingkup anak. Ng-repeat, ng-switch, dan ng-include dapat membuat Anda tersandung jika Anda mencoba untuk mengikat ke primitif (misalnya, angka, string, boolean) dalam lingkup induk dari dalam lingkup anak. Ini tidak berfungsi seperti yang diharapkan kebanyakan orang. Ruang lingkup anak mendapatkan properti sendiri yang menyembunyikan / membayangi properti induk dengan nama yang sama. Solusi Anda adalah
New AngularJS pengembang sering tidak menyadari bahwa
ng-repeat
,ng-switch
,ng-view
,ng-include
danng-if
semua membuat lingkup anak yang baru, sehingga masalah sering muncul ketika arahan ini terlibat. (Lihat contoh ini untuk ilustrasi singkat masalah.)Masalah dengan primitif ini dapat dengan mudah dihindari dengan mengikuti "praktik terbaik" untuk selalu memiliki '.' di ng-model Anda - tonton 3 menit. Misko menunjukkan masalah mengikat primitif dengan
ng-switch
.Memiliki sebuah '.' dalam model Anda akan memastikan bahwa warisan prototypal sedang dimainkan. Jadi, gunakan
Jawaban panjang :
Warisan Prototip JavaScript
Juga ditempatkan di wiki AngularJS: https://github.com/angular/angular.js/wiki/Understanding-Scopes
Penting untuk terlebih dahulu memiliki pemahaman yang kuat tentang pewarisan prototypal, terutama jika Anda berasal dari latar belakang sisi server dan Anda lebih akrab dengan warisan klasik. Jadi mari kita tinjau dulu itu.
Misalkan parentScope memiliki properti aString, aNumber, anArray, anObject, dan aFunction. Jika childScope mewarisi secara prototipe dari parentScope, kami memiliki:
(Perhatikan bahwa untuk menghemat ruang, saya menunjukkan
anArray
objek sebagai objek biru tunggal dengan tiga nilainya, daripada objek biru tunggal dengan tiga literal abu-abu yang terpisah.)Jika kami mencoba mengakses properti yang ditentukan di parentScope dari cakupan anak, JavaScript pertama-tama akan melihat dalam lingkup anak, tidak menemukan properti, kemudian melihat dalam lingkup yang diwarisi, dan menemukan properti. (Jika tidak menemukan properti di parentScope, itu akan melanjutkan rantai prototipe ... sampai ke lingkup root). Jadi, ini semua benar:
Misalkan kita melakukan ini:
Rantai prototipe tidak dikonsultasikan, dan properti aString baru ditambahkan ke childScope. Properti baru ini menyembunyikan / membayangi properti parentScope dengan nama yang sama. Ini akan menjadi sangat penting ketika kita membahas ng-repeat dan ng-include di bawah ini.
Misalkan kita melakukan ini:
Rantai prototipe dikonsultasikan karena objek (anArray dan anObject) tidak ditemukan di childScope. Objek ditemukan di parentScope, dan nilai properti diperbarui pada objek asli. Tidak ada properti baru yang ditambahkan ke childScope; tidak ada objek baru yang dibuat. (Perhatikan bahwa dalam array dan fungsi JavaScript juga objek.)
Misalkan kita melakukan ini:
Rantai prototipe tidak dikonsultasikan, dan ruang lingkup anak mendapat dua properti objek baru yang menyembunyikan / membayangi properti objek parentScope dengan nama yang sama.
Takeaways:
Satu skenario terakhir:
Kami menghapus properti childScope terlebih dahulu, kemudian ketika kami mencoba mengakses properti lagi, rantai prototipe dikonsultasikan.
Warisan Lingkup Sudut
Peserta:
scope: true
, direktif dengantransclude: true
.scope: { ... }
. Ini menciptakan ruang lingkup "mengisolasi".Catatan, secara default, arahan tidak membuat ruang lingkup baru - yaitu, defaultnya adalah
scope: false
.ng-sertakan
Misalkan kita ada di controller kita:
Dan dalam HTML kami:
Setiap ng-include menghasilkan ruang lingkup anak baru, yang secara prototipe mewarisi dari ruang lingkup orang tua.
Mengetik (katakanlah, "77") ke dalam kotak teks input pertama menyebabkan lingkup anak untuk mendapatkan
myPrimitive
properti lingkup baru yang menyembunyikan / bayangan properti lingkup induk dengan nama yang sama. Ini mungkin bukan yang Anda inginkan / harapkan.Mengetik (katakanlah, "99") ke kotak teks input kedua tidak menghasilkan properti anak baru. Karena tpl2.html mengikat model ke properti objek, pewarisan prototipal muncul ketika ngModel mencari objek myObject - ia menemukannya dalam lingkup induk.
Kita dapat menulis ulang templat pertama yang menggunakan $ parent, jika kita tidak ingin mengubah model kita dari yang primitif ke objek:
Mengetik (misalnya, "22") ke dalam kotak teks input ini tidak menghasilkan properti anak baru. Model sekarang terikat ke properti dari lingkup induk (karena $ parent adalah properti lingkup anak yang mereferensikan lingkup induk).
Untuk semua lingkup (prototipal atau tidak), Angular selalu melacak hubungan orangtua-anak (yaitu, hierarki), melalui properti lingkup $ parent, $$ childHead, dan $$ childTail. Saya biasanya tidak menunjukkan properti lingkup ini dalam diagram.
Untuk skenario di mana elemen bentuk tidak terlibat, solusi lain adalah mendefinisikan fungsi pada lingkup induk untuk memodifikasi primitif. Kemudian pastikan anak selalu memanggil fungsi ini, yang akan tersedia untuk ruang lingkup anak karena warisan prototypal. Misalnya,
Berikut adalah contoh biola yang menggunakan pendekatan "fungsi orangtua" ini. (Biola ditulis sebagai bagian dari jawaban ini: https://stackoverflow.com/a/14104318/215945 .)
Lihat juga https://stackoverflow.com/a/13782671/215945 dan https://github.com/angular/angular.js/issues/1267 .
ng-switch
ng-switch lingkup warisan berfungsi seperti ng-include. Jadi, jika Anda memerlukan data 2 arah yang mengikat ke primitif di lingkup induk, gunakan $ parent, atau ubah model menjadi objek dan kemudian ikat ke properti objek itu. Ini akan menghindari menyembunyikan lingkup anak / membayangi properti lingkup orangtua.
Lihat juga AngularJS, ikat ruang lingkup switch-case?
ng-ulangi
Ng-repeat bekerja sedikit berbeda. Misalkan kita ada di controller kita:
Dan dalam HTML kami:
Untuk setiap item / iterasi, ng-repeat menciptakan ruang lingkup baru, yang secara purwarupa mewarisi dari ruang lingkup induk, tetapi juga menetapkan nilai item ke properti baru pada ruang lingkup anak baru . (Nama properti baru adalah nama variabel loop.) Inilah kode sumber Angular untuk ng-repeat sebenarnya:
Jika item adalah primitif (seperti dalam myArrayOfPrimitive), pada dasarnya salinan nilai diberikan ke properti lingkup anak baru. Mengubah nilai properti lingkup anak (yaitu, menggunakan ng-model, maka lingkup anak
num
) tidak mengubah array referensi lingkup induk. Jadi pada ng-repeat pertama di atas, setiap ruang lingkup anak mendapatkannum
properti yang independen dari array myArrayOfPrimitive:Ng-repeat ini tidak akan berfungsi (seperti yang Anda inginkan / harapkan). Mengetik ke dalam kotak teks mengubah nilai dalam kotak abu-abu, yang hanya terlihat dalam cakupan anak. Apa yang kami inginkan adalah agar input memengaruhi array myArrayOfPrimitive, bukan properti primitif lingkup anak. Untuk mencapai ini, kita perlu mengubah model menjadi array objek.
Jadi, jika item adalah objek, referensi ke objek asli (bukan salinan) ditugaskan ke properti lingkup anak baru. Mengubah nilai properti lingkup anak (yaitu, menggunakan ng model, maka
obj.num
) tidak mengubah objek lingkup referensi induk. Jadi pada ng-repeat kedua di atas, kita memiliki:(Saya mewarnai satu garis abu-abu hanya supaya jelas ke mana ia pergi.)
Ini berfungsi seperti yang diharapkan. Mengetik ke dalam kotak teks mengubah nilai dalam kotak abu-abu, yang terlihat oleh cakupan anak dan orangtua.
Lihat juga Kesulitan dengan ng-model, ng-repeat, dan input dan https://stackoverflow.com/a/13782671/215945
ng-controller
Pengendali yang bersarang menggunakan ng-controller menghasilkan warisan prototypal normal, seperti ng-include dan ng-switch, sehingga teknik yang sama berlaku. Namun, "ini dianggap sebagai bentuk buruk bagi dua pengendali untuk berbagi informasi melalui $ lingkup warisan" - http://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/ Layanan harus digunakan untuk berbagi data antara pengendali sebagai gantinya.
(Jika Anda benar-benar ingin berbagi data melalui warisan lingkup pengontrol, tidak ada yang perlu Anda lakukan. Ruang lingkup anak akan memiliki akses ke semua properti lingkup induk. Lihat juga Urutan beban pengontrol berbeda ketika memuat atau menavigasi )
arahan
scope: false
) - arahan tidak membuat ruang lingkup baru, jadi tidak ada warisan di sini. Ini mudah, tetapi juga berbahaya karena, misalnya, arahan mungkin berpikir itu menciptakan properti baru pada ruang lingkup, padahal sebenarnya itu clobber properti yang sudah ada. Ini bukan pilihan yang baik untuk arahan penulisan yang dimaksudkan sebagai komponen yang dapat digunakan kembali.scope: true
- Arahan menciptakan ruang lingkup anak baru yang secara prototipe mewarisi dari ruang lingkup orang tua. Jika lebih dari satu arahan (pada elemen DOM yang sama) meminta ruang lingkup baru, hanya satu ruang lingkup anak baru dibuat. Karena kita memiliki pewarisan prototipal "normal", ini seperti ng-include dan ng-switch, jadi berhati-hatilah dengan pengikatan data 2 arah ke primitif cakupan orangtua, dan menyembunyikan anak / membayangi properti lingkup orangtua.scope: { ... }
- Arahan menciptakan ruang lingkup isolat / terisolasi baru. Ini tidak mewarisi secara prototipe. Ini biasanya pilihan terbaik Anda saat membuat komponen yang dapat digunakan kembali, karena arahan tidak dapat secara tidak sengaja membaca atau memodifikasi lingkup induk. Namun, arahan tersebut sering membutuhkan akses ke beberapa properti lingkup induk. Hash objek digunakan untuk mengatur pengikatan dua arah (menggunakan '=') atau pengikatan satu arah (menggunakan '@') antara lingkup induk dan cakupan isolat. Ada juga '&' untuk mengikat ekspresi lingkup induk. Jadi, ini semua membuat properti lingkup lokal yang diturunkan dari lingkup induk. Perhatikan bahwa atribut digunakan untuk membantu mengatur pengikatan - Anda tidak bisa hanya merujuk nama properti lingkup induk dalam hash objek, Anda harus menggunakan atribut. Misalnya, ini tidak akan berfungsi jika Anda ingin mengikat ke properti indukparentProp
dalam ruang lingkup yang terisolasi:<div my-directive>
danscope: { localProp: '@parentProp' }
. Atribut harus digunakan untuk menentukan setiap properti induk yang ingin dirujuk oleh direktif ke:<div my-directive the-Parent-Prop=parentProp>
danscope: { localProp: '@theParentProp' }
.Mengisolasi
__proto__
Objek referensi lingkup . Isolate scope's $ parent mereferensikan lingkup parent, jadi meskipun ia diisolasi dan tidak mewarisi prototipically dari lingkup parent, itu masih lingkup anak.Untuk gambar di bawah ini yang kami miliki
<my-directive interpolated="{{parentProp1}}" twowayBinding="parentProp2">
danscope: { interpolatedProp: '@interpolated', twowayBindingProp: '=twowayBinding' }
Juga, asumsikan arahan melakukan ini dalam fungsi tautannya:
scope.someIsolateProp = "I'm isolated"
Untuk informasi lebih lanjut tentang cakupan isolat lihat http://onehungrymind.com/angularjs-sticky-notes-pt-2-isolated-scope/
transclude: true
- arahan menciptakan ruang lingkup anak baru "ditransklusikan", yang secara purwarupa mewarisi dari ruang lingkup orang tua. Ruang lingkup ditransklusikan dan terisolasi (jika ada) adalah saudara kandung - properti $ parent dari setiap lingkup referensi lingkup induk yang sama. Ketika ada ruang lingkup yang ditransklusikan dan terisolasi, mengisolasi properti lingkup $$ nextSibling akan merujuk pada ruang lingkup yang ditransklusikan. Saya tidak mengetahui adanya nuansa dengan ruang lingkup yang ditransklusikan.Untuk gambar di bawah ini, asumsikan arahan yang sama seperti di atas dengan tambahan ini:
transclude: true
Biola ini memiliki
showScope()
fungsi yang dapat digunakan untuk memeriksa ruang lingkup isolat dan transklusi. Lihat instruksi dalam komentar di biola.Ringkasan
Ada empat jenis cakupan:
scope: true
scope: {...}
. Yang ini bukan prototipe, tetapi '=', '@', dan '&' menyediakan mekanisme untuk mengakses properti lingkup induk, melalui atribut.transclude: true
. Yang ini juga merupakan warisan normal lingkup prototipe, tetapi juga saudara kandung dari setiap lingkup isolasi.Untuk semua lingkup (prototipal atau tidak), Angular selalu melacak hubungan orangtua-anak (yaitu, hierarki), melalui properti $ parent dan $$ childHead dan $$ childTail.
Diagram dihasilkan dengan graphvizFile "* .dot", yang ada di github . " Belajar JavaScript dengan Object Graphs " dari Tim Caswell adalah inspirasi untuk menggunakan GraphViz untuk diagram.
sumber
__proto__
Objek referensi lingkup ." seharusnya menjadi "Mengisolasi__proto__
referensi lingkup objek Lingkup." Jadi, dalam dua gambar terakhir, kotak oranye "Objek" seharusnya menjadi kotak "Cakupan".Saya sama sekali tidak ingin bersaing dengan jawaban Mark, tetapi hanya ingin menyoroti bagian yang akhirnya membuat semuanya klik sebagai seseorang yang baru dalam warisan Javascript dan rantai prototipe-nya .
Hanya properti yang membaca pencarian rantai prototipe, bukan tulisan. Jadi saat Anda mengatur
Itu tidak mencari rantai, tetapi ketika Anda mengatur
ada pembacaan halus yang terjadi dalam operasi penulisan yang mencoba untuk mencari myThing sebelum menulis ke prop. Jadi itu sebabnya menulis ke object.properties dari anak didapat di objek orang tua.
sumber
Saya ingin menambahkan contoh pewarisan prototipikal dengan javascript ke jawaban @Scott Driscoll. Kami akan menggunakan pola pewarisan klasik dengan Object.create () yang merupakan bagian dari spesifikasi EcmaScript 5.
Pertama kita membuat fungsi objek "Induk"
Kemudian tambahkan prototipe ke fungsi objek "Induk"
Buat fungsi objek "Anak"
Tetapkan prototipe anak (Buat prototipe anak diturunkan dari prototipe induk)
Tetapkan konstruktor prototipe "Anak" yang tepat
Tambahkan metode "changeProps" ke prototipe anak, yang akan menulis ulang nilai properti "primitif" di objek Anak dan mengubah nilai "object.one" baik di objek Anak dan Orang Tua
Memulai objek Induk (ayah) dan Anak (putra).
Panggil Anak (anak) metode changeProps
Periksa hasilnya.
Properti primitif induk tidak berubah
Properti primitif anak berubah (ditulis ulang)
Properti object.one Induk dan Anak berubah
Contoh kerja di sini http://jsbin.com/xexurukiso/1/edit/
Info lebih lanjut tentang Object.create di sini https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/create
sumber