对JavaScript范围非常困惑

时间:2013-04-03 06:54:58

标签: javascript

以下是从SlickGrid示例派生的大大简化的代码片段:

model.js:

(function($) {
    function RemoteModel() {
        var someArr=[0];
        var someVar=0;
        var onDataLoadedSuccess = new Slick.Event();

        function ensureData(from, to) {
            h_request = setTimeout(function() {
                req = $.ajax({
                    url: url,
                    context: this,
                    success: function(resp) {
                        onSuccess();
                    }
                });
            }, 3000);
        }

        function onSuccess() {
            someVar=someVar+100;
            someArr[0]=someArr[0]+100;
            console.log(someVar + " " + someArr[0]); // yes values are changing properly for both !

            onDataLoadedSuccess.notify({ from: from, to: to });
        }

        return {
            "someArr": someArr,
            "someVar": someVar,
            "onDataLoadedSuccess": onDataLoadedSuccess,
        };
    }
})(jQuery);

main.js:

var loader=new RemoteModel();
var grid=new Slick.Grid("#wlGrid", loader.data, columns, options);

grid.onViewportChanged.subscribe(function(e, args) {
    var vp = grid.getViewport();
    loader.ensureData(vp.top, vp.bottom);
});

loader.onDataLoadedSuccess.subscribe(function(e, args) {
    // someVar is always 0 :( ! someArr[0] is changing fine .. why ???
    console.log(loader.someVar + " " + loader.someArr[0]); 
});

因此,网格上的事件(onViewportChanged)正在模型上调用ensureData 增加someVarsomeArr[0],两者都由返回值公开 RemoteModel

在递增事件后,模型会触发onDataLoadedSuccess 在该事件中,只有loader.someArr[0]的值发生了变化。

为什么someVaronDataLoadedSuccess的值始终为零?这是范围问题吗? 为什么someArr[0]的值变化很好?

如何正确阅读someVar中的onDataLoadedSuccess的值?

5 个答案:

答案 0 :(得分:0)

创建模型时,将其返回到loader

return {
  "someArr": someArr,
  "someVar": someVar,
  "onDataLoadedSuccess": onDataLoadedSuccess,
}

loader.someArr指向闭包中someArr指向的相同数组。但是,someVar是一个值为0的属性,从封锁中的someVar复制

使用对象(包括数组)分配变量或对象属性时,您将引用该对象。因此,loader.someArr指向闭包中someArr指向的相同数组。可以通过someArrloader.someArr进行更改,因为它们指向同一个数组。

但是在someVar的情况下,您从loader.someVar分配了someVar。由于0是原始值,因此值从<{1}}复制someVar。因此,loader.someVarsomeVar并未指向同一事物。修改loader.someVar不会修改someVar,反之亦然。

答案 1 :(得分:0)

RemoteModel函数包含someVarsomeArr的局部变量。这些也在创建loader时作为对象属性返回。不同之处在于someVar的数字值在创建时有效地复制到loader

数组也被复制到someArr,但副本是引用的。因此,您有两个指向同一个数组的东西。事件执行时,它会更新局部变量和本地数组。 loader指向同一个数组,因此您看到someArr[0]更改,但它没有“指向”该数字,因此当更改本地someArrloader值不是。

编辑以解决评论

一个选项是首先定义返回的对象,然后让私有函数引用它。这可能仅限于数字或适用于所有公众成员;为了清楚起见,我会做后者。使用这种方法,只需要更改类,而不是调用它的代码。

function RemoteModel() {
    var ret = {
        "someArr": [0],
        "someVar": 0,
        "onDataLoadedSuccess": new Slick.Event()
    };        

    function onSuccess() {
        ret.someVar=ret.someVar+100;
        // ...
    }

    return ret;
    // ...

第二个选项是创建一个getSomeVar方法,该方法位于您返回的对象中,以便您在课程外loader.getSomeVar()而不只是loader.someVar。 (如果需要可以在外部设置,你可以创建一个setSomeVar(newValue)

function RemoteModel() {
    var someVar=0;
    // ...

    function getSomeVar() {
        return someVar;
    }

    return {
        "someArr": someArr,
        "getSomeVar": getSomeVar,
        "onDataLoadedSuccess": onDataLoadedSuccess,
    };
}

然后:

loader.onDataLoadedSuccess.subscribe(function(e, args) {
    console.log(loader.getSomeVar() + " " + loader.someArr[0]); 
});

答案 2 :(得分:0)

数字和其他非对象基元按值返回,而不是通过引用返回:

    return {
        "someArr": someArr, // <-- object
        "someVar": someVar, // <-- number
        "onDataLoadedSuccess": onDataLoadedSuccess,
    };

someArr是一个对象(数组),它将通过引用返回。但是someVar只是一个简单的数字。因此,您将返回一个值。

您应该使用RemoteModel作为正确的构造函数,而不是返回RemoteModel中的对象:

    this.someArr=[0];
    this.someVar=0;
    this.onDataLoadedSuccess = new Slick.Event();

    /* move other functions into the prototype, get rid of the return */

如果您不熟悉将函数用作构造函数,请参阅Working with Objects on MDN

答案 3 :(得分:0)

这是因为javascript正在处理这些值。

(function($) {
    function RemoteModel() {
        this.someArr=[0];
        this.someVar=0;
        this.onDataLoadedSuccess = new Slick.Event();

        var _this = this;

        function ensureData(from, to) {
            h_request = setTimeout(function() {
                req = $.ajax({
                    url: url,
                    context: this,
                    success: function(resp) {
                        onSuccess();
                    }
                });
            }, 3000);
        }

        function onSuccess() {
            _this.someVar=_this.someVar+100;
            _this.someArr[0]=_this.someArr[0]+100;
            console.log(_this.someVar + " " + _this.someArr[0]); // yes values are changing properly for both !

            _this.onDataLoadedSuccess.notify({ from: from, to: to });
        }
    }
})(jQuery);

答案 4 :(得分:0)

多数民众赞成在你回来的那一刻

{
        "someArr": someArr,
        "someVar": someVar,
        "onDataLoadedSuccess": onDataLoadedSuccess,
};

someVar0

someVar拥有String,一种原始类型。

someArr引用Array非原始类型。

字符串(以及其他原始类型 通过引用传递按值,数组(以及其他非原始类型更具体:对象的变量引用按值传递(传递时,新的var包含引用的副本)

所以你返回的是你的String的“副本”