Pemahaman mendalam tentang siri JavaScript (18): Pelaksanaan ECMAScript bagi pengaturcaraan berorientasikan objek_Pengetahuan asas

WBOY
Lepaskan: 2016-05-16 16:11:12
asal
1347 orang telah melayarinya

Pengenalan

Bab ini adalah bahagian kedua tentang pelaksanaan berorientasikan objek ECMAScript Dalam bahagian pertama, kami membincangkan pengenalan dan perbandingan CEMAScript Jika anda belum membaca bahagian pertama, sebelum meneruskan bab ini, saya amat mengesyorkan bahawa anda membaca bahagian pertama 1 artikel, kerana artikel ini terlalu panjang (35 halaman).

Teks asal bahasa Inggeris:http://dmitrysoshnikov.com/ecmascript/chapter-7-2-oop-ecmascript-implementation/
Nota: Oleh kerana panjangnya artikel ini, ralat tidak dapat dielakkan dan sentiasa disemak.

Dalam pengenalan, kami meluaskan kepada ECMAScript Sekarang, apabila kami mengetahui pelaksanaan OOPnya, mari kita takrifkannya dengan tepat:

Salin kod Kod adalah seperti berikut:

ECMAScript ialah bahasa pengaturcaraan berorientasikan objek yang menyokong mewakilkan warisan berdasarkan prototaip.

ECMAScript ialah bahasa berorientasikan objek yang menyokong warisan delegasi berasaskan prototaip.
Kami akan menganalisisnya daripada jenis data yang paling asas Perkara pertama yang perlu difahami ialah ECMAScript menggunakan nilai dan objek primitif untuk membezakan entiti Oleh itu, apa yang dikatakan oleh beberapa artikel "Dalam JavaScript, semuanya adalah objek" adalah Salah (tidak cukup betul), nilai primitif ialah beberapa jenis data yang akan kita bincangkan di sini.

Jenis Data

Walaupun ECMAScript ialah bahasa ditaip secara dinamik yang lemah yang boleh menukar jenis secara dinamik, ia masih mempunyai jenis data. Dalam erti kata lain, objek mesti tergolong dalam jenis sebenar.
Terdapat 9 jenis data yang ditakrifkan dalam spesifikasi standard, tetapi hanya 6 yang boleh diakses secara langsung dalam program ECMAScript iaitu: Undefined, Null, Boolean, String, Number dan Object.

Tiga jenis lain hanya boleh diakses pada peringkat pelaksanaan (objek ECMAScript tidak boleh menggunakan jenis ini) dan digunakan dalam spesifikasi untuk menerangkan beberapa gelagat operasi dan menyimpan nilai perantaraan. 3 jenis ini ialah: Rujukan, Senarai dan Penyusunan.

Oleh itu, Rujukan digunakan untuk menerangkan operator seperti padam, jenis, dan ini, dan mengandungi objek asas dan nama sifat menerangkan kelakuan senarai parameter (dalam ungkapan baru dan panggilan fungsi); untuk menerangkan tingkah laku break, continue, return dan throw statement.

Jenis nilai primitif
Melihat kembali 6 jenis data yang digunakan dalam atur cara ECMAScript, 5 yang pertama ialah jenis nilai primitif, termasuk Undefined, Null, Boolean, String, Number dan Object.
Contoh jenis nilai primitif:

Salin kod Kod adalah seperti berikut:

var a = undefined;
var b = null;
var c = benar;
var d = 'ujian';
var e = 10;

Nilai ini dilaksanakan secara langsung pada lapisan bawah Ia bukan objek, jadi tiada prototaip atau pembina.

Nota pakcik: Walaupun nilai asli ini serupa dengan nama yang biasa kita gunakan (Boolean, String, Number, Object), nilai tersebut bukanlah perkara yang sama. Oleh itu, keputusan typeof(true) dan typeof(Boolean) adalah berbeza, kerana hasil typeof(Boolean) ialah fungsi, jadi fungsi Boolean, String dan Number mempunyai prototaip (juga disebut dalam bab atribut membaca dan menulis di bawah) .

Jika anda ingin mengetahui jenis data itu, sebaiknya gunakan typeof Terdapat contoh yang perlu anda perhatikan Jika anda menggunakan typeof untuk menentukan jenis null, hasilnya adalah objek. kenapa? Kerana jenis null ditakrifkan sebagai Null.

Salin kod Kod adalah seperti berikut:

alert(typeof null); // "objek"

Sebab "objek" dipaparkan adalah kerana spesifikasi menetapkan bahawa jenis nilai rentetan mengembalikan "objek" untuk nilai Null.

Spesifikasi tidak membayangkan untuk menerangkan perkara ini, tetapi Brendan Eich (pencipta JavaScript) mendapati bahawa null kebanyakannya digunakan di tempat objek muncul, berbanding undefined, seperti menetapkan objek kepada rujukan null. Walau bagaimanapun, sesetengah orang dalam beberapa dokumen mengaitkannya dengan pepijat, dan meletakkan pepijat dalam senarai pepijat yang Brendan Eich turut serta dalam perbincangan Hasilnya ialah keputusan jenis null telah ditetapkan untuk membantah (walaupun 262-3 The standard mentakrifkan bahawa jenis null ialah Null, dan 262-5 telah mengubah suai standard untuk mengatakan bahawa jenis null ialah objek).

Jenis objek

Seterusnya, jenis Objek (jangan dikelirukan dengan pembina Objek, kami hanya membincangkan jenis abstrak sekarang) ialah satu-satunya jenis data yang menerangkan objek ECMAScript.

Objek ialah koleksi tidak tertib pasangan nilai kunci.
Objek ialah koleksi tidak tertib pasangan nilai kunci

Nilai utama objek dipanggil atribut dan atribut ialah bekas untuk nilai primitif dan objek lain. Jika nilai atribut ialah fungsi, kami memanggilnya kaedah.

Contohnya:

Salin kod Kod adalah seperti berikut:

var x = { // Objek "x" mempunyai 3 atribut: a, b, c
a: 10, // nilai asal
b: {z: 100}, // Objek "b" mempunyai atribut z
c: fungsi () { // fungsi (kaedah)
makluman('kaedah x.c');
}
};

makluman(x.a); // 10
alert(x.b); // [Objek objek]
makluman(x.b.z); // 100
x.c(); // 'kaedah x.c'

Dinamik

Seperti yang kami nyatakan dalam Bab 17, objek dalam ES adalah dinamik sepenuhnya. Ini bermakna kita boleh menambah, mengubah suai atau memadam sifat objek sesuka hati semasa program sedang dilaksanakan.

Contohnya:

Salin kod Kod adalah seperti berikut:

var foo = {x: 10};

//Tambah atribut baharu
foo.y = 20;
console.log(foo); // {x: 10, y: 20}

// Ubah suai nilai atribut kepada fungsi
foo.x = fungsi () {
console.log('foo.x');
};

foo.x(); // 'foo.x'

// Padam atribut
padam foo.x;
console.log(foo); // {y: 20}

Sesetengah sifat tidak boleh diubah suai - (sifat baca sahaja, sifat dipadamkan atau sifat tidak boleh dikonfigurasikan). Kami akan menerangkannya kemudian dalam sifat atribut.

Selain itu, spesifikasi ES5 menetapkan bahawa objek statik tidak boleh dilanjutkan dengan sifat baharu dan halaman sifatnya tidak boleh dipadamkan atau diubah suai. Ia adalah apa yang dipanggil objek beku, yang boleh diperolehi dengan menggunakan kaedah Object.freeze(o).

Salin kod Kod adalah seperti berikut:

var foo = {x: 10};

// Bekukan objek
Object.freeze(foo);
console.log(Object.isFrozen(foo)); // benar

