简体   繁体   中英

Javascript - Accessing 'this' members from sub-objects

In an object, for nothing more than purely aesthetic reasons, I'm wondering if there's a way to allow access to 'this' members from within sub-objects.

Take this example (which I've mirrored at http://jsfiddle.net/Wzq7W/5/ ):

function BlahObj() {
    this.users = {};
}
BlahObj.prototype = {
    userGet: function(id) {
        if (this.users[id] !== undefined) {
            return this.users[id];
        }
    },
    userAdd: function(id, name) {
        if (this.users[id] === undefined) {
            this.users[id] = name;
        }
    },
    user: {
        get: function(id) {
            if (this.users[id] !== undefined) {
                return this.users[id];
            }
        }
    }
}

var test = new BlahObj();
test.userAdd(1, 'User A');
test.userAdd(2, 'User B');
test.userAdd(3, 'User C');
test.userGet(2);
test.user.get(1);

The userGet() method will properly return 'User B', but due to scope, user.get() cannot access the base member.

In context, this object will have many more members and methods that go along with it, so camel-casing seems so dirty; being able to separate them out by group (user.get(), queue.foo(), room.data()) seems like a better approach in theory. Is it practical? Is there a way to do what I'm asking, in the way I'm looking to do it, or would I just be better off with camelCase?

Thanks for your thoughts!

The userGet() method will properly return 'User B', but due to scope, user.get() cannot access the base member.

The value of a function's this key word has nothing to do with scope. In a function, this is a local variable whose value is set by the call, not by how the function is declared or initialised.

You can use ES5 bind to change that (and add Function.prototype.bind for those environments that don't have it), or you can use a closure or similar strategy instead of this .

Edit

Consider the camelCase option instead, I think it's much cleaner than creating a sub–level, ie

getUser: function() {...},
addUser: function() {...},
...

rather than:

user: {
  get: function(){},
  add: function(){},
  ...
}

It's one character less to type too (eg addUser vs user.add )

DEMO

Change this:

user: {
    get: function(id) {
        if (this.users[id] !== undefined) {
            return this.users[id];
        }
    }
}

To:

user: function() {
    var that = this;

    return {
        get: function(id) {
            if (that.users[id] !== undefined) {
                return that.users[id];
            }
        }
    };
}

You have to call it like test.user().get(1) instead of test.user.get(1) though

Another friend of mine suggested a loop in the constructor that would bind() similar to what both @RobG and @Twisol had suggested, but without having to specifically write out each line.

There are probably other ways to do that loop so it's not specific to two levels deep, but this might be good enough for now. Are there any implications I should be aware of, specifically performance, or other considerations I may not be taking into account?

function BlahObj() {
    this.users = {};

    for (sub in this) {
        for (key in this[sub]) {
            if (typeof this[sub][key] == 'function') {
                this[sub][key] = this[sub][key].bind(this);
            }
        }
    }
}
BlahObj.prototype = {
    userGet: function(id) {
        if (this.users[id] !== undefined) {
            return this.users[id];
        }
    },
    userAdd: function(id, name) {
        if (this.users[id] === undefined) {
            this.users[id] = name;
        }
    },
    user: {
        get: function(id) {
            if (this.users[id] !== undefined) {
                return this.users[id];
            }
        }
    }
}

var test = new BlahObj();
test.userAdd(1, 'User A');
test.userAdd(2, 'User B');
test.userAdd(3, 'User C');
test.userGet(2);
test.user.get(1);

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