2

我不知道如何为这个问题命名,但我将揭示模块模式与构造函数一起使用,并注意到一些让我感到困惑的行为。这段代码的实际目的与我的问题无关,我只想了解我看到的这种行为。

这是我遇到问题的代码:

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 在做什么会导致此问题?

4

2 回答 2

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

这样,您可以同时保持命名约定并通过 (inner) 明确声明您的范围。

于 2014-06-19T17:07:20.673 回答
1

构造函数创建一个范围。构造函数中的参数定义了该范围内的局部变量。因此,当您为变量赋值时说“回调”,它指的是构造函数作用域中的变量,而不是父作用域中的变量。

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.
    }
}
于 2014-06-19T17:08:01.363 回答