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() コマンドで undefined が表示されました。

問題は、パラメーター名がローカル変数と同じであることがわかりました。次のようなことをして問題を解決しました。

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

引数を追加すると、指定した名前のローカル スコープが予約されます。外側のスコープには、アクセスしようとしている名前のバージョンがあるため、それらの名前を再利用することはできません。そうしないと、衝突によってローカル スコープのみが使用されることになります。2 番目の呼び出し関数は外側のスコープにしかアクセスできないため、内側のスコープに設定された値を取得する方法はありません。

レキシカルスコープは奇妙になる可能性がありますが、例を考えてみましょう:

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 に答える