JavaScript: Prevent Array.push()

JavaScript: Prevent Array.push()

本文关键字:push Array Prevent JavaScript      更新时间:2023-09-26

我有一个密封对象,其中包含一个数组成员,我希望在其上防止直接压入。

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()。将播放和报告

你可以重写push方法:

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

,当您需要在模块中使用它时,调用Array.prototype.push:

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

我还没有对此做任何性能测试,但这肯定有助于保护您的数组。

(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

这样,你可以在内部保留一个没有全局的接口副本,以允许你的辅助函数像正常一样修改数组,但是任何使用.push .splice等的尝试都会失败,要么直接,要么使用.bind(myArr,arg)方法。

它仍然不是完全不透水,但是一个很好的保护器。您可以使用Object.defineProperty方法为前900个索引生成受保护的属性,但我不确定这样做的含义。也有方法Object.preventExtensions(),但我不知道有一种方法来撤销这种效果,当你需要改变它自己

谢谢你,dandavis!

我使用了slice方法:

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.slice(); } // UPDATED
        });
        _b.updateC = function (newValue) {
            _c = newValue;
        };
        _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({}); // no more update = happiness

这允许我防止直接push调用强制执行一些逻辑。