When you want to keep the main reference immutable you need to work with shallow copies, similar to what happen when you work with primitives.
An example with Strings (when literally declared they are treated as primitives):
var a = 'Hello';
var b = a; 
b += ' World';
console.log({a, b});
 
 
As you can see, mutating b the variable a remains immutable... this because primitives are passed by value.
Because Object is not a primitive it is passed by reference and the above example becomes:
var a = { prop: 'Hello' };
var b = a;
b.prop += ' World';
console.log({a, b});
 
 
how do add them one at time to mainObject.prop2 without mutating mainObject ...?
var a = { prop: 'Hello' }
var minorA = { prop1: ' World' };
var minorB = { prop2: ' Moon' };
var b = Object.assign({}, a); //create a shallow copy
Object.assign(b, minorA, minorB)
// because you can pass as many params you want, you can simplify the previous code in this way:
var b = Object.assign({}, a, minorA, minorB);
 
 
how can it be done with ES6 spread?
  The Object spread operator, that is in esnext, you can use it with babel and Object rest spread transform
var b = {...a};