2

I wasn't sure how to title this question, but I'm using the revealing module pattern with constructors and noticed some behavior that confused me. The actual purpose of this code is irrelevant to my question I just want to understand this behavior I saw.

Here is the code that I had issues with:

var ajaxModule = (function() {
    //private
    var data,callBack,type,dataType,url,
    //public
    constructor;

    constructor = function(callBack,data,type,dataType,url) {
        //Required
        callBack = callBack || null;
        data = data || null;
        //Defaults
        type = type || 'POST';
        dataType = dataType || 'json';
        url = url || 'DBConnect.php';
    };

    //methods
    function setAjaxQuery() {
        log('in setAjaxQuery')
        dir(callBack);
        dir(data);
        dir(type);
        dir(dataType);
        dir(url);
    };

    constructor.prototype = {
        constructor: ajaxModule,
        setAjaxQuery: setAjaxQuery,
    };

    return constructor;
}());

Now calling it:

var ajax = new ajaxModule('someObject','someData');
ajax.setAjaxQuery();

Now doing this I was seeing undefined for all of those console.dir() commands in the setAjaxQuery function.

I found the problem was that the parameter names were the same as the local variables. I resolved the issue by doing something like this:

constructor = function(zcallBack,zdata,ztype,zdataType,zurl) {
    //Required
    callBack = zcallBack || null;
    data = zdata || null;
    //Defaults
    type = ztype || 'POST';
    dataType = zdataType || 'json';
    url = zurl || 'DBConnect.php';
}

My question though is why does this happen? What is javascript doing that causes this problem when the names are the same?

red888
  • 27,709
  • 55
  • 204
  • 392

2 Answers2

1

When you add arguments, they reserve the local scope for the names you provide. Since the outer scope has a version of those names that you are trying to access you cannot reuse those names or the collision will result in using the local scope only. Since the second calling function only has access to the outer scope there is no way of retrieving the values set in the inner scope.

Lexical scoping can get weird but let's consider an example:

function x(){
  var a = 5; // scoped to x
  var b = 10; // scoped to x
  function c() {
    var a = 10; // scoped to c, this is equivalent to having an argument named a
    return a + b; // b is scoped to x
  }
  return c; // scoped to x
}

My advice for these cases would be to use parameterization to solve the problem:

var ajaxModule = (function() {
    //private
    var inner = {
      data: null,
      callBack: null,
      type: 'POST',
      dataType: 'json',
      url: 'DBConnect.php'
    },
    //public
    constructor;

    constructor = function(callBack,data,type,dataType,url) {
        //Required
        inner.callBack = callBack || null;
        inner.data = data || null;
        //Defaults
        inner.type = type || 'POST';
        inner.dataType = dataType || 'json';
        inner.url = url || 'DBConnect.php';
    };

    //methods
    function setAjaxQuery() {
        log('in setAjaxQuery')
        dir(inner.callBack);
        dir(inner.data);
        dir(inner.type);
        dir(inner.dataType);
        dir(inner.url);
    };

    constructor.prototype = {
        constructor: ajaxModule,
        setAjaxQuery: setAjaxQuery,
    };

    return constructor;
}());

This way you are simultaneously keeping your naming convention and explicitly declaring your scope via (inner).

Gabriel
  • 18,322
  • 2
  • 37
  • 44
  • I think I understand. So would I be able to get away with something like this in a dynamically scoped language? – red888 Jun 19 '14 at 17:15
  • Not sure what you mean by dynamically scoped, there is block scope and lexical scope. My advice would be to put this all behind an object interface in your private scope. – Gabriel Jun 19 '14 at 17:30
1

The constructor function creates a scope. The parameters in the constructor define local variables in that scope. So, when you assign a value to the variable say "callback", it refers to the variable in the constructor's scope and not the variable in the parent scope.

setAjaxQuery method defines it's own scope. In this scope, when you access "callback", it refers to the variable in parent scope which was left untouched and so undefined.

function(){
    var abc = null;//scope outer

    function A(abc){
        abc = "something";
        //abc here refers to the variable in the local scope i.e. parameter of function A.
        //abc variable in outer scope is still null.
    }

    function B(){
        console.log( abc );
        //abc here refers to the variable in the outer scope which was never changed.
    }
}
gp.
  • 8,074
  • 3
  • 38
  • 39