2011-09-30 15 views
10

ile geri çağrıların program aracılığıyla uygulanması Bu yüzden bir web uygulaması yazıyorum. Hemen hemen her şey istemci tarafında yapılır, sunucu ama RESTful bir arayüz. JQuery'yi benim seçim çerçevem ​​olarak kullanıyorum ve kodumu bir Revealing Module Pattern içinde uyguluyorum.JS/jQuery

benim kod tel kafes kabaca şöyledir:

(function($){ 
    $.fn.myplugin = function(method) 
    { 
     if (mp[method]) 
     { 
      return mp[method].apply(this, Array.prototype.slice.call(arguments, 1)); 
     } 
     else if (typeof method === 'object' || ! method) 
     { 
      return mp.init.apply(this, arguments); 
     } 
     else 
     { 
      $.error('Method ' + method + ' does not exist on $.myplugin'); 
     } 
    }; 

    var mp = 
    { 
     init : function(options) 
     { 
      return this.each(function() 
      { 
       // stuff 
      } 
     }, 
     callbacks : {}, 
     addCallback : function(hook_name, cb_func, priority) 
     { 
      // some sanity checking, then push cb_func onto a stack in mp.callbacks[hook_name] 
     }, 
     doCallbacks : function(hook_name) 
     { 
      if (!hook_name) { hook_name = arguments.callee.caller.name; } 
      // check if any callbacks have been registered for hook_name, if so, execute one after the other 
     } 
    }; 
})(jQuery); 

Oldukça basit, değil mi?

Şimdi, uygulama kapsamının dışından olduğu kadar içeriden de (çoklu, hiyerarşik) geri çağrıları kaydedebiliyoruz. Beni rahatsız eden nedir

:

foo : function() { 
    mp.doCallbacks('foo_before'); 
    // do actual stuff, maybe some hookpoints in between 
    mp.doCallbacks('foo_after');   
} 

Uygulamamda içindeki her bir fonksiyon başlangıç ​​ve bitiş olurdu: Mümkün olduğunca genişletilebilir hâle getirmek için, ben bu satırlar boyunca bir şey başvurmak zorunda kalacak bunun gibi. Bu sadece doğru görünmüyor.

Yani, JS sihirbazları SO - ne yapar?

+0

önce ve kancalar sonra başvurmuştur uygulanan görüldü ettik Hemen hemen her eklenti mimarisi: Burada

bir örnek kodudur. Çoğu zaman sadece insanların istediklerini, istedikleri kadarıyla istediklerini uygularsınız. Bildiğim sihirli bir çözüm yok. –

+0

Bu, aşırı mühendislik gerektiren bir durum gibi görünüyor. Geri aramalardan önce ve sonra destek almak için kütüphanenizde her _internal_ işlevine gerçekten ihtiyacınız var mı? Raylar, dev bir geri arama sandviçi gibidir ve hatta Rails'deki yöntemlerin sadece% 1'i geri aramaları desteklemektedir. Ayrıca, geri aramaları destekledikleri yerde, tam olarak örnek _foo_ işlevinde özetlediğiniz gibi yaparlar. –

cevap

10

Başka bir işlevi argüman olarak alan bir işlev yazabilirsiniz ve bu argümanın etrafına kancalarınızı çağıran yeni bir işlev döndürür. Örneğin:

foo: withCallbacks("foo", function() { 
    // Do actual stuff, maybe some hookpoints in between. 
}) 
+0

+1 Hemen hemen yazılarınızı düzenlemeden önce iyi bir çözüm dedim! – Chad

+0

@Chad, teşekkürler, fonksiyondan kanca isimlerini çıkaramadığımı kabul etmem biraz zaman aldı :) –

+0

Benim 'doCallbacks()' uygulamasında, 'hook_name' verilmediyse' argümanlar kullanıyorum. callee.caller.name 'işlevinden kanca ismini çıkarmak için ...' withCallbacks() ';) – vzwick

2

aslında jQueryUI Widget factory ait sadeleştirilmiş versiyon uyguladıklarını:

function withCallbacks(name, func) 
{ 
    return function() { 
     mp.doCallbacks(name + "_before"); 
     func(); 
     mp.doCallbacks(name + "_after"); 
    }; 
} 

Sonra gibi bir şey yazabilirsiniz. Bunu kendiniz yuvarlamaktan kaçınmak için bu işlevselliği kullanmanızı tavsiye ederim.

$("#foo").myPlugin("myMethod", "someParam") 

bağımsız değişken olarak 'someParam' ile eklenti örneğinde myMethod arayacak:

Widget fabrika otomatik sihirli öyle ki yöntem çağrılarına dizeleri, eşler. Ayrıca, özel bir etkinliği tetiklerseniz, kullanıcılar etkinlik adıyla eşleşen seçeneklere bir özellik ekleyerek geri aramalar ekleyebilir.

Örneğin

, tabs widget başlatma sırasında seçeneklere bir select özellik ekleyerek içine dokunabilirsiniz bir select olayı vardır:

tabii
$("#container").tabs({ 
    select: function() { 
     // This gets called when the `select` event fires 
    } 
}); 

, sen önce ve sonra eklemeniz gerekir Bu işlevselliği ödünç alabilmek için etkinlikler olarak kancalar, ancak bu genellikle her zaman daha kolay bakım sağlar. yardımcı olur

umudu. şerefe!

Eğer myplugin kodunda doğrudan geri aramalar çağırmak için kodu bulunmamalıdır neden görmüyorum çünkü doğru soru anlaşılamamıştır olabilir
+0

İşaretçi için teşekkürler, bunun farkındaydı, ama yine de;) – vzwick

4

:

