0

Say I have the following object definition:

var car = function (engine) { 
    this.engine = engine;

    this.calculateSpeed = function () {
        return engine.power * 20;
    };
};

the engine object in the calculateSpeed() function refers to the engine object passed in via the constructor and not the car's public engine member(which is the one I want).

If I wanted to change the engine of the car after creating the car object I want the calcuateSpeed() function to reference the new engine.

The Inner function cannot access outer functions variable question is similar to mine. But does not quite cover my use case as the OP is using a local variable and has no constructor.

If you could also explain why this won't work, that would also help a lot:

var car = function (engineInit) { 
    this.engine = engineInit;

    this.calculateSpeed = function () {
        return engine.power * 20;
    };
};

I know for this simple example I could use this.calculateSpeed = engine.power * 20; but for my real use case, I need a function.

Storm Muller
  • 511
  • 4
  • 19
  • 1
    what problem are you having using `this.engine.power`? – Musa Jun 16 '18 at 23:02
  • As I mentioned in my question, `this.engine.power` is scoped to the `calculateSpeed` function and not to the car object. – Storm Muller Jun 16 '18 at 23:05
  • Possible duplicate of [What underlies this JavaScript idiom: var self = this?](https://stackoverflow.com/questions/962033/what-underlies-this-javascript-idiom-var-self-this) – Josh Jun 16 '18 at 23:06
  • @StormMuller not sure a I agree. Did you check my answer? – user3210641 Jun 16 '18 at 23:22
  • @user3210641 yea, you're right. Seemed I was experiencing a closure issue. I accepted Josh's as it answered the question I posted. As well as helped me avoid my closure issue. – Storm Muller Jun 16 '18 at 23:31

3 Answers3

2

You can actually use this.engine.power.

// Define Car constructor
const Car = function (engineInit) { 
    this.engine = engineInit;

    this.calculateSpeed = function () {
        return this.engine.power * 20;
    };
};

// Create new car instance
const carInstance = new Car({ power: 100 });

// Log the result of calculateSpeed function
console.log(carInstance.calculateSpeed());
user3210641
  • 1,427
  • 9
  • 13
  • @Musa why did you edit the answer? Or more accurately why did you add unrelated code? – user3210641 Jun 16 '18 at 23:17
  • To show that in the calculation the engine being used is not the one passed in the constructor. If the edit is wrong you can always undo it. – Musa Jun 16 '18 at 23:19
  • How would the `this` keyword behave if `calculateSpeed` had a local `engine` variable? – Storm Muller Jun 16 '18 at 23:32
1

If you want the internal engine to be separate from the engine property visible on the outside of the instantiated Car, you might consider simply having another locally scoped variable, separate from the initial engine parameter, and reassigning it as necessary:

var Car = function(initialEngine) {
  this.engine = initialEngine;
  let engine = initialEngine;
  this.calculateSpeed = function() {
    return engine.power * 20;
  };
  this.changeEngine = function(newEngine) {
    engine = newEngine;
  }
};

var car = new Car({ power: 20 });
console.log(car.calculateSpeed());
car.changeEngine({ power: 40 });
console.log(car.calculateSpeed());
CertainPerformance
  • 260,466
  • 31
  • 181
  • 209
  • now add `console.log(car.engine.power);` to the last line. Car's engine hasn't changed. – Storm Muller Jun 16 '18 at 23:37
  • I thought you said you wanted them to be different for some reason? `refers to the engine object passed in via the constructor and not the car's public engine member(which is the one I want).` If you actually do want to assign to the property of the instantiated object, just assign to it as normal, `this.engine = newEngine` – CertainPerformance Jun 16 '18 at 23:40
0

This question is a duplicate of many other questions on stackoverflow, but here is something to get you started anyway.

By capturing this in another variable:

var car = function(engineInit) { 
  this.engine = engineInit;
  var self = this;
  this.calculateSpeed = function () {
    return self.engine.power * 20;
  };
};

By using bind:

var car = function(engineInit) { 
  this.engine = engineInit;
  this.calculateSpeed = function () {
    return this.engine.power * 20;
  }.bind(this);
};

By using a function's prototype:

function Car(engine) { 
  this.engine = engine;
}

Car.prototype.calculateSpeed = function() {
  return this.engine.power * 20;
};

var engine = new Engine();
var car = new Car(engine);
var speed = car.calculateSpeed();
Josh
  • 16,016
  • 6
  • 43
  • 62
  • Cool thanks Josh, could you please add the duplicates to my question, so that other who come here can see them too? Also I like the `var self = this` solution. Bind is changing scope of the local function which seems like a code smell and the prototype solution means that my object lives in 2 places which is also not ideal. Thanks! – Storm Muller Jun 16 '18 at 23:09
  • Prototype is actually your best pick as you can only create the `calculateSpeed` function once. In all the other instances (including the one mentioned in my answer) you create new function every time you create a new Car instance. – user3210641 Jun 16 '18 at 23:38