// Tidak boleh diubah suai
foo.x = 100;

// Tidak boleh dikembangkan
foo.y = 200;

// Tidak boleh memadam
padam foo.x;

console.log(foo); // {x: 10}

Dalam spesifikasi ES5, kaedah Object.preventExtensions(o) juga digunakan untuk menghalang sambungan, atau kaedah Object.defineProperty(o) digunakan untuk menentukan sifat:

Salin kod Kod adalah seperti berikut:

var foo = {x : 10};

Object.defineProperty(foo, "y", {
nilai: 20,
boleh ditulis: palsu, // baca sahaja
boleh dikonfigurasikan: palsu // Tidak boleh dikonfigurasikan
});

// Tidak boleh diubah suai
foo.y = 200;

// Tidak boleh memadam
padam foo.y; // palsu

// Sambungan Pencegahan dan Kawalan
Object.preventExtensions(foo);
console.log(Object.isExtensible(foo)); // false

//Tidak boleh menambah atribut baharu
foo.z = 30;

console.log(foo); {x: 10, y: 20}

Objek terbina dalam, objek asli dan objek hos

Perlu ambil perhatian bahawa spesifikasi juga membezakan antara objek terbina dalam, objek elemen dan objek hos.

Objek terbina dalam dan objek elemen ditakrifkan dan dilaksanakan oleh spesifikasi ECMAScript, dan perbezaan antara kedua-duanya adalah tidak ketara. Semua objek yang dilaksanakan oleh ECMAScript ialah objek asli (sesetengahnya adalah objek terbina dalam, sesetengahnya dicipta apabila program dilaksanakan, seperti objek yang ditentukan pengguna). Objek terbina dalam ialah subset objek asli yang dibina ke dalam ECMAScript sebelum program bermula (contohnya, parseInt, Match, dll.). Semua objek hos disediakan oleh persekitaran hos, biasanya penyemak imbas, dan mungkin termasuk tetingkap, amaran, dsb.

Perhatikan bahawa objek hos mungkin dilaksanakan oleh ES sendiri, mematuhi sepenuhnya semantik spesifikasi. Dari sudut pandangan ini, mereka boleh dipanggil objek "hos asli" (secepat mungkin secara teori), tetapi spesifikasi tidak mentakrifkan konsep objek "hos asli".

Objek Boolean, Rentetan dan Nombor

Selain itu, spesifikasi juga mentakrifkan beberapa kelas pembungkusan khas asli Objek ini ialah:

1. Objek Boolean
2. Objek rentetan
3. Objek digital

Objek ini dicipta melalui pembina terbina dalam yang sepadan dan mengandungi nilai asli sebagai sifat dalaman objek ini boleh menukar nilai primitif dan sebaliknya.

Salin kod Kod adalah seperti berikut:

var c = Boolean baharu(benar);
var d = new String('test');
var e = Nombor baharu(10);

//Tukar kepada nilai asal
// Gunakan fungsi tanpa kata kunci baharu
с = Boolean(c);
d = Rentetan(d);
e = Nombor(e);

// Tukar semula kepada objek
с = Objek(c);
d = Objek(d);
e = Objek(e);

Selain itu, terdapat objek yang dicipta oleh pembina terbina dalam khas: Fungsi (pembina objek fungsi), Array (pembina tatasusunan) RegExp (pembina ungkapan biasa), Matematik (modul matematik), Tarikh (pembina tarikh) (bekas) , dsb. Objek ini juga merupakan nilai jenis objek Objek Perbezaannya antara satu sama lain diuruskan oleh sifat dalaman, yang kita bincangkan di bawah.

Tersurat

Untuk nilai tiga objek: objek, tatasusunan dan ungkapan biasa, mereka mempunyai pengecam singkatan yang dipanggil: pemula objek, pemula tatasusunan dan ungkapan biasa:

Salin kod Kod adalah seperti berikut:

// Bersamaan dengan Tatasusunan baharu(1, 2, 3);
// Atau array = new Array();
// tatasusunan[0] = 1;
// tatasusunan[1] = 2;
// tatasusunan[2] = 3;
tatasusunan var = [1, 2, 3];

// Bersamaan dengan
// var object = new Object();
// object.a = 1;
// object.b = 2;
// object.c = 3;
objek var = {a: 1, b: 2, c: 3};

// Bersamaan dengan RegExp baharu("^\d $", "g")
var re = /^d $/g;

Perhatikan bahawa jika tiga objek di atas ditetapkan semula kepada jenis baharu, maka semantik pelaksanaan seterusnya akan digunakan mengikut jenis yang baru ditetapkan Contohnya, dalam pelaksanaan semasa Rhino dan versi lama SpiderMonkey 1.7, ia akan Objek berjaya dibuat menggunakan pembina kata kunci baharu, tetapi dalam beberapa pelaksanaan (pada masa ini Spider/TraceMonkey) semantik literal tidak semestinya berubah selepas jenis ditukar.

Salin kod Kod adalah seperti berikut:

var getClass = Object.prototype.toString;

Objek = Nombor;

var foo = Objek baharu;
alert([foo, getClass.call(foo)]); // 0, "[Nombor objek]"

bar var = {};

// Rhino, SpiderMonkey 1.7 - 0, "[Nombor objek]"
// Lain-lain: masih "[objek objek]", "[objek objek]"
alert([bar, getClass.call(bar)]);

//Array mempunyai kesan yang sama
Tatasusunan = Nombor;

foo = Tatasusunan baharu;
alert([foo, getClass.call(foo)]); // 0, "[Nombor objek]"

bar = [];

// Rhino, SpiderMonkey 1.7 - 0, "[Nombor objek]"
// Lain-lain: masih "", "[Objek objek]"
alert([bar, getClass.call(bar)]);

// Tetapi untuk RegExp, semantik literal tidak diubah. semantik daripada literal
// tidak diubah dalam semua pelaksanaan yang diuji

RegExp = Nombor;

foo = RegExp baharu;
alert([foo, getClass.call(foo)]); // 0, "[Nombor objek]"

bar = /(?!)/g;
alert([bar, getClass.call(bar)]); // /(?!)/g, "[objek RegExp]"

Tersurat ungkapan biasa dan objek RegExp

Perhatikan bahawa dalam dua contoh berikut, semantik ungkapan biasa adalah setara dalam edisi ketiga spesifikasi Regexp literal hanya wujud dalam satu ayat dan dicipta dalam peringkat penghuraian, tetapi yang dicipta oleh pembina RegExp. ialah Ia adalah objek baharu, jadi ini mungkin menyebabkan beberapa masalah Sebagai contoh, nilai lastIndex adalah salah semasa ujian:

Salin kod Kod adalah seperti berikut:

untuk (var k = 0; k < 4; k ) {
var re = /ecma/g;
makluman(re.lastIndex); // 0, 4, 0, 4
alert(re.test("ecmascript")); // benar, salah, benar, salah
}

// Bandingkan

untuk (var k = 0; k < 4; k ) {
var re = new RegExp("ecma", "g");
makluman(re.lastIndex); // 0, 0, 0, 0
alert(re.test("ecmascript")); // benar, benar, benar, benar
}

Nota: Walau bagaimanapun, masalah ini telah diperbetulkan dalam edisi ke-5 spesifikasi ES Sama ada ia berdasarkan literal atau pembina, peraturan biasa mencipta objek baharu.

Susun atur bersekutu

Pelbagai perbincangan statik teks, objek JavaScript (selalunya dibuat menggunakan pemula objek {}) dipanggil jadual cincang, jadual cincang atau nama mudah lain: cincang (konsep dalam Ruby atau Perl), Array pengurusan (konsep dalam PHP) , kamus (konsep dalam Python), dsb.

