0
\$\begingroup\$

So for practice with Javascript, I wrote this lightweight widget factory which is similar in functionality (okay, that's a bit of a stretch) to jQuery's UI Widget factory. I was hoping I could get some pointers as far as idiom usage and also if there are any glaring problems with the below code.

var _CreateWidget = function(namespace, implementation) {
 _Widget = {
 _create: function() {},
 _destroy: function() {},
 _set: function() {},
 _get: function() {},
 options: {},
 context: undefined,
 namespace: namespace,
 apply: function(element) {
 this.context = element;
 element[namespace] = this;
 this._create();
 }
 };
 for (var item in implementation) {
 if (implementation.hasOwnProperty(item)) {
 _Widget[item] = implementation[item];
 }
 }
 return _Widget;
}
Widget = function(namespace, implementation) {
 return function(element) {
 var instance = _CreateWidget(namespace, implementation);
 instance.apply(element);
 return instance
 };
}

An example widget would be defined as follows:

//Example widget
Page = Widget("page", {
 _create: function() {
 //initialization code here
 },
 _destroy: function() {
 },
 _set: function() {
 },
 _get: function() {
 },
 request: function() {
 //custom function
 },
 options: {
 url: ''
 }
});

And the widget would be applied to an element like so:

myElement = document.getElementById("myUniqueId");
appliedPageReference = Page(myElement);
myElement.page.request(); //One way to call the custom request() function;
appliedPageReference.request(); //Another way to call the custom request() function;
asked Apr 30, 2013 at 3:09
\$\endgroup\$
1
  • \$\begingroup\$ You're missing the var keyword in several places. \$\endgroup\$ Commented Apr 30, 2013 at 8:15

1 Answer 1

2
\$\begingroup\$
  • Although DOM elements can be used like normal JavaScript objects, I'd avoid attaching JavaScript other than handlers. The problem is when they form circular references. In browser garbage collectors, they won't collect garbage if something still references them. If you accidentally form circular references, this will lead to memory leaks (unfreed memory)

    jQuery avoids this by creating objects in an internal cache and assigns an ID to the element. That way, you are assigning a primitive to the element, not an object. This is how jQuery collects and manages event handlers, data attributes, and others.

    So a general tip is: What is from JavaScript, stays in JavaScript. (And not cross over to the DOM)

  • In jQuery's case, the functions are not actually attached to the element. That's the purpose of the "jQuery object". In a gist, a jQuery object is just an array-like object (let's call it "pseudo-array" from this point on), that contains DOM elements. The prototype of the jQuery object is where the functions live. These functions operate on each value in the collection.

    Executing a function in a jQuery object is not like this:

    someElement.doSomething();
    

    But rather, something like this:

    collectionOfStuff.forEach(function(DOMElement,i,arr){
     //do something for each in the collection
    });
    
  • Widget in your code is some function that manufactures "widget templates" which are used to bind to your elements. Rather than having Widget return the template, why not make Widget your namespace. You can then create a function that attaches to the namespace.

    It would be synonymous to doing jQuery.fn.extend(function(){...})

    //define widget
    Widget.defineWidget('Page',function(){
     //local stuff, aka "private"
     var privateVar = 'foo';
     function privateFn(){...}
     //anything attached to `this` is "public"
     //we will execute this function, providing an object as `this`
     this.publicVar = 'bar';
     this.publicFn = function(){...}
    });
    //access widget
    var reference = Widget.Page(bindingTarget);
    

    defineWidget define a creator function into the namespace that uses the widget definition to build instances, something like:

    Widget.defineWidget = function(name,fn){
     //store
     wigetCache[name] = fn;
     //attach to namespace
     Widget[name] = function(){
     //BaseClass could be some constructor with prototype containing
     //all functions that widgets should have
     var instance = new BaseClass();
     //run the instance through the definition to attach the internals
     return fn.call(instance);
     } 
    }
    
answered Apr 30, 2013 at 4:04
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.