2

I have the following code inside my revealing module, but I am uncertain with how to declare/define imageListItem, which is strictly a DTO and doesn't really require any information hiding. Am I correctly defining this object?

var imageListItem = function() {
    var _title;
    Object.defineProperty(this, "title", {
        get: function () { return _title; },
        set: function (value) { _title = value; }
        }
    );
};

var imageList = (function () {
    var buffer = new CBuffer();
    return {
        populate: function (listItems) {
            buffer.push(listItems);
        },
        rotate: function() {
             buffer.rotateLeft();
        }
    }
})();

With imageListItem, I want to declare an object structure for later use. That declaration should not logically be dependent on how that object will later be used. That is, I don't want to find myself dynamically assigning new properties to, or deleting properties from, imageListItem by accident. Any assignment to properties should strictly be only to properties that have already been declared on the object.

Object.freeze() almost accomplihses this, by preventing properties being added or removed, but it also prevents properties being changed.

E.g. I want this:

var obj = {
  prop: function() {},
  foo: 'bar'
};

// New properties may be added, existing properties may be changed or removed
obj.foo = 'baz';
obj.lumpy = 'woof';

var o = Object.freeze(obj);

// Now any changes will fail
function fail(){
  'use strict';
  obj.delete(foo); // throws a TypeError
  obj.quaxxor = 'the friendly duck'; // throws a TypeError
}

I dont' want this:

// Now any changes will fail
function fail(){
  'use strict';
  obj.foo = 'sparky'; // throws a TypeError
}

You see? I want freeze to prevent quaxxor being added to obj, but I don't want it to prevent me changing the value of foo.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
ProfK
  • 49,207
  • 121
  • 399
  • 775
  • If you don't need any information hiding (or logic), why do you make it an accessor property? Just use `this.title = undefined;`. – Bergi Dec 13 '14 at 12:38
  • (Not sure if that's the whole answer you are looking for) – Bergi Dec 13 '14 at 12:39
  • @Bergi, it's probably close. I just didn't know how to declare an uninitialized variable. – ProfK Dec 13 '14 at 13:39
  • Why is the property uninitialized at all? What code does actually create them? – Bergi Dec 14 '14 at 10:35
  • @Bergi Nothing creates them except `this.title = undefined` or `Object.defineProperty`. That is the point of my question - how to declare them. – ProfK Dec 14 '14 at 12:14
  • No, I mean: What code calls `imageListItem`? What code uses the properties? Are they ever getting actual values beyond `undefined`, and how? – Bergi Dec 14 '14 at 13:27
  • [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) ? or show the accident example. – zb' Mar 08 '15 at 16:21
  • @eicto @eicto `Object.freeze()` does what I want in preventing properties being added or removed, but I don't want to prevent properties being changed, as the Mozilla doc example shows it does. See my edits. – ProfK Mar 08 '15 at 20:29

1 Answers1

5

What you are looking for may be either Object.preventExtensions() or Object.seal().

Similarly to Object.freeze(), both methods prevent new properties from being added to the object, nevertheless allow changing values of existing properties.

The difference between seal and preventExtensions is that seal strictly disallows deletion and conversion of properties from/to data accessors, while preventExtensions doesn't actually prevent existing properties from being deleted: this behavior depends on the JS engine you're using (some engines may let you delete the property, other ones may not).

So basically, quoting from the MDN Documentation:

The Object.preventExtensions() method prevents new properties from ever being added to an object (i.e. prevents future extensions to the object). [...] Note that the properties of a non-extensible object, in general, may still be deleted.

The Object.seal() method seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable. [...] Attempting to delete or add properties to a sealed object, or to convert a data property to accessor or vice versa, will fail.

Here's an example to demonstrate the behavior of both methods:

var myFirstObj = { foo: 1 },
    mySecondObj = { bar: "baz" };

Object.preventExtensions(myFirstObj);
Object.seal(mySecondObj);

myFirstObj.foo = false; // Works fine
mySecondObj.baz = "hello"; // Works fine
delete myFirstObj.foo; // May work fine depending on your JS engine

(function() {
    'use strict';
    myFirstObj.qux = 'something'; // Throws a TypeError
    mySecondObj.qux = 'something'; // Throws a TypeError
    delete mySecondObj.foo; // Throws a TypeError
})();

Now, talking about your ImageListItem Object, you can achieve what you want simply adding a line of code:

var ImageListItem = function() {
    var _title;
    Object.defineProperty(this, "title", {
        get: function () { return _title; },
        set: function (value) { _title = value; }
    });

    // Choose the one which fits your needs
    Object.preventExtensions(this);
    // or
    Object.seal(this);
};
Community
  • 1
  • 1
Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • the Mozilla page you link to says "Note that the properties of a non-extensible object, in general, may still be *deleted*." I didn't specify that in my question, but it would be better to have and I'm adding it now. Yet your example suggests that a `delete` *wilI* fail. But `preventExtensions` is great to know about, thanks. – ProfK Mar 11 '15 at 05:25
  • How is this not the correct answer? Above you just got through saying you wanted `preventing properties being added or removed`? I'd try for an answer, but your responses are far too fickle to attempt. – dclowd9901 Mar 12 '15 at 05:19
  • @ProfK I edited my answer adding what you needed, take a look at it :) – Marco Bonelli Mar 12 '15 at 07:34