JavaScript:Prevent Array.push()

时间:2015-09-08 20:01:34

标签: javascript arrays

我有一个带有数组成员的密封对象,我想要阻止直接推送。

var myModule = (function () {
    "use strict";
    var a = (function () {
        var _b = {},
            _c = _c = "",
            _d = [];
        Object.defineProperty(_b, "c", {
            get: function () { return _c; }
        });
        Object.defineProperty(_b, "d", {
            get { return _d; }
        });
        _b.addD = function (newD) {
            _d.push(newD);
        };
        Object.seal(_b);
        return _b;
    }());
    var _something = { B: _b };
    return {
        Something: _something,
        AddD: _b.addD
    };
}());

myModule.Something.c = "blah"; // doesn't update = WIN!!
myModule.AddD({}); // pushed = WIN!
myModule.Something.d.push({}); // pushed = sadness

如何阻止推送?

更新

感谢所有的想法。我最终需要将JSON发送到服务器。看起来我可能需要为数组使用一个对象,然后找出生成并返回所需JSON的方法,或者更改 _something 以使用.slice()。将播放和报道。

2 个答案:

答案 0 :(得分:2)

你可以覆盖推送方法:

var _d = [];
_d.__proto__.push = function() { return this.length; }

当您需要在模块中使用它时,请致电Array.prototype.push

_b.addD = function (newD) {
    Array.prototype.push.call(_d, newD);
};

答案 1 :(得分:1)

我还没有对此进行任何性能测试,但这肯定有助于保护您的阵列。

(function(undefined) {
    var protectedArrays = [];
    protectArray = function protectArray(arr) {
        protectedArrays.push(arr);
        return getPrivateUpdater(arr);
    }
    var isProtected = function(arr) {
        return protectedArrays.indexOf(arr)>-1;
    }
    var getPrivateUpdater = function(arr) {
        var ret = {};
        Object.keys(funcBackups).forEach(function(funcName) {
            ret[funcName] = funcBackups[funcName].bind(arr);
        });
        return ret;
    }

    var returnsNewArray = ['Array.prototype.splice'];
    var returnsOriginalArray = ['Array.prototype.fill','Array.prototype.reverse','Array.prototype.copyWithin','Array.prototype.sort'];
    var returnsLength = ['Array.prototype.push','Array.prototype.unshift'];
    var returnsValue = ['Array.prototype.shift','Array.prototype.pop'];

    var funcBackups = {};
    overwriteFuncs(returnsNewArray, function() { return []; });
    overwriteFuncs(returnsOriginalArray, function() { return this; });
    overwriteFuncs(returnsLength, function() { return this.length; });
    overwriteFuncs(returnsValue, function() { return undefined; });

    function overwriteFuncs(funcs, ret) {
        for(var i=0,c=funcs.length;i<c;i++)
        {
            var func = funcs[i];
            var funcParts = func.split('.');
            var obj = window;
            for(var j=0,l=funcParts.length;j<l;j++)
            {
                (function() {
                    var part = funcParts[j];
                    if(j!=l-1) obj = obj[part];
                    else if(typeof obj[part] === "function")
                    {
                        var funcBk = obj[part];
                        funcBackups[funcBk.name] = funcBk;
                        obj[part] = renameFunction(funcBk.name, function() {
                            if(isProtected(this)) return ret.apply(this, arguments);
                            else return funcBk.apply(this,arguments);
                        });
                    }
                })();
            }
        }
    }
    function renameFunction(name, fn) {
        return (new Function("return function (call) { return function " + name +
            " () { return call(this, arguments) }; };")())(Function.apply.bind(fn));
    };
})();

你会像这样使用它:

var myArr = [];
var myArrInterface = protectArray(myArr);
myArr.push(5); //Doesn't work, but returns length as expected
myArrInterface.push(5); //Works as normal

通过这种方式,您可以在内部保留一个不是全局的接口副本,以允许助手func正常修改数组,但是尝试使用.push .splice等将直接失败,或使用.bind(myArr,arg)方法失败。

它仍然不是完全无懈可击,但却是一个非常好的保护者。您可以使用Object.defineProperty方法为前900个索引生成受保护的属性,但我不确定其含义。还有方法Object.preventExtensions(),但我不知道在你需要自己改变时撤消这种效果的方法