$.fn.myplugin = function(method) 
{ 
    if (mp[method]) 
    { 
     var params = Array.prototype.slice.call(arguments, 1), ret; 
     // you might want the callbacks to receive all the parameters 
     mp['doCallbacks'].apply(this, method + '_before', params); 
     ret = mp[method].apply(this, params); 
     mp['doCallbacks'].apply(this, method + '_after', params); 
     return ret; 
    } 
    // ... 
} 

DÜZENLEME:

Tamam, yorumunuzu okuduktan sonra başka bir çözümün (tabii ki) başka bir dolaylılık olacağını düşünüyorum. Yani, yapıcıdan ve kendi aralarında yapılan çağrılar için diğer genel yöntemlerden kullanılan bir çağırma işlevi vardır.Özel yöntemlere bağlanmanın, kancaları kapsülleme işlemini kopardığı gibi, yalnızca kamusal yöntemler için işe yarayacağını belirtmek isterim. Aslında yanı eklentileri arasında tekrarını azaltacak biraz daha fazla kod, yazdık,

function invoke(method) { 
    var params = Array.prototype.slice.call(arguments, 1), ret; 
    // you might want the callbacks to receive all the parameters 
    mp['doCallbacks'].apply(this, method + '_before', params); 
    ret = mp[method].apply(this, params); 
    mp['doCallbacks'].apply(this, method + '_after', params); 
} 

$.fn.myplugin = function() { 
    // ... 
    invoke('init'); 
    // ... 
}; 

Ama:

basit versiyonu böyle bir şey olurdu. Bu eklenti oluştururken sonunda

(function() { 

function getResource() { 
    return {lang: "JS"}; 
} 

var mp = NS.plugin.interface({ 
    foo: function() { 
     getResource(); // calls "private" method 
    }, 
    bar: function() { 
     this.invoke('foo'); // calls "fellow" method 
    }, 
    init: function() { 
     // construct 
    } 
}); 

$.fn.myplugin = NS.plugin.create(mp); 

})(); 

içinde nasıl görüneceğini Ve bu kısmi uygulama gibi görünüyor: Tabii

NS = {}; 
NS.plugin = {}; 

NS.plugin.create = function(ctx) { 
    return function(method) { 
     if (typeof method == "string") { 
      arguments = Array.prototype.slice.call(arguments, 1); 
     } else { 
      method = 'init'; // also gives hooks for init 
     } 

     return ctx.invoke.apply(ctx, method, arguments); 
    }; 
}; 

// interface is a reserved keyword in strict, but it's descriptive for the use case 
NS.plugin.interface = function(o) { 
    return merge({ 
     invoke:  NS.plugin.invoke, 
     callbacks: {}, 
     addCallback: function(hook_name, fn, priority) {}, 
     doCallbacks: function() {} 
    }, o); 
}; 

NS.plugin.invoke = function(method_name) { 
    if (method_name == 'invoke') { 
     return; 
    } 

    // bonus (if this helps you somehow) 
    if (! this[method]) { 
     if (! this['method_missing') { 
      throw "Method " + method + " does not exist."; 
     } else { 
      method = 'method_missing'; 
     } 
    } 

    arguments = Array.prototype.slice.call(arguments, 1); 

    if (method_name in ["addCallbacks", "doCallbacks"]) { 
     return this[method_name].apply(this, arguments); 
    } 

    this.doCallbacks.apply(this, method_name + '_before', arguments); 
    var ret = this[method_name].apply(this, arguments); 
    this.doCallbacks.apply(this, method_name + '_after', arguments); 

    return ret; 
}; 

bu tamamen denenmemiş olduğu :) Temelde

+0

Bu, harici olarak adlandırılan/"genel" yöntemler için geçerli olurdu. Hala çalışmakta olan geri çağrılarla "iç" 'mp'den this.someFunction()' ın yapılabilmesi gerekiyor. – vzwick

+0

Yorumunuzu düzenlememde ele aldım. Kullanım durumunuz için çok karmaşık olabilir, ama yazmak eğlenceliydi. – deviousdodo

+0

Temiz parça kodu! – vzwick

1

Geri aramalardan kaçınmayı ve bunun yerine etkinlikleri kullanmayı tercih ediyorum. Sebebi simle. Verilen etkinliği dinlemek için birden fazla fonksiyon ekleyebilirim, geri çağırma parametreleriyle uğraşmak zorunda değilim ve geri arama tanımlanmış olup olmadığını kontrol etmek zorunda değilim. Tüm yöntemleriniz $.fn.myplugin aracılığıyla çağrıldığı sürece, yöntem çağrısından önce ve sonra olayları tetiklemek kolaydır.

(function($){ 
    $.fn.myplugin = function(method) 
    { 
     if (mp[method]) 
     { 
      $(this).trigger("before_"+method); 
      var res = mp[method].apply(this, Array.prototype.slice.call(arguments, 1)); 
      $(this).trigger("after_"+method); 
      return res; 
     } 
     else if (typeof method === 'object' || ! method) 
     { 
      return mp.init.apply(this, arguments); 
     } 
     else 
     { 
      $.error('Method ' + method + ' does not exist on $.myplugin'); 
     } 
    }; 

    var mp = 
    { 
     init : function(options) 
     { 
      $(this).bind(options.bind); 
      return this.each(function() 
      { 
       // stuff 
      }); 
     }, 

     foo: function() { 
      console.log("foo called"); 
     } 
    }; 
})(jQuery); 


$("#foo").myplugin({ 
    bind: { 
     before_foo: function() { 
      console.log("before foo"); 
     }, 

     after_foo: function() { 
      console.log("after foo"); 
     } 
    } 
}); 
$("#foo").myplugin("foo");