3

I'm trying to set enumerable property, with setter, on an object prototype.

function Foo(){}
Object.defineProperty(Foo.prototype, 'tag', {
    enumerable: true, configurable: true,
    set: function(x){ 
        if(typeof(x) == "string") {
            Object.defineProperty(this, 'tag', {value: x});
        }
    }
});
var bar = new Foo();

bar.tag = 7;     console.log(bar.tag); // undefined
bar.tag = "baz"; console.log(bar.tag); // "baz"

console.log(bar); // {}
console.log(bar.propertyIsEnumerable('tag')); // false

Everything work as expected, except the last two line.
I just tested the code in node v0.10.25 . I don't understand why the property tag isn't enumerable.
As a workaround, I'm using Object.defineProperty in the constructor against this instead of Foo.prototype, but I would like to understand why object in javascript can't inherit from enuerable properties.

gkr
  • 469
  • 7
  • 11

3 Answers3

4

The problem is that your two Object.defineProperty call define different properties:

  • One setter property on the prototype
  • One value property on each this, i.e. instance

While the one on the prototype is enumerable and configurable, the instance property will not "inherit" these descriptors; and they will default to false on the new descriptor. You will need to set them explicitly:

Object.defineProperty(Foo.prototype, 'tag', {
     enumerable: true, configurable: true,
     set: function(x){ 
         if (typeof(x) == "string")
             Object.defineProperty(this, 'tag', {
                 enumerable:true, configurable:true, // still non-writable
                 value: x
             });
     }
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Hi Bergi, something strange happens when I set the invoking object to be Foo.prototype and `this` in the setter will refer to the same `tag` member:`Foo.prototype.tag = "hello"; console.log(bar); // { tag="hello" } console.log(bar.propertyIsEnumerable('tag')); // false` even though enumerable is false the logged 'bar' shows the 'tag' member (in Firefox with FireBug). I guess it's because bar doesn't have an own property named tag : `console.log(Foo.prototype.propertyIsEnumerable('tag')); //=true` – HMR Feb 04 '14 at 11:46
  • 1
    I'd say you need `Object.getPrototypeOf(bar).propertyIsEnumerable('tag') // true` as this works only for *own* properties, which `bar` does not yet have :-) – Bergi Feb 04 '14 at 11:49
  • yes, correct: `console.log(bar.propertyIsEnumerable('nonexisting')); // false` I guess `propertyIsEnumerable` will return false if the object `hasOwnProperty` is false. Can't find a reference but that seems to be the case. – HMR Feb 04 '14 at 11:52
2

You re assign tag in the set function (shadowing tag by creating the member on the Foo instance named bar) but don't set it to enumarable or configurable try the following:

function Foo(){}
Object.defineProperty(Foo.prototype, 'tag', {
    enumerable: true, configurable: true,
    set: function(x){ 
        if(typeof(x) == "string") {
            Object.defineProperty(this, 'tag', {
              value: x,
              enumerable: true
            });
        }
    }
});
var bar = new Foo();

bar.tag = 7;     console.log(bar.tag); // undefined
bar.tag = "baz"; console.log(bar.tag); // "baz"

console.log(bar); // { tag="baz" }
console.log(bar.propertyIsEnumerable('tag')); // true

More info about shadowing members, constructor functions and prototype: https://stackoverflow.com/a/16063711/1641941

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
-1

I think you can only define properties for an object, not on a prototype or a constructor function, see also this post.

That why putting the object.defineProperty inside the constructor will work: this inside the constructor is an object.

Community
  • 1
  • 1
Sonata
  • 2,177
  • 25
  • 32
  • 2
    No, prototype is can be set with anything you like. You can use defineProperty, defineProperties or Object.create to set the value of prototype. – HMR Feb 04 '14 at 11:30