Search code examples
javascriptinheritanceextjsextjs4.1extend

Issue with inheritance


I have created 3 classes as following

  1. Ext.mine.TextParent - Inherting from Textfield
  2. Ext.mine.child.TextChildA - Inherting from Ext.mine.TextParent
  3. Ext.mine.child.TextChildB - Inherting from Ext.mine.TextParent

This way, Child A and Child B classes are siblings as both are inherting from the Parent class.

I have added a new config property to parent class as testConfig with defalult value being {}.

In the initComponent of Child A class, I have assigned a new key:value to this testConfig as - me.testConfig.childAKey='Child A Value';

Now, there is a form which is using all the three textfields and in the afterrender of the textChildB type, I am printing the value of its testConfig.

As the value of testConfig is not modified in Child B, thus, it is expected that this should print blank object (as the default value in parent of this testConfig is blank object).

But it actually prints the values from Child A.

Child A and Child B are siblings, thus, how can the value from Child A come to Child B?

Could someone please guide that what could be the reason behind this?

Below is the test case:

<html>
    <head>
        <title>Inheritance Test</title>
        <link rel='stylesheet' href='resources/extjs/resources/css/ext-all.css' />
        <script type='text/javascript' src='resources/extjs/ext-all-dev.js'></script>

        <script type='text/javascript'>

            //Defining the Parent below
            Ext.define('Ext.mine.TextParent', {
                extend: 'Ext.form.field.Text',
                alias: 'widget.textParent',
                testConfig:{}
            });

            //Defining the Child A below
            Ext.define('Ext.mine.child.TextChildA', {
                extend: 'Ext.mine.TextParent',
                alias: 'widget.textChildA',
                initComponent:function(){
                    var me  =   this;
                    me.testConfig.childAKey     =       'Child A Value';//Adding the key value to Child A
                    me.callParent();
                }
            });

            //Defining the Child B below
            Ext.define('Ext.mine.child.TextChildB', {
                extend: 'Ext.mine.TextParent',
                alias: 'widget.textChildB'
            });
        </script>

        <script type='text/javascript'>

            Ext.onReady(function(){
                Ext.create('Ext.form.Panel', {
                    title: 'Basic Form',
                    renderTo: Ext.getBody(),
                    width: 350,
                    url: 'save-form.php',
                    items: [{
                        xtype: 'textParent',//Creating field for Parent
                        fieldLabel: 'Text Parent',
                        flex:1
                    },{
                        xtype: 'textChildA',//Creating field for Child A
                        fieldLabel: 'Text Child A',
                        flex:1
                    },{
                        xtype: 'textChildB',//Creating field for Child B
                        fieldLabel: 'Text Child B',
                        flex:1,
                        listeners:{
                            afterrender:function(){
                                /*
                                    **Printing to console the value of testConfig for Child B
                                    **Instead of giving a blank object, it is giving the values of Child A - childKey:Child A Value
                                    **How the value from a sibling got associated with another one?
                                */
                                console.log(this.testConfig);
                            }
                        }
                    }]
                });
            });
        </script>

    </head>
    <body>
    </body>
</html>

Solution

  • The problem here is that you are trying to inherit an object (pass by reference), which works completely different than pass by value types like numbers and strings in Extjs. If you change your example to:

    <script type='text/javascript'>
    
                //Defining the Parent below
                Ext.define('Ext.mine.TextParent', {
                    extend: 'Ext.form.field.Text',
                    alias: 'widget.textParent',
                    testConfig:"cheese"
                });
    
                //Defining the Child A below
                Ext.define('Ext.mine.child.TextChildA', {
                    extend: 'Ext.mine.TextParent',
                    alias: 'widget.textChildA',
                    constructor:function(){
                        var me  =   this;
                        me.testConfig     =       'Child A Value';//Adding the key value to Child A
                        me.callParent();
                    }
                });
    
                //Defining the Child B below
                Ext.define('Ext.mine.child.TextChildB', {
                    extend: 'Ext.mine.TextParent',
                    alias: 'widget.textChildB'
                });
            </script>
    

    it will work correctly because testConfig is now a pass by value type of string. The reason behind this is because Extjs is not designed to make new instances of objects in sub-classes because deep copies of objects in javascript is very complex and not even possible for many objects. This is because some deep copies may not even make logical sense (think of copying an object that has a circular reference, how does the copy function know when to stop?). Since some deep copies don't make logical sense to do on objects, there can be no generalized solution to doing deep copies, therefore if you create an object at the highest level in your inheritance tree, this object will actually be the same instance of that object for every subclass.

    If you notice the extjs core code, almost 100% of the time, all config options are pass by value types, and this is the model you are supposed to follow. In the example you provided, there is no logical necessity for creating an object of config options when you could just put all those config options at the class level and it will logically mean the same thing.

    I guess if you really want to simplify what I'm trying to say here, it boils down to pass by reference versus pass by value. You can only use pass by value for config options if you want the inheritance to work like it should.