11

I have done some research on the ExtJS forum regarding private methods and fields inside a extended class, and I couldn't find any real answer to this.

And when I say an extended class I mean something like this:

Ext.ux.MyExtendedClass = Ext.extend(Ext.util.Observable, {
    publicVar1: 'Variable visible from outside this class',
    constructor: function(config) { this.addEvents("fired"); this.listeners = config.listeners; }, // to show that I need to use the base class
    publicMethod1: function() { return 'Method which can be called form everywhere'; },
    publicMethod2: function() { return this.publicMethod1() + ' and ' + this.publicVar1; } // to show how to access the members from inside another member
});

The problem here is that everything is public. So, how do I add a new variable o method within the scope of MyExtendedClass that cannot be accessed from outside but can be access by the public methods?

Mariano Desanze
  • 7,847
  • 7
  • 46
  • 67

3 Answers3

23

The following example shows the Upper Stage way to define privileged private & public members. But it also shows how to define private static members (also called class members), and public non-privileged members. Using these last 2 instead of the privileged ones, we will reduce initialization time as they are not parsed every time you create a new object of your class:

Ext.ux.MyExtendedClass = Ext.extend(Ext.util.Observable, 
  (function() {
    // private static fields (can access only to scope: minimum privileges).
    var privStaticVar = 0;
    // private static functions (can access only to scope and arguments, but we can send them the scope by param)
    var privateFunc1 = function(me) { return me.name + ' -> ClassVar:' + privStaticVar; };
    var privateFunc2 = function(me) { return me.publicMethod1() + ' InstanceVar:' + me.getPrivateVar(); };
    return {
      constructor: function(config) {
        // privileged private/public members (can access to anything private and public)
        var privateVar = config.v || 0;
        var privInstFunc = function() { privateVar += 1; };
        this.name = config.name;
        this.incVariables = function(){ privInstFunc(); privStaticVar += 1; };
        this.getPrivateVar = function(){ return privateVar; };
      },
      // public members (can access to public and private static, but not to the members defined in the constructor)
      publicMethod1: function() { this.incVariables(); return privateFunc1(this); },
      publicMethod2: function() { return privateFunc2(this); }
    };
  }())
);

function test() {
  var o1 = new Ext.ux.MyExtendedClass({name: 'o1', v: 0});
  var o2 = new Ext.ux.MyExtendedClass({name: 'o2', v: 10});
  var s = o1.publicMethod2() + '<br>' + o1.publicMethod2() + '<br><br>' + o2.publicMethod2() + '<br>' + o2.publicMethod2();
  Ext.get("output").update(s);
}
<link href="//cdnjs.cloudflare.com/ajax/libs/extjs/3.4.1-1/resources/css/ext-all.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/extjs/3.4.1-1/adapter/ext/ext-base.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/extjs/3.4.1-1/ext-all.js"></script>

<p>Click the button to instantiate 2 objects and call each object 2 times:</p>

<button onclick="test();">Test</button>

<p>You can click the button again to repeat. You'll see that the static variable keep increasing its value.</p>

<p>&nbsp;</p>
<div id="output"></div>
Community
  • 1
  • 1
Mariano Desanze
  • 7,847
  • 7
  • 46
  • 67
  • 4
    this is commonly called the Module Pattern, you can find it written about on the YUI blog – seanmonstar Apr 29 '10 at 00:10
  • 1
    I'm convinced that the *privStaticVar*, *privateFunc1* & *privateFunc2* will be parsed just once (and with the tests I made I got the right results). Because the `new` keyword will not affect the members of this module pattern, as they are parsed while the Ext.extend is executed (way before the constructor or any other method comes into scene). Also, module patterns are singletons, so I think that would explain it too. – Mariano Desanze Apr 29 '10 at 13:33
  • I agree; sorry for the earlier comment. – Upperstage Apr 29 '10 at 14:04
  • @Proton the module pattern doesn't have to produce singletons. its more to describe the action of wrapping variable declarations in a self-executing function, and returning and object that has access to those variables. – seanmonstar Apr 29 '10 at 16:23
  • @seanmonstar [yuiblog/module-pattern](http://www.yuiblog.com/blog/2007/06/12/module-pattern/) says: `Douglas Crockford has been teaching a useful singleton pattern for achieving this discipline, and I thought his pattern might be of interest to those of you building on top of YUI. Douglas calls this the “module pattern.”` But you're right! Cause I use it in this class, and the class as a whole is not a Singleton. Although it shares the staticness of a Singleton. – Mariano Desanze Apr 30 '10 at 00:53
4

I use something like the following.

  var toolbarClass = Ext.extend( Ext.Container,
  {
    /**
     * constructor (public)
     */
    constructor: function( config )
    {
      config = config || {};

      // PRIVATE MEMBER DATA ========================================
      var accountId = Ext.id( null, 'ls-accountDiv-');

      // PUBLIC METHODS ========================================
      this.somePublicMethod = function(){
         console.log( accountId );
      };

...
Upperstage
  • 3,747
  • 8
  • 44
  • 67
  • +1 It works! But I just realized another solution that seems better. Because with your solution, the creation time of every instance will be increased, and I believe mine does not. I'm going to post it now, so people can say if I'm wrong or not. – Mariano Desanze Apr 29 '10 at 00:02
  • 5
    I welcome this conversation. Arguably too few developers are concerned about data hiding when developing in JavaScript. – Upperstage Apr 29 '10 at 12:22
1

@Protron: Your answer is awesome! Thanks! I went a little bit further and created my own class extender method.

/**
 * Instead of call Ext.extend method to create your new class extensions, you can use
 * My.extend. It is almost the same thing, but you pass a function as body for your new class, not
 * an object. Ex.:
 *
 * MyNewExtCompoment = My.extend(Ext.Compoment, function() {
 *     var myPrivateVar = 0;
 *
 *     //private
 *     function reset() {
 *       myPrivateVar = 0;
 *     }
 *
 *     //public
 *     function add(value) {
 *       try{
 *         myPrivateVar = myPrivateVar + value;
 *       } catch(err){
 *         reset();
 *       }
 *       return myPrivateVar;
 *     }
 *
 *     return {
 *         add: add
 *     }
 * }, 'ux-my-new-component');
 *
 * @param extendedClass my extended class
 * @param newClassBodyFunction my new class body
 * @param newClassXtype (optional) the xtype of this new class
 */
My.extend = function(extendedClass, newClassBodyFunction, newClassXtype) {
    var newClass = Ext.extend(extendedClass, newClassBodyFunction.call());
    if(newClassXtype) {
        Ext.reg(newClassXtype, newClass);
    }
    return newClass;
}

This way we can save some extra "()", and we have the "Ext.reg" called for free. []s