简体   繁体   中英

Undefined Property after new object construction using 'this' in constructor with self-executing function inside Object.defineProperty

I'm attempting to use a TypeScript-like structure to emulate an enumeration in a self-executing function for the 'value:' property. I checked all over for examples of ECMAScript5 / Crockford Object.defineProperty() use in the constructor (using 'this') but could not find much. As far as I can tell, the self-executing function should fill up both objects with 10 properties, and the closure is correct ('this' is not the window object). Here's my design pattern:

var player = {};
var Foo = function () {
    this.levelIndex;
    Object.defineProperty(this, 'levelIndex', {
        value: (function (levelIndex) {
            levelIndex[levelIndex.ONE =   0] = 'ONE';
            levelIndex[levelIndex.TWO =   1] = 'TWO';
            levelIndex[levelIndex.THREE = 2] = 'THREE';
            levelIndex[levelIndex.FOUR =  3] = 'FOUR';
            levelIndex[levelIndex.FIVE =  4] = 'FIVE';
            // setup the player object with the properties of the levelIndex
            // before it becomes non-enumerable:
            for (var i in levelIndex) {
                Object.defineProperty(player, i, {
                  value: isNaN(levelIndex[i]) ? 'LEVEL_' + levelIndex[i] : levelIndex[i],
                  enumerable: false
                });
                alert(player[i]); //<-- LEVEL_ONE, LEVEL_TWO, LEVEL_THREE, LEVEL_FOUR, LEVEL_FIVE, 0, 1, 2, 3, 4
            }
            alert('window? : ' + this === window); //<-- false
        })(this.levelIndex || (this.levelIndex = {})),
        writable: false,
        enumerable: false,
        configurable: false
    });
};
var foo = new Foo();
alert(player.ONE); //<-- 0
alert(foo.levelIndex); //<-- undefined
alert(foo.levelIndex.ONE); //<-- Uncaught TypeError: Cannot read property 'ONE' of undefined 

Why is foo.levelIndex undefined here?

EDIT: Fixed with return levelIndex; added to anonymous function call.

Any comments on my design pattern or suggestions for improvement welcome!

You're calling the anonymous function with a plain function call, so the value of this will be undefined in strict mode, or the global object ( window ) otherwise. What it definitely won't be is a reference to the object literal you're defining.

The first line of your "Foo" function looks a little suspicious:

    this.levelIndex;

That will have no effect on anything; specifically, it will not cause there to be a property named "levelIndex" created on the object referenced by this .

edit — also, as I look more at what you're doing, it's pretty clear why the "levelIndex" property ends up undefined : that object literal has a "value" property that's set to the return value of that anonymous function call. The anonymous function, however, has no return statement, so the call to Object.defineProperty involves a property object with the "value" property set to undefined . If you add

    return levelIndex;

to the end of that anonymous function, it ( might ) work. (I think it would.)

Based on help from @Pointy and @Bergi, here is the updated working code:

http://jsfiddle.net/5j9W5/

var player = {};
var Foo = function () {
    Object.defineProperty(this, 'levelIndex', {
        value: (function (levelIndex) {
            levelIndex[levelIndex.ONE = 0] = 'ONE';
            levelIndex[levelIndex.TWO = 1] = 'TWO';
            levelIndex[levelIndex.THREE = 2] = 'THREE';
            levelIndex[levelIndex.FOUR = 3] = 'FOUR';
            levelIndex[levelIndex.FIVE = 4] = 'FIVE';
            return levelIndex;
        })(this.levelIndex || (this.levelIndex = {})),
        writable: false,
        enumerable: false,
        configurable: false
    });
    // setup the player object with the properties of the levelIndex:
    for (var i in this.levelIndex) {
        Object.defineProperty(player, i, {
            value: isNaN(this.levelIndex[i]) ? 'LEVEL_' + this.levelIndex[i] : this.levelIndex[i],
            enumerable: false
        });
    }
};
var foo = new Foo();
alert(player[0]); //<-- LEVEL_ONE
alert(foo.levelIndex.FIVE); //<-- 4
for (var i in player) {
    alert(i + ' ' + player[i]); //<-- no output (properties are not enumerable here)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM