Q:

"best" object notation / context binding in jquery

i'm switching from prototype to jquery, and i'm struggling with the "best" object/class notation.
what bothers me most is the "this" context, which disallows me to access the object itself when used in callback functions (e.g. event handlers, or even jquery's .each() method). in prototype I use .bind() or .bindAsEventListener(), but in jquery i found only the following approaches:
1) cache the object pointer (which does not work well for me in literal notation)
2) use $.proxy() method
I find both methods very inelegant/ugly.
I have 2 variants of the same functionality (simple popup window), please let me know which one is the one you prefer, and why. alternatively you can suggest some improvements. thanks very much for your help.
variant 1:
Dialog = function(container, callback) {
  this.init(container, callback);
}

$.extend(Dialog.prototype, {
   init: function(container, callback) {
     this.dialog = $(container);
     this.callback = callback;
     this.buttons = {
       ok: this.dialog.find('[data-type="ok"]'),
       cancel: this.dialog.find('[data-type="cancel"]')
     }
     $.each(this.buttons, $.proxy(function(key, button) {
       button.click($.proxy(function() { this.hide(key); }, this));
     }, this));
   },

   show: function() {
     this.dialog.show();
   },

   hide: function(key) {
     this.dialog.hide();
     this.callback(key);
   }   
});

variant 2:
function Dialog2(container, callback) {
  var self = this;
  self.dialog = $(container);
  self.callback = callback;
  self.buttons = {
    ok: self.dialog.find('[data-type="ok"]'),
    cancel: self.dialog.find('[data-type="cancel"]')
  }
  $.each(self.buttons, function(key, button) {
    button.click(function() { self.hide(key); });
  });

  self.show = function() {
    self.dialog.show();
  }

  self.hide = function(key) {
    self.dialog.hide();
    self.callback(key);
  }
}

Instance is created using e.g.:
var dialog = new Dialog($('#dialog'), function(result){ alert(result); });
dialog.show();

(Also, I'm not quite sure, why in variant 1, "this.dialog" is defined inside the block "this.buttons = { ... }" . I would suggest "this" inside the nested object points to the anonymous nested object itself...)

A:

There must be an almost infinite number of ways to approach this.
Definitely avoid the nested $.proxy(), which will confuse less experienced programmers who might have to maintain your code in the future, and more experienced programmers who will ask "why?".
Sticking with the idea of a POJS constructor, you can avoid this almost entirely by writing the whole constructor with private members plus a couple of this... at the end to expose certain functions as public methods.
(untested) :
var Dialog = function(container, callback) {
    //define private members
    var dialog, buttons;
    var init = function() {
        dialog = $(container);
        buttons = {
            ok: dialog.find('[data-type="ok"]'),
            cancel: dialog.find('[data-type="cancel"]')
        };
        $.each(buttons, function(key, button) {
            button.on('click', function() {
                hide(key);
            });
        });
    };
    var show = function() {
        dialog.show();
    };
    var hide = function(key) {
        dialog.hide();
        callback(key);
    };

    //initialize
    init();

    //expose public methods
    this.show = show;
    this.hide = hide;
}

Note how private functions have direct access to other private members including other private functions and the formal variables passed on instantiation. And public methods are just references to private functions.
Moving away from straightforward constructors, you might like to consider:

John Resig's thoughts on Simple “Class” Instantiation 
Writing a jQuery plugin, allowing instantiation with $('#myDialog').dialog({callback:function(){...}}),.

A plugin might look something like this (untested) :
(function($){
    // **********************************
    // ***** Start: Private Members *****
    var pluginName = 'dialog';
    // ***** Fin: Private Members *****
    // ********************************

    // *********************************
    // ***** Start: Public Methods *****
    var methods = {
        init : function(options) {
            //"this" is a jquery object on which this plugin has been invoked.
            return this.each(function(index){
                var $this = $(this);
                var data = $this.data(pluginName);
                // If the plugin hasn't been initialized yet
                if (!data){
                    var settings = {
                        callback: function(){}
                    };
                    if(options) { $.extend(true, settings, options); }

                    var buttons = {
                        ok: $this.find('[data-type="ok"]'),
                        cancel: $this.find('[data-type="cancel"]')
                    };
                    $.each(buttons, function(key, button) {
                        $this.on('click', button, function() {
                            methods.hide.call($this, key);
                        });
                    });

                    $this.data(pluginName, {
                        target : $this,
                        settings: settings
                    });
                }
            });
        },
        show: function() {
            return this.each(function(index){
                $(this).show();
            });
        },
        hide: function(key) {
            return this.each(function(index){
                $(this).hide().data(pluginName).settings.callback(key);
            });
        }
    };
    // ***** Fin: Public Methods *****
    // *******************************

    // *****************************
    // ***** Start: Supervisor *****
    $.fn[pluginName] = function( method ) {
        if ( methods[method] ) {
            return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
        } else if ( typeof method === 'object' || !method ) {
            return methods.init.apply( this, arguments );
        } else {
            $.error( 'Method ' + method + ' does not exist in jQuery.' + pluginName );
        }
    };
    // ***** Fin: Supervisor *****
    // ***************************
})( jQuery );

