Created
December 1, 2012 01:13
-
-
Save JesterXL/4179932 to your computer and use it in GitHub Desktop.
Quick explanation of how to do classes + modules + privacy in JavaScript.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>JavaScript Classes</title> | |
</head> | |
<body> | |
<script src="libs/log4javascript/log4javascript_uncompressed.js"></script> | |
<script> | |
// setup logging | |
var logger = log4javascript.getLogger("RouterExample"); | |
logger.setLevel(log4javascript.Level.ALL); | |
logger.addAppender(new log4javascript.BrowserConsoleAppender()); | |
window.logger = logger; | |
logger.debug("index.html::log4javascript ready to rock."); | |
// Step 1: globals | |
logger.debug("*************************************************"); | |
logger.debug("*** Step 1: Globals ***"); | |
var myObject = {}; | |
logger.debug("myObject: ", myObject); | |
logger.debug("window.myObject: ", window.myObject); | |
logger.debug("equal?: ", window.myObject == myObject); | |
// Step 2: local variables | |
logger.debug("*************************************************"); | |
logger.debug("*** Step 2: local variables ***"); | |
function init() | |
{ | |
logger.debug("before..."); | |
logger.debug("myObject: ", myObject); | |
logger.debug("equal?: ", window.myObject == myObject); | |
var myObject = {}; | |
logger.debug("after..."); | |
logger.debug("myObject: ", myObject); | |
logger.debug("equal?: ", window.myObject == myObject); | |
var myInnerObject = {}; | |
} | |
init(); | |
try | |
{ | |
logger.debug("myInnerObject:", myInnerObject); | |
} | |
catch(e) | |
{ | |
logger.error("myInnerObject can't be reached outside."); | |
} | |
// Step 3: Declarations | |
logger.debug("*************************************************"); | |
logger.debug("*** Step 3: Declarations ***"); | |
var myFunction = function() | |
{ | |
logger.debug("before..."); | |
logger.debug("myObject: ", myObject); | |
logger.debug("equal?: ", window.myObject == myObject); | |
var myObject = {}; | |
logger.debug("after..."); | |
logger.debug("myObject: ", myObject); | |
logger.debug("equal?: ", window.myObject == myObject); | |
var myDeclarationInnerObject = {}; | |
} | |
myFunction(); | |
try | |
{ | |
logger.debug("myDeclarationInnerObject:", myDeclarationInnerObject); | |
} | |
catch(e) | |
{ | |
logger.debug("myDeclarationInnerObject can't be reached outside."); | |
} | |
// Step 4: Globals via Function parameters | |
logger.debug("*************************************************"); | |
logger.debug("Step 4: Globals via Function parameters"); | |
var myGlobal = "imma global var"; | |
function readSomeGlobals(aGlobal) | |
{ | |
logger.debug("normal global reference via name, myGlobal:", myGlobal); | |
logger.debug("global reference via function parameter, aGlobal:", aGlobal); | |
logger.debug("aGlobal == myGlobal:", aGlobal == myGlobal); | |
} | |
readSomeGlobals(myGlobal); | |
// Step 5: Prototype classes with no privacy | |
logger.debug("*************************************************"); | |
logger.debug("*** Step 5: Prototype classes with no privacy ***"); | |
function SomeRandomClass(first, last) | |
{ | |
this.firstName = first; | |
this.lastName = last; | |
} | |
SomeRandomClass.prototype.getName = function() | |
{ | |
return this.lastName + ", " + this.firstName; | |
}; | |
var instance = new SomeRandomClass("Jesse", "Warden"); | |
logger.debug("instance:", instance); | |
logger.debug("instance firstName:", instance.firstName, ", lastName:", instance.lastName); | |
logger.debug("instance.getName():", instance.getName()); | |
instance.firstName = "Cow"; | |
logger.debug("I can change firstName to cow:", instance.getName()); | |
logger.debug("Our class' constructor:", SomeRandomClass); | |
logger.debug("Notice, it's smack dab on window/global:", window.SomeRandomClass); | |
logger.debug("this is why people will fake C like #ifdefs to test if the class is defeined before defining it."); | |
// Step 6: Closure classes with privacy | |
logger.debug("*************************************************"); | |
logger.debug("*** Step 6: Closure classes with privacy ***"); | |
var ClosureClass = function(first, last) | |
{ | |
var firstName = first; | |
var lastName = last; | |
return { | |
getName: function() | |
{ | |
return lastName + ", " + firstName; | |
} | |
}; | |
}; | |
var closureInstance = new ClosureClass("Jesse", "Warden"); | |
logger.debug("closureInstance:", closureInstance); | |
logger.debug("can't access private variable firstName: ", closureInstance.firstName); | |
logger.debug("However, it still exists via our internal getter method:", closureInstance.getName()); | |
var secondClosureInstance = new ClosureClass("Bruce", "Campbell"); | |
logger.debug("secondClosureInstance:", secondClosureInstance); | |
logger.debug("notice his name is different, and it's thus instance based, not static:", secondClosureInstance.getName()); | |
logger.debug("Same problem, though... he's on global:", window.ClosureClass); | |
logger.debug("ClosureClass == window.ClosureClass:", ClosureClass == window.ClosureClass); | |
logger.debug("...thus requring yet another #ifdef... *face palm*"); | |
// Step 7: Fun with closures (or an anonymous function that calls a function as its only paramter #lineOfCoke #lolWut) *** | |
logger.debug("*************************************************"); | |
logger.debug("*** Step 7: Fun with closures (or an anonymous function that calls a function as its only paramter #lineOfCoke #lolWut) ***"); | |
var myPackageOrModule = (function(){ | |
var MySimpleObjectClass; | |
function MySimpleObjectClass(first, last) | |
{ | |
setFirstName(this, first); | |
this.lastName = last; | |
} | |
function setFirstName(scope, newValue) | |
{ | |
logger.debug("private setFirstName called, scope:", scope, ", newValue:", newValue); | |
scope.firstName = newValue; | |
} | |
MySimpleObjectClass.prototype.publicGetName = function() | |
{ | |
return this.lastName + ", " + this.firstName; | |
}; | |
return MySimpleObjectClass; | |
})(); | |
try | |
{ | |
logger.debug("privateGetFirstName:", privateGetFirstName); | |
} | |
catch(e) | |
{ | |
logger.debug("notice we can't see the private function declaration, privateGetFirstName."); | |
} | |
logger.debug("myPackageOrModule:", myPackageOrModule); | |
logger.debug("Notice our actual class name can't be found, MySimpleObjectClass:", typeof MySimpleObjectClass == 'undefined'); | |
logger.debug("Now, you CAN use MySimpleObjectClass as the return value. Here, it'll be put on global.") | |
logger.debug("But if used in another module, it won't... which is hot... and halfway there."); | |
var myModuleClassInstance = new myPackageOrModule("Jesse", "Warden"); | |
logger.debug("myModuleClassInstance:", myModuleClassInstance); | |
logger.debug("myModuleClassInstance.publicGetName():", myModuleClassInstance.publicGetName()); | |
try | |
{ | |
myModuleClassInstance.setFirstName("Cow"); | |
} | |
catch(privateError) | |
{ | |
logger.debug("also, private doesn't work, heh, myModuleClassInstance.setFirstName('Cow'):", privateError); | |
} | |
var myModuleClassInstance2 = new myPackageOrModule("Mary", "Poppins"); | |
logger.debug("myModuleClassInstance2:", myModuleClassInstance2); | |
logger.debug("just to show normal unique instances are supported, myModuleClassInstance2 and 1.publicGetName():", myModuleClassInstance2.publicGetName(), myModuleClassInstance.publicGetName()); | |
// Step 8: Wrapping up, a closure example done the same way *** | |
logger.debug("*************************************************"); | |
logger.debug("*** Step 8: Wrapping up, prototype and closure based classes via module pattern ***"); | |
var theClosureClass = (function(){ | |
var MySimpleClosureClass = function(first, last) | |
{ | |
var closure; | |
var setFirstName = function(newValue) | |
{ | |
logger.debug("private setFirstName called, scope:", closure, ", newValue:", newValue); | |
closure.firstName = newValue; | |
} | |
closure = { | |
firstName: null, | |
lastName: last, | |
getName: function() | |
{ | |
return this.lastName + ", " + this.firstName; | |
} | |
}; | |
setFirstName(first); | |
return closure; | |
}; | |
return MySimpleClosureClass; | |
})(); | |
logger.debug("theClosureClass:", theClosureClass); | |
var trueClosureInstance = new theClosureClass("Jesse", "Warden"); | |
logger.debug("trueClosureInstance:", trueClosureInstance); | |
logger.debug("trueClosureInstance.getName():", trueClosureInstance.getName()); | |
try | |
{ | |
trueClosureInstance.setFirstName("Cow"); | |
} | |
catch(privateErrorAgain) | |
{ | |
logger.debug("Notice the closure retains his private method:", privateErrorAgain); | |
} | |
var trueClosureInstance2 = new theClosureClass("Kungfu", "Panda"); | |
logger.debug("trueClosureInstance2:", trueClosureInstance2); | |
logger.debug("trueClosureInstance2.getName():", trueClosureInstance2.getName()); | |
// Final note; to make a static, just return the object itself, | |
// remove the 'var MySimpleClosureClass = function(first, last)' part. | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment