Calling JavaScript function for a top Level Object by using a String

JavaScript Code

var myPanel = null;              /* Our object */
function testFunctions(func) {
    logStatus("Function " + func + " Active !");
    myPanel = createPanel();
    var info = "Calling ANONYMOUS Function" ;
    myPanel.testfunc(info);
    info = "Calling NAMED     Function" ;
    myPanel.testfunc2(info);
    info = "Calling ANONYMOUS Function VIA String" ;
    myPanel.invokeFuncByString(myPanel, "testfunc",info);
    info = "Calling NAMED     Function VIA String" ;
    myPanel.invokeFuncByString(myPanel, "testfunc2",info);
}

function createPanel(vSingleClickInProgess, vClickTimeout) {
    var panel = Object.create(Object.prototype, {
        objectName: {value: "panel", writable: true}      /* just provide a name for our object */
    });
    panel.testfunc  = function (info) {
        logStatus ("__ " + info);
    };
    panel.testfunc2  = function (info) {
        logStatus ("__ " + info);
    };
    panel.invokeFuncByString = function(obj,funcName,info) {
        var fnString= funcName;
        var fn = obj[fnString];
                // is object a function?
        if (typeof fn === "function")
            fn(info + " - ObjectName: " +  obj.objectName  + " - functionName: " + funcName);
        else {
            logErrorT(logHeader, "__ fn IS NOT A Function - : fnString " + fnString + " type: " + typeof fn )
        }
    }
    return panel;
};

Output

17:05:12:740 STATUS::__ Calling ANONYMOUS Function
17:05:12:740 STATUS::__ Calling NAMED     Function
17:05:12:741 STATUS::__ Calling ANONYMOUS Function VIA String - ObjectName: panel - functionName: testfunc
17:05:12:742 STATUS::__ Calling NAMED     Function VIA String - ObjectName: panel - functionName: testfunc2

References

PlugINs and Editor Setting for WordPress Projects

Currently I’m using 2 PlugINs and I’ve disabled WYSIWYG Editor for my WordPress Projects

  • Insert_PHP [ used to run PHP code ]
  • Raw HTML [ need to be version 1.5 – 1.4 converts HTML during saving the Code
  • Disable Visual Editing first User -> Profil -> Disable WYSIWYG Editor

Note :  You CAN’T run [insert_php]…[/insert_php] Block inside a [raw] .. [/raw] Block
Raw Html need to be at version 1.5 !


Insert_PHP Plugin: 
Descriptiuon: 
   Run PHP code inserted into WordPress posts and pages.
  
- In the sample below we use Insert_PHP Plugin to call get_stylesheet_directory_uri() 
- get_stylesheet_directory is used by Child Themes [ here we reference an Image location]

Sample:
<td class="firstImageAbu" rowspan="5"><img src=[insert_php]echo get_stylesheet_directory_uri()."/images/htmlImages/abu1.jpg 
   alt='Sponsoren FCW' width=215 />";[/insert_php]</td>  
    

Raw HTML PlugIN
Descripton:
    Lets you enter any HTML/JS/CSS in your posts without WP changing it, as well as disable 
    automatic formatting on a per-post basis. Usage: Wrap your code in [raw]...[/raw] tags. 
    To avoid problems, only edit posts that contain raw code in HTML mode. Upgrade to Pro 
    to be able to use Visual editor on the same posts without it messing up the code.

Note: Used that PlugIN for a ControlPanel. Without that code the Buttons get strange aligned when 
using <CR> to beautify your Code !

Sample:
[raw]
<div id="AbuControlPanel">
  <input id="infoButton" class="mediumButton" type="submit" value="" />
  <input id="homeButton" class="mediumButton" type="submit" value="" />
  <div id="AbuButtonGroup" class="button-group">
    <input id="stopButton" class="mediumButton" type="submit" value="" />
    <input id="playButton" class="mediumButton" type="submit" value="" />
    <input id="prevButton" class="mediumButton" type="submit" value="" />
    <input id="nextButton" class="mediumButton" type="submit" value="" />
  </div>
</div>
[/raw]

WordPress: Using Duplicator PlugIN

Step 1:  Install WP Plugin: Duplicator 1.1.8

  • Nothing to add here just install the WordPress Plugin

 

Step2 : Create a Package and upload Installer.php and archive to your local host

  • Duplicator -> Package -> Create New -> Next

Create a New Package

Duplicator1

  • You can ignore the Database Warning [ see video below ]

Lets have a closer look on the PHP errors

Duplicator2

  • Ignoring that Warning for now – but notice that I’m not able to restore that package on the original Server again —>  Not a good  situation but lets move forward 
  • Check:  

 

BUILD the package

  • Finally if we don’t get a timeout you will see the following message .

Duplicator3

  • Click on both buttons to download the both files
  •  Note : Even you may get a Timeout the action may still run successfully to the end

 

Step 3 Create a MySql Database via phpAdmin

Duplicator4

 

Step 4:  Deploy the WordPress Backup to our local XAMPP webserver

  • Verify both files are under the proper newly created directory [ xampp\htdocs ]

Duplicator5

 

Invoke installer.php and test your MySQL connection

Duplicator6

  • Testing MySql Database worked fine

 

Update Files and Database

Duplicator7

 

Apply Final Steps

  • Test Site
  • Security Cleanup

Duplicator8

 

Testing the duplicated Website

Duplicator9

  • As you see the Website Duplication to our localhost works!
  • Now you can remove the Duplicator Files by running Security Cleanup

Reference

 

Google Maps Markers disappear after multiple fitBounds() calls

Problem Description

  • You have mulitple fitBounds() calls to display your Google Map
  • After some calls  your draggable Markers disappear from your Map
  • If you slightly move the Map the Markers get displayed again
  • Very likely you hit Googe Maps Bug 5176

Workaround 1

  • Create your Maps Marker with draggable set to false
var tMarker = new google.maps.Marker({
    position: markerPos,
    draggable: false, 

Workaround 2

  • In case your need a draggable marker call map.panBy(0,0) to fix the problem
  • Flow
    • fitBounds() triggers the center_changed event
    • Within this event call panBy() without moving the map at all
google.maps.event.addListenerOnce(gMap, 'center_changed', function() {
    var gMap = myControlPanel.getMap();  // Die and give up if gMap is null
    gMap.panBy(0,0);
});

gMap.fitBounds(bounds);

JavaScript: Prototypal Object Inheritance

Overview and Key Facts

  • According to Douglas Crockford and  Eric Elliot Prototypal Inheritance should be preferred over Classical  Inheritance
  • Objects in Javascript inherit their Properties/Methods from what is called a prototype instead of a class definition
  • Prototypes are standard objects themselves and builds up a Prototype Chain      
  • The Prototype Chain top level object is Object.prototype and delegates methods like toString() to each JavaScriot Object with a working Prototype Chain
  • Same prototype means same object instance
  • Every JavaScript Object should get its behavior from functions in its prototype
  • ECMAScript 5 introduced a new method: Object.create() which simplifies creating a Prototype Chain without Douglas Crockfords “WorkAround” simulating Object.create()

 Inherit from a Object Literal using Object.create

  • Object.create(Mammal, …  uses the Mammel Object  created from am  Object Literal  as its Prototype
  • MammelObj inherits Properties type and objectName from Mammel
  • MammelObj inherits Function getDetails from Mammel
  • MammelObj adds _objectName Data Descriptor
  • MammelObj adds an Accessor Descriptor objectName which works on _objectName property
JavaScript Code:

var Mammal = {
    type: 'Mammal',
    objectName : 'Mammal BASE Object',
    getDetails: function () {
        return("Type: " + this.type + " - ObjectName:  "  + this.objectName);
    }

};

function runObjectTest9() {
    logMessage('--- Prototypal Inheritance via Object.create [ for details check JavaScript console ] ---');
    logMessage("Global Mammal Object created via String Literals:: type: " + Mammal.type + " - getDetails(): " + Mammal.getDetails());
    var MammalObj = Object.create(Mammal, {
        _objectName : { value: 'INITIAL MammelObj', writable: true},
        objectName: {
            configurable: true,
            get: function getObjectName () {
                logMessage('[MammalObj objectName get]:: ' + this._objectName );
                return this._objectName;
            },
            set: function setObjectName (value) {
                oldVal = this._objectName;
                this._objectName = value;
                logMessage("[MammalObj objectName set]:: NEW Value: " + this._objectName + " - Old Value: " + oldVal );
            }
         }
    });

    logMessage('[MammalObj.objectName]:: ' + MammalObj.objectName);
    MammalObj.objectName = "MammalObj";
    MammalObj.isWalking = false;
    logMessage("[MammalObj.getDetails()]:: " + MammalObj.getDetails());
    logMessage("[MammalObj.type]:: " + MammalObj.type);
    checkProperties(MammalObj);
    logMessage('------------------------------------------------------------------------');
}

Output: 
--- Prototypal Inheritance via Object.create [ for details check JavaScript console ] ---
Global Mammal Object created via String Literals:: type: Mammal - getDetails(): Type: Mammal - ObjectName:  Mammal BASE Object
[MammalObj objectName get]:: INITIAL MammelObj
[MammalObj.objectName]:: INITIAL MammelObj
[MammalObj objectName set]:: NEW Value: MammalObj - Old Value: INITIAL MammelObj
[MammalObj objectName get]:: MammalObj
[MammalObj.getDetails()]:: Type: Mammal - ObjectName:  MammalObj
[MammalObj.type]:: Mammal
[MammalObj objectName get]:: MammalObj
  • Output and Code for checkProperties(MammalObj) can be found here 

What happens with undefined properties and Methods  ?

  • Note we are uisng the above Objects Mammal and MammalObj
  • Referencing a NON existent function raises an Error:  TypError xxx.xxx  is not a function
  • Referencing a a NON existent property returns undefined
JavaScript Code:

logMessage('--- Testing TypeError: Object.function  is not a function ---');
try {
    logMessage("MammalObj.getAge():: " + MammalObj.getAge());
}   catch (e) {
    logMessage("FATAL ERROR:: Catching Exception running MammalObj.getAge() :: " + e);
}
logMessage("MammalObj.age :: " + MammalObj.age);
checkProperties(MammalObj);
logMessage('------------------------------------------------------------------------');

Output: 
--- Testing TypeError: Object.function  is not a function ---
FATAL ERROR:: Catching Exception running MammalObj.getAge() :: TypeError: MammalObj.getAge is not a function
MammalObj.age :: undefined
[MammalObj objectName get]:: MammalObj

 

Overwrite Object Methods

  • Note we still use  the object definition from our first sample for Mammal and MammalObj
JavaScript Code:

logMessage('--- Testing Overwrite Methodes ---');
var CatObj =  Object.create(MammalObj);
CatObj.legs =4;
CatObj.canWalk = function ()  {
    return("I've " + this.legs + " legs and can walk" );
};
CatObj.getDetails = function ()  {
    var myType = this.type;
    var myLegs = this.legs;
    return("[CatObj] - Hi, I'm a " + myType + " and I've " + myLegs + " legs and can walk" );
};
logMessage("catObj.canWalk():: " + CatObj.canWalk());
logMessage("catObj.getDetails():: " + CatObj.getDetails());
checkProperties(CatObj);
logMessage('------------------------------------------------------------------------');

Output: 
--- Testing Overwrite Methodes ---
catObj.canWalk():: I've 4 legs and can walk
catObj.getDetails():: [CatObj] - Hi, I'm a Mammal and I've 4 legs and can walk
[MammalObj objectName get]:: MammalObj


 Stripped Output from checkProperties(CatObj);

--- Detailed ProtoType Function walking down the Prototype Chain - Level:  1---
    ...
--- Detailed ProtoType Function walking down the Prototype Chain - Level:  2---
    ...
--- Detailed ProtoType Function walking down the Prototype Chain - Level:  3---
    ...
      
       [Mammal BASE Object, level:3][Found a FUNCTION] - Function Name: getDetails - Type: function - getPrototypeOf: function () {} - Constructor: function Function() { [native code] }
       [Mammal BASE Object, level:3] - Data Descriptor: Configurable: true - Enumerable: true - Writable: true - Value: function () {
        return("Type: " + this.type + " - ObjectName:  "  + this.objectName);
    }
   
   [CatObj, level:1][Found a FUNCTION] - Function Name: getDetails - Type: function - getPrototypeOf: function () {} - Constructor: function Function() { [native code] }
   [CatObj, level:1] - Data Descriptor: Configurable: true - Enumerable: true - Writable: true - Value: function ()  {
        var myType = this.type;
        var myLegs = this.legs;
        return("[CatObj] - Hi, I'm a " + myType + " and I've " + myLegs + " legs and can walk" );
    } 
  • There is a getDetails methode implemented by CatObj and Mammal BASE Object
  • The first method from catObj [ CatObj, level 1 ] is picked up when walking down the Prototype Chain

Modifying an underlying Object down in the Prototype Chain

  • Let’s create 2 cats named Fini and Felicitas based on CatObj
  • Change the type to Fish and modify their number of legs to 2
JavaScript Code : 

var CatObj =  Object.create(MammalObj);
CatObj.legs =4;
CatObj.objectName = 'CatObj';
CatObj.type = 'CAT';
CatObj.canWalk = function ()  {
    return("I've " + this.legs + " legs and can walk" );
};
CatObj.getDetails = function ()  {
    return("[CatObj] - Hi, I'm a " + this.type + " and I've " +  this.legs + " legs and can walk" );
};
logMessage("CatObj.getDetails():: " + CatObj.getDetails());
checkProperties(CatObj);
logMessage('------------------------------------------------------------------------');

logMessage('--- Modifying an underlying Object down in the Prototype Chain ----');
var felicitas =  Object.create(CatObj);
felicitas.age = 11;
felicitas.name = 'Felicitas';
logMessage("felicitas.getDetails():: " + felicitas.getDetails());

var fini = Object.create(CatObj);
fini.age = 9;
fini.name = 'Fini';
logMessage("fini.getDetails():: " + fini.getDetails());

logMessage("  ------ Lets modify all Cats to become a FISH type with 2 Legs ! ----- ")
CatObj.type = 'FISH';
CatObj.legs = 2;

logMessage("felicitas.getDetails() after morphing  Cats to a Fish :: " + felicitas.getDetails());
logMessage("fini.getDetails() after morphing Cats to a Fish:: " + fini.getDetails());
logMessage('------------------------------------------------------------------------');
 
Output : 
--- Modifying an underlying Object down in the Prototype Chain ----
felicitas.getDetails():: [CatObj] - Hi, I'm a CAT and I've 4 legs and can walk
fini.getDetails():: [CatObj] - Hi, I'm a CAT and I've 4 legs and can walk
  ------ Lets modify all Cats to become a FISH type with 2 Legs ! ----- 
felicitas.getDetails() after morphing  Cats to a Fish :: [CatObj] - Hi, I'm a FISH and I've 2 legs and can walk
fini.getDetails() after morphing Cats to a Fish:: [CatObj] - Hi, I'm a FISH and I've 2 legs and can walk

 

Directly Calling Methods from a Prototype Object

  • Calling Methods from a Prototype Object may return undefined
  • You should use call() or apply() to provide the correct THIS context [ felicitat Object ]
  • felicitas.getDetailsFromCat() fails because THIS context is pointing to the CatObj Object which doesn’t have properties this.name and  this.age
JavaScript Code:

    logMessage('--- Directly Calling Methods from a Prototype Object---');
    CatObj.type = 'CAT';
    CatObj.legs = 4;
    CatObj.getDetails = function ()  {
        return("Hi, my name is " +  this.name + " and I'm " + this.age + " years old. I'm a " + this.type
        + " and I've " + this.legs + " legs and can walk" );
    };
    var felicitas =  Object.create(CatObj);
    felicitas.age = 11;
    felicitas.name = 'Felicitas';
    felicitas.fObjectName = 'felicitas';

    felicitas.getDetailsFromCat = function () {
        CatObj.getDetails();     // this context is pointing to CatObj having NO this.name and this.age property
    };
    logMessage("felicitas.getDetailsFromCat() :: " +  CatObj.getDetails() );

    felicitas.getDetailsFromCat2 = function () {
        // this context now pointing to felicitas having this.name and this.age property
        logMessage("felicitas.getDetailsFromCat2() :: " +  CatObj.getDetails.call(this) );
    };
    felicitas.getDetailsFromCat2();
    logMessage("felicitas.getDetails():: " + felicitas.getDetails());
    logMessage('------------------------------------------------------------------------');

Output:  
  --- Directly Calling Methods from a Prototype Object---
felicitas.getDetailsFromCat() :: Hi, my name is undefined and I'm undefined years old. I'm a CAT and I've 4 legs and can walk
felicitas.getDetailsFromCat2() :: Hi, my name is Felicitas and I'm 11 years old. I'm a CAT and I've 4 legs and can walk
felicitas.getDetails():: Hi, my name is Felicitas and I'm 11 years old. I'm a CAT and I've 4 legs and can walk


 

References

Object Reflection in JavaScript by Sample

Overview

  • Javascrript has an API to use Reflection similar to  JAVA
  • You can follow the Prototypal Chain to figure out the Object Properties and Functions
  • Of course you can use Chrome Dev Tools  but if you need a programatically approach continue reading
  • The code may not be prefect - any comments/improvements are appreciated
  • checkProperties(MammalObj) is used to walk down the Prototype Chain

JavaScript Code for Object inheritance

var Mammal = {
    type: 'Mammal',
    objectName : 'Mammal BASE Object',
    getDetails: function () {
        return("Type: " + this.type + " - ObjectName:  "  + this.objectName);
    }
};

function runObjectTest9() {
    logMessage('--- Prototypal Inheritance via Object.create [ for details check JavaScript console ] ---');
    logMessage("Global Mammal Object created via String Literals:: type: " + Mammal.type + " - getDetails(): " + Mammal.getDetails());
    var MammalObj = Object.create(Mammal, {
        _objectName : { value: 'INITIAL MammelObj', writable: true},
        objectName: {
            configurable: true,
            get: function getObjectName () {
                logMessage('[MammalObj objectName get]:: ' + this._objectName );
                return this._objectName;
            },
            set: function setObjectName (value) {
                oldVal = this._objectName;
                this._objectName = value;
                logMessage("[MammalObj objectName set]:: NEW Value: " + this._objectName + " - Old Value: " + oldVal );
            }
         }
    });

    logMessage('[MammalObj.objectName]:: ' + MammalObj.objectName);
    MammalObj.objectName = "MammalObj";
    MammalObj.isWalking = false;
    logMessage("[MammalObj.getDetails()]:: " + MammalObj.getDetails());
    logMessage("[MammalObj.type]:: " + MammalObj.type);
    checkProperties(MammalObj);
    logMessage('------------------------------------------------------------------------');
}
Output: 
--- Prototypal Inheritance via Object.create [ for details check JavaScript console ] ---
Global Mammal Object created via String Literals:: type: Mammal - getDetails(): Type: Mammal - ObjectName:  Mammal BASE Object
[MammalObj objectName get]:: INITIAL MammelObj
[MammalObj.objectName]:: INITIAL MammelObj
[MammalObj objectName set]:: NEW Value: MammalObj - Old Value: INITIAL MammelObj
[MammalObj objectName get]:: MammalObj
[MammalObj.getDetails()]:: Type: Mammal - ObjectName:  MammalObj
[MammalObj.type]:: Mammal
[MammalObj objectName get]:: MammalObj

 

Implementation of Reflection Code

function checkProperties(o) {
    level =1 ;
    checkPropertiesPrototype(o,level);
}
var endOfPrototypeChainReached = false;

function checkPropertiesPrototype(oProto,level) {
        // limit the Prototype Chain depth
    if ( level === 10) {
        logMessage("Prototype Chain > 10 : termination - Check for CodeBUGS !");
        return;
    }
    /*
     * Abort walking down the Property Chain action when we reach the  Object.prototype object
     * Note:  Object.getPrototypeOf returns null when we reach the TOP Level Object.prototype object
     */
    var o = oProto;
    var prefix = getPrefix(level*2) + " [" + o.objectName + ", level:" + level + "]";
    var oProtoNext = Object.getPrototypeOf(oProto); 
    if ( !oProtoNext) {
            // o.objectName is undefined for  Object.prototype  - Fix this
        prefix = getPrefix(level*2) + " [Object.prototype, level:" + level + "]";
           // logMessage(prefix + " End of protoype Chain reached: Level: " + level);
           // Mark that we have reached the END of our Prototype Chain
        endOfPrototypeChainReached = true;
        return;
       }     
    logMessage("--- Detailed ProtoType Function walking down the Prototype Chain - Level:  " + level + '---');
    Object.getOwnPropertyNames(o).forEach(function(val) 
    {
        var objRef =  o[val];
        var foundIt = "";
        var p =   Object.getPrototypeOf(objRef);
        /*
            JavaScript has 5 primitives: Number, String, Boolean ,Undefined, Null [  Anything else is considered an object ]
            Object.getPrototypeOf(objRef) returns NULL for Primitives like  Number, String, Boolean ,Undefined, Null
        */
        var foundFunc = false;
        if ( typeof(objRef) === 'function') {
            foundIt = "[Found a FUNCTION]";
            foundFunc = true;
        }
        else if ( typeof(objRef) === 'object') {
            foundIt = "[Found an OBJECT]";
        }

        /*
        Object Properties:
            Data Descriptor     : configurable, enumerable, value, writable
            Accessor descriptor : configurable, enumerable, set, get
        */
        var propRes = "Oops- check getOwnPropertyDescriptor Code";
        d = Object.getOwnPropertyDescriptor(o, val);
        if ( d ) {
            if ( !d.get  || !d.set ) {
                propRes = " - Data Descriptor: Configurable: " +  d.configurable + " - Enumerable: " + d.enumerable +
                    " - Writable: " + d.writable + " - Value: " + d.value;
            }
            else {
                propRes = " - Accessor Descriptor: Configurable " +  d.configurable + " - Enumerable " + d.enumerable +
                    " - Set: " + d.set + " - Get: " +  d.get;
            }
        }
        if (foundFunc) {
            logMessage(prefix + foundIt + " - Function Name: " + val + " - Type: " + typeof(objRef)
                + " - getPrototypeOf: " + p  + " - Constructor: " + objRef.constructor + "\n" + prefix +  propRes);
        }
        else {
            logMessage(prefix + foundIt + " - Property Name: " + val + " - Value: " + objRef + " - Type: " + typeof(objRef)
                + " - getPrototypeOf: " + p  + " - Constructor: " + objRef.constructor + "\n" + prefix +  propRes);
        }
        
        var oProto = Object.getPrototypeOf(o);
            // Dont walk down the Prototype Chain more than ONCE
        if ( !endOfPrototypeChainReached)
            checkPropertiesPrototype(oProto,level+1);
    });
   level++;
}

function getPrefix(len)
{
var eStr = '';
while( eStr.length < len )
    {
        eStr = eStr + ' ';
    }        
return eStr;
}

 

Output from Reflection Script

--- Detailed ProtoType Function walking down the Prototype Chain - Level:  1---

   [MammalObj, level:1] - Property Name: _objectName - Value: MammalObj - Type: string - getPrototypeOf:  - Constructor: function String() { [native code] }
   [MammalObj, level:1] - Data Descriptor.: Configurable: false - Enumerable: false - Writable: true - Value: MammalObj

--- Detailed ProtoType Function walking down the Prototype Chain - Level:  2---

     [Mammal BASE Object, level:2] - Property Name: type - Value: Mammal - Type: string - getPrototypeOf:  - Constructor: function String() { [native code] }
     [Mammal BASE Object, level:2] - Data Descriptor.: Configurable: true - Enumerable: true - Writable: true - Value: Mammal

     [Mammal BASE Object, level:2] - Property Name: objectName - Value: Mammal BASE Object - Type: string - getPrototypeOf:  - Constructor: function String() { [native code] }
     [Mammal BASE Object, level:2] - Data Descriptor.: Configurable: true - Enumerable: true - Writable: true - Value: Mammal BASE Object

     [Mammal BASE Object, level:2][Found a FUNCTION] - Function Name: getDetails - Type: function - getPrototypeOf: function () {} - Constructor: function Function() { [native code] }
     [Mammal BASE Object, level:2] - Data Descriptor.: Configurable: true - Enumerable: true - Writable: true - Value: function () {
                                     return("Type: " + this.type + " - ObjectName:  "  + this.objectName);  }
                                     
   [MammalObj, level:1] - Property Name: objectName - Value: MammalObj - Type: string - getPrototypeOf:  - Constructor: function String() { [native code] }
   [MammalObj, level:1] - Accessor Descriptor: Configurable true - Enumerable false - Set: function setObjectName(value) {
                oldVal = this._objectName;
                this._objectName = value;
                logMessage("[MammalObj objectName set]:: NEW Value: " + this._objectName + " - Old Value: " + oldVal );
            } - Get: function getObjectName() {
                logMessage('[MammalObj objectName get]:: ' + this._objectName );
                return this._objectName;
            }
   [MammalObj, level:1] - Property Name: isWalking - Value: false - Type: boolean - getPrototypeOf: false - Constructor: function Boolean() { [native code] }
   [MammalObj, level:1] - Data Descriptor.: Configurable: true - Enumerable: true - Writable: true - Value: false

JavaScript: Tackling THIS Object, Nested Functions/Closures by Samples

Overiew  – Some key Facts You Should Know

  • JavaScript has lexical scoping with function scope.
  • JavaScript looks like it should have block scope because it uses curly braces { },
    BUT a new scope is created only when you create a new function !
  •  JavaScript loses scope of THIS when used inside of a function that is contained inside of another function [ Nested Function, Closure ]
  • When Scope gets lost, by default, THIS will be bound to the global window object
  • The way  lexical scoping works in JavaScript works can’t be modified
  • Only the control of the context in which functions are called can be modified
  • Nested Function Definition : The nested (inner) function is private to its containing (outer) function. It also forms a closure.
  •  A closure is an expression (typically a function) that can have free variables together with an environment that binds those variables (that “closes” the expression).

This and Using String Literals for Object Creation

var john1 = 
{
    firstName: "John",
    sayHi:     function()
    {
        console.log("  Hi " + this.firstName + " !" );
        checkOBJ(this);
    }
};

function runThisTest1()
{
    console.log("--- Testing Object Literal with Dot Notation: john1.sayHi()  ---");
    john1.sayHi();
    console.log("--- Testing Object Literal with Bracket Notation:  john1['sayHi']() ---");
    john1['sayHi']();
    console.log("----------------------------------------------------");
}

Console.log 

js_Obj1

  • JavaScript methods can be called with Dot and Bracket Notation
  • The Constructor of Objects created via  Object Literals is the Object Type

This and Objects created with the NEW Operator

function User(name) 
{
    this.name = name;
    this.sayHi = function() 
    {
        console.log(" Hi I am "  + this.name);
        checkOBJ(this);
    };
}

function runThisTest3()
{
    console.log("--- New Operator:  var john = new User('John'); ---");
    var john = new User("John");
    john.sayHi();
    console.log("----------------------------------------------------");
}

js_Obj2

  • The constructor for our john object is the  User() Type
  • __proto__ points onto the Object type   [  == Prototypal inheritance ]

–> So far nothing is very special and things are straight forward

Understand the Lexical Environment – This step is needed for NESTED FUNCTIONS

var i = 99; 

var createAdders = function() 
{
    var fns = [];
    for (var i=1; i<4; i++) { 
        fns[i] = (function(n) {
            return i+n;
        });
    }
    i = 10;
    return fns;
};

function runThisTest5()
{
    console.log("--- Lexical Envrironment gives us Strange Results --- ");    
    var adders = createAdders();
    for ( var i = 1; i<4; i++  )
    {
        console.log("Result: " + adders[i](7)  + " - Function  adders[" + i + "] : " + adders[1] ); //17 ??
    }
    console.log("----------------------------------------------------");
}

Console.log

js_Obj3

  • Function createAdders() creates 3 different Adder Functions
  • All of these function references variable i - no obvious erorr
  • You may expect that these functions return 8,9 and 10 but they don’t do that
  • Instead all of these three functions returns 17 Why ?

Lexical environments

  • When a function is called, an NEW Lexical environment is created for the new scope that is entered.
  • Each new Lexical Evironment has a field called outer that points to the outer scope’s environment and is set up via [[Scope]]
  • There is always a chain of environments, starting with the currently active environment, continuing with its outer environment
  • Every chain ends with the global environment (the scope of all initially invoked functions). The field outer of the global environment is null.
  • An environment record records the identifier bindings that are created within the scope of this lexical environment
  • That is, an environment record is the storage of variables appeared in the context
  •  A lexical environment defines the association of identifiers to the values of variables and functions based upon the  lexical nesting structures of ECMAScript code.
globalEnvironment = { 
  environmentRecord: {
    // built-ins:
    Object: function,
    Array: function,
    // etc ...
    // our bindings:
    i: 99
  },
  outer: null // no parent environment
};

// environment of the "runThisTest5" function
runThisTest5 = {
  environmentRecord: {
    i :   1 [ 2,3 ] 
    adders:  Reference  to createAdders fn[]
  },
  outer: globalEnvironment

// environment of the "createAdders" function 
createAdders = {
  environmentRecord: {
    i: 10
    fn[] = [1] function (n) { return i+n; }
           [2] function (n) { return i+n; }
           [3] function (n) { return i+n; }
  },
  outer: runThisTest5
};

  • In the above sample i = 10 is used from the lexical env of the “createAdders” function
  • This is the reason why all of our functions returns the same result

 Note:

  • As we have different lexical Environments for every new function the THIS scope changes too !
  • Nested Functions are one well know sample for this behavior .

This and Nested Functions a potential problem

var catname = "Im a REAL PROBLEM";
function Cat6 ( name, color, age)
{
   this.catname = name;
   this.color = color;
   this.age = age;
   this.printInfo =  function() 
   {
      var that = this;
      nestedFunction = function() 
      {
         console.log("   Object Properties: Name:", this.catname, " - Color:", this.color, " - Age:", this.age );
         checkOBJ(this);
      }; 
      nestedFunction2 = function() 
      {
         "use strict";
        try
        {
            console.log("   Object Properties: Name:", this.catname, " - Color:", this.color, " - Age:", this.age );
        }  catch ( err )
        {
            console.log(" Error getting Object details: Error : " + err );
        }
      };
      
      nestedFunction3 = function() 
      {
         console.log("   Object Properties: Name:", that.catname, " - Color:", that.color, " - Age:", that.age );
         checkOBJ(that);
      }; 
      
      console.log('--- Call nestedFunction() - Not VALID THIS object generates a huge PROBLEM ---');
      nestedFunction();  
      console.log("----------------------------------------------------");
      console.log('--- Call nestedFunction() using a saved THIS Context armed by "use strict"; ---');
      nestedFunction2();
      console.log("----------------------------------------------------");
      console.log('--- Call nestedFunction() using a saved THIS Context ---');
      nestedFunction3(); 
    };
};

function runThisTest6()
{     
    console.log('--- THIS behavior with Nested Functions and NEW Operator  [ test6 ] ---');    
    var myCat6 = new Cat6('Fini', 'black', 7);
    myCat6.printInfo();   
    console.log("----------------------------------------------------");
}

 

Console.log

js_Obj4

  • nestedFunction()  has lost the original THIS context and is working with Window context
  • This can be quite dangerous as we may pick up wrong object properties [ like this.catname from the window object ]. All other properties become undefined.
  • nestedFunction2()  shows how we can detect this error by using “use strict”;  directive
  • nestedFunction3() shows a solution for the problem by storing  the current this object reference in the lexical Environment [ var that = this; ]. Later on this object reference is used to read object details.

THIS behavior with Nested Functions and NEW Operator using call(), apply(), bind()

  • To Fix the problem with an INVALID THIS context use call(), apply(), bind()
var name = "This SHOULD NEVER be printed !";
function Cat9 ( name, color, age)
{
   this.name = name;
   this.color = color;
   this.age = age;
   this.printInfo =  function() 
   {
      
      console.log("   Contructor Name:", this.name, " - Color:" + this.color, " - Age:" + this.age );
      nestedFunction = function() 
      {  
        // Window object alway will raise Error : caught TypeError: Converting circular structure to JSON 
        console.log("   Object Properties: Name:", this.name, " - Color:", this.color, "- Age:", this.age  );
        checkOBJ(this);
      };
      
      console.log('--- Using call() by providing this context as first parameter ---' );
      nestedFunction.call(this,  'Using call() by providing this context as first parameter' );
      
       console.log('--- Using apply() by providing this context as first parameter ---');
      nestedFunction.apply(this, []);
 
      var storeFunction = nestedFunction.bind(this);
      console.log('---  Using bind to save this context ---');
      storeFunction();
      
      console.log('---  Call nestedFunction() - SHOULD FAIL with undefined as THIS points now to WINDOW object---');
      nestedFunction();      
    };
};

function runThisTest9()
{   
    console.log('--- THIS behavior with Nested Functions and NEW Operator using call(), apply(), bind() [ test9 ] ---');    
    var myCat9 = new Cat9('Fini', 'black', 7);
    console.dir(myCat9);
    myCat9.printInfo();   
    console.log("----------------------------------------------------");
}

Console.log

js_Obj5

  • Using call(), apply() and bind() fixes the problem with a lost THIS context
  • The last test stresses again the fatal error that can happen when loosing THIS context

Real Sample: Borrow an Object Function Methode via call(), apply()

function Adder2 () 
{
    this.add = function(a,b) {  return a + b;  };
}

function runThisTest11()
{     
    var adder2 =  new Adder2;
    var res = adder2.add(2,3);
    console.log("--- Borrow an Object Function Methode via call(), apply()");
    console.log("    result of original Adder  created by NEW operator: " + res);
    
    console.log("    Adder called via call(): " + adder2.add.call( this, 2,3));
    console.log("    Adder called via apply(): " + adder2.add.apply( this, [2,3] ));
    console.log("----------------------------------------------------");
}

Console.log

js_Obj6

  • In this sample we borrow the add() function from the adder2 object by using call(), apply()

Real Sample: Using bind() within an AJAX request

function AjaxLoad2(url) 
{
    this.url = url;
    this.loadDoc = function(topLevelObject) 
    {
      var xhttp = new XMLHttpRequest();
      console.log("--- AJAX testing with bind() : Loading HTML page - URL: " + this.url);
     
      xhttp.onreadystatechange = function() 
        {        
         // When readyState is 4 and status is 200, the response is ready:
        // checkOBJ(this);
        if (xhttp.readyState === 4 && xhttp.status === 200) 
        {
            console.log("Return from AJAX Request:: HTML Page Loaded - URL: " + this.url +  " - status: " + xhttp.status);  
        }
        else if (  xhttp.readyState === 4 && xhttp.status === 0 )
        {
          console.log("Return from AJAX Request:: Error Loading Page - URL: " + this.url +  " - status: " + xhttp.status + " - readyState " + xhttp.readyState);  
          checkOBJ(this);
        }
      }.bind(topLevelObject);
           // .bind(this); should work too 
      
      xhttp.open("GET", url, true);
      xhttp.send();
    };
}

function runThisTest13()
{  
    /* 
     * Note: This Ajax request is expected to fail with  
     *        XMLHttpRequest cannot load https://google.com/. 
     *        No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
     */
console.log("--- Real Sample using bind() in an AJAX request --- ");    
var ajax = new AjaxLoad2("https://google.com");    
ajax.loadDoc(ajax);
console.log("----------------------------------------------------");
}

Console.log

js_Obj7

  • The XHR failure is expected
  • .bind(topLevelObject);  line allows us to pick up the URL via this.URL when printing the error text

 Function checkOBJ() to display Object Details

function checkOBJ(origThis)
{
    var construtorName = origThis.constructor.name;
    var objectDetails = "";
    try
    {
        objectDetails = JSON.stringify(origThis);
    }  catch ( err )
    {
       objectDetails = " Error to stringify Object " + err; 
    }
    console.dir(origThis); 
    console.log("Constructor Name: " + construtorName + " - this Object Details: " + objectDetails);
}

Reference

ObjectOriented JavaScript Programming by Samples

Creating New Objects using Object Literals

function initObjectsTest()
{
feli.sayMiaow();
}

var feli = 
{
    name: 'Felicitas',
    typ: 'Cat',
    sayMiaow: function ()
    {
        logMessageNEW("Miaow !");
    }  
};

Output:
8:28:35:254 Miaow !
  • Use Object Literals for Simple Objects
  • Objects created via Object Literals are Singelton Objects [ no need to implement a specific design pattern like in Java ]

 

Creating New Objects using a Constructor Function