Hanya terdapat istilah sedemikian, terutamanya kerana strukturnya adalah serupa, iaitu, menggunakan pasangan "nilai-kunci" untuk menyimpan objek, yang benar-benar konsisten dengan struktur data yang ditakrifkan oleh teori "tatasusunan bersekutu" atau "cincang meja". Selain itu, jenis data abstrak jadual hash biasanya digunakan pada peringkat pelaksanaan.

Walau bagaimanapun, walaupun terminologi menerangkan konsep ini, ia sebenarnya satu kesilapan Dari perspektif ECMAScript: ECMAScript hanya mempunyai satu objek dan jenis serta subjenisnya, yang tidak berbeza daripada simpanan pasangan "nilai-kunci", jadi Terdapat. bukanlah konsep khusus mengenai perkara ini. Kerana sifat dalaman mana-mana objek boleh disimpan sebagai pasangan nilai kunci:

Salin kod Kod adalah seperti berikut:

var a = {x: 10};
a['y'] = 20;
a.z = 30;

var b = Nombor baharu(1);
b.x = 10;
b.y = 20;
b['z'] = 30;

var c = Fungsi baharu('');
c.x = 10;
c.y = 20;
c['z'] = 30;

// Tunggu, subjenis mana-mana objek "subjenis"

Selain itu, memandangkan objek boleh kosong dalam ECMAScript, konsep "hash" juga tidak betul di sini:

Salin kod Kod adalah seperti berikut:

Object.prototype.x = 10;

var a = {}; // Cipta "hash" kosong

alert(a["x"]); // 10, tetapi tidak kosong
alert(a.toString); // fungsi

a["y"] = 20; // Tambah pasangan nilai kunci baharu pada "cincang"
makluman(a["y"]); // 20

Object.prototype.y = 20; // Tambah atribut prototaip

padamkan ["y"]; // Padam
alert(a["y"]); // Tetapi kunci dan nilai di sini masih mempunyai nilai - 20

Sila ambil perhatian bahawa piawaian ES5 membenarkan kami mencipta objek tanpa prototaip (dilaksanakan menggunakan kaedah Object.create(null) Daripada perspektif ini, objek tersebut boleh dipanggil jadual cincang:

Salin kod Kod adalah seperti berikut:

var aHashTable = Object.create(null);
console.log(aHashTable.toString); // Undefined

Selain itu, sesetengah sifat mempunyai kaedah pengambil/penetap khusus, jadi yang juga boleh menyebabkan kekeliruan tentang konsep ini:
Salin kod Kod adalah seperti berikut:

var a = new String("foo");
a['panjang'] = 10;
makluman(a['panjang']); // 3

Walau bagaimanapun, walaupun dianggap bahawa "cincang" mungkin mempunyai "prototaip" (cth., kelas yang mewakilkan objek cincang dalam Ruby atau Python), dalam ECMAScript, istilah ini tidak betul kerana terdapat jurang antara kedua-dua perwakilan. Tiada perbezaan semantik (iaitu menggunakan notasi titik a.b dan a["b"] notasi).

Konsep dan semantik "atribut sifat" dalam ECMAScript tidak dipisahkan daripada "kunci", indeks tatasusunan dan kaedah Pembacaan dan penulisan sifat semua objek di sini mesti mengikut peraturan yang sama: semak rantai prototaip.

Dalam contoh Ruby berikut, kita dapat melihat perbezaan semantik:

Salin kod Kod adalah seperti berikut:

a = {}
a.kelas # Hash

a.panjang # 0

# pasangan "nilai kunci" baharu
a['panjang'] = 10;

# Secara semantik, titik digunakan untuk mengakses sifat atau kaedah, bukan kunci

a.panjang # 1

#Pengindeks mengakses kunci dalam cincang

a['panjang'] # 10

# Ia serupa dengan mengisytiharkan kelas Hash secara dinamik pada objek sedia ada
# Kemudian isytiharkan sifat atau kaedah baharu

Hash kelas
def z
100
tamat
tamat

# Atribut baharu boleh diakses

a.z # 100

# Tetapi bukan "kunci"

a['z'] # tiada

Piawaian ECMA-262-3 tidak mentakrifkan konsep "cincang" (dan seumpamanya). Walau bagaimanapun, jika terdapat teori struktur sedemikian, adalah mungkin untuk menamakan objek itu selepasnya.

Penukaran objek

Untuk menukar objek kepada nilai primitif, anda boleh menggunakan kaedah valueOf Seperti yang kami katakan, apabila pembina fungsi dipanggil sebagai fungsi (untuk beberapa jenis), tetapi jika kata kunci baharu tidak digunakan, objek ditukar kepada nilai primitif , yang bersamaan dengan panggilan kaedah valueOf:

Salin kod Kod adalah seperti berikut:

var a = Nombor baharu(1);
var primitiveA = Number(a); // Panggilan "valueOf" tersirat
var alsoPrimitiveA = a.valueOf(); // Panggilan eksplisit

makluman([
jenis a, // "objek"
jenis primitifA, // "nombor"
jenis jugaPrimitifA // "nombor"
]);

Pendekatan ini membenarkan objek untuk mengambil bahagian dalam pelbagai operasi, seperti:
Salin kod Kod adalah seperti berikut:

var a = Nombor baharu(1);
var b = Nombor baharu(2);

makluman(a b); // 3

// Malah

var c = {
x: 10,
y: 20,
valueOf: function () {
Kembalikan this.x this.y;
}
};

var d = {
x: 30,
y: 40,
//Sama seperti valueOf fungsi c
valueOf: c.valueOf
};

makluman(c d); // 100

Nilai lalai valueOf akan berubah mengikut jenis objek (jika tidak dibatalkan untuk sesetengah objek, ia mengembalikan ini - contohnya: Object.prototype.valueOf(), dan nilai yang dikira : Date.prototype .valueOf() mengembalikan tarikh dan masa:

Salin kod Kod adalah seperti berikut:

var a = {};
alert(a.valueOf() === a); // true, "valueOf" mengembalikan ini

var d = new Date();
alert(d.valueOf()); // masa
alert(d.valueOf() === d.getTime()); // true

Di samping itu, objek mempunyai perwakilan yang lebih primitif - perwakilan rentetan. Kaedah toString ini boleh dipercayai dan digunakan secara automatik untuk operasi tertentu:
Salin kod Kod adalah seperti berikut:

var a = {
valueOf: function () {
Pulangan 100;
},
toString: fungsi () {
Kembalikan '__test';
}
};

// Dalam operasi ini, kaedah toString secara automatik dipanggil
makluman(a); // "__test"

// Tetapi di sini, kaedah valueOf() dipanggil
makluman(a 10); // 110

// Tetapi, setelah valueOf dipadamkan
// toString boleh dipanggil semula secara automatik
padamkan a.valueOf;
makluman(a 10); // "_test10"

Kaedah toString yang ditakrifkan pada Object.prototype mempunyai makna istimewa Ia mengembalikan nilai atribut [[Class]] dalaman yang akan kita bincangkan di bawah.

Berbanding dengan menukar kepada nilai primitif (ToPrimitive), menukar nilai kepada jenis objek juga mempunyai spesifikasi penukaran (ToObject).

Kaedah eksplisit ialah menggunakan pembina Objek terbina dalam sebagai fungsi untuk memanggil ToObject (agak serupa dengan kata kunci baharu):

Salin kod Kod adalah seperti berikut:

var n = Objek(1); // [Nombor objek]
var s = Objek('ujian'); // [Rentetan objek]

//Sesuatu yang serupa, anda juga boleh menggunakan operator baharu
var b = Objek baharu(benar); // [objek Boolean]

// Jika parameter Objek baharu digunakan, objek mudah dicipta
var o = new Object(); // [objek Objek]

// Jika parameter ialah objek sedia ada
// Hasil penciptaan adalah dengan hanya mengembalikan objek
var a = [];
makluman(a === Objek baharu(a)); // benar
alert(a === Objek(a)); // true

Tiada peraturan am tentang memanggil pembina terbina dalam, sama ada menggunakan operator baharu atau tidak, ia bergantung kepada pembina. Contohnya, Tatasusunan atau Fungsi menghasilkan hasil yang sama apabila digunakan sebagai pembina menggunakan operator baharu atau fungsi mudah yang tidak menggunakan operator baharu:

Salin kod Kod adalah seperti berikut:

var a = Array(1, 2, 3); // [Array objek]
var b = new Array(1, 2, 3); // [object Array]
var c = [1, 2, 3]; // [Array objek]

var d = Function(''); // [Fungsi objek]
var e = new Function(''); // [Fungsi objek]

Apabila sesetengah pengendali digunakan, terdapat juga beberapa penukaran tersurat dan tersirat:
Salin kod Kod adalah seperti berikut:

var a = 1;
var b = 2;

// Tersirat
var c = a b; // 3, nombor
var d = a b '5' // "35", rentetan

// eksplisit
var e = '10'; // "10", rentetan
var f = e; // 10, nombor
var g = parseInt(e, 10); // 10, nombor

// Tunggu

Ciri-ciri atribut

Semua hartanah boleh mempunyai banyak atribut.

1.{ReadOnly} - Abaikan operasi tulis untuk memberikan nilai kepada harta, tetapi sifat baca sahaja boleh diubah oleh gelagat persekitaran hos - iaitu, ia bukan "nilai malar";
2.{DontEnum}——Atribut tidak boleh dihitung dengan for..in loop
3.{DontDelete}——Tingkah laku pengendali padam diabaikan (iaitu, ia tidak boleh dipadamkan);
4. {Internal} - Atribut dalaman, tiada nama (hanya digunakan pada peringkat pelaksanaan), atribut tersebut tidak boleh diakses dalam ECMAScript.

Perhatikan bahawa dalam ES5 {ReadOnly}, {DontEnum} dan {DontDelete} dinamakan semula kepada [[Boleh Ditulis]], [[Boleh Dihitung]] dan [[Boleh Dikonfigurasikan]], yang boleh dilalui secara manual melalui Object.defineProperty atau serupa kaedah untuk menguruskan hartanah ini.

Salin kod Kod adalah seperti berikut:

var foo = {};

Object.defineProperty(foo, "x", {
nilai: 10,
boleh ditulis: benar, // iaitu {ReadOnly} = palsu
enumerable: false, // iaitu {DontEnum} = true
boleh dikonfigurasikan: benar // iaitu {DontDelete} = palsu
});

console.log(foo.x); // 10

// Dapatkan atribut set ciri
melalui deskriptor var desc = Object.getOwnPropertyDescriptor(foo, "x");

console.log(desc.enumerable); // palsu
console.log(desc.writable); // benar
// Tunggu

Sifat dalaman dan kaedah

Objek juga boleh mempunyai sifat dalaman (sebahagian daripada tahap pelaksanaan) yang tidak boleh diakses secara langsung kepada program ECMAScript (tetapi seperti yang akan kita lihat di bawah, sesetengah pelaksanaan membenarkan akses kepada beberapa sifat sedemikian). Sifat ini diakses melalui kurungan empat segi bersarang [[ ]]. Mari kita lihat beberapa daripada mereka Penerangan sifat ini boleh didapati dalam spesifikasi.

Setiap objek harus melaksanakan sifat dan kaedah dalaman berikut:

1.[[Prototaip]] - prototaip objek (akan diperkenalkan secara terperinci di bawah)
2.[[Kelas]] - perwakilan objek rentetan (contohnya, Tatasusunan Objek, Objek Fungsi, Fungsi, dsb.); 3.[[Dapatkan]]——Kaedah untuk mendapatkan nilai atribut
4.[[Put]]——Kaedah untuk menetapkan nilai atribut
5.[[CanPut]]——Periksa sama ada atribut boleh ditulis
6.[[HasProperty]]——Periksa sama ada objek sudah mempunyai sifat ini
7.[[Padam]]——Padamkan atribut daripada objek
8.[[DefaultValue]] mengembalikan nilai asal objek (memanggil kaedah valueOf, sesetengah objek mungkin membuang pengecualian TypeError).
Nilai sifat dalaman [[Kelas]] boleh diperoleh secara tidak langsung melalui kaedah Object.prototype.toString(), yang sepatutnya mengembalikan rentetan berikut: "[objek " [[Kelas]] "]" . Contohnya:

Salin kod Kod adalah seperti berikut:
var getClass = Object.prototype.toString;

getClass.call({}); // [Objek objek]
getClass.call([]); // [Array objek]
getClass.call(Nombor baharu(1)); // [Nombor objek]
// Tunggu

Fungsi ini biasanya digunakan untuk menyemak objek, tetapi spesifikasi mengatakan bahawa [[Kelas]] objek hos boleh menjadi sebarang nilai, termasuk nilai atribut [[Kelas]] objek terbina dalam, jadi secara teorinya ia tidak boleh dijamin 100% tepat. Sebagai contoh, atribut [[Class]] bagi kaedah document.childNodes.item(...) mengembalikan "String" dalam IE, tetapi ia mengembalikan "Fungsi" dalam pelaksanaan lain.

Salin kod Kod adalah seperti berikut:
// dalam IE - "String", dalam lain - "Fungsi"
alert(getClass.call(document.childNodes.item));

Pembina

Jadi, seperti yang kami nyatakan di atas, objek dalam ECMAScript dicipta melalui apa yang dipanggil pembina.

Pembina ialah fungsi yang mencipta dan memulakan objek yang baru dibuat.

Pembina ialah fungsi yang mencipta dan memulakan objek yang baru dibuat.
Penciptaan objek (peruntukan memori) dijaga oleh kaedah dalaman pembina [[Construct]]. Tingkah laku kaedah dalaman ini ditakrifkan dengan baik, dan semua pembina menggunakan kaedah ini untuk memperuntukkan memori untuk objek baharu.

Pengamatan diuruskan dengan memanggil fungsi ini ke atas dan ke bawah objek baharu, yang bertanggungjawab untuk kaedah dalaman [[Panggilan]] pembina.

Perhatikan bahawa kod pengguna hanya boleh diakses semasa fasa permulaan, walaupun semasa fasa permulaan kita boleh mengembalikan objek lain (mengabaikan objek tihs yang dibuat dalam fasa pertama):


Salin kod Kod adalah seperti berikut:
fungsi A() {
// Kemas kini objek yang baru dibuat
ini.x = 10;
// Tetapi ia mengembalikan objek yang berbeza
kembali [1, 2, 3];
}

var a = new A();
console.log(a.x, a); tidak ditentukan, [1, 2, 3]

Merujuk kepada Bab 15 Fungsi - Algoritma untuk Mencipta Fungsi, kita dapat melihat bahawa fungsi itu ialah objek asli, termasuk atribut [[Construct]] ] dan [[Panggil]] ] serta atribut prototaip prototaip yang dipaparkan - masa hadapan Prototaip objek (Nota: NativeObject ialah konvensyen untuk objek asli dan digunakan dalam pseudokod di bawah).


Salin kod Kod adalah seperti berikut:
F = NativeObject baharu();

F.[[Kelas]] = "Fungsi"

.... // Atribut lain

F.[[Panggilan]] = // fungsi itu sendiri

F.[[Construct]] = internalConstructor // Pembina dalaman biasa

.... // Atribut lain

// Prototaip objek dicipta oleh pembina F
__objectPrototype = {};
__objectPrototype.constructor = F // {DontEnum}
F.prototaip = __objectPrototype

[[Panggil]] ] ialah cara utama untuk membezakan objek selain daripada atribut [[Kelas]] (di sini bersamaan dengan "Fungsi"), jadi atribut dalaman [[Panggil]] objek dipanggil sebagai fungsi. Menggunakan operator jenis pada objek sedemikian mengembalikan "fungsi". Walau bagaimanapun, ia berkaitan terutamanya dengan objek asli Dalam beberapa kes, pelaksanaan menggunakan typeof untuk mendapatkan nilai adalah berbeza. Contohnya: kesan window.alert (...) dalam IE:

Salin kod Kod adalah seperti berikut:

// Dalam pelayar IE - "Objek", "objek", pelayar lain - "Fungsi", "fungsi"
alert(Object.prototype.toString.call(window.alert));
alert(typeof window.alert); // "Objek"

Kaedah dalaman [[Construct]] diaktifkan dengan menggunakan pembina dengan operator baharu Seperti yang kami katakan, kaedah ini bertanggungjawab untuk peruntukan memori dan penciptaan objek. Jika tiada parameter, kurungan untuk memanggil pembina juga boleh ditinggalkan:

Salin kod Kod adalah seperti berikut:

fungsi A(x) { // pembina А
ini.x = x || 10;
}

// Jika tiada parameter diluluskan, kurungan boleh ditinggalkan
var a = new A; // atau new A();
makluman(a.x); // 10

//Lepaskan secara eksplisit dalam parameter x
var b = baharu A(20);
makluman(b.x); // 20

Kami juga tahu bahawa shis dalam pembina (fasa permulaan) ditetapkan kepada objek yang baru dibuat.

Mari kita kaji algoritma penciptaan objek.

Algoritma untuk penciptaan objek

Kelakuan kaedah dalaman [[Construct]] boleh diterangkan seperti berikut:

Salin kod Kod adalah seperti berikut:

F.[[Construct]](initialParameters):

O = new NativeObject();

// Harta [[Kelas]] ditetapkan kepada "Objek"
O.[[Kelas]] = "Objek"

// Dapatkan objek g
apabila merujuk F.prototaip var __objectPrototype = F.prototype;

// Jika __objectPrototype ialah objek, maka:
O.[[Prototaip]] = __objectPrototype
// Jika tidak:
O.[[Prototaip]] = Objek.prototaip;
// Di sini O.[[Prototaip]] ialah prototaip objek Objek

// F.[[Panggilan]]
digunakan semasa memulakan objek yang baru dibuat. // Tetapkan ini kepada objek yang baru dibuat O
//Parameter adalah sama dengan initialParameters dalam F
R = F.[[Panggilan]](initialParameters); ini === O;
// Di sini R ialah nilai pulangan [[Panggil]]
// Lihat dalam JS, seperti ini:
// R = F.apply(O, initialParameters);

// Jika R ialah objek
kembalikan R
// Jika tidak
kembalikan O

Sila ambil perhatian dua ciri utama:

1. Pertama, prototaip objek yang baru dicipta diperoleh daripada atribut prototaip fungsi pada saat semasa (ini bermakna prototaip dua objek yang dicipta yang dicipta oleh pembina yang sama boleh berbeza kerana atribut prototaip bagi fungsinya juga boleh berbeza) .
2. Kedua, seperti yang kami nyatakan di atas, jika [[Panggil]] mengembalikan objek apabila objek dimulakan, ini betul-betul hasil yang digunakan untuk keseluruhan operator baharu:

Salin kod Kod adalah seperti berikut:

fungsi A() {}
A.prototaip.x = 10;

var a = new A();
alert(a.x); // 10 – dapatkan
daripada prototaip
// Tetapkan sifat .prototype kepada objek baharu
// Mengapa mengisytiharkan sifat .constructor secara eksplisit dijelaskan di bawah
A.prototaip = {
pembina: A,
y: 100
};

var b = baru A();
// Objek "b" mempunyai sifat baharu
makluman(b.x); // tidak ditentukan
alert(b.y); // 100 – dapatkan
daripada prototaip
// Tetapi prototaip objek masih boleh mendapatkan hasil asal
alert(a.x); // 10 - dapatkan
daripada prototaip
fungsi B() {
ini.x = 10;
kembalikan Array baharu();
}

// Jika pembina "B" tidak kembali (atau mengembalikan ini)
// Kemudian objek ini boleh digunakan, tetapi dalam kes berikut, array
dikembalikan var b = B baharu();
makluman(b.x); // tidak ditentukan
alert(Object.prototype.toString.call(b)); // [Array objek]

Mari kita lihat dengan lebih dekat prototaip

Prototaip

Setiap objek mempunyai prototaip (kecuali beberapa objek sistem). Komunikasi prototaip dijalankan melalui sifat prototaip [[Prototaip]] dalaman, tersirat, dan tidak boleh diakses secara langsung Prototaip boleh menjadi objek atau nilai nol.

Pembina hartanah

Terdapat dua titik pengetahuan penting dalam contoh di atas Yang pertama adalah mengenai atribut prototaip atribut pembina fungsi Dalam algoritma penciptaan fungsi, kita tahu bahawa atribut pembina ditetapkan kepada atribut prototaip fungsi semasa fasa penciptaan fungsi , nilai atribut pembina adalah rujukan penting kepada fungsi itu sendiri:

Salin kod Kod adalah seperti berikut:

fungsi A() {}
var a = new A();
alert(a.constructor); // fungsi A() {}, oleh delegasi
alert(a.constructor === A); // benar

Biasanya dalam kes ini, terdapat salah faham: sifat pembina pembina adalah salah sebagai harta objek yang baru dibuat itu sendiri, tetapi, seperti yang kita lihat, harta ini tergolong dalam prototaip dan diakses melalui warisan.

Dengan mewarisi contoh atribut pembina, anda secara tidak langsung boleh mendapatkan rujukan kepada objek prototaip:

Salin kod Kod adalah seperti berikut:

fungsi A() {}
A.prototype.x = Nombor baharu(10);

var a = new A();
alert(a.constructor.prototype); // [Objek objek]

alert(a.x); // 10, melalui prototaip
// Kesan yang sama seperti a.[[Prototaip]].x
alert(a.constructor.prototype.x); // 10

alert(a.constructor.prototype.x === a.x); // true

Tetapi sila ambil perhatian bahawa atribut pembina dan prototaip fungsi boleh ditakrifkan semula selepas objek dicipta. Dalam kes ini, objek kehilangan mekanisme yang diterangkan di atas. Jika anda mengedit prototaip elemen melalui atribut prototaip fungsi (menambah objek baharu atau mengubah suai objek sedia ada), anda akan melihat atribut yang baru ditambah pada contoh itu.

Walau bagaimanapun, jika kita menukar sepenuhnya sifat prototaip fungsi (dengan memperuntukkan objek baharu), rujukan kepada pembina asal hilang, kerana objek yang kita cipta tidak termasuk harta pembina:

Salin kod Kod adalah seperti berikut:

fungsi A() {}
A.prototaip = {
x: 10
};

var a = new A();
makluman(a.x); // 10
makluman(a.pembina === A); // palsu!

Oleh itu, rujukan prototaip kepada fungsi perlu dipulihkan secara manual:
Salin kod Kod adalah seperti berikut:

fungsi A() {}
A.prototaip = {
pembina: A,
x: 10
};

var a = new A();
makluman(a.x); // 10
alert(a.constructor === A); // benar

