SitePoint Sponsor

User Tag List

Results 1 to 12 of 12
  1. #1
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,108
    Mentioned
    28 Post(s)
    Tagged
    2 Thread(s)

    higher order functions

    I'm reading Functional Programming -- Eloquent JavaScript at the moment.
    Code javascript:
    // call function for each item
    function forEach(items, fn) {
    	for (var i=0, ii=items.length; i<ii; i++) fn(items[i]);
    }
     
    // reduce an array to a single value by calling a function on each item and adding to a base value
    function reduce(fn, base, array) {
    	forEach(array, function (item) {
    		base = fn(base, item);
    	});
    	return base;
    }
     
    // calls a function on each item in array
    function map(fn, array) {
    	var result = [];
    	forEach(array, function (element) {
    		result.push(fn(element));
    	});
    	return result;
    }
     
    // creates an array from iterable items starting at an index
    function asArray(items, start) {
    	var result = [];
    	for (var i=start || 0, ii=items.length; i<ii; i++) result.push(items[i]);
    	return result;
    }
     
    // define a new function that calls a fn with a list of arguments at the start pre-specified.
    function partial(fn) {
    	var fixedArgs = asArray(arguments, 1);
    	return function() {
    		return fn.apply(null, fixedArgs.concat(asArray(arguments)));
    	}
    }
     
    // define a new function that calls fn1 and fn2 on the result.
    function compose(fn1, fn2) {
    	return function() {
    		return fn2(fn1.apply(null, arguments)); // I switched the order around here, doesn't this make more sense?
    	};
    }
    Certain functions I see would be very useful:
    forEach & map I would commonly write our with for loops and arrays but I see that I've been wasting a lot of time writing these loops by hand.

    Any other functions like these you guys use regularly?

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,688
    Mentioned
    100 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by markbrown4 View Post
    Any other functions like these you guys use regularly?
    String.trim() is a useful one, along with Array.indexOf() and Array.filter()
    The array pages provide some compatibility code which becomes very useful.

    Other common functions that are used regularly are addClass(), removeClass(), hasClass() as well as createCookie(), readCookie(), eraseCookie()
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,108
    Mentioned
    28 Post(s)
    Tagged
    2 Thread(s)
    Thanks Paul, I use those often as well.

    I was more interested in specific alogithms or functions that change how you actually write your javascript.
    The functions I listed are all functions within functons, things like that have always done my head in but I'm finding a few of them really useful.

  4. #4
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,688
    Mentioned
    100 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by markbrown4 View Post
    The functions I listed are all functions within functons, things like that have always done my head in but I'm finding a few of them really useful.
    Ahh, I now see that the two functions you can't see below the fold are functions within functions.
    Yes, they are useful, and I too agree that my head sometimes explodes due to them.

    Have you seen the one about deriving the y-combinator in seven easy steps? That one is a quite instructional favourite.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  5. #5
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,108
    Mentioned
    28 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by paul_wilkins View Post
    Have you seen the one about deriving the y-combinator in seven easy steps? That one is a quite instructional favourite.
    WTF?

    Thanks.

  6. #6
    I meant that to happen silver trophybronze trophy Raffles's Avatar
    Join Date
    Sep 2005
    Location
    Tanzania
    Posts
    4,662
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    I like this one for creating elements with attributes easily:

    Code javascript:
    function $c(el, attrs, str) {
      el = document.createElement(el);
      if (attrs && attrs.constructor === Object) for (var a in attrs) el[a] = attrs[a];
      if (str !== undefined) el.appendChild(document.createTextNode(str));
      return el;
    }
     
    // example:
     
    var el = $c('a', {
      href:'http://sitepoint.com',
      title:'Sitepoint',
      onclick:function(){
        alert('hi');
      }
    }, 'Click me');

    I also wrote my own map function to cope with objects as well as arrays:

    Code javascript:
    function map(iterable, prop, f) {
      var i, j;
      prop = prop ? prop.toString() : '';
      if (!iterable.length || typeof f !== 'function') return iterable;
      function iterate(x) {
        if (prop in iterable[x]) iterable[x][prop] = f(iterable[x][prop]);
        else if (prop) return;
        else iterable[x] = f(iterable[x]);
      }
      if (iterable.constructor === Object) for (i in iterable) iterate(i);
      else for (i = 0, j = iterable.length; i < j; i++) iterate(i);
      return iterable;
    }

    I also wrote an ajax function that kept trying (up to 3 times) if something failed, and could upload stuff (for newer browsers - it doesn't check if the browser is capable).

    Code javascript:
    var ajax = function(data, callback, url, current, prog) {
      var req, requests = ajax.requests = ajax.requests || [], attempt, upload = current === 'upload', ctype = upload ? "application/octet-stream" : "application/x-www-form-urlencoded";
      if (window.XMLHttpRequest) req = new XMLHttpRequest();
      else {
        try {req = new ActiveXObject('Msxml2.XMLHTTP')}
        catch (e) {
          try {req = new ActiveXObject('Microsoft.XMLHTTP')}
          catch (e) {}
        }
      }
      if (current === undefined) {
        current = requests.push([]) - 1;
      }
      if (!upload) {
        attempt = requests[current].length;
        requests[current][attempt] = {timer:null, req:req};
      }
      req.onreadystatechange = function() {
        if (!upload) window.clearTimeout(requests[current][attempt].timer);
        if (!upload && req.readyState < 4 && attempt < 3) {
          requests[current][attempt].timer = window.setTimeout(function() {
            ajax(data, callback, url, current);
          }, 5000);
        }
        if (req.readyState === 4) {
          if (req.status === 200) {
            if (callback) callback(req.responseText);
            if (!upload) for (var i = 0; i < requests[current].length; i++) {
              requests[current][i].req.abort();
              window.clearTimeout(requests[current][i].timer);
            }
          }
          else if (!upload && attempt > 2) {
            switch(req.status) {
              case 404:
                error(req.status + " Not Found.\n\nURL for XMLHttpRequest appears to be wrong. Check it is correct:\n\n" + url);
                break;
              case 403:
                error(req.status + " Forbidden.\n\nLooks like accessing " + url + " isn't allowed. Perhaps a password is required?");
              case 500:
                error(req.status + " Server Error.\n\nSomething wrong on the server end meant this request failed - even after three attempts!\n\n");
                break;
            }
          }
        }
      }
      if (upload && req.upload) {
        if (prog) req.upload.onprogress = function(e) {
          if (e.lengthComputable) prog(e.loaded, e.total);
        }
      }
      req.open('POST', url, true);
      req.setRequestHeader("Content-type", ctype);
      if (upload) req.setRequestHeader("X-File-Name", data.name);
      if (upload && data.getAsBinary) req.sendAsBinary(data.getAsBinary());
      else req.send(data);
      return req;
    }

  7. #7
    SitePoint Guru
    Join Date
    Apr 2006
    Posts
    802
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    // Every up-to date browser (including IE9) includes these Array methods, and String.trim.
    // I add them to older browsers, when I need them, by including them in a script file
    // if the client does not define Array.prototype.reduce.


    Code:
    (function(){
        var AP= Array.prototype, SP= String.prototype, equalizer={
            every: function(fun, scope){
                var len= this.length;
                if(typeof fun== 'function'){
                    for(var i= 0; i < len; i++){
                        if(i in this){
                            if(fun.call(scope, this[i], i, this)== false) return false;
                        }
                    }
                    return true;
                }
            },
            filter: function(fun){
                var A= [], i= 0, itm, L= this.length;
                if(typeof fun== 'function'){
                    while(i< L){
                        itm= this[i];
                        if(!!fun(itm, i, this)) A[A.length]= itm;
                        ++i;
                    }
                }
                return A;
            },
            forEach: function(fun, scope){
                var L= this.length, i= 0;
                if(typeof fun== 'function'){
                    while(i< L){
                        if(i in this){
                            fun.call(scope, this[i], i, this);
                        }
                        ++i;
                    }
                }
                return this;
            },
            indexOf: function(what, i){
                i= i || 0;
                var L= this.length;
                while(i< L){
                    if(this[i]=== what) return i;
                    ++i;
                }
                return -1;
            },
            lastIndexOf: function(what, i){
                var L= this.length;
                i= i || L-1;
                if(isNaN(i) || i>= L) i= L-1;
                if(i< 0) i += L;
                while(i> -1){
                    if(this[i]=== what) return i;
                    --i;
                }
                return -1;
            },
            map: function(fun, scope){
                var L= this.length, A= Array(L), i= 0;
                if(typeof fun== 'function'){
                    while(i< L){
                        if(i in this){
                            A[i]= fun.call(scope, this[i], i, this);
                        }
                        ++i;
                    }
                    return A;
                }
            },
            reduce: function(fun, temp, scope){
                var i= 0, T= this, len= T.length, temp;
                if(scope== undefined) scope= window;
                if(typeof fun=== 'function'){
                    if(temp== undefined) temp= T[i++];
                    while(i < len){
                        if(i in T) temp= fun.call(scope, temp, T[i], i, T);
                        i++;
                    }
                }
                return temp;
            },
            reduceRight: function(fun, temp, scope){
                var T= this.concat().reverse();
                return T.reduce(fun, temp, scope);
            },
            some: function(fun, scope){
                var t= this, L= t.length;
                if(typeof fun== "function"){
                    for(var i= 0; i < L; i++){
                        if(i in t && fun.call(scope, t[i], i, t))
                        return true;
                    }
                    return false;
                }
            }
        };
        for(var p in equalizer){
            if(!AP[p]) AP[p]= equalizer[p];
        }
        if(!SP.trim){
            SP.trim=function(){
                return this.replace(/^\s+|\s+$/g, '');
            }
        }
    })();

  8. #8
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,108
    Mentioned
    28 Post(s)
    Tagged
    2 Thread(s)
    Thanks guys,

    Another one I use regularly is extend for copying properties from one object to another.
    Code javascript:
    function extend(dest, src) {
      for (var prop in src || {}) dest[prop] = src[prop];
      return dest;
    }
    Useful for providing default options for a function and merging in the overriding options.

  9. #9
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,688
    Mentioned
    100 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by markbrown4 View Post
    Another one I use regularly is extend for copying properties from one object to another.
    Code javascript:
    function extend(dest, src) {
      for (var prop in src || {}) dest[prop] = src[prop];
      return dest;
    }
    Useful for providing default options for a function and merging in the overriding options.
    Is it worthwhile using compatability code for a more recent native version of the same task, called defineProperties() ?
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  10. #10
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,108
    Mentioned
    28 Post(s)
    Tagged
    2 Thread(s)
    Interesting, I didn't know that existed.

    It's doing a bit more than extend, not sure i'd fallback to that because of the differences..

  11. #11
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,688
    Mentioned
    100 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by markbrown4 View Post
    Interesting, I didn't know that existed.

    It's doing a bit more than extend, not sure i'd fallback to that because of the differences.
    The only extra stuff it's doing is to perform sanity checks on the provided objects, and to ensure that they are allowed to have properties added.

    You can always make the code simpler, so that it does just enough to work according to how you use it, but I do think that it's important to allow the code you write to be capable of making use of native web browser methods when they become available.

    The following code is very much simplified from actual compatibility code, but it might meet the needs for much of what it's used for.

    Code javascript:
     
    if (!Object.prototype.defineProperty) {
        Object.prototype.defineProperty = function (prop, descriptor) {
            this.prop = descriptor;
        };
    }
    if (!Object.prototype.defineProperties) {
        Object.prototype.defineProperties = function (props) {
            var prop;
            for (prop in props) {
                if (props.hasOwnProperty(prop)) {
                    this[prop] = props[prop];
                }
            }
        };
    }
     
    // setup
    var car = {};
     
    // test
    car.defineProperties({
        make: 'Tesla',
        model: 'Roadster',
        color: 'Brilliant yellow'
    });
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  12. #12
    padawan silver trophybronze trophy markbrown4's Avatar
    Join Date
    Jul 2006
    Location
    Victoria, Australia
    Posts
    4,108
    Mentioned
    28 Post(s)
    Tagged
    2 Thread(s)
    Good points, I can see how that works.

    Some others I have thought of are bind() and times() which can avoid you rewriting logic for it all over the place:
    Code javascript:
    Function.prototype.bind = function(scope) {
      var _function = this;
      return function() {
        return _function.apply(scope, arguments);
      }
    }
    function times(n, fn, arg) {
      for (var i= 0; i < n; ++i) fn(arg);
    }


Bookmarks

Posting Permissions

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