Saya punya banyak pertanyaan tentang kelas ES6.
Apa manfaat menggunakan class
sintaks? Saya membaca bahwa publik / privat / statis akan menjadi bagian dari ES7, apakah itu alasannya?
Selain itu, apakah class
jenis OOP yang berbeda atau masih merupakan warisan prototipe JavaScript? Bisakah saya memodifikasinya menggunakan .prototype
? Atau hanya objek yang sama tetapi dua cara berbeda untuk mendeklarasikannya.
Apakah ada manfaat kecepatan? Mungkin lebih mudah untuk mempertahankan / memahami jika Anda memiliki aplikasi besar seperti aplikasi besar?
javascript
ecmascript-6
ssbb.dll
sumber
sumber
Jawaban:
class
Sintaks baru , untuk saat ini , sebagian besar adalah gula sintaksis. (Tapi, Anda tahu, baik jenis gula.) Tidak ada dalam ES2015-ES2020 yangclass
dapat melakukan itu Anda tidak dapat melakukan dengan fungsi konstruktor danReflect.construct
(termasuk subclassingError
danArray
¹). (Ini adalah kemungkinan akan ada beberapa hal dalam ES2021 yang dapat Anda lakukan denganclass
bahwa Anda tidak bisa melakukan sebaliknya: bidang swasta , metode swasta , dan bidang statis / metode statis pribadi .)Ini adalah warisan prototipe yang sama yang selalu kita miliki, hanya dengan sintaks yang lebih bersih dan nyaman jika Anda suka menggunakan fungsi konstruktor (
new Foo
, dll.). (Khususnya dalam hal mengambil dariArray
atauError
, yang tidak dapat Anda lakukan di ES5 dan sebelumnya. Sekarang Anda dapat melakukannya denganReflect.construct
[ spec , MDN ], tetapi tidak dengan gaya ES5 yang lama.)Ya, Anda masih bisa memodifikasi
prototype
objek pada konstruktor kelas setelah Anda membuat kelas. Misalnya, ini legal:class Foo { constructor(name) { this.name = name; } test1() { console.log("test1: name = " + this.name); } } Foo.prototype.test2 = function() { console.log("test2: name = " + this.name); };
Dengan memberikan idiom khusus untuk ini, saya kira mungkin saja mesinnya dapat melakukan pengoptimalan pekerjaan yang lebih baik. Tapi mereka sudah sangat pandai dalam mengoptimalkan, saya tidak mengharapkan perbedaan yang signifikan.
Singkatnya: Jika Anda tidak menggunakan fungsi konstruktor pada awalnya, lebih suka
Object.create
atau serupa,class
tidak berguna bagi Anda.Jika Anda menggunakan fungsi konstruktor, ada beberapa keuntungan untuk
class
:Sintaksnya lebih sederhana dan tidak terlalu rentan terhadap kesalahan.
Jauh lebih mudah (dan sekali lagi, lebih sedikit rawan kesalahan) untuk menyiapkan hierarki pewarisan menggunakan sintaks baru daripada dengan yang lama.
class
melindungi Anda dari kesalahan umum karena gagal menggunakannew
fungsi konstruktor (dengan meminta konstruktor melontarkan pengecualian jikathis
bukan objek yang valid untuk konstruktor).Memanggil versi metode prototipe induk jauh lebih sederhana dengan sintaks baru daripada yang lama (
super.method()
bukanParentConstructor.prototype.method.call(this)
atauObject.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this)
).Berikut perbandingan sintaks untuk hierarki:
// ***ES2015+** class Person { constructor(first, last) { this.first = first; this.last = last; } personMethod() { // ... } } class Employee extends Person { constructor(first, last, position) { super(first, last); this.position = position; } employeeMethod() { // ... } } class Manager extends Employee { constructor(first, last, position, department) { super(first, last, position); this.department = department; } personMethod() { const result = super.personMethod(); // ...use `result` for something... return result; } managerMethod() { // ... } }
Contoh:
Tampilkan cuplikan kode
// ***ES2015+** class Person { constructor(first, last) { this.first = first; this.last = last; } personMethod() { return `Result from personMethod: this.first = ${this.first}, this.last = ${this.last}`; } } class Employee extends Person { constructor(first, last, position) { super(first, last); this.position = position; } personMethod() { const result = super.personMethod(); return result + `, this.position = ${this.position}`; } employeeMethod() { // ... } } class Manager extends Employee { constructor(first, last, position, department) { super(first, last, position); this.department = department; } personMethod() { const result = super.personMethod(); return result + `, this.department = ${this.department}`; } managerMethod() { // ... } } const m = new Manager("Joe", "Bloggs", "Special Projects Manager", "Covert Ops"); console.log(m.personMethod());
vs.
// **ES5** var Person = function(first, last) { if (!(this instanceof Person)) { throw new Error("Person is a constructor function, use new with it"); } this.first = first; this.last = last; }; Person.prototype.personMethod = function() { // ... }; var Employee = function(first, last, position) { if (!(this instanceof Employee)) { throw new Error("Employee is a constructor function, use new with it"); } Person.call(this, first, last); this.position = position; }; Employee.prototype = Object.create(Person.prototype); Employee.prototype.constructor = Employee; Employee.prototype.employeeMethod = function() { // ... }; var Manager = function(first, last, position, department) { if (!(this instanceof Manager)) { throw new Error("Manager is a constructor function, use new with it"); } Employee.call(this, first, last, position); this.department = department; }; Manager.prototype = Object.create(Employee.prototype); Manager.prototype.constructor = Manager; Manager.prototype.personMethod = function() { var result = Employee.prototype.personMethod.call(this); // ...use `result` for something... return result; }; Manager.prototype.managerMethod = function() { // ... };
Contoh Langsung:
Tampilkan cuplikan kode
// **ES5** var Person = function(first, last) { if (!(this instanceof Person)) { throw new Error("Person is a constructor function, use new with it"); } this.first = first; this.last = last; }; Person.prototype.personMethod = function() { return "Result from personMethod: this.first = " + this.first + ", this.last = " + this.last; }; var Employee = function(first, last, position) { if (!(this instanceof Employee)) { throw new Error("Employee is a constructor function, use new with it"); } Person.call(this, first, last); this.position = position; }; Employee.prototype = Object.create(Person.prototype); Employee.prototype.constructor = Employee; Employee.prototype.personMethod = function() { var result = Person.prototype.personMethod.call(this); return result + ", this.position = " + this.position; }; Employee.prototype.employeeMethod = function() { // ... }; var Manager = function(first, last, position, department) { if (!(this instanceof Manager)) { throw new Error("Manager is a constructor function, use new with it"); } Employee.call(this, first, last, position); this.department = department; }; Manager.prototype = Object.create(Employee.prototype); Manager.prototype.constructor = Manager; Manager.prototype.personMethod = function() { var result = Employee.prototype.personMethod.call(this); return result + ", this.department = " + this.department; }; Manager.prototype.managerMethod = function() { // ... }; var m = new Manager("Joe", "Bloggs", "Special Projects Manager", "Covert Ops"); console.log(m.personMethod());
Seperti yang Anda lihat, banyak hal berulang dan bertele-tele di sana yang mudah salah dan membosankan untuk diketik ulang (itulah sebabnya saya menulis skrip untuk melakukannya , dulu).
¹ "Tidak ada dalam ES2015-ES2018 yang
class
dapat melakukan hal yang tidak dapat Anda lakukan dengan fungsi konstruktor danReflect.construct
(termasuk subclassingError
danArray
)"Contoh:
Tampilkan cuplikan kode
// Creating an Error subclass: function MyError(...args) { return Reflect.construct(Error, args, this.constructor); } MyError.prototype = Object.create(Error.prototype); MyError.prototype.constructor = MyError; MyError.prototype.myMethod = function() { console.log(this.message); }; // Example use: function outer() { function inner() { const e = new MyError("foo"); console.log("Callng e.myMethod():"); e.myMethod(); console.log(`e instanceof MyError? ${e instanceof MyError}`); console.log(`e instanceof Error? ${e instanceof Error}`); throw e; } inner(); } outer();
.as-console-wrapper { max-height: 100% !important; }
sumber
class
. JavaScript cukup fleksibel sehingga Anda tidak perlu menggunakan fungsi konstruktor jika Anda tidak mau, itulah yang Anda lakukan, tetapi itu berbeda dariclass
- intinyaclass
adalah menyederhanakan pewarisan pseudo-klasik dalam JavaScript.Symbol
mengacaukannya). Tetapi hal yang hebat, sekali lagi, adalah jika Anda tidak ingin menggunakannya, Anda tidak perlu melakukannya. Saya menemukan dalam pekerjaan saya bahwa fungsi konstruktor masuk akal di banyak tempat dan semantiknew
meningkatkan kejelasan; danObject.create
masuk akal di banyak tempat lain, khususnya. situasi fasad. Mereka saling melengkapi. :-)class
sintaks tidak membuat JavaScript menjadi lebih diketik daripada sebelumnya. Seperti yang saya katakan di jawaban, itu sebagian besar hanya (jauh) sintaks yang lebih sederhana untuk apa yang sudah ada. Benar-benar ada kebutuhan untuk itu, orang-orang terus-menerus salah menggunakan sintaks lama. Ini juga tidak berarti Anda harus menggunakan warisan lebih dari yang Anda lakukan sebelumnya. (Dan tentu saja, jika Anda tidak pernah menyiapkan "kelas" dengan sintaks lama, Anda juga tidak perlu melakukannya dengan sintaks baru.)Kelas ES6 adalah gula sintaksis untuk sistem kelas prototipe yang kita gunakan saat ini. Mereka membuat kode Anda lebih ringkas dan mendokumentasikan diri sendiri, yang merupakan alasan yang cukup untuk menggunakannya (menurut saya).
Menggunakan Babel untuk memindahkan kelas ES6 ini:
class Foo { constructor(bar) { this._bar = bar; } getBar() { return this._bar; } }
akan memberi Anda sesuatu seperti:
var Foo = (function () { function Foo(bar) { this._bar = bar; } Foo.prototype.getBar = function () { return this._bar; } return Foo; })();
Versi kedua tidak jauh lebih rumit, ini lebih banyak kode yang harus dipertahankan. Ketika Anda terlibat dalam warisan, pola-pola itu menjadi lebih rumit.
Karena kelas mengkompilasi ke pola prototipe yang sama yang telah kita gunakan, Anda dapat melakukan manipulasi prototipe yang sama pada mereka. Itu termasuk menambahkan metode dan sejenisnya pada waktu proses, mengakses metode
Foo.prototype.getBar
, dll.Ada beberapa dukungan dasar untuk privasi di ES6 hari ini, meskipun itu didasarkan pada tidak mengekspor objek yang tidak ingin Anda akses. Misalnya, Anda dapat:
const BAR_NAME = 'bar'; export default class Foo { static get name() { return BAR_NAME; } }
dan
BAR_NAME
tidak akan tersedia untuk modul lain sebagai referensi secara langsung.Banyak perpustakaan telah mencoba mendukung atau menyelesaikan ini, seperti Backbone dengan mereka
extends
helper yang mengambil hash yang tidak tervalidasi dari fungsi dan properti seperti metode, tetapi tidak ada sistem yang konsisten untuk mengekspos warisan prototipe yang tidak melibatkan penyia-nyiaan dengan prototipe.Karena kode JS menjadi lebih rumit dan basis kode lebih besar, kami mulai mengembangkan banyak pola untuk menangani hal-hal seperti pewarisan dan modul. IIFE yang digunakan untuk membuat ruang lingkup privat untuk modul memiliki banyak tanda kurung dan kurung; kehilangan salah satu dari mereka dapat menghasilkan skrip valid yang melakukan sesuatu yang sama sekali berbeda (melewatkan titik koma setelah modul dapat meneruskan modul berikutnya sebagai parameter, yang jarang bagus).
tl; dr: itu gula untuk apa yang sudah kita lakukan dan membuat niat Anda jelas dalam kode.
sumber