Perhatikan bahawa walaupun atribut pembina telah dipulihkan secara manual, berbanding dengan prototaip asal yang hilang, ciri {DontEnum} tidak lagi tersedia, yang bermaksud pernyataan gelung for..in dalam A.prototype tidak disokong, tetapi dalam edisi ke-5 spesifikasi , menyediakan keupayaan untuk mengawal keadaan terbilang yang boleh dikira melalui atribut [[Enumerable]].

Salin kod Kod adalah seperti berikut:

var foo = {x: 10};

Object.defineProperty(foo, "y", {
nilai: 20,
enumerable: false // aka {DontEnum} = true
});

console.log(foo.x, foo.y); // 10, 20

untuk (var k in foo) {
console.log(k); // hanya "x"
}

var xDesc = Object.getOwnPropertyDescriptor(foo, "x");
var yDesc = Object.getOwnPropertyDescriptor(foo, "y");

console.log(
xDesc.ennumerable, // benar
yDesc.enumerable // false
);

Prototaip eksplisit dan atribut [[Prototaip]] tersirat

Secara amnya, adalah tidak betul untuk merujuk prototaip objek secara eksplisit melalui atribut prototaip fungsi Ia merujuk kepada objek yang sama, atribut [[Prototaip]] objek:

a.[[Prototaip]] ----> Prototaip <---- A.prototaip

Selain itu, nilai [[Prototaip]] contoh sememangnya diperoleh daripada atribut prototaip pembina.

