0

I have an object that contains both properties and methods:

var foo = {
    bar: function(a) {},
    baz: 42
}

I'm trying to restructure, moving all methods into a new methods object:

var foo = {
    methods: {
        bar: function(a) {},
    }
    baz: 42
}

Is it possible to remove foo.bar() while also preserving backwards compatibility? E.g. when a user tries foo.bar(a), it's aliased to foo.methods.bar(a)?

Matt
  • 3,778
  • 9
  • 35
  • 36
  • Just a suggestion, you might want to look into OO JS vs. this approach. – Madbreaks Jan 04 '17 at 22:21
  • 1
    if you remove foo.bar then it's not there, so "a user" wont be able to "try" `foo.bar()` - you could do something like `Object.keys(foo.methods).forEach(key => foo[key] = foo.methods[key]);` as long as there's no "gotchas" with any use of `this` in any of those methods – Jaromanda X Jan 04 '17 at 22:24
  • Backwards compatibility is key here. This code is on hundreds of sites, where removing `foo.bar()` would break any 3rd party code that expects it – Matt Jan 04 '17 at 22:29

2 Answers2

0

Object literals are typically used for storage of state (properties), rather than objects that also have behavior (methods), unless we are talking about objects with static methods.

In general, when making objects with methods, you should make constructor functions rather than object literals and then you should set up the methods on the prototype of that object.

// Use constructor functions for instantiatable objects
function foo () {
  // Local variables and instance properties are defined in the constructor
  
  // Set up a simple proxy reference:
  this.bar = this.methods.bar;
  this.baz = 42;
}

// Set up methods on the prototype, not the constructor because the 
// algorithms stored in these properties won't change from instance
// to instance, so we should just store them once - on the prototype
// that all instances of foo will inherit. 
foo.prototype.methods = {
  bar : function (a) {
    console.log("Hello from foo.methods.bar!");
  }
};

// Instantiate a "foo"
var f = new foo();

// Call the proxy
f.bar();
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • Difficult to see why *methods* should be on *Foo.prototype* rather than directly on *Foo*. – RobG Jan 04 '17 at 22:38
  • Since methods (behavior) don't change from instance to instance, putting them on the prototype is recommended to reduce the size of the object. No reason for every instance to store the same function. This is not true with properties that store instance data though, each instance might need to store a different value for the property, so those properties get written directly into the constructor function. – Scott Marcus Jan 04 '17 at 22:40
  • Thanks for the answer but this doesn't address the main question of how to preserve backwards-compatibility when methods are moved. It looks like ES6 proxies are the answer. – Matt Jan 05 '17 at 01:31
  • I guess my point was that the prototype is being used as just a hook to hang *methods* off. There's no functional difference between `this.methods.bar` to `foo.methods.bar` other than that in the second it's explicit that it's *foo.methods* that are being used to extend instances. This is similar to the [*OLOO pattern*](http://stackoverflow.com/questions/29788181/kyle-simpsons-oloo-pattern-vs-prototype-design-pattern). – RobG Jan 05 '17 at 02:19
-1

Updated

var foo = { 
    methods: { 
        bar: function(a) { return true; } 
    }, 
    baz: 42,
    bar: function() { return this.methods.bar() } 
}

You will need to keep a reference, something like:

bar: this.methods.bar

Community
  • 1
  • 1
Bagofjuice
  • 282
  • 2
  • 13
  • That syntax won't work. You can only use `this` inside a method, not when just assigning the original literal. – Barmar Jan 04 '17 at 22:38