0

I have one problem with OOP in JavaScript. In following example, when newObj.position.x is increased, newObj.x is not increased, despite the fact that I wrote this.x = this.position.x. Can you tell me why is that happening?

ClassOne = function( x, y )
{
	this.x = x;
	this.y = y;
};

ClassTwo = function( x, y, w, h )
{
	this.x = x;
	this.y = y;
	this.w = w;
	this.h = h;
	
	this.position = new ClassOne( this.x, this.y );
	this.x = this.position.x;
	this.y = this.position.y;
}

var newObj = new ClassTwo( 10, 20, 30, 40 );

for ( var i = 0; i < 15; i++ )
{
	newObj.position.x++;
	console.log( newObj.x );
}

Result of the console.log is 10, 10, 10, 10, 10...

2
  • 2
    newObj.position.x++; just increases newObj.position.x... Commented Sep 9, 2017 at 11:22
  • Class Two's x is set only once, during construction Commented Sep 9, 2017 at 11:24

5 Answers 5

2

when you are creating an Object with new then it allocates the separate memory in the heap. So the statement this.position = new ClassOne( this.x, this.y ); allocates the new memory for the this.position and now this.position.x and this.x both belongs to separate memory. So when you changes the instance property for newObj.position.x this would not get reflect to newObj.x

Sign up to request clarification or add additional context in comments.

2 Comments

As I understood, the point is that in the line this.position = new ClassOne( this.x, this.y );, a new object is created. So, basically newObj and newObj.position are 2 different objects, right?
This has nothing to do with "separate memory in the heap" or the fact that there are two objects. This answer fails to even mention the terms "pass-by-value" or "primitive", so it completely misses the point. Please see my answer for the correct explanation.
2

If you want both properties to always equal, you may use getters and setters ( and i will use the class syntax because i like it):

class Position {
 constructor(x,y){
   this.x = x;
   this.y = y;
 }
 toString(){
  return this.x+":"+this.y;
 }
}

class Instance {
  constructor(x,y,w,h){
    this.w = w;
    this.h = h;
    this.position = new Position(x,y);
  }

  get x(){
    return this.position.x;
  }
  set x(x){
   this.position.x = x;
  }

  get y(){
    return this.position.y;
  }
  set y(y){
   this.position.y = y;
  }
}

So one can do:

var player = new Instance(0,0,0,0);
player.x++;
player.position;//0:1

2 Comments

Okay that's just creepy
Yes I can see that >_>
2

Try using class with getter and setter member methods so that it internally references the position's values:

class One {
  constructor (x, y) {
    this.x = x
    this.y = y
  }
}

class Two {
  constructor (x, y, w, h) {
    this.w = w
    this.h = h

    this.position = new One(x, y)
  }
  
  get x () {
    return this.position.x
  }
  
  set x (v) {
    return this.position.x = v
  }
  
  get y () {
    return this.position.y
  }
  
  set y (v) {
    return this.position.y = v
  }
}

let newObj = new Two(10, 20, 30, 40)

for (let i = 0; i < 15; i++) {
  newObj.position.x++
  console.log(newObj.x)
}

Comments

2

newObj.position.x and newObj.x are 2 different values.

by doing

newObj.position.x++

You are increasing the x field of the position field of newObj. Thus, the x field of newObj itself won't change, as the 2 fields are not linked.

One way to link them is by adding accessors.

With function objects, you can create those like this :

ClassOne = function (x, y) {
  this.x = x;
  this.y = y;
};

ClassTwo = function (x, y, w, h) {
  this.w = w;
  this.h = h;

  this.position = new ClassOne(x, y);

  Object.defineProperty(this, 'x', {
    get: function () { return this.position.x; },
    set: function (newValue) { this.position.x = newValue; },
    enumerable: true,
    configurable: true
  });

  Object.defineProperty(this, 'y', {
    get: function () { return this.position.y; },
    set: function (newValue) { this.position.y = newValue; },
    enumerable: true,
    configurable: true
  });
}

var newObj = new ClassTwo(10, 20, 30, 40);

for (var i = 0; i < 15; i++) {
  newObj.position.x++;
  console.log(newObj.x);
}

Edit: after seeing the other answers, I would like to say that I know using es6 classes is better (at least for readability), but I wanted to keep OP's functions.

2 Comments

You should probably remove this.x = x; this.y = y; from ClassTwo and change this.position = new ClassOne(this.x, this.y); to this.position = new ClassOne(x, y);, since those assignments are extraneous otherwise.
Yeah right, I forgot to change that from OP's code. Thanks for pointing it out.
1

@ArunRedhu's answer has completely missed the point. This has nothing to do with newObj and newObj.position being separate objects, and everything to do with the fact that x and y are primitive values.

If you leave the code provided in the question the exact same except for replacing the values of x and y passed to the ClassTwo constructor with non-primitive values like arrays, then the properties will reflect as expected, as shown at the bottom of this answer. This proves that the reason has nothing to do with the instantiation of separate objects in heap memory, and is simply a result of the type of parameter used.

The difference between primitives and non-primitives is that primitives are pass-by-value while non-primitives are pass-by-reference. Because of this, assigning a primitive to another variable or property causes the value to be copied rather than referenced.

ClassOne = function( x, y )
{
	this.x = x;
	this.y = y;
};

ClassTwo = function( x, y, w, h )
{
	this.x = x;
	this.y = y;
	this.w = w;
	this.h = h;
	
	this.position = new ClassOne( this.x, this.y );
	this.x = this.position.x;
	this.y = this.position.y;
}

var newObj = new ClassTwo( [10], [20], 30, 40 );

for ( var i = 0; i < 15; i++ )
{
	newObj.position.x[0]++;
	console.log( newObj.x[0] );
}

Comments

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.