Walau bagaimanapun, penyerahan atribut prototaip tidak akan menjejaskan prototaip objek yang telah dibuat (ia hanya akan terjejas apabila atribut prototaip pembina berubah, maksudnya, hanya objek yang baru dibuat akan mempunyai prototaip baharu dan objek yang telah dibuat masih akan mempunyai prototaip baharu Rujukan kepada prototaip lama yang asal (prototaip ini tidak boleh diubah suai lagi).

Salin kod Kod adalah seperti berikut:

// Situasi sebelum mengubah suai prototaip A.prototaip
a.[[Prototaip]] ----> Prototaip <---- A.prototaip

// Selepas pengubahsuaian
A.prototaip ----> Prototaip baharu // Objek baharu akan mempunyai prototaip ini
a.[[Prototaip]] ----> Prototaip // But prototaip asal

Contohnya:

Salin kod Kod adalah seperti berikut:

fungsi A() {}
A.prototaip.x = 10;

var a = new A();
makluman(a.x); // 10

A.prototaip = {
pembina: A,
x: 20
y: 30
};

// Objek a ialah nilai yang diperoleh daripada prototaip minyak mentah melalui rujukan [[Prototaip]] tersirat
makluman(a.x); // 10
alert(a.y) // undefined

var b = baru A();

// Tetapi objek baharu ialah nilai yang diperoleh daripada prototaip baharu
makluman(b.x); // 20
makluman(b.y) // 30

Oleh itu, beberapa artikel mengatakan bahawa "mengubah suai prototaip secara dinamik akan menjejaskan semua objek dan semua objek akan mempunyai prototaip baharu" yang salah Prototaip baharu hanya akan berkuat kuasa pada objek yang baru dibuat selepas prototaip diubah suai.

Peraturan utama di sini ialah: prototaip objek dicipta apabila objek dibuat, dan tidak boleh diubah suai kepada objek baharu selepas itu Jika ia masih merujuk kepada objek yang sama, ia boleh dirujuk melalui prototaip eksplisit daripada pembina. Selepas objek dicipta, hanya sifat prototaip boleh ditambah atau diubah suai.

Atribut __proto__ bukan standard

Walau bagaimanapun, sesetengah pelaksanaan, seperti SpiderMonkey, menyediakan atribut eksplisit __proto__ bukan standard untuk merujuk prototaip objek:

Salin kod Kod adalah seperti berikut:

fungsi A() {}
A.prototaip.x = 10;

var a = new A();
makluman(a.x); // 10

var __newPrototype = {
pembina: A,
x: 20,
y: 30
};

//Rujukan kepada objek baharu
A.prototaip = __newPrototype;

var b = baru A();
makluman(b.x); // 20
makluman(b.y); // 30

// Objek "a" masih menggunakan prototaip lama
makluman(a.x); // 10
makluman(a.y); // tidak ditentukan

// Ubah suai prototaip secara eksplisit
a.__proto__ = __newPrototype;

// Kini objek "а" merujuk kepada objek baharu
makluman(a.x); // 20
makluman(a.y); // 30

Ambil perhatian bahawa ES5 menyediakan kaedah Object.getPrototypeOf(O), yang secara langsung mengembalikan sifat [[Prototype]] objek - prototaip awal contoh. Walau bagaimanapun, berbanding __proto__, ia hanya pengambil dan tidak membenarkan nilai yang ditetapkan.
Salin kod Kod adalah seperti berikut:

var foo = {};
Object.getPrototypeOf(foo) == Object.prototype; // true

Objek bebas daripada pembina
Oleh kerana prototaip contoh adalah bebas daripada pembina dan atribut prototaip pembina, pembina boleh dipadamkan selepas menyelesaikan kerja utamanya (membuat objek). Objek prototaip terus wujud dengan merujuk atribut [[Prototaip]]:

Salin kod Kod adalah seperti berikut:

fungsi A() {}
A.prototaip.x = 10;

var a = new A();
makluman(a.x); // 10

// Set A kepada null - tunjukkan pembina rujukan
A = batal;

// Tetapi jika sifat .constructor tidak berubah,
// Anda masih boleh mencipta objek melaluinya
var b = new a.constructor();
makluman(b.x); // 10

// Rujukan tersirat juga dipadamkan
padamkan a.constructor.prototype.constructor;
padamkan b.constructor.prototype.constructor;

// Objek tidak boleh lagi dicipta melalui pembina A
// Tetapi kedua-dua objek ini masih mempunyai prototaip mereka sendiri
makluman(a.x); // 10
makluman(b.x); // 10

Ciri-ciri instanceof operator
Kami memaparkan prototaip rujukan melalui atribut prototaip pembina, yang berkaitan dengan pengendali instanceof. Operator ini berfungsi dengan rantai prototaip, bukan pembina Dengan mengambil kira perkara ini, selalunya berlaku salah faham apabila mengesan objek:

Salin kod Kod adalah seperti berikut:

if (foo instanceof Foo) {
...
}

Ini tidak digunakan untuk mengesan sama ada objek foo dicipta menggunakan pembina Foo Semua instanceof operator hanya memerlukan satu sifat objek - foo.[[Prototaip]], dan semak kewujudannya bermula dari Foo.prototype dalam rantai prototaip. Operator instanceof diaktifkan melalui kaedah dalaman [[HasInstance]] dalam pembina.

Mari kita lihat contoh ini:

Salin kod Kod adalah seperti berikut:

fungsi A() {}
A.prototaip.x = 10;

var a = new A();
makluman(a.x); // 10

makluman(contoh A); // benar

// Jika prototaip ditetapkan kepada null
A.prototaip = null;

// ..."a" masih boleh mengakses prototaip melalui a.[[Prototaip]]
makluman(a.x); // 10

// Walau bagaimanapun, instanceof operator tidak lagi boleh digunakan seperti biasa
// Kerana ia dilaksanakan daripada atribut prototaip pembina
alert(a instanceof A); // Ralat, A.prototype bukan objek

Sebaliknya, objek boleh dibuat oleh pembina, tetapi jika atribut [[Prototaip]] objek dan nilai atribut prototaip pembina ditetapkan kepada nilai yang sama, instanceof akan kembali benar apabila disemak:

Salin kod Kod adalah seperti berikut:

fungsi B() {}
var b = B baharu();

makluman(b instanceof B); // benar

fungsi C() {}

var __proto = {
pembina: C
};

C.prototaip = __proto;
b.__proto__ = __proto;

makluman(b instanceof C); // benar
makluman(b instanceof B); // palsu

Prototaip boleh menyimpan kaedah dan berkongsi sifat
Prototaip digunakan dalam kebanyakan program untuk menyimpan kaedah objek, keadaan lalai dan sifat objek kongsi.

Sebenarnya, objek boleh mempunyai keadaannya sendiri, tetapi kaedahnya selalunya sama. Oleh itu, kaedah biasanya ditakrifkan dalam prototaip untuk pengoptimuman memori. Ini bermakna semua kejadian yang dibuat oleh pembina ini boleh berkongsi kaedah ini.

Salin kod Kod adalah seperti berikut:

fungsi A(x) {
ini.x = x || 100;
}

A.prototaip = (fungsi () {

// Mulakan konteks
// Gunakan objek tambahan

var _someSharedVar = 500;

fungsi _someHelper() {
alert('pembantu dalaman: ' _someSharedVar);
}

kaedah fungsi1() {
makluman('kaedah1: ' ini.x);
}

kaedah fungsi2() {
makluman('kaedah2: ' ini.x);
_someHelper();
}

// Prototaip itu sendiri
Kembali {
pembina: A,
Kaedah1: kaedah1,
Kaedah2: kaedah2
};

})();

var a = baru A(10);
var b = baharu A(20);

a.method1(); // kaedah1: 10
a.method2(); // kaedah2: 10, pembantu dalaman: 500

b.method1(); // kaedah1: 20
b.method2(); // kaedah2: 20, pembantu dalaman: 500

// Kedua-dua objek menggunakan kaedah yang sama dalam prototaip
makluman(a.method1 === b.method1); // true
makluman(a.kaedah2 === b.kaedah2); // benar

Baca dan tulis atribut

Seperti yang kami nyatakan, membaca dan menulis nilai sifat adalah melalui kaedah dalaman [[Get]] dan [[Put]]. Kaedah dalaman ini diaktifkan melalui pengakses sifat: notasi titik atau notasi indeks:

Salin kod Kod adalah seperti berikut:

// tulis
foo.bar = 10; // Dipanggil [[Put]]

console.log(foo.bar); // 10, dipanggil [[Dapatkan]]
console.log(foo['bar']); // Kesan yang sama

Mari lihat cara kaedah ini berfungsi dalam pseudokod:

Kaedah [[Dapatkan]]

[[Get]] juga akan menanyakan sifat daripada rantai prototaip, jadi sifat dalam prototaip juga boleh diakses melalui objek.

O.[[Dapatkan]](P):

Salin kod Kod adalah seperti berikut:

// Jika ia adalah atribut anda sendiri, kembalikan
jika (O.hasOwnProperty(P)) {
Kembalikan O.P;
}

// Jika tidak, teruskan menganalisis prototaip
var __proto = O.[[Prototaip]];

// Jika prototaip adalah batal, kembalikan tidak ditentukan
// Ini mungkin: Object.prototype peringkat atas.[[Prototaip]] adalah batal
jika (__proto === null) {
Kembali tidak ditentukan;
}

// Jika tidak, panggil [[Get]] secara rekursif pada rantai prototaip dan cari atribut dalam prototaip setiap lapisan
// Sehingga prototaip adalah batal
kembalikan __proto.[[Dapatkan]](P)

Sila ambil perhatian bahawa [[Dapatkan]] juga akan kembali tidak ditentukan dalam situasi berikut:
Salin kod Kod adalah seperti berikut:

if (window.someObject) {
...
}

Di sini, jika sifat someObject tidak ditemui dalam tetingkap, ia akan dicari dalam prototaip, kemudian dalam prototaip prototaip, dan seterusnya Jika ia tidak ditemui, undefined akan dikembalikan mengikut definisi.

Nota: Operator dalam juga boleh bertanggungjawab untuk mencari sifat (ia juga akan mencari rantai prototaip):

Salin kod Kod adalah seperti berikut:

if ('someObject' dalam tetingkap) {
...
}

Ini membantu mengelakkan beberapa masalah khas: contohnya, walaupun someObject wujud, apabila someObject adalah sama dengan false, pusingan pertama pengesanan akan gagal.

Kaedah [[Put]]

Kaedah

[[Put]] boleh mencipta dan mengemas kini sifat objek itu sendiri dan menutup sifat nama yang sama dalam prototaip.

O.[[Put]](P, V):

Salin kod Kod adalah seperti berikut:

// Jika nilai tidak boleh ditulis pada atribut, keluar
jika (!O.[[CanPut]](P)) {
Kembali;
}

// Jika objek tidak mempunyai sifatnya sendiri, ciptakannya
// Semua atribut adalah palsu
jika (!O.hasOwnProperty(P)) {
createNewProperty(O, P, atribut: {
Baca Sahaja: palsu,
DontEnum: palsu,
JanganPadam: palsu,
Dalaman: palsu
});
}

// Tetapkan nilai jika atribut wujud, tetapi jangan ubah sifat atribut
O.P = V

kembali;

Contohnya:
Salin kod Kod adalah seperti berikut:

Object.prototype.x = 100;

var foo = {};
console.log(foo.x); // 100, sifat warisan

foo.x = 10; // [[Letak]]
console.log(foo.x); // 10, atribut sendiri

padam foo.x;
console.log(foo.x); //set semula kepada 100, warisi sifat
Sila ambil perhatian bahawa sifat baca sahaja dalam prototaip tidak boleh ditutup dan keputusan tugasan akan diabaikan Ini dikawal oleh kaedah dalaman [[CanPut]].

// Contohnya, panjang atribut ialah baca sahaja, mari cuba tutup panjangnya

fungsi SuperString() {
/* tiada */
}

SuperString.prototype = new String("abc");

var foo = SuperString baharu();

console.log(foo.length); // 3, panjang "abc"

// Percubaan untuk menutup
foo.length = 5;
console.log(foo.length); // Pegun 3


Tetapi dalam mod ketat ES5, jika atribut baca sahaja bertopeng, TypeError akan disimpan.

Akses harta

Kaedah dalaman [[Get]] dan [[Put]] diaktifkan melalui notasi titik atau pengindeksan dalam ECMAScript Jika pengecam atribut ialah nama yang sah, ia boleh diakses melalui ".", dan pengindeksan Parti berjalan secara dinamik. nama yang ditentukan.

Salin kod Kod adalah seperti berikut:

var a = {testProperty: 10};

alert(a.testProperty); // 10, klik
alert(a['testProperty']); // 10, indeks

var propertyName = 'Property';
alert(a['test' propertyName]); // 10, sifat dinamik diindeks

Terdapat ciri yang sangat penting di sini - pengakses harta sentiasa menggunakan spesifikasi ToObject untuk merawat nilai di sebelah kiri "." Penukaran tersirat ini berkaitan dengan pepatah "semuanya dalam JavaScript adalah objek" (namun, seperti yang kita sedia maklum, tidak semua nilai dalam JavaScript adalah objek).

Jika pengakses atribut digunakan untuk mengakses nilai asal, nilai asal akan dibalut oleh objek (termasuk nilai asal) sebelum mengakses, dan kemudian atribut akan diakses melalui objek yang dibalut selepas atribut diakses , objek yang dibalut akan dipadamkan.

Contohnya:

Salin kod Kod adalah seperti berikut:

var a = 10; // Nilai asal

// Tetapi kaedah boleh diakses (sama seperti objek)
alert(a.toString()); // "10"

// Selain itu, kita boleh mencipta atribut jantung pada
a.test = 100; // Nampaknya tiada masalah

// Walau bagaimanapun, kaedah [[Dapatkan]] tidak mengembalikan nilai harta, tetapi mengembalikan tidak ditentukan
alert(a.test); // undefined

Jadi, mengapakah nilai asal dalam keseluruhan contoh boleh mempunyai akses kepada kaedah toString, tetapi bukan sifat ujian yang baru dibuat?

Jawapannya mudah:

Pertama sekali, seperti yang kami katakan, selepas menggunakan pengakses harta, ia bukan lagi nilai asal, tetapi objek perantaraan yang dibalut (keseluruhan contoh menggunakan Nombor(a) baharu), dan kaedah toString dihantar pada ini masa Ditemui dalam rantaian prototaip:

Salin kod Kod adalah seperti berikut:

//Prinsip melaksanakan a.toString():

1. pembalut = Nombor baharu(a);
2. wrapper.toString(); // "10"
3. padamkan pembalut;

Seterusnya, apabila kaedah [[Put]] mencipta atribut baharu, ia juga dilakukan melalui objek berpakej:
Salin kod Kod adalah seperti berikut:

//Prinsip melaksanakan a.test = 100:

1. pembalut = Nombor baharu(a);
2. wrapper.test = 100;
3. padamkan pembalut;

Kami melihat bahawa dalam langkah 3, objek yang dibalut dipadamkan dan halaman sifat yang baru dibuat dipadamkan - memadamkan objek pembalut itu sendiri.

Apabila menggunakan [[Get]] untuk mendapatkan nilai ujian, objek pembungkusan dicipta semula, tetapi kali ini objek yang dibalut tidak lagi mempunyai atribut ujian, jadi undefined dikembalikan:

Salin kod Kod adalah seperti berikut:

//Prinsip melaksanakan a.test:

1. pembalut = Nombor baharu(a);
2. wrapper.test; // undefined

Kaedah ini menerangkan cara nilai asal dibaca Selain itu, jika sebarang nilai asal sering digunakan untuk mengakses atribut, pertimbangan kecekapan masa akan menggantikannya secara langsung dengan objek sebaliknya, jika ia tidak diakses dengan kerap, atau hanya digunakan Untuk tujuan pengiraan, borang ini boleh disimpan.

Warisi

Kami tahu bahawa ECMAScript menggunakan warisan delegasi berasaskan prototaip. Rantaian dan prototaip telah pun disebut dalam rantaian prototaip. Malah, semua pelaksanaan delegasi dan carian serta analisis rantaian prototaip dipekatkan ke dalam kaedah [[Get]].

Jika anda memahami sepenuhnya kaedah [[Dapatkan]], maka persoalan pewarisan dalam JavaScript akan menjadi penjelasan sendiri.

Apabila saya sering bercakap tentang warisan dalam JavaScript di forum, saya sentiasa menggunakan satu baris kod untuk menunjukkannya, sebenarnya, kita tidak perlu mencipta sebarang objek atau fungsi kerana bahasa itu sudah berdasarkan warisan adalah seperti berikut:

Salin kod Kod adalah seperti berikut:

alert(1..toString()); // "1"

Kita sudah tahu cara kaedah [[Dapatkan]] dan pengakses harta berfungsi, mari lihat apa yang berlaku:

1 Mula-mula, buat objek pembungkusan daripada nilai asal 1 melalui Nombor(1)
baharu 2. Kemudian kaedah toString diwarisi daripada objek pembungkusan ini

Mengapa ia diwarisi? Oleh kerana objek dalam ECMAScript boleh mempunyai sifatnya sendiri, objek pembalut dalam kes ini tidak mempunyai kaedah toString. Jadi ia mewarisi daripada prinsip, iaitu Number.prototype.

Perhatikan terdapat kehalusan, dua titik dalam contoh di atas bukanlah ralat. Titik pertama mewakili bahagian perpuluhan dan titik kedua ialah pengakses atribut:

Salin kod Kod adalah seperti berikut:

1.toString(); // Ralat sintaks!

(1).toString(); // OK

1..toString(); // OK

1['toString'](); // OK

Rantai Prototaip

Mari tunjukkan cara membuat rantai prototaip untuk objek yang ditentukan pengguna, ia sangat mudah:

Salin kod Kod adalah seperti berikut:

fungsi A() {
makluman('A.[[Panggilan]] diaktifkan');
ini.x = 10;
}
A.prototaip.y = 20;

var a = new A();
makluman([a.x, a.y]); // 10 (diri), 20 (diwarisi)

fungsi B() {}

// Kaedah rantaian prototaip terbaharu adalah untuk menetapkan prototaip objek kepada objek baharu yang lain
B.prototaip = baru A();

// Baiki sifat pembina prototaip, jika tidak, ia akan menjadi A
B.prototype.constructor = B;

var b = B baharu();
makluman([b.x, b.y]); // 10, 20, 2 diwarisi

// [[Dapatkan]] b.x:
// b.x (tidak) -->
// b.[[Prototaip]].x (ya) - 10

// [[Dapatkan]] b.y
// b.y (tidak) -->
// b.[[Prototaip]].y (tidak) -->
// b.[[Prototaip]].[[Prototaip]].y (ya) - 20

// di mana b.[[Prototaip]] === B.prototaip,
// dan b.[[Prototaip]].[[Prototaip]] === A.prototaip

Kaedah ini mempunyai dua ciri:

Pertama, B.prototype akan mengandungi atribut x. Pada pandangan pertama ini mungkin kelihatan tidak betul, anda mungkin berfikir bahawa sifat x ditakrifkan dalam A dan bahawa pembina B menjangkakannya juga. Walaupun warisan prototaip tiada masalah dalam keadaan biasa, pembina B kadangkala tidak memerlukan atribut x Berbanding dengan warisan berasaskan kelas, semua atribut disalin ke subkelas turunan.

Walau bagaimanapun, jika terdapat keperluan (untuk mensimulasikan warisan berasaskan kelas) untuk menetapkan atribut x kepada objek yang dicipta oleh pembina B, terdapat beberapa cara, salah satunya akan kami tunjukkan kemudian.

Kedua, ini bukan ciri tetapi kelemahan - apabila prototaip subkelas dicipta, kod pembina juga dilaksanakan, dan kita dapat melihat bahawa mesej "A.[[Panggil]] diaktifkan" dipaparkan dua kali - Apabila menggunakan pembina A untuk mencipta objek dan menetapkannya kepada sifat B.prototype, adegan lain ialah apabila objek mencipta dirinya sendiri!

Contoh berikut adalah lebih kritikal, pengecualian yang dilemparkan oleh pembina kelas induk: Mungkin ia perlu diperiksa apabila objek sebenar dibuat, tetapi jelas, kes yang sama, iaitu, apabila menggunakan objek induk ini sebagai prototaip Sesuatu akan berlaku.

Salin kod Kod adalah seperti berikut:

fungsi A(param) {
jika (!param) {
baling 'Param diperlukan';
}
this.param = param;
}
A.prototaip.x = 10;

var a = baru A(20);
makluman([a.x, a.param]); // 10, 20

fungsi B() {}
B.prototype = new A(); // Ralat

Selain itu, mempunyai terlalu banyak kod dalam pembina kelas induk juga merupakan satu kelemahan.

Untuk menyelesaikan "fungsi" dan masalah ini, pengaturcara menggunakan corak standard rantaian prototaip (ditunjukkan di bawah.

Salin kod Kod adalah seperti berikut:

fungsi A() {
makluman('A.[[Panggilan]] diaktifkan');
ini.x = 10;
}
A.prototaip.y = 20;

var a = new A();
makluman([a.x, a.y]); // 10 (diri), 20 (bersepadu)

fungsi B() {
// Atau gunakan A.apply(this, arguments)
B.superproto.constructor.apply(this, arguments);
}

// Warisan: sambungkan prototaip bersama melalui pembina perantaraan kosong
var F = fungsi () {};
F.prot
Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan