4

Trying to get the following code to work; As the store variable is assigned an anonymous object, the "data" property is populated using a function call. This setting should also set the contents of the other object's property "masterData". I expected the "this" keyword to refer to the anonymous object being created, but I'm wrong...

    var store = {

        masterData : [],

        revert: function() {

            this.data = shallowCopy(this.masterData);
        },

        data: (function() {

            var overviewData = getOverviewData();
            this.masterData = overviewData;
            return chartData;

            }).call(),
    };

See also that "revert" property; It's given a function that'll create a copy of the object's data property contents.

What should be used, since "this" returns the DOMWindow object?

Jem
  • 6,226
  • 14
  • 56
  • 74

4 Answers4

3

The data function is being called before the object is defined; this will not be an alias to an object prior to that object's coming into existence. You can use a constructor function with a private, short-lived variable:

var getOverviewData = function () { return "I'm overview data!" }
var chartData = "I'm chart data!"

var store = new function () {
  var _masterData
  this.revert = function() {
    this.data = shallowCopy(this.masterData)
  }
  this.data = (function() {
    _masterData = getOverviewData()
    return chartData
  }).call()
  this.masterData = _masterData
}()

console.log(store)
// { revert: [Function],
//   data: 'I\'m chart data!',
//   masterData: 'I\'m overview data!' }
Richard Simões
  • 12,401
  • 6
  • 41
  • 50
2

i have the same problem and i solved it by just sending the scope with the function.

    var self = this;
    setInterval( yourFunction(self), 1000);

    yourFunction:function( self )
    {
        console.log(this);
        console.log(self);
    }

you see when it logs "this" it refers to the DOM and the self is refering to where ever you came from. i hope this helps! :)

EDIT: instead of inside Data to set the masterData, set the Master data after the Data is created.

var store = {

    masterData : [],

    revert: function() {

        this.data = shallowCopy(this.masterData);     //Here is where you create data
        masterData = this.data.overviewData;       //set the masterData
    },

    data: (function() {

        var overviewData = getOverviewData();
        return chartData;

        }).call(),
};

i think this should work, otherwise sorry :)

Arjen van Heck
  • 1,086
  • 1
  • 7
  • 9
  • Hi, thanks for your input. The problem here is to obtain a pointer to the object being created. Data, revert and masterData are three members of this anonymous objects. And the definition of data needs to access the "masterData" member. – Jem Apr 26 '12 at 10:49
  • 1
    I think when you give the data the reference (self), you can say: self.masterdata = overviewData; (also in this case the name "self" isnt very clear though XD – Arjen van Heck Apr 26 '12 at 10:54
  • sadly it's not possible to data: (var me = this; function() { var overviewData = getOverviewData(); me.masterData = overviewData; return chartData; }).call(), :( – Jem Apr 26 '12 at 11:00
  • Nice try :) But I guess the explanation to this is like Richard mentions in an other answer below: "this will not be an alias to an object prior to that object's coming into existence". Thanks anyway! – Jem Apr 26 '12 at 11:15
  • The `self` idea doesn't really work (and is usually an antipattern even when it does work). In the IIFE that sets `data`, `store` does not exist so there's no way to access `this` to assign a `self` variable to. It's adding another layer of indirection but doesn't avoid the fact that `this` (referring to `store`) and `store` itself doesn't exist. I'm not sure what the `setInterval` is supposed to do. The "workaround" in the second snippet is totally different logic. – ggorlen Nov 23 '20 at 16:25
1

change the flolowing line

}).call(),

to

})()

or, if you really want to use the call method :

 }).call(store)
gion_13
  • 41,171
  • 10
  • 96
  • 108
0

this won't refer to the current object when the anonymous function is called because it doesn't follow the obj.func() pattern which sets this in the func scope to obj and is not bound by some other means. Instead, it refers to the global window object (or undefined in strict mode).

call, apply and bind don't help since the store object hasn't been defined yet so there's no object to give as a thisArg parameter. On the other hand, with store.revert, this refers to store by the time it's called and behaves as expected.

The example seems overly contrived because there's no obvious motivation for setting masterData as a side effect of the anonymous function that also sets data. It seems you could just do something like:

// some sameple funcs and data for testing purposes
var getOverviewData = function () { return [42]; };
var shallowCopy = function (a) { return a.slice(); };
var chartData = [24];

var store = {
  masterData: getOverviewData(),

  revert: function () {
    this.data = shallowCopy(this.masterData);
  },

  data: chartData,
};

console.log(store.data);
store.revert();
console.log(store.data);

In general, if a pattern seems awkward to apply, it's often unnecessary or is a suboptimal way to achieve the desired result. The data IIFE setter proposed by OP breaks rules of thumb: it doesn't have single responsibility and causes a side effect by mutating an object.

Now, assuming you really do need to access this from an IIFE while defining this property (or for future readers who do have a legitimate motivation), there are multiple ways to achieve this.

The most obvious and direct solution is to create the store object and then add the data and mutate it in a second step:

var getOverviewData = function () { return [42]; };
var shallowCopy = function (a) { return a.slice(); };
var chartData = [24];

var store = {
  masterData: [],

  revert: function () {
    this.data = shallowCopy(this.masterData);
  },
};
store.data = (function () {
  this.masterData = getOverviewData();
  return chartData;
}).call(store);

console.log(store.data);
store.revert();
console.log(store.data);

If you don't like this as two steps, you can toss it in an IIFE:

var getOverviewData = function () { return [42]; };
var shallowCopy = function (a) { return a.slice(); };
var chartData = [24];

var store = (function () {
  var store = {
    masterData: [],

    revert: function () {
      this.data = shallowCopy(this.masterData);
    },
  };
  store.data = (function () {
    this.masterData = getOverviewData();
    return chartData;
  }).call(store);
  return store;
})();

console.log(store.data);
store.revert();
console.log(store.data);

An alternative is using a constructor function as shown here and invoking it with new which gives you access to the this context.

The IIFE you're using could be a separate function entirely that accepts an object as a parameter and mutates the two properties on it but this is only mentioned for completeness; it's probably only useful if you plan to reuse the function for multiple objects, in which case you might as well create a full-blown class or non-IIFE constructor function.

ggorlen
  • 44,755
  • 7
  • 76
  • 106