0

Background

I have a prototype object that has dozens of functions that access info from a list like this:

var prototype_object = {
  yellow: function(list) { return list[this.type+'_yellow']; },
  green: function(list) { return list[this.type+'_green']; },
  // ... 50 more functions here
}

The 'this.type' is supposed to come from the object that is assigned the prototype in a function:

function accessor(type) {
    var widget = Object.create(prototype_object);
    widget.type = type;
    return widget;
}

I have a central list of information so now I can call:

var access_foo = accessor('foo'); // Create the accessor
access_foo.green(list); //Find 'foo_green' in the list

Problem

These accessor fxns are passed to different areas of the app and called after being assigned to new objects. Thus the 'this' in the prototype functions is being reassigned (as expected in javascript) and resulting in undefined types.

"SO BIND IT": We can bind to functions to set the 'this' which will create new functions. I can't afford to instantiate 60 new functions for 100s of dozens of object types in dozens of places.

"CALL IT": Call would require that I pass the original accessor as the 'this', but as I said, access_foo.green is passed somewhere else in the app and cannot reference back to access_foo on call.

"CHANGE THE PROTOTYPE_OBJECT PARAMS": Not an option the way the app is written.

In the end I need an object that knows its type and shares access to a large list of functions. Am I right in saying there's no way to create custom accessors that can be called out of context without having them all instantiate/bind to the full set of possible prototype functions?

1
  • you can define the prototype method in the constructor instead, and use closure to keep a ref like "that" (from 'that=this;') further up in the constructor. otherwise, i think you know what must be done. you can also simply call bind in the constructor, which you need to call to make the objects anyway, so not much extra todo in that case. Commented Feb 20, 2015 at 20:21

1 Answer 1

2

You seem to have ruled out all the possible solutions. If you want a solution, you will have to adapt to use one of the options. If you're passing around accessor functions lots of places and you want them permanently bound to your object (so you don't have to also pass the object), then you will have to change how you're doing things.

The cleanest way would be to define the methods in the constructor and have the methods use a constructor local variable for the object reference and rely on the constructor closure instead of using this. Then, they would still work even though this was wrong. You will have to redo how you create your object in order to do that.

You could also redefine all your methods to pre-bind themselves. I will show code examples of both options.

Here's the closure method:

function myObj() {
    var self = this;
    self.prop1 = "foo";

    // define all methods in here and use self instead of this
    self.method1 = function() {
        console.log(self.prop1);
    }
}

var x = new myObj();
var m = x.method1;
m();

And, here's the pre-bind method while changing as little as possible of your existing code:

var prototype_object = {
  yellow: function(list) { return list[this.type+'_yellow']; },
  green: function(list) { return list[this.type+'_green']; },
  // ... 50 more functions here
}

function accessor(type) {
    var widget = Object.create(prototype_object);
    widget.type = type;
    // reassign prototype methods to the actual object and make them pre-bound
    // to this particular instance
    for (var method in prototype_object) {
       if (typeof method === "function") {
           // assign pre-bound method to the instance
           widget[method] = widget[method].bind(widget);
       }
    }
    return widget;
}

This one is a little more optimized version of the previous one that doesn't put the new methods on the prototype at all:

var prototype_object = {
  yellow: function(list) { return list[this.type+'_yellow']; },
  green: function(list) { return list[this.type+'_green']; },
  // ... 50 more functions here
}

function accessor(type) {
    var widget = {};
    widget.type = type;
    // reassign prototype methods to the actual object and make them pre-bound
    // to this particular instance
    for (var method in prototype_object) {
       if (typeof method === "function" && prototype_object.hasOwnProperty(method)) {
           // assign pre-bound method to the instance
           widget[method] = prototype_object[method].bind(widget);
       }
    }
    return widget;
}
Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for being quick on the draw. I've toyed with closures, but it seems I lose the benefits of the prototyping. The method you've presented is valid, but it requires us to create a new set of functions for every object, right?
@jbodily - yes, the first method requires the methods to be defined inline as closures use lexical scoping so access to self requires inline definition. The second example (pre-bind) allows the methods to be external.
@jbodily - I'm not sure what advantages of "prototyping", you're trying to achieve. You put all your prototoype_object methods in one constructor of a base object and just derive other objects from that one. There's no particular advantage to having them defined separately rather than associated with an object definition. If you plan on having tens of thousands of these objects instantiated at once, then using the actual .prototype to store the methods would be more memory efficient, but if you're not going to large numbers of instances, then you probably won't notice the difference,
@jbodily - you are asking how to forcibly bind methods to an object so you have to make a compromise from the "pure prototype" definition somewhere because that way of doing things doesn't hard bind to an instance. It relies on the caller causing this` to get set by calling the method appropriately. If you're asking how to break that requirement, you have to deviate from the standard .prototype definition somewhere.
I was hoping to prototype just so I wasn't having to reassign functions. There can be a lot of functions and objects but you're probably right—not so many that it causes a memory problem. Thanks for being clear with your examples.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.