试图理解这个函数定义的行为

时间:2014-06-19 17:02:05

标签: javascript module-pattern revealing-module-pattern

我不确定如何标题这个问题,但我正在使用带有构造函数的揭示模块模式,并注意到一些使我感到困惑的行为。这段代码的实际目的与我的问题无关,我只是想了解我看到的这种行为。

以下是我遇到的问题代码:

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;
}());

现在称之为:

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

现在这样做我看到setAjaxQuery函数中所有这些console.dir()命令都未定义。

我发现问题是参数名称与局部变量相同。我通过这样的方式解决了这个问题:

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';
}

我的问题是为什么会发生这种情况?当名称相同时,javascript正在做什么导致这个问题?

2 个答案:

答案 0 :(得分:1)

添加参数时,它们会为您提供的名称保留本地范围。由于外部作用域具有您尝试访问的那些名称的版本,因此无法重用这些名称,否则碰撞将导致仅使用本地作用域。由于第二个调用函数只能访问外部作用域,因此无法检索内部作用域中设置的值。

词汇范围可能会变得奇怪,但让我们考虑一个例子:

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
}

我对这些案例的建议是使用参数化来解决问题:

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;
}());

通过这种方式,您可以同时保持命名约定并通过(内部)明确声明范围。

答案 1 :(得分:1)

构造函数创建范围。构造函数中的参数定义该范围内的局部变量。因此,当您为变量赋值" callback"时,它引用构造函数范围中的变量,而不是父范围中的变量。

setAjaxQuery方法定义了它自己的范围。在此范围内,当您访问"回调"时,它引用父范围中的变量,该变量未被触及且未定义。

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.
    }
}