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 8 of 8
  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

    reflecting on functions and methods

    how can i tell by looking at a function variable if the function acts as a method or as a function?

    for example:

    Code:
    var a,b;
    if(Math.random()<0.5){
     a="".bold; b=escape;
    } else {
     a=ecsape; b="".bold;
    }
    alert(
      a("hello world")
    );
    or
    Code:
    var a,b;
    if(Math.random()<0.5){
     a="".bold; b=escape;
    } else {
     a=ecsape; b="".bold;
    }
    alert(
      a.call("hello world")
    );
    each one of these should fail about half the time.

    i want to switch between the last expressions as needed based on some conditional expression, one i can't figure out how to code...


    either invocation is fine, and i don't mind always using call() and passing what needs to be there for this and arg[0], but i need to know ahead of time: which one i can use that won't throw when the function body only says [Native Code].
    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
    Master Coder felgall's Avatar
    Join Date
    Sep 2005
    Location
    Sydney, Australia
    Posts
    6,480
    Thanks
    0
    Thanked 635 Times in 625 Posts
    In JavaScript all functions are methods and all methods are objects. When you use call() the first parameter should be the object you are borrowing from.

    Each of those two versions will fail 50% of the time because "ecsape" is undefined and not a function/method and so whe "a" is set to that trying to run a() or a.call() will fail.
    Stephen
    Learn Modern JavaScript - http://javascriptexample.net/
    Helping others to solve their computer problem at http://www.felgall.com/

    Don't forget to start your JavaScript code with "use strict"; which makes it easier to find errors in your code.

  • #3
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    25,020
    Thanks
    75
    Thanked 4,323 Times in 4,289 Posts
    Ummm...but except for your misspelling of "ecsape", they do *NOT* fail.

    Instead, they just produce "undefined". It would be easy if they did fail, of course.

    But undefined can work for us, can't it?

    How about this somewhat hacky code for starters:
    Code:
    <script type="text/javascript">
    var a,b,s;
    if ( Math.random() < 0.5 )
    {
       a="zamboni".bold; b=escape;
    } else {
       b="zamboni".bold; a=escape;
    }
    
    s = a("hello world");
    if ( (/undefined|object/i).test(s) ) { 
        document.write( "a doesn't work");
    } else {
        document.write( s ); 
    }
    document.write("<hr>");
    s = b("hello world");
    if ( (/undefined|object/i).test(s) ) { 
        document.write( "b doesn't work");
    } else {
        document.write( s ); 
    }
    </script>
    The |object is there for, what else, MSIE. A true hack.
    Last edited by Old Pedant; 03-06-2013 at 01:44 AM.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • #4
    Regular Coder
    Join Date
    Mar 2006
    Posts
    725
    Thanks
    35
    Thanked 132 Times in 123 Posts
    //This example chooses the expression it returns:

    //Edited- it is simpler to use strings:

    Code:
    var a= Math.round(Math.random())? 'toUpperCase':'escape',
    c= "hello world";
    
    
    if(c[a]) alert(c[a]());
    else if(window[a]) alert(window[a](c));
    else alert(a);

    //


    with some more work you can account for input
    that can be functions or nested Object property names

    Code:
    String.prototype.reval= function(O){
    	var N, s= String(this);
    	O= O || window;
    	N= s.split('.');
    	while(O && N.length) O= O[N.shift()];
    	return O || s;
    }
    
    Function.prototype.named= function(){
    	var i= 0, Rx=  /function\s+([^(\s]+)\s*\(/,
    	tem= this.toString().match(Rx) || '';
    	if(tem) tem= tem[1];
    	return tem || '';
    }
    
    var a= Math.round(Math.random())? ''.toUpperCase:escape,
    c= "hello world";
    
    var as= a.named? a.named():a;
    a=a.reval? a.reval(): a;
    
    if(c[as]) alert(c[as]());
    else if(typeof a=='function') alert(a(c));
    else alert(as);
    Last edited by mrhoo; 03-06-2013 at 04:04 AM. Reason: simplified

  • #5
    Supreme Master coder! Old Pedant's Avatar
    Join Date
    Feb 2009
    Posts
    25,020
    Thanks
    75
    Thanked 4,323 Times in 4,289 Posts
    Well, I did say mine was a hack. <grin/>

    Nice, MrHoo. And nicely efficient, too.
    An optimist sees the glass as half full.
    A pessimist sees the glass as half empty.
    A realist drinks it no matter how much there is.

  • Users who have thanked Old Pedant for this post:

    mrhoo (03-06-2013)

  • #6
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,190
    Thanks
    10
    Thanked 569 Times in 550 Posts
    hey guy, thanks for posting, i appreciate it a lot. There's some good feedback here.

    Let me explain further.

    I'm trying to make a Function.prototype method that is closely related to .bind(), but with one upgrade of knowing whether or not the function need "de-methodized", for lack of a better term. That is to say that much like using Function.call, the function prototype should return a new function that expects "this" as it's first argument, but only if needed.

    whereas a "free floater" function can be called, a method (ideally generic) needs to have "this" set at call-time.

    for example, this works fine:
    Code:
    [1,2,3].map(Math.floor) // ===[1,2,3]
    but this does not:
    Code:
    [1,2,3].map("".bold) // ===["<b>undefined</b>", "<b>undefined</b>", "<b>undefined</b>"]
    but this 'problem' can easily be fixed using bind:
    Code:
    [1,2,3].map(  Function.call.bind("".bold)  ) // ===["<b>1</b>", "<b>2</b>", "<b>3</b>"]

    so, i want a prototype method that keeps me from having to type out the call wrapper for methods.

    that part i can handle, no problem:

    Code:
    Object.defineProperty(
      Function.prototype, 
      "free",
      {get:function(){ return Function.call.bind(this); }}
    );

    which means i can now do this:
    Code:
    [1,2,3].map("".bold.free) // === ["<b>1</b>", "<b>2</b>", "<b>3</b>"]

    now, i'm spoiled and i want to be able to call ANY function with this prototype, not just methods. I would also like to be able to apply several functions in some automated manner, and NOT have to manually set each one to use ".free" or not. It;s asking a lot, i know.


    The proposed solutions require global objct membership, which i can't guarntee. This means that they won't work for me. I also don't want to rely on naming conventions or strict prototype method membership because of generics like
    Code:
    (function(){return [].sort.call(arguments);}) (3,1,2)
    and
    Code:
    [].map.call("12345", Number )
    .


    Consider that the function might not be global, and that this prototype should run without the window Object in places like node.js.


    One final restriction: ideally, the function in question should not have to be pre-executed. while that's fine for "".bold, if the function was dbTables.dropAll(), i wouldn't want it firing without me telling it to.

    To be fair, i'm not sure this can even be done.

    I am hoping there is some way of inferring the this binding using inspection instead of execution.
    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

  • #7
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,190
    Thanks
    10
    Thanked 569 Times in 550 Posts
    im guessing from the lack of subsequent responses that what i'm asking cannot be done.

    this is mainly for using built-in methods. i think PM might be on to something with his scanning. I wonder if a whitelist could be fashioned to instruct the prototype method on what are native methods. Still, i'd hate to have to maintain such a list...
    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

  • #8
    Senior Coder rnd me's Avatar
    Join Date
    Jun 2007
    Location
    Urbana
    Posts
    4,190
    Thanks
    10
    Thanked 569 Times in 550 Posts

    eureaka

    ok, so i figured out a way to do this.
    it just takes iterating the available primitive prototype methods of native types, and flagging them as needing this/call.



    what good is all this?


    it let's you do quite a bit of data and string manipulation on complete sets of values instead of one-at-a-time.
    Normally, it takes quite a few hand-written function to really use [].map/[].filter in a project. Some of these are core logic, but many are tiny wrapper around native code.


    With argument homogenization, these built-in methods can run side-by-side with normal functions.

    knowing that, we can now stack them in pre-arranged ways ( .before/.after ).

    with a few functional programming helper methods on every function, we get enough fine-grain control over flow and execution that the paradigm starts to become usable.

    this is just a demo, but hopefully it shows the potential for these types:

    Code:
    (function useBuilder(primitives){
    
    
    
    //this bad-boy powers all the others:
    if (!Function.use) {
    	Object.defineProperty(
    	Function.prototype, "use", {
    		get: function _get() { 
    			return this[" gotta use call "]===null ? Function.call.bind(this) : this.bind(self);
    		} /* end _get() */
    	});
    } /* end if(!Function.use) */
    
    
    //this is the good one:
    Function.prototype.after = function(fn) {
    	var me = this,
    		early = [].slice.call(arguments, 1);
    	return function(aa) {
    		var args = [].slice.call(arguments);
    		return me.use.apply(this, [fn.use.apply(this, [aa].concat(early))].concat(args.slice(1)))
    	}
    };
    
    Function.prototype.before=function(fn){
    	var me = this,
    		early = [].slice.call(arguments, 1);
    	return function(aa) {
    		var args = [].slice.call(arguments);
    		return fn.use.apply(this, [me.use.apply(this, [aa].concat(args).slice(1))].concat(early))
    	}
    };
    
    
    
    
    /* next 3 are just used for functional programming HTML table  builder:
    Function.prototype.rebind=function(arg1, arg2){
      var that=this.use;
       return function(a,b,c){ return that.use(a,arg1,arg2!==undefined?arg2:b) }
    }
    Function.prototype.or=function(fn){
      var that=this.use;
       return function(a,b,c){ return that.use.call(this, a)||fn.call(this,a) || a; }
    }
    Function.prototype.if=function(fn){
      var that=this.use;
       return function(a,b,c){ if(b){that=fn.use;} return that.call(this,a); }
    }
    
    
    
    
    
    
    //flag all native this-needing primitive prototype methods with a low-collision hidden property:
      primitives.map(function(primitive) {
    	var mom = primitive.constructor.prototype;
    	return Object.getOwnPropertyNames(mom).map(function(methodName) {
    		var functionObject = primitive[methodName];
    		if (
    		 ["valueOf", "toString", "constructor"].indexOf(methodName) === -1  &&
    		  functionObject && functionObject.call &&
    		  !functionObject.hasOwnProperty(" gotta use call ")
    		){
    			Object.defineProperty(functionObject , " gotta use call ", {
    				value: null 
    			});
    		} /* end if usable method */
    		return methodName;
    	});//next proto
    });//next primitive
    
    
    
    //flag the prototype methods of these instances as needing a special "this" context:
    }(  [   "", 1, /./, false, new Date, []  ]    ));

    how about an example?
    sure, here's a demo step-up:
    Code:
    [1].map( "".bold.use ); 	// ["<b>1</b>"]
    [1].map( btoa ); 		// ["MQ=="]
    [1].map( btoa.use ); 		// ["MQ=="]
    [1].map( "".bold.after(btoa) ); 	  	     	// ["<b>MQ==</b>"]
    [1].map( btoa.before("".link, "#sample") )	// ["<a href=\"#sample\">MQ==</a>"]


    when you combine them, things get interesting quickly:

    Code:
    ["a"].map( 
       "".toUpperCase
          .before("".link, "#sample")
       .after("".big) 
     );
    which outputs:
    Code:
     ["<a href=\"#sample\"><BIG>A</BIG></a>"]
    you can change the order of execution by swapping before and after, or by flipping the logic itself.

    this isn't the simplest way to make a table from a csv, but it works and shows how you can actually get something done without using a single variable or return statement to perform the task:

    csv to HTML table in 1 expression and no returns:

    Code:
    "a,b,c\n1,2,3\n4,5,6"
    
    .split("\n").map(
      "".split.rebind(/,/, 999 )
    ).map(  
       [].map.rebind( String.or(Number) ) 
    )
    .map(  
      [].map.rebind( "".big.rebind(null) ) 
    ).map(  
      [].join.rebind("")  
    ).map(  
      "".bold.if("".fixed.rebind(null))
    ).join("\n")
     .replace(/big>/g,"td> ")
     .replace(/tt>/g,"tr> ")
     .replace(/<b>/g,"<table><thead>\n<tr> ")
     .replace(/<\/b>/g,"</tr></thead><tbody> ")
     .replace(/<\/tr>\s*$/,"</tr></tbody></table> ")
    note all the jank at the bottom. you can see how it's better at processing lists than formatting them.

    i've already started using this before/after pattern on my SQL interpreter for Arrays of Objects, and it's been the missing piece that can add minor forks to my existing logic with little repetition.
    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


  •  

    Posting Permissions

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