Jakov
Jakov

Reputation: 999

JavaScript new object inside function

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...

Upvotes: 0

Views: 754

Answers (5)

Patrick Roberts
Patrick Roberts

Reputation: 51846

@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] );
}

Upvotes: 1

Seblor
Seblor

Reputation: 7136

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.

Upvotes: 2

Patrick Roberts
Patrick Roberts

Reputation: 51846

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)
}

Upvotes: 2

Jonas Wilms
Jonas Wilms

Reputation: 138257

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

Upvotes: 2

Arun Redhu
Arun Redhu

Reputation: 1579

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

Upvotes: 2

Related Questions