0

I was trying to make my code more readable and ran into an oddity with my build and events objects that I attached to the prototype. Is there a way to do this?

function Something(){
    this.$container = $('<div>');

    return this;
}

Something.prototype.build = {
    list: function(){
        this.$container.append(...);
    }
}

Currently what happens is when build.list calls this, this refers to the build object, not the Something object.

I can't quite figure out another way of using it this way without renaming everything to buildList, buildComponentArray, and etc. Semantics maybe.

2778
  • 814
  • 1
  • 11
  • 32
  • possible duplicate of [Organize prototype javascript while perserving object reference and inheritance](http://stackoverflow.com/q/15884096/1048572) - just don't do that. Renaming to `buildList` etc. is fine. – Bergi May 25 '16 at 14:04
  • 1
    Give up on sub-objects in this context as Javascript does not really support what you're trying to do in a simple manner. Change `build.list` to `buildList` and life will be simple again. – jfriend00 May 25 '16 at 14:18
  • very well then... silly me – 2778 May 25 '16 at 14:31

1 Answers1

0

This is a possible solution

function Something(){
    this.$container = "<div></div>";

    //this creates a new object in the prototype chain for every instance
    this.build = Object.create(this.build);
    for (var prop in this.build) {
        if (this.build.hasOwnProperty(prop) && typeof this.build[prop] === "function") {
            this.build[prop] = this.build[prop].bind(this);
        }
    }
    return this;
}

Something.prototype.build = {
    list: function(){
        console.log(this.$container);
    }
}

Basically you'll bind every function in your this.build object to a function bound to this. You could extract this in a utility function and then bind all nested object you need to this. I suppose it must be possible too by manipulating/build the prototype chain but I haven't found it.

I know most of you recommend refactoring, but I thought to put this out there just so you have a complete set of solutions.

Edit

As pointed out, you need to move the build interface to your constructor in order to have this bound correctly. This solution is just a way to do this without having to move your code.

solution without using Object.create

This one is cleaner and more explicitly states what it does without messing with prototypes.

 function Something(){
    this.$container = "<div></div>";

    var build = this.build;
    this.build = {};
    for (var prop in build) {
        if (build.hasOwnProperty(prop) && typeof build[prop] === "function") {
            this.build[prop] = build[prop].bind(this);
        }
    }
    return this;
}

Something.prototype.build = {
    list: function(){
        console.log(this.$container);
    }
}
froginvasion
  • 833
  • 6
  • 19
  • That actually does *not* work, as it doesn't take into account multiple instances. You're binding the methods of the prototype `build` object to the last-created `Something` instance. You rather should simply move the `build` definition into the constructor. – Bergi May 25 '16 at 14:42
  • 1
    @Bergi you're absolutely right, I updated my answer to include two working solutions. – froginvasion May 25 '16 at 15:38