Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 3 of 3
  1. #1
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,190
    Thanks
    10
    Thanked 569 Times in 550 Posts

    Classicalish Syntax for Javascript

    Simpler constructor coding with increased control: Crass.


    The need:
    Talking to programmers who start in languages other than javascript, i realized a need for a little more structure in constructors. To too many classical programmers, it's not always intuitive to implement fine-toothed control over scope, inheritance and concealment.
    Eg: forgetting "var" still works, but creates a global?!?!?!

    I thought it would be nice to use additional descriptors when writing constructor functions. This is the format I came up with:


    Example:
    Code:
    function Person(strName){ 
    
    	var born=new Date;			// private property (default)
    	private: var safeName=	escape(strName);// (default)
    	static:  var lastBorn= 	born; 		// bound to constructor object
    	proto: 	 var previous= 	7567; 		// prototype property (inherently static)
    	global:	 var lastRun= 	born.getTime();	// global property (not in resulting object)
    	public:  var bornOn=	born;		// regular properties are usually public
    	public:  var getBirth=	function(){	// regular methods are usually public
    	  		return String(this.bornOn); 
    		  }//end getBirthday
    	lambda:	var age=	function(){return (new Date).getTime() - born.getTime(); }
    
       this.api=(Crass(eval(Crass(arguments,this))));	// run Crass!
    
    }//end Person()


    Roles:

    • public: a normal this.property style property
    • private: a variable unreachable from outside the constructor, but still available later to anything defined within the constructor.
    • static: a variable held by the constructor's function object. The same variable is available to all instance of the class. To reach it, add the constructor's name before properties name eg(Person.lastBorn)
    • global: this variable is pushed to the top, becoming available to all scripts as a property of window instead of the new object.
    • prototype: A static property available to and inherited by all instance of the object. Any changes to these properties are instantly inherited by all instances. Often used for utility functions, inside of which this refers to the object's instance.
    • lambda: this is a special type of variable. Instead of holding a primitive property, it holds an expression in an anonymous function that returns the primitive you need. This can be used to provide extra properties without building each ahead of time. It should be used read-only, because assigning something to a lambda property will break it, and the regular primitive will stick.




    Resulting Object from Example Constructor (re-arranged for nicer visual formatting):

    Code:
    api : 	object : safeName,lastBorn,previous,lastRun,bornOn,getBirth,age
    bornOn:	object : Wed Jul 22 03:01:24 CDT 2009
    age : 	object : 8
    getBirth:function : function () {
        return String(this.bornOn);
    }

    Code:

    Formatted:
    Code:
    function Crass(fna, scope){ // class builder by dandavis.
    	var X=Crass;
    	if(scope){
    		
    		var ExportList =[], Flags={}, fn=fna.callee;
    		var varList=( fn.toString2().match(/(\n[\t ]*\w+\:\s+)+var \w+/g)||[]);
    		varList.map(function(a){ 
    			halves=a.split(/var \s*/);
    			var vName=halves[1].split(/[\s]+/g)[0], tokens ={};
    			 halves[0].split(/[\s:]+/g).filter(String).map(function(z){tokens[z]=1;});
    			ExportList.push(vName);
    			Flags[vName]=tokens;
    		}); //end variable list iteration
    		
    
    		X.flags=Flags; //1
    		X.pack=ExportList; //1
    		X.that=scope;
    		X.called=fn; //1
    
    	    return ("(["+  X.pack.join(",")   +"])");
    	}//scope?
       return obValsl(fna).map(function(a,n){
    	var key=Crass.pack[n]; 
    	var ob= this ;
    	var Fs=X.flags[key];
    
       if(Fs){ 
    	if(Fs.proto===1){
    		ob= (this.Crass == X) ? Object.prototype : Crass.called.prototype;
    		ob[key] = a; 
    	}
    	
    	if(Fs.global===1){
    		window[key] = a
    	}
    
    	if(Fs.private===1){
    		// this is the default, nothing to do here for now...
    	}
    
    	if(Fs.static===1){
    		X.called[key] = a
    	}
    
    	if(Fs.public===1){
    		ob[key] = a
    	}
    
    	if(Fs.lambda===1){ 	// lambda expression (use sparingly)
    		ob[key] = {	valueOf:function(){return a.call(ob,ob);},
    				toString:function(){return this.valueOf(); }	};
    	}
    
       }else{	  
    	ob[key] = a; 
       } //end if flags
    
    
      return key;
    } , Crass.that);
    
     	// Crass depends:
    	function obValsl(ob){var r=[],i=0,mx=ob.length;
    		for(var z=0;z<mx;z++){r[z]=ob[z];}return r;}
    
    }

    IE/FF compatibility code (1kb)
    Code:
    //compat code (1 kb):
    if(/a/[-1]){Function.prototype.toString2=function(){return String(this).replace(/(\w+:\n)+?/mg, function(j,s){
      return j.replace(/\n$/g," ");});}}else{Function.prototype.toString2=Function.prototype.toString;
    }//end if FF? patch for standard-format function views:
    
    if(![].map){//from http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:map
    Array.prototype.map=function(fun){var len=this.length;if(typeof fun!="function"){throw new TypeError;}var res=new Array(len);var thisp=arguments[1];for(var i=0;i<len;i++){if(i in this){res[i]=fun.call(thisp,this[i],i,this);}}return res;};}
    
    if(![].filter){//from http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:filter
    Array.prototype.filter=function(fun){var len=this.length;if(typeof fun!="function"){throw new TypeError;}var res=new Array;var thisp=arguments[1];for(var i=0;i<len;i++){if(i in this){var val=this[i];if(fun.call(thisp,val,i,this)){res.push(val);}}}return res;};}


    Compressed:
    Code:
    // compressed version of Crass (<1k, un-comment to use): 
    function Crass(e,f){var X=Crass;if(f){var L=[],F={},fn=e.callee;var V=(fn.toString2().match(/(\n[\t ]*\w+\:\s+)+var \w+/g)||[]);V.map(function(a){halves=a.split(/var \s*/);var b=halves[1].split(/[\s]+/g)[0],tokens={};halves[0].split(/[\s:]+/g).filter(String).map(function(z){tokens[z]=1});L.push(b);F[b]=tokens});X.f=F;X.pack=L;X.that=f;X.c=fn;return("(["+X.pack.join(",")+"])")}return O(e).map(function(a,n){var b=Crass.pack[n];var c=this;var d=X.f[b];if(d){if(d.proto===1){c=(this.Crass==X)?Object.prototype:Crass.c.prototype;c[b]=a}if(d.global===1){"dandavis"; window[b]=a};if(d.static===1){X.c[b]=a};if(d.public===1){c[b]=a};if(d.lambda===1){c[b]={valueOf:function(){return a.call(c,c)},toString:function(){return this.valueOf()}}}}else{c[b]=a};return b},Crass.that);function O(a){var r=[],i=0,mx=a.length;for(var z=0;z<mx;z++){r[z]=a[z]}return r}}


    Example in action:
    Code:
    // let's create an new object instance from the constructor:
    var me= new Person("dan davis"); 	
    
    //now let's look at it:
    document.write("<h2> Output of constructed object (prototype properties not visible) </h2>");
    
    obMap(me).map(function _viewer(a) {
    	document.write(  String(a[0]).bold().big() + " : " +
    			 String(typeof a[1]).fixed() + " : " + 
    			 String(a[1]) + "\n\n" 		);
    });//end _viewer()/demo
    
    function obMap(ob) { //obMap is required only for this demo
    	var r = [];var i = 0;for (var z in ob) {if (ob.hasOwnProperty(z)) {r[i++] = [z, ob[z]];}}return r;}
    ( Results of Example at top of post )


    While the example is boring, most class library example are.
    The main point is that you can easily control variables more using Crass.


    Instructions:

    1. Add the IE compat code and the compressed or formatted version of crass to one of your project's script files.
    2. write a Constructor function.
    3. add one of the following lines to the end of the constructor function, as shown in example:
    Code:
    this.api=(Crass(eval(Crass(arguments,this))));
    //or, if you don't want the array of upgraded properties, just use:
    (Crass(eval(Crass(arguments,this))));
    4. that's it, you don't need to modify the Crass call to use it, just cut and paste!


    Caveats:
    • one var per line
    • one role per var
    • must start with "var "
    • function x(){} wont work, use var x=function(){} instead



    Enjoy!
    Last edited by rnd me; 07-25-2009 at 03:49 AM.
    my site (updated 13/9/26)
    BROWSER STATS [% share] (2014/1/19) IE7:0.2, IE8:6.7, IE11:7.4, IE9:3.8, IE10:4.4, FF:18.3, CH:43.6, SF:7.8, MOBILE:27.5

  • #2
    Regular Coder
    Join Date
    Jun 2007
    Location
    USA
    Posts
    527
    Thanks
    26
    Thanked 74 Times in 72 Posts
    Interesting. I had no idea that labels(1) could be used in such a fashion (edit: oh, I see... eval). Kudos.

    Does the code support inheritance?

    Instead of doing this:
    Code:
    this.api=(Crass(eval(Crass(arguments,this))));
    Could the following work:
    Code:
    runCrass (arguments, this);
    
    where runCrass is defined as
    
    function runCrass (args, self) {
      self.api=(Crass(eval(Crass(args,self))));
    }
    (1) A label is the following:
    Code:
    label: statementOrControl;
    Last edited by Trinithis; 07-25-2009 at 06:11 AM.
    Trinithis

  • #3
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,190
    Thanks
    10
    Thanked 569 Times in 550 Posts
    Quote Originally Posted by Trinithis View Post
    Could the following work:
    Code:
    runCrass (arguments, this);
    
    where runCrass is defined as
    
    function runCrass (args, self) {
      self.api=(Crass(eval(Crass(args,self))));
    }
    No, it won't work. If the eval does not happen within the function body of the constructor, the vars cannot be reached...

    The eval is run on a "[]"-wrapped csv of variable names, turning it into an array of objects, each index representing one of the variables.
    In this fashion, you grab a "pointer" to an object, or at least assess a declared primitive.

    Using both the variable name and the actual value, this is modified as instructed by the labels just before the constructor returns.

    I wouldn't mind shortening it, but after lots of testing, the double-call is the only way that works in all browsers.

    This is the 2nd generation of this code i've written. The first one used a naming convention (_$) to accomplish two of the features of this one.
    the first one also grabs all named functions and tacks them onto this, which allows an anon function to be used as a "class" or as a package, just by including/omitting "new" in front of the anon.

    The posted version is about as far as i can get without dynamic code generation, which is coming in the next version.
    It will re-write a function's code, allowing block scope, bundled roles like above (several at once in blocks), getters, debugging, browser-conditional properties, private-scope loops, strong-types, and a few other tricks like performance optimization.
    target size: 5kb...

    If anyone has any ideas/feature requests, i'd love to hear them.
    my site (updated 13/9/26)
    BROWSER STATS [% share] (2014/1/19) IE7:0.2, IE8:6.7, IE11:7.4, IE9:3.8, IE10:4.4, FF:18.3, CH:43.6, SF:7.8, MOBILE:27.5


  •  

    Tags for this Thread

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •