Prevent pushing values to an array in JavaScript by disabling the Array

I am facing a challenge with a sealed object that has an array member, and I want to restrict direct pushes.

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

How can I prevent the push?

UPDATE:

Thank you for all the input. I will need to eventually send the JSON to the server. It seems like I may have to use an object for the array and then find a way to generate and return the necessary JSON, or modify _something to use .slice(). Will experiment and provide updates.

Answer №1

To customize the push method, you can do the following:

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

Then, when you want to implement it in your script, call Array.prototype.push:

_custom.addToArray = function (newItem) {
    Array.prototype.push.call(_arr, newItem);
};

Answer №2

While I haven't conducted any performance tests on this technique, it definitely serves as a valuable shield for your array.

(function(undefined) {
    var protectedArrays = [];
    protectArray = function guardArray(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));
    };
})();

To utilize this safeguard, follow these steps:

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

By employing this method, you can maintain an internal copy of the interface that isn't exposed globally, allowing your helper functions to modify the array without any issue. Attempts to use methods such as .push and .splice will be thwarted either directly or through using the .bind(myArr,arg) approach.

While not foolproof, this protector offers solid defense. Another potential strategy involves utilizing the Object.defineProperty method to create protected properties for the initial 900 indexes. However, there are considerations regarding its implications. Additionally, there's the option of using Object.preventExtensions(), although reversing its effects when necessary remains a challenge.

Answer №3

Appreciation to dandavis for the help!

I opted for the slice method in my approach:

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"; // no update = WIN!!
myModule.AddD({}); // item successfully added = WIN!
myModule.Something.d.push({}); // prevented from updating = happiness

This approach ensures protection against direct push calls and enforces specific logic.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The value for $routeParams.param appears to be undefined

I have set up a route and am attempting to send parameters to a controller: app.js .config(function($stateProvider, $urlRouterProvider) { $stateProvider .state('spot', { url: "/spot/:param", templateUrl: "templates/spot.html", ...

Potential performance concerns and side effects of passing a parent component as a prop to its children in React and Redux

As I review code in a React-Redux project, I've noticed a recurring pattern where a parent smart component is being passed as a prop to its child component: import React from 'react'; import Child from '../components/Child'; expo ...

Error message: The function pokemon.map does not exist

I'm new to learning react and I've been working on creating a react app using the pokemon API. However, I've encountered an error that says TypeError: pokemon.map is not a function. I'm unsure why .map is not recognized as a function in ...

The ng-repeat directive is limited to functionality only within parent divs that have the id of

Is there a way to make ng-repeat work only in one specific place by defining an id where it should apply? I would like to achieve this functionality from the controller. For example, using something like $(#1).(ng-repeat work) Here is an example: <t ...

Error: React import from 'react' is invalid. Identifier not recognized

Whenever I attempt to run my server.js file locally by typing "node server.js" in the terminal, I keep encountering a: SyntaxError: Unexpected Identifier for "import React from react All I want to do is check if I receive a console.log("connected") mes ...

adjusting the vertical axis scale on the Google Charts API

During my experimentation with setting the vertical axis scale in Google charts, I found that the automatic scaling was not satisfactory for comparing two results. I wanted both results to be on an equal scale for a fair comparison. Despite setting the ma ...

PHP Foreach function generates distinct arrays in a separated manner

The array generated within a foreach loop is individual, as shown in the example below. It is necessary for them to be contained within a parent Array. Array ( [0] => Array ( [url] => https://example.com/product/osaka-entry-t ...

Having trouble with an Angular subscribe error that says "Property 'subscribe' does not exist on type 'AngularFireList<unknown>'.ts(2339)"? Let's find a solution together!

My goal is to retrieve data from a Firebase RDB in the data service by using the following function: this.getOrgId() .subscribe((orgId: string) => localStorage.setItem('orgId', orgId)); getOrgId() { return this.db.list(/users/${this.uId ...

retrieve an array including its data converted to string format

I'm working with an array and I want to return it with the name and square brackets, but I'm struggling to achieve this. Currently, all I get is s[[LCat;@3b250bf7] Here's my array along with a class representing cats. I initialize it at siz ...

Changing the stylesheet on a single-page application

Our angular-built single page app features a drag-and-drop interface for code snippets, each with its own unique styles separate from the main Bootstrap-themed admin panel. However, due to the differences in styling, navigating to the drag-and-drop sectio ...

Adjust the appearance of input fields that exclusively have the value "1"

When designing my website, I encountered a problem with the style I chose where the number "1" looks like a large "I" or a small "L." I am wondering if there is a way to use JavaScript to dynamically change the font style for input fields that only contai ...

JavaScript syntax issue detected, semicolon missing

I've encountered an issue with a form that contains potential errors defined in PHP. To dynamically change the form action based on the presence of errors, I have incorporated JavaScript into the process. The PHP error variable, $errors, has been conv ...

Issue with AngularJS script halting when reaching factory function that returns a promise

I have been working on a beginner project that is essentially a simple task manager (similar to those todo list projects). I created a user login page for this project and here is how it functions. There are two functions, siteLogin() for logging in, and ...

Automatically proceed to the following page after the video in the Qualtrics iframe comes to an end

I'm attempting to integrate a vimeo video using an iframe in my Qualtrics survey. Once the video is finished, I want it to automatically move on to the next page by pressing the "next button". Previously, I had my videos stored on Dropbox and used the ...

Interacting with jQuery before the doctype declaration can manipulate the text output in various ways

I am currently troubleshooting a PHP application that is displaying a PHP notice. The text appears in the browser before any other content, even before the DOCTYPE declaration. <br /> <b>Notice</b>: Undefined property: bla bla b ...

Customizing the label styles within MUI's Chip component

Incorporating React with MUI's library has been a seamless experience for me. One of the key MUI components I have integrated is the Chip Within this particular Chip, there lies the label attribute, offering the option to showcase a text f ...

How can you customize the appearance of the filledInput component within a TextField component in Material UI?

I need some guidance on how to change the color of the FilledInput component within a TextField. Unlike InputProps, FilledInputProps are not directly accessible for styling with classes. Any suggestions on how I can customize the styling of the FilledInpu ...

Executing python code from a JavaScript (Node.js) program without the need to create a child process

I have a unique setup where my hardware is running on nodejs, while my machine learning code is written in python3. My goal is to invoke the python3 program from nodejs (javascript) and pass data as arguments to the Python script. While researching, I cam ...

Indexing text fields for MongoDB collection that have been populated

Currently, I am in the process of learning how to use indexing with Mongoose/MongoDB and I am facing an issue that I can't seem to resolve. This is the schema I am working with: const timeSchema = new mongoose.Schema({ actionId:{ type:St ...

What could be the reason behind the disappearance of text from the previously highlighted button in my calculator's "button grid" when I change the highlighted button?

Currently, I am in the midst of creating a tip calculator with a grid consisting of various percentage buttons. My main objective is to change the font and background color when any tip button is selected. Nevertheless, an issue has surfaced - whenever I h ...