Birçok düzenlemeden sonra, bu cevap uzun süren bir canavar oldu. Şimdiden özür dilerim.
Her şeyden önce, eval()
her zaman kötü değildir ve örneğin tembel değerlendirmede kullanıldığında performanstan fayda sağlayabilir. Tembel değerlendirme tembel yüklemeye benzer, ancak temel olarak kodunuzu dizelerde saklar ve sonra kodu değerlendirmek için eval
veya new Function
kullanırsınız. Bazı hileler kullanırsanız, o zaman kötülükten çok daha kullanışlı hale gelecektir, ancak kullanmazsanız, kötü şeylere yol açabilir. Bu modeli kullanan modül sistemime bakabilirsiniz: https://github.com/TheHydroImpulse/resolve.js . Resolve.js new Function
öncelikle her bir modüldeki CommonJS exports
ve module
değişkenleri modellemek yerine eval kullanır ve new Function
kodunuzu anonim bir işlev içinde sarar, ancak her modülü eval ile birlikte yaptığım bir işlevde tamamlamayı bırakarım.
Bu konuda aşağıdaki iki makalede daha fazlasını okudunuz, daha sonra birincisine de değiniyoruz.
Harmony Jeneratörleri
Şimdi, jeneratörler nihayet V8'e ve dolayısıyla Node.js'ye bir bayrak ( --harmony
veya --harmony-generators
) altına girdiler . Bunlar, sahip olduğunuz geri arama miktarını büyük ölçüde azaltır. Zaman uyumsuz kod yazmayı gerçekten harika kılar.
Jeneratörleri kullanmanın en iyi yolu bir çeşit kontrol-akış kütüphanesi kullanmaktır. Bu, jeneratörler içinde verdiğiniz gibi çalışmaya devam etmek için akmaya olanak sağlayacaktır.
Recap / Genel Bakış:
Jeneratörlere yabancıysanız, özel fonksiyonların (jeneratör olarak adlandırılan) yürütülmesini duraklatma uygulamasıdır. Bu uygulamaya anahtar kelime kullanılarak verim denir yield
.
Örnek:
function* someGenerator() {
yield []; // Pause the function and pass an empty array.
}
Böylece, bu fonksiyonu ilk ne zaman çağırırsanız, yeni bir jeneratör örneği döndürür. Bu next()
, jeneratörü başlatmak veya devam ettirmek için o nesneyi çağırmanızı sağlar .
var gen = someGenerator();
gen.next(); // { value: Array[0], done: false }
Sen aramaya devam ediyorum next
kadar done
getiri true
. Bu, jeneratörün yürütmesini tamamen bitirdiği ve daha fazla yield
açıklama olmadığı anlamına gelir .
Kontrol akışı:
Gördüğünüz gibi, jeneratörlerin kontrolü otomatik değildir. Her birine manuel olarak devam etmeniz gerekiyor. Bu yüzden co gibi kontrol akışı kütüphaneleri kullanılır.
Örnek:
var co = require('co');
co(function*() {
yield query();
yield query2();
yield query3();
render();
});
Bu, Düğüm'deki her şeyi (ve, uyumlu olarak üreteçleri kullanan ve tam uyumlu ES5 kodunu tam olarak dağıtan ES5 kodunu dağıtan kaynak kodunu alan Facebook Regenerator ile tarayıcı) yazabilme imkanı sağlar .
Jeneratörler hala oldukça yeni ve bu nedenle Node.js> = v11.2 gerektiriyor. Bunu yazarken, v0.11.x hala dengesiz ve bu nedenle birçok yerel modül bozuldu ve yerel API'nin sakinleşeceği v0.12'ye kadar olacak.
Orijinal cevabımı eklemek için:
Geçenlerde JavaScript’te daha işlevsel bir API tercih ettim. Kongre gerektiğinde perde arkasında OOP kullanıyor ancak her şeyi basitleştiriyor.
Örneğin bir görüntü sistemi (istemci veya sunucu) düşünün.
view('home.welcome');
Okumak veya takip etmek çok daha kolaydır:
var views = {};
views['home.welcome'] = new View('home.welcome');
view
Aynı görünüm zaten yerel bir haritası varsa fonksiyon sadece denetler. Görünüm yoksa, yeni bir görünüm oluşturur ve haritaya yeni bir giriş ekler.
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 = {};
Son derece basit, değil mi? Kamusal arayüzü çarpıcı biçimde basitleştirdiğini ve kullanımını kolaylaştırdığını buluyorum. Ayrıca zincir yetenek kullanıyorum ...
view('home.welcome')
.child('menus')
.child('auth')
Tower, geliştirdiğim (başkasıyla birlikte) veya bir sonraki sürümü (0.5.0) geliştirdiğim bir çerçeve, bu işlevsel yaklaşımı ortaya çıkaran arayüzlerde kullanacak.
Bazı insanlar "geri arama cehennemi" ni önlemenin bir yolu olarak elyaflardan yararlanır. JavaScript için oldukça farklı bir yaklaşım ve ben onun çok büyük bir hayranı değilim, ancak birçok çerçeve / platform bunu kullanıyor; Meteor dahil, Node.js'i bir iş parçacığı / bağlantı platformu olarak gördükleri için.
Geri arama cehennemini önlemek için soyutlanmış bir yöntem kullanmayı tercih ederim. Bu hantal hale gelebilir, ancak gerçek uygulama kodunu büyük ölçüde basitleştirir. TowerJS çerçevesini oluşturmaya yardımcı olurken , sorunlarımızın çoğunu çözdü, ancak hala belli düzeyde bir geri çağırma düzeyine sahip olacaksınız, ancak yuvalama derin değil.
// 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
})
});
Halen geliştirilmekte olan, rotalama sistemi ve "denetleyicilere" bir örnek, geleneksel "ray benzeri" den oldukça farklı olsa da. Ancak örnek son derece güçlüdür ve geri aramaların miktarını en aza indirir ve olayları oldukça belirgin hale getirir.
Bu yaklaşımla ilgili sorun, her şeyin soyutlanmasıdır. Hiçbir şey olduğu gibi çalışamaz ve arkasında bir "çerçeve" gerektirmez. Ancak, bu tür özellikler ve kodlama stili bir çerçeve içinde uygulanırsa, o zaman büyük bir kazançtır.
JavaScript'teki desenler için, dürüstçe bağlıdır. Kalıtım, yalnızca CoffeeScript, Ember veya herhangi bir "sınıf" çerçevesini / altyapısını kullanırken gerçekten yararlıdır. "Saf" bir JavaScript ortamındaysanız, geleneksel prototip arayüzünü kullanmak bir cazibe işlevi görür:
function Controller() {
this.resource = get('resource');
}
Controller.prototype.index = function(req, res, next) {
next();
};
Ember.js, en azından benim için, nesneleri inşa etmek için farklı bir yaklaşım kullanmaya başladı. Her prototip yöntemi bağımsız olarak oluşturmak yerine, modül benzeri bir arabirim kullanırsınız.
Ember.Controller.extend({
index: function() {
this.hello = 123;
},
constructor: function() {
console.log(123);
}
});
Bütün bunlar farklı "kodlama" stilleridir ancak kod tabanınıza ekleyin.
Polimorfizm
Polimorfizm, kalıtımsal çalışma ve "sınıf" benzeri bir modelin kopyalanması için çok fazla kodlama kodu gerektiren saf JavaScript'te yaygın olarak kullanılmaz.
Etkinlik / Bileşen Tabanlı Tasarım
Etkinlik temelli ve Bileşen temelli modeller kazananlar IMO'dur, ya da özellikle yerleşik bir EventEmitter bileşenine sahip olan Node.js ile çalışırken, bu tür yayınlayıcıları uygulamak önemsiz olmakla birlikte, çalışılması en kolay olanıdır. .
event.on("update", function(){
this.component.ship.velocity = 0;
event.emit("change.ship.velocity");
});
Sadece bir örnek, ama çalışmak için güzel bir model. Özellikle oyun / bileşen odaklı bir projede.
Komponent tasarımı ayrı bir konsepttir, ancak etkinlik sistemlerine kombinasyon halinde oldukça iyi çalıştığını düşünüyorum. Oyunlar geleneksel olarak, nesne yönelimli programlamanın sizi yalnızca şimdiye kadar götürdüğü, bileşen tabanlı tasarımlarla bilinir.
Bileşen tabanlı tasarımın kullanımları vardır. Binanızın ne tür bir sistem olduğuna bağlı. Web uygulamaları ile çalışacağına eminim, ancak oyun ortamında, nesne sayısı ve ayrı sistemler nedeniyle oldukça iyi çalışıyordu, ancak başka örnekler de var.
Pub / Alt Desen
Olay bağlama ve pub / sub benzer. Pub / sub şablonu, birleştirici dil nedeniyle Node.js uygulamalarında gerçekten parlıyor, ancak herhangi bir dilde çalışabiliyor. Gerçek zamanlı uygulamalarda, oyunlarda vb. Son derece iyi çalışır.
model.subscribe("message", function(event){
console.log(event.params.message);
});
model.publish("message", {message: "Hello, World"});
Gözlemci
Bazıları Gözlemci modelini pub / sub olarak düşünmeyi seçtikleri için bu öznel olabilir, ancak farklılıkları vardır.
“Gözlemci, bir nesnenin (özne olarak bilinen) nesneye bağlı olarak bir nesne listesini (gözlemciler) tuttuğu ve durumdaki değişiklikleri otomatik olarak bildiren bir tasarım desenidir.” - Gözlemci Deseni
Gözlemci modeli, tipik pub / alt sistemlerin ötesinde bir adımdır. Nesnelerin birbirleriyle sıkı ilişkileri veya iletişim yöntemleri vardır. Bir "Nesne" nesnesi, "Gözlemciler" bağımlılarının bir listesini tutar. Konu gözlemcilerini güncel tutacaktır.
Reaktif Programlama
Reaktif programlama, özellikle JavaScript'te daha küçük, daha bilinmeyen bir kavramdır. Bu "reaktif programlamayı" kullanmak için API ile kolay bir çalışma sergileyen bir çerçeve / kütüphane (bildiğim kadarıyla) var.
Reaktif programlama ile ilgili kaynaklar:
Temel olarak, bir dizi senkronizasyon verisine sahip (değişkenler, fonksiyonlar vb.).
var a = 1;
var b = 2;
var c = a + b;
a = 2;
console.log(c); // should output 4
Reaktif programlamanın, özellikle zorunlu dillerde, oldukça gizli olduğuna inanıyorum. Şaşırtıcı derecede güçlü bir programlama paradigması, özellikle de Node.js. Meteor , çerçevenin temelde dayandığı kendi reaktif motorunu yarattı. Meteor'un reaktivitesi perde arkasında nasıl çalışıyor? dahili olarak nasıl çalıştığına dair harika bir genel bakış.
Meteor.autosubscribe(function() {
console.log("Hello " + Session.get("name"));
});
Bu, normal olarak yürütülür, değerini gösterir name
, ancak değiştirirsek
Session.set ('isim', 'Bob');
Konsol göstergesini tekrar gösterecek Hello Bob
. Temel bir örnek, ancak bu tekniği gerçek zamanlı veri modelleri ve işlemlere uygulayabilirsiniz. Bu protokolün arkasında son derece güçlü sistemler oluşturabilirsiniz.
Meteor en ...
Reaktif model ve Gözlemci model oldukça benzerdir. Asıl fark, gözlemci modelinin genel olarak tüm nesneler / sınıflarla veri akışını tanımlamasıdır - reaktif programlama, bunun yerine belirli özelliklere veri akışını tanımlar.
Meteor reaktif programlama için harika bir örnek. JavaScript'in yerel değer değişikliği olaylarının olmayışı nedeniyle çalışma zamanı biraz karışıktır (Harmony proxy'ler bunu değiştirir). Diğer istemci tarafındaki çerçeveler, Ember.js ve AngularJS de reaktif programlamadan faydalanmaktadır (bir dereceye kadar).
Daha sonraki iki çerçeve reaktif örüntüyü en çok şablonlarında kullanır (ki otomatik güncelleme). Angular.js basit bir kirli kontrol tekniği kullanır. Buna tam olarak reaktif programlama demezdim, ama kirli kontrol gerçek zamanlı olmadığı için yakın. Ember.js farklı bir yaklaşım kullanıyor. Köz kullanımı set()
ve get()
bağlı değerleri hemen güncellemelerini sağlayan yöntemler. Onların runloopları ile oldukça verimlidir ve açının teorik bir limitinin olduğu yerlerde daha fazla bağımlı değere izin verir.
sözler
Geri aramalar için bir düzeltme değildir, ancak bazı girintileri ortadan kaldırır ve iç içe geçmiş işlevleri en aza indirir. Aynı zamanda soruna bazı güzel sözdizimi ekler.
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
});
Geri arama işlevlerini aynı zamanda yayılmayacak şekilde dağıtabilirsiniz, ancak bu başka bir tasarım kararıdır.
Bir başka yaklaşım, olayları birleştirmek ve olayları uygun bir şekilde gönderme işlevinin nerede olacağına dair sözleri birleştirmek olacaktır, daha sonra gerçek işlevsel işlevler (içinde gerçek mantığı olan kişiler) belirli bir olaya bağlanır. Daha sonra gönderici yöntemini her geri arama pozisyonunun içinden geçirirsiniz, ancak, hangi işlevlerin gönderileceğini bilmek gibi parametreler gibi akla gelebilecek bazı garip şeyleri çözmeniz gerekir.
Tek İşlevli İşlev
Çok büyük bir geri arama cehennemi olmak yerine, tek bir işlevi tek bir görevde tutun ve bu görevi iyi yapın. Bazen kendinize öne geçebilir ve her fonksiyona daha fazla fonksiyonellik ekleyebilirsiniz, ancak kendinize sorun: Bu bağımsız bir fonksiyon olabilir mi? İşleve bir ad verin ve bu girintinizi temizler ve sonuç olarak geri arama cehennemi sorununu temizler.
Sonunda, uygulamanız için temelde yalnızca bir omurga geliştirmeyi veya küçük bir "çerçeve" kullanmanızı öneririm ve soyutlamalar yapmak, olaya dayalı bir sisteme karar vermek veya "küçük modüller yükü" almak için zaman ayırın. bağımsız "sistem. Kodun özellikle geri arama cehennemi ile çok karışık olduğu, ancak kodlamaya başlamadan önce düşünce eksikliğinin olduğu Node.js projeleriyle çalıştım. API ve sözdizimi açısından farklı olasılıkları düşünmek için zaman ayırın.
Ben Nadel , JavaScript hakkında gerçekten iyi blog yazıları ve durumunuzda işe yarayabilecek oldukça katı ve gelişmiş modeller yaptı. Vurgulayacağım bazı iyi yazılar:
Ters çevirme-of-Control
Tam olarak geri arama cehennemi ile ilgili olmasa da, özellikle birim testlerinde genel mimariye yardımcı olabilir.
Kontrolün inversiyonunun iki ana alt versiyonu Bağımlılık Enjeksiyonu ve Servis Bulucu'dur. Bağımlılık Enjeksiyonunun aksine, Hizmet Bulucu'yu JavaScript içinde en kolay buluyorum. Neden? Temel olarak, JavaScript dinamik bir dil olduğundan ve statik yazı olmadığından. Java ve C #, diğerleri arasında, bağımlılık enjeksiyonuyla "tanınır" çünkü türleri tespit edebiliyorsunuz ve arayüzler, sınıflar vb. Bununla birlikte, bu işlevselliği JavaScript'te yeniden oluşturabilirsiniz, ancak aynı ve biraz rahatsız edici olmayacak, sistemlerimde bir servis bulucu kullanmayı tercih ediyorum.
Herhangi bir kontrolün tersine çevrilmesi, kodunuzu istediğiniz zaman alay edilebilecek veya taklit edilebilecek ayrı modüllere dönüştürür. İşleme motorunuzun ikinci bir versiyonunu mu tasarladınız? Müthiş, sadece yenisi için eski arayüzü yerine. Hizmet konumlandırıcıları özellikle yeni Harmony Proxies ile ilgi çekici olsa da, yalnızca Node.js içinde etkin bir şekilde kullanılabiliyorsa, kullanmak Service.get('render');
yerine ve yerine daha iyi bir API sağlar Service.render
. Şu anda bu tür bir sistem üzerinde çalışıyorum: https://github.com/TheHydroImpulse/Ettore .
Statik yazma eksikliği (statik yazma, Java, C #, PHP bağımlılık enjeksiyonunda etkili kullanımlar için olası bir nedendir, ancak statik değil, ancak tip ipuçlarına sahiptir.) Olumsuz bir nokta olarak bakılabilir. kesinlikle güçlü bir noktaya çevirin. Her şey dinamik olduğu için "sahte" statik bir sistem oluşturabilirsiniz. Servis bulucu ile birlikte, her bir bileşen / modül / sınıf / örnek bir türe bağlı olabilir.
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'));
Basit bir örnek. Gerçek bir dünya için, etkili bir kullanım için, bu konsepti daha da ileri götürmeniz gerekir, ancak gerçekten geleneksel bağımlılık enjeksiyonunu istiyorsanız, sisteminizin ayrıştırılmasına yardımcı olabilir. Bu konsept ile biraz uğraşmanız gerekebilir. Önceki örneğe çok fazla düşünmedim.
Model-View-Controller
En belirgin desen ve web üzerinde en çok kullanılanlar. Birkaç yıl önce, JQuery tüm öfke ve böylece, JQuery eklentileri doğdu. Müşteri tarafında tam bir çerçeveye ihtiyacınız yoktu, sadece jquery ve birkaç eklenti kullanın.
Şimdi, büyük bir müşteri tarafı JavaScript çerçeve savaşı var. Bunların çoğu MVC paternini kullanıyor ve hepsi farklı kullanıyor. MVC her zaman aynı şekilde uygulanmaz.
Geleneksel prototip arayüzleri kullanıyorsanız, bazı el işi yapmak istemediğiniz sürece, MVC ile çalışırken sözdizimsel bir şeker veya hoş bir API almakta zorlanabilirsiniz. Ember.js bunu bir "class" / object "sistemi yaratarak çözer.
var Controller = Ember.Controller.extend({
index: function() {
// Do something....
}
});
Çoğu müşteri tarafı kitaplığı, MVC şablonunu, görünüm yardımcıları (görünüm olma) ve şablonlar (görünüm olma) tanıtarak genişletir.
Yeni JavaScript Özellikleri:
Bu yalnızca Node.js kullanıyorsanız etkili olacaktır, ancak yine de paha biçilmezdir. NodeConf'ta Brendan Eich'in bu konuşması bazı harika yeni özellikler getiriyor. Önerilen işlev sözdizimi ve özellikle Task.js js kütüphanesi.
Bu muhtemelen işlev yerleştirme ile ilgili sorunların çoğunu çözecek ve işlev yükü eksikliği nedeniyle biraz daha iyi performans getirecektir.
V8'in bunu yerel olarak destekleyip desteklemediğinden emin değilim, en son bazı bayrakları etkinleştirmek için gerekli olduğunu kontrol ettim, ancak bu, SpiderMonkey kullanan bir Node.js bağlantı noktasında çalışıyor .
Ekstra Kaynaklar: