Setelah banyak pengeditan, jawaban ini telah menjadi monster. Sebelumnya saya minta maaf.
Pertama-tama, eval()
tidak selalu buruk, dan dapat membawa manfaat dalam kinerja ketika digunakan dalam evaluasi malas, misalnya. Malas-evaluasi mirip dengan lazy-loading, tetapi Anda pada dasarnya menyimpan kode Anda dalam string, dan kemudian menggunakan eval
atau new Function
untuk mengevaluasi kode. Jika Anda menggunakan beberapa trik, maka itu akan menjadi jauh lebih berguna daripada kejahatan, tetapi jika Anda tidak melakukannya, itu dapat menyebabkan hal-hal buruk. Anda dapat melihat sistem modul saya yang menggunakan pola ini: https://github.com/TheHydroImpulse/resolve.js . Resolve.js menggunakan eval alih-alih new Function
terutama untuk memodelkan CommonJS exports
dan module
variabel yang tersedia di setiap modul, dan new Function
membungkus kode Anda dalam fungsi anonim, meskipun, saya akhirnya membungkus setiap modul dalam suatu fungsi yang saya lakukan secara manual dalam kombinasi dengan eval.
Anda membaca lebih lanjut tentang hal itu di dua artikel berikut, yang kemudian juga merujuk pada yang pertama.
Generator Harmoni
Sekarang generator akhirnya mendarat di V8 dan dengan demikian di Node.js, di bawah bendera ( --harmony
atau --harmony-generators
). Ini sangat mengurangi jumlah panggilan balik yang Anda miliki. Itu membuat menulis kode asinkron benar-benar hebat.
Cara terbaik untuk menggunakan generator adalah dengan menggunakan semacam perpustakaan aliran kontrol. Ini akan memungkinkan mengalir terus saat Anda menghasilkan di dalam generator.
Rekap / Ikhtisar:
Jika Anda tidak terbiasa dengan generator, itu adalah praktik penghentian sementara eksekusi fungsi khusus (disebut generator). Praktek ini disebut menghasilkan menggunakan yield
kata kunci.
Contoh:
function* someGenerator() {
yield []; // Pause the function and pass an empty array.
}
Jadi, setiap kali Anda memanggil fungsi ini pertama kali, itu akan mengembalikan generator contoh baru. Ini memungkinkan Anda untuk memanggil next()
objek itu untuk memulai atau melanjutkan generator.
var gen = someGenerator();
gen.next(); // { value: Array[0], done: false }
Anda akan terus menelepon next
sampai done
kembali true
. Ini berarti generator telah sepenuhnya menyelesaikan eksekusi, dan tidak ada lagi yield
pernyataan.
Aliran kontrol:
Seperti yang Anda lihat, mengendalikan generator tidak otomatis. Anda harus melanjutkan secara manual. Itu sebabnya control-flow libraries seperti co digunakan.
Contoh:
var co = require('co');
co(function*() {
yield query();
yield query2();
yield query3();
render();
});
Hal ini memungkinkan kemungkinan untuk menulis semuanya di Node (dan browser dengan Regenerator Facebook yang mengambil, sebagai input, kode sumber yang menggunakan generator harmoni dan membagi kode ES5 yang sepenuhnya kompatibel) dengan gaya sinkron.
Generator masih sangat baru, dan karenanya membutuhkan Node.js> = v11.2. Saat saya menulis ini, v0.11.x masih tidak stabil dan dengan demikian banyak modul asli rusak dan akan sampai v0.12, di mana API asli akan tenang.
Untuk menambah jawaban asli saya:
Saya baru-baru ini lebih memilih API yang lebih fungsional dalam JavaScript. Konvensi memang menggunakan OOP di belakang layar ketika dibutuhkan tetapi itu menyederhanakan semuanya.
Ambil contoh sistem tampilan (klien atau server).
view('home.welcome');
Jauh lebih mudah dibaca atau diikuti daripada:
var views = {};
views['home.welcome'] = new View('home.welcome');
The view
Fungsi hanya memeriksa apakah pandangan yang sama sudah ada dalam peta lokal. Jika tampilan tidak ada, itu akan membuat tampilan baru dan menambahkan entri baru ke peta.
function view(name) {
if (!name) // Throw an error
if (view.views[name]) return view.views[name];
return view.views[name] = new View({
name: name
});
}
// Local Map
view.views = {};
Sangat mendasar, bukan? Saya merasa secara dramatis menyederhanakan antarmuka publik dan membuatnya lebih mudah digunakan. Saya juga menggunakan kemampuan rantai ...
view('home.welcome')
.child('menus')
.child('auth')
Tower, kerangka kerja yang saya kembangkan (dengan orang lain) atau mengembangkan versi berikutnya (0.5.0) akan menggunakan pendekatan fungsional ini di sebagian besar antarmuka yang terbuka.
Beberapa orang memanfaatkan serat sebagai cara untuk menghindari "panggilan balik neraka". Ini pendekatan yang sangat berbeda untuk JavaScript, dan saya bukan penggemar berat itu, tetapi banyak kerangka kerja / platform menggunakannya; termasuk Meteor, karena mereka memperlakukan Node.js sebagai platform utas / per koneksi.
Saya lebih suka menggunakan metode abstrak untuk menghindari panggilan balik neraka. Ini mungkin menjadi rumit, tetapi sangat menyederhanakan kode aplikasi yang sebenarnya. Ketika membantu membangun kerangka TowerJS , itu memecahkan banyak masalah kami, meskipun, Anda pasti masih memiliki beberapa tingkat panggilan balik, tetapi sarangnya tidak dalam.
// app/config/server/routes.js
App.Router = Tower.Router.extend({
root: Tower.Route.extend({
route: '/',
enter: function(context, next) {
context.postsController.page(1).all(function(error, posts) {
context.bootstrapData = {posts: posts};
next();
});
},
action: function(context, next) {
context.response.render('index', context);
next();
},
postRoutes: App.PostRoutes
})
});
Contoh dari kami, yang saat ini sedang dikembangkan, sistem perutean dan "pengontrol", meskipun agak berbeda dari "rel-seperti" tradisional. Tapi contohnya sangat kuat dan meminimalkan jumlah panggilan balik dan membuat semuanya tampak jelas.
Masalah dengan pendekatan ini adalah bahwa semuanya disarikan. Tidak ada yang berjalan apa adanya, dan membutuhkan "kerangka kerja" di belakangnya. Tetapi jika fitur dan gaya pengkodean seperti ini diterapkan dalam suatu kerangka kerja, maka ini merupakan kemenangan besar.
Untuk pola-pola dalam JavaScript, itu benar-benar tergantung. Warisan hanya benar-benar berguna ketika menggunakan CoffeeScript, Ember, atau kerangka / infrastruktur "kelas" apa pun. Saat Anda berada di dalam lingkungan JavaScript "murni", menggunakan antarmuka prototipe tradisional berfungsi seperti pesona:
function Controller() {
this.resource = get('resource');
}
Controller.prototype.index = function(req, res, next) {
next();
};
Ember.js memulai, setidaknya bagi saya, menggunakan pendekatan berbeda untuk membangun objek. Alih-alih membangun setiap metode prototipe secara independen, Anda akan menggunakan antarmuka seperti modul.
Ember.Controller.extend({
index: function() {
this.hello = 123;
},
constructor: function() {
console.log(123);
}
});
Semua ini berbeda gaya "coding", tetapi tambahkan ke basis kode Anda.
Polimorfisme
Polimorfisme tidak banyak digunakan dalam JavaScript murni, di mana bekerja dengan pewarisan dan menyalin model "kelas" seperti membutuhkan banyak kode boilerplate.
Desain Berbasis Acara / Komponen
Model berbasis peristiwa dan berbasis komponen adalah pemenang IMO, atau yang paling mudah untuk diajak bekerja sama, terutama ketika bekerja dengan Node.js, yang memiliki komponen EventEmitter bawaan, meskipun, menerapkan emitor seperti itu sepele, itu hanya tambahan yang bagus .
event.on("update", function(){
this.component.ship.velocity = 0;
event.emit("change.ship.velocity");
});
Contoh saja, tapi ini model yang bagus untuk dikerjakan. Terutama dalam proyek berorientasi permainan / komponen.
Desain komponen adalah konsep yang terpisah dengan sendirinya, tetapi saya pikir itu bekerja sangat baik dalam kombinasi dengan sistem acara. Game secara tradisional dikenal untuk desain berbasis komponen, di mana pemrograman berorientasi objek hanya membawa Anda sejauh ini.
Desain berbasis komponen memiliki kegunaannya. Tergantung pada jenis sistem bangunan Anda. Saya yakin ini akan bekerja dengan aplikasi web, tetapi itu akan bekerja dengan sangat baik di lingkungan permainan, karena jumlah objek, dan sistem yang terpisah, tetapi contoh lain pasti ada.
Pola Pub / Sub
Pengikatan acara dan pub / sub serupa. Pola pub / sub benar-benar bersinar di aplikasi Node.js karena bahasa pemersatu, tetapi dapat bekerja di bahasa apa pun. Bekerja sangat baik dalam aplikasi waktu nyata, permainan, dll.
model.subscribe("message", function(event){
console.log(event.params.message);
});
model.publish("message", {message: "Hello, World"});
Pengamat
Ini mungkin subyektif, karena beberapa orang memilih untuk menganggap pola Pengamat sebagai pub / sub, tetapi mereka memiliki perbedaan.
"Pengamat adalah pola desain di mana suatu objek (dikenal sebagai subjek) memelihara daftar objek tergantung padanya (pengamat), secara otomatis memberi tahu mereka tentang segala perubahan yang terjadi." - Pola Pengamat
Pola pengamat adalah langkah di luar sistem pub / sub khas. Objek memiliki hubungan yang ketat atau metode komunikasi satu sama lain. Objek "Subjek" akan menyimpan daftar tanggungan "Pengamat". Subjek akan membuat pengamatnya tetap mutakhir.
Pemrograman Reaktif
Pemrograman reaktif adalah konsep yang lebih kecil, lebih tidak dikenal, terutama dalam JavaScript. Ada satu kerangka / pustaka (yang saya tahu) yang memperlihatkan API yang mudah untuk digunakan menggunakan "pemrograman reaktif" ini.
Sumber daya pada pemrograman reaktif:
Pada dasarnya, ini memiliki satu set data yang disinkronkan (baik itu variabel, fungsi, dll.).
var a = 1;
var b = 2;
var c = a + b;
a = 2;
console.log(c); // should output 4
Saya percaya pemrograman reaktif sangat tersembunyi, terutama dalam bahasa imperatif. Ini adalah paradigma pemrograman yang luar biasa kuat, terutama di Node.js. Meteor telah menciptakan mesin reaktif sendiri di mana kerangka dasarnya didasarkan. Bagaimana reaktivitas Meteor bekerja di belakang layar? adalah gambaran bagus tentang cara kerjanya secara internal.
Meteor.autosubscribe(function() {
console.log("Hello " + Session.get("name"));
});
Ini akan mengeksekusi secara normal, menampilkan nilai name
, tetapi jika kita mengubahnya
Session.set ('name', 'Bob');
Ini akan menampilkan ulang tampilan console.log Hello Bob
. Contoh dasar, tetapi Anda dapat menerapkan teknik ini untuk model dan transaksi data waktu nyata. Anda dapat membuat sistem yang sangat kuat di belakang protokol ini.
Meteor ...
Pola reaktif dan pola pengamat sangat mirip. Perbedaan utama adalah bahwa pola pengamat biasanya menggambarkan aliran data dengan seluruh objek / kelas vs pemrograman reaktif menggambarkan aliran data ke properti tertentu.
Meteor adalah contoh yang bagus dari pemrograman reaktif. Itu runtime sedikit rumit karena kurangnya JavaScript dari peristiwa perubahan nilai asli (Harmony proksi mengubahnya). Kerangka kerja sisi klien lainnya, Ember.js dan AngularJS juga menggunakan pemrograman reaktif (hingga batas tertentu).
Dua kerangka kerja berikutnya menggunakan pola reaktif yang paling menonjol pada templatnya (yaitu memperbarui otomatis). Angular.js menggunakan teknik pemeriksaan kotor sederhana. Saya tidak akan menyebut pemrograman reaktif ini persis, tetapi dekat, karena pemeriksaan kotor tidak real-time. Ember.js menggunakan pendekatan yang berbeda. Penggunaan set()
dan get()
metode Ember yang memungkinkan mereka untuk segera memperbarui nilai-nilai yang tergantung. Dengan runloop mereka sangat efisien dan memungkinkan nilai yang lebih tergantung, di mana sudut memiliki batas teoritis.
Janji
Bukan perbaikan untuk callback, tetapi menghapus lekukan, dan menjaga fungsi bersarang ke minimum. Itu juga menambahkan beberapa sintaks yang bagus untuk masalah.
fs.open("fs-promise.js", process.O_RDONLY).then(function(fd){
return fs.read(fd, 4096);
}).then(function(args){
util.puts(args[0]); // print the contents of the file
});
Anda juga bisa menyebarkan fungsi panggilan balik sehingga tidak sejajar, tapi itu keputusan desain lainnya.
Pendekatan lain adalah dengan menggabungkan acara dan janji di mana Anda akan memiliki fungsi untuk mengirimkan acara secara tepat, maka fungsi fungsional nyata (yang memiliki logika nyata di dalamnya) akan mengikat ke acara tertentu. Anda kemudian akan melewati metode dispatcher di dalam setiap posisi panggilan balik, meskipun, Anda harus bekerja di luar beberapa ketegaran yang akan terlintas dalam pikiran, seperti parameter, mengetahui fungsi mana untuk mengirim ke, dll ...
Fungsi Fungsi Tunggal
Alih-alih memiliki kekacauan panggilan balik yang sangat besar, pertahankan satu fungsi untuk satu tugas, dan lakukan tugas itu dengan baik. Terkadang Anda bisa maju sendiri dan menambahkan lebih banyak fungsi dalam setiap fungsi, tetapi tanyakan pada diri sendiri: Bisakah ini menjadi fungsi independen? Beri nama fungsinya, dan ini membersihkan lekukan Anda dan, sebagai hasilnya, membersihkan masalah neraka panggil balik.
Pada akhirnya, saya sarankan mengembangkan, atau menggunakan "kerangka" kecil, pada dasarnya hanya tulang punggung untuk aplikasi Anda, dan luangkan waktu untuk membuat abstraksi, memutuskan sistem berbasis acara, atau "banyak modul kecil yang sistem "independen. Saya telah bekerja dengan beberapa proyek Node.js di mana kode itu sangat berantakan dengan panggilan balik neraka khususnya, tetapi juga kurangnya pemikiran sebelum mereka mulai coding. Luangkan waktu Anda untuk memikirkan berbagai kemungkinan yang berbeda dalam hal API, dan sintaksis.
Ben Nadel telah membuat beberapa posting blog yang sangat bagus tentang JavaScript dan beberapa pola yang cukup ketat dan canggih yang dapat bekerja dalam situasi Anda. Beberapa postingan bagus yang akan saya tekankan:
Inversi-of-Control
Meskipun tidak persis terkait dengan panggilan balik neraka, ini dapat membantu Anda secara keseluruhan arsitektur, terutama dalam unit test.
Dua sub-versi utama inversi-of-control adalah Dependency Injection dan Service Locator. Saya menemukan Service Locator sebagai yang termudah dalam JavaScript, bukan Injeksi Ketergantungan. Mengapa? Terutama karena JavaScript adalah bahasa yang dinamis dan tidak ada pengetikan statis. Java dan C #, antara lain, "dikenal" untuk injeksi dependensi karena Anda dapat mendeteksi jenis, dan mereka telah dibangun di antarmuka, kelas, dll ... Ini membuat semuanya cukup mudah. Anda dapat, bagaimanapun, menciptakan kembali fungsi ini dalam JavaScript, meskipun, itu tidak akan menjadi identik dan sedikit meretas, saya lebih suka menggunakan pelacak layanan di dalam sistem saya.
Setiap jenis inversi-of-control akan secara dramatis memisahkan kode Anda menjadi modul terpisah yang dapat diejek atau dipalsukan kapan saja. Dirancang versi kedua dari mesin rendering Anda? Luar biasa, ganti saja antarmuka lama dengan yang baru. Penelusur layanan sangat menarik dengan Proksi Harmony baru, meskipun, hanya dapat digunakan secara efektif dalam Node.js, ia menyediakan API yang lebih baik, daripada menggunakan Service.get('render');
dan sebagai gantinya Service.render
. Saat ini saya sedang mengerjakan sistem semacam itu: https://github.com/TheHydroImpulse/Ettore .
Meskipun kurangnya pengetikan statis (pengetikan statis menjadi alasan yang memungkinkan untuk penggunaan efektif dalam injeksi dependensi di Java, C #, PHP - Ini bukan pengetikan statis, tetapi memiliki pengetikan jenis.) Mungkin dipandang sebagai titik negatif, Anda dapat pasti mengubahnya menjadi poin yang kuat. Karena semuanya dinamis, Anda dapat merekayasa sistem statis "palsu". Dalam kombinasi dengan pencari lokasi layanan, Anda dapat membuat setiap komponen / modul / kelas / instance diikat ke suatu jenis.
var Service, componentA;
function Manager() {
this.instances = {};
}
Manager.prototype.get = function(name) {
return this.instances[name];
};
Manager.prototype.set = function(name, value) {
this.instances[name] = value;
};
Service = new Manager();
componentA = {
type: "ship",
value: new Ship()
};
Service.set('componentA', componentA);
// DI
function World(ship) {
if (ship === Service.matchType('ship', ship))
this.ship = new ship();
else
throw Error("Wrong type passed.");
}
// Use Case:
var worldInstance = new World(Service.get('componentA'));
Contoh sederhana. Untuk dunia nyata, penggunaan yang efektif, Anda harus membawa konsep ini lebih jauh, tetapi ini dapat membantu memisahkan sistem Anda jika Anda benar-benar menginginkan injeksi ketergantungan tradisional. Anda mungkin perlu mengutak-atik konsep ini sedikit. Saya belum terlalu memikirkan contoh sebelumnya.
Model-View-Controller
Pola paling jelas, dan paling banyak digunakan di web. Beberapa tahun yang lalu, JQuery sangat populer, dan karena itu, plugin JQuery lahir. Anda tidak memerlukan kerangka kerja lengkap di sisi klien, cukup gunakan jquery dan beberapa plugin.
Sekarang, ada perang kerangka JavaScript sisi klien yang besar. Sebagian besar menggunakan pola MVC, dan mereka semua menggunakannya secara berbeda. MVC tidak selalu menerapkan hal yang sama.
Jika Anda menggunakan antarmuka prototipe tradisional, Anda mungkin kesulitan mendapatkan gula sintaksis atau API yang bagus ketika bekerja dengan MVC, kecuali jika Anda ingin melakukan pekerjaan manual. Ember.js menyelesaikan ini dengan membuat sistem "class" / object ". Pengontrol mungkin terlihat seperti:
var Controller = Ember.Controller.extend({
index: function() {
// Do something....
}
});
Sebagian besar pustaka sisi klien juga memperluas pola MVC dengan memperkenalkan view-helper (menjadi tampilan) dan templat (menjadi tampilan).
Fitur JavaScript Baru:
Ini hanya akan efektif jika Anda menggunakan Node.js, namun demikian, ini sangat berharga. Pembicaraan di NodeConf oleh Brendan Eich ini menghadirkan beberapa fitur baru yang keren. Sintaks fungsi yang diusulkan, dan terutama pustaka Task.js js.
Ini mungkin akan memperbaiki sebagian besar masalah dengan fungsi bersarang dan akan membawa kinerja yang sedikit lebih baik karena kurangnya fungsi overhead.
Saya tidak terlalu yakin apakah V8 mendukung ini secara asli, terakhir saya memeriksa Anda perlu mengaktifkan beberapa flag, tetapi ini berfungsi di port Node.js yang menggunakan SpiderMonkey .
Sumber Daya Ekstra: