2

I'm trying to work out the best way to modify an object without writing out a similar object three times. So I have these three objects:

var object1 = {
    start: start,
    end: end,
    type: 1
}

var object2 = {
    start: start,
    end: end,
    type: 2
}

var object3 = {
    start: start,
    end: end,
    type: 3
}

The only thing that changes is the type. Is there a better way to write this as so I'm not repeating myself?

2
  • Identify what is static and what is variable, then create a function that contains the static part and accepts the variable part as argument. If you are not familiar with functions in JavaScript: eloquentjavascript.net/03_functions.html . Commented Oct 31, 2015 at 0:49
  • Are start and end Date or Number? Commented Oct 31, 2015 at 1:08

4 Answers 4

2

You can set the common properties to a prototype object. For Example:

function ObjectMaker (typeVal) {
  this.type = typeVal;
}

ObjectMaker.prototype.start = "start";
ObjectMaker.prototype.end = "end";

var object1 = new ObjectMaker("1");
var object2 = new ObjectMaker("2");

gives

> object1.start
"start"
> object1.end
"end"
> object1.type
"1"

You could pass in an object to the maker function if number of variables are more.

Since the prototype is shared across objects, you will have a lighter memory footprint than having the same on each object.

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

2 Comments

You are assigning strings "end" and "start" to theprototype.end and prototype.start'. Also using JSON.stringify(object1)` and console.log(object1) will not present the properties end and start.
@Blindman67 yes I did that just for example output. Second point is true, console.log(Object.getPrototypeOf(object1)) works for debugging though.
2

Create a function

function createObj(start,end,type){
    return  {
        start : start,
        end   : end,
        type  : type
    }
}
var object1 = createObj(start,end,1);
var object2 = createObj(start,end,2);
var object3 = createObj(start,end,3);

// if start and end are in scope to the function 
function createObj(type){
    return  {
        start : start,
        end   : end,
        type  : type
    }
}
var object1 = createObj(1);
var object2 = createObj(2);

UPDATE in lieu of the various answers given.

As there are three answers that are all valid I will present speed tests from jperf.com for both creation and object use.

The test can be found Here jsperf.com

Testing in Chrome 47.0.2526.27 on Windows Server 2008 R2 / 7

Creation Tests

  • Create via Object Assign : 84,270 ±0.94%
  • Create via function : 18,267,444 ±1.72%
  • Create via new and prototype :7,886,088 ±1.69%
  • Create inline (literals) : 29,203,404 ±1.34% Fastest Create

Use tests.

  • Use for Object Assign : 266,301,340 ±2.45%
  • Use for function created : 301,185,103 ±1.78% Fastest Use
  • Use for new and prototype created : 18,628,401 ±3.14%
  • Use for inline created : 272,981,998 ±2.74%

As can be seen object.assign is very slow on when it come to creating object, but holds its own when the objects are being used.

Creating prototypes is about the worst that you can do. Though not as slow as Object assign it seriously suffers when it comes to using objects created this way. Running at below one tenth the speed of the next fastest.

As expected creating objects inline is by far the fastest, though not the most convenient. Yet it was not the fastest when it came to use which is a bit of a surprise.

The fastest for use is creation via function. Though I do not know why and suspect it has to do with V8's optimisation.

Each method has its pros and cons and should be judged on its use and project standards and conventions. In most cases speed is not an issue. Though keep in mind "green coding" favours code that takes the minimum number of cpu cycles, of which execution speed can give a good estimate of. Saving cycles saves power, money, and the lovely world we live on.

Below is the code for the tests. I think I was fair on all four methods.

//================================================================
// Create via Object Assign
var object1 = {
    start: start,
    end: end,
    type: 1
};

var object2 = Object.assign({}, object1, {type: 2});
var object3 = Object.assign({}, object1, {type: 3});

//================================================================
//Create via function
function createObj(type){
    return  {
        start : start,
        end   : end,
        type  : type
    }
}
var object1 = createObj(1);
var object2 = createObj(2);
var object3 = createObj(3);

//================================================================
//Create via new and prototype
function ObjectMaker (typeVal) {
  this.type = typeVal;
}

ObjectMaker.prototype.start = start;
ObjectMaker.prototype.end = end;

var object1 = new ObjectMaker(1);
var object2 = new ObjectMaker(2);
var object2 = new ObjectMaker(3);

//================================================================
// Create inline objects
var object1 = {
    start: start,
    end: end,
    type: 1
};
var object2 = {
    start: start,
    end: end,
    type: 2
};
var object3 = {
    start: start,
    end: end,
    type: 3
};

Use tests.

//================================================================
// Use case for object created with Object.assign
objectB2.end += 1;
objectB2.start += 1;
objectB2.type += 1;

//================================================================
// Use case for object create with new
objectA1.end += 1;
objectA1.start += 1;
objectA1.type += 1;

//================================================================
// Use case for function created object
objectC1.end += 1;
objectC1.start += 1;
objectC1.type += 1;

//================================================================
// Use of literal object create
objectD1.end += 1;
objectD1.start += 1;
objectD1.type += 1;

Setup code

  Benchmark.prototype.setup = function() {
    // assuming start and end are global
    var start = 0;
    var end = 10;

    // Prototype Method
    // object for use test 
    function ObjectMakerA (typeVal) {
      this.type = typeVal;
    }
    ObjectMakerA.prototype.start = start;
    ObjectMakerA.prototype.end = end;
    var objectA1 = new ObjectMakerA(1);

    // Object assign method 
    // for use test
    var objectB1 = {
        start: start,
        end: end,
        type: 1
    };
    // object to use
    var objectB2 = Object.assign({}, objectB1, {type: 2});

    // Anonymous object
    // for use test
    function createObj1(type){
        return  {
            start : start,
            end   : end,
            type  : type
        }
    }
    // object for use test
    var objectC1 = createObj1(1);

    // Literal object for use test
    var objectD1 = {
        start: start,
        end: end,
        type: 1
    };
  };

3 Comments

May be relevant, jsperf.com/constructor-with-use (setting an own with the same name as an inherited is unusual -- in fact it might be worth it's own perf to test that scenario with o.x = o.x + 1 vs ++o.x vs o.x++ vs o.x += 1
For the best performance avoid the use of the + because it has the extra overhead of type testing for string concatenation. o.x += 1; is slower than o.x -= -1; The difference is less than a 1 - 2% and inside the error range of jsperf with a = a +1;, a += 1, a = a - -1 and, a -= -1 all apparently the fastest. jsperf.com/plus-and-minus
What I actually meant was this, jsperf.com/operators-on-inherited-to-own looks like no real difference in chrome
2

If we are dealing with primitives for all of these properties you can use Object.assign with no worries

var objectTemplate = {
    start: start,
    end: end,
    type: 0
};

var object1 = Object.assign({}, objectTemplate, {type: 1})
    object2 = Object.assign({}, objectTemplate, {type: 2}),
    object3 = Object.assign({}, objectTemplate, {type: 3});

You don't have to use a template object, you could jump straight into the first object (see previous revision), but as @Blindman67 points out: if you can't guarantee an object will be as you expect then it may be better to use a template which you can guarantee.

3 Comments

You must be careful with this approach as it can for the unexperianced introduce a hard to spot bug. If you assign object1's properties end or start new objects will get the current object1 state. That can easily be overlooked and a hard bug to find. Eg say end = 10; use var on object2 and object3 so you can add the line object1.end = 20; between them. Now object3 will have the value for end object3.end === 20; // true
@Blindman67 What do you mean? var start = 'foo', end = 'bar'; then my code, then object1.start = 'baz'; now test another of the Objects, object3.start; // "foo", did not update. Behaviour as expected. --- Perhaps you mean if start or end was an Object and not a primitive?
Sorry now I read it I was obscure. I meant that Object.assign will copy the current state of the object you are assigning from. Any changes made to object1 will be reflected in any objects created with Object.assign. Generally the object you assign from should represent the default state of the object. It's a trap for newbies, but i think you should rename object1 to something like` objectTemplate` or objectDefault and create object1 with object2 & 3 just to make it clear that Object.assign is live
0

I assume you want to create multiple sets of objects with the same start/end pair, but different types.

One approach would be write a function, to which you'd pass a start/end pair, and which returns a function to create an object with a particular type:

function make_creator(start, end) {
  return function(type) {
    return { start, end, type };
  };
}

Now you can create your objects as

var creator = make_creator(start, end);
var object1 = creator(1);
var object2 = creator(2);
var object3 = creator(3);

In this case, you can think of the make_creator function as "storing" the values of start and end for use within the returned closure. It's conceptually related to "storing" them within the "template" proposed in another answer.

Prototypes

Let's consider using prototypes. It's not usually a good idea idea to place values in prototypes. You are likely to shoot yourself in the foot when you try to set the property and end up creating a sort of shadow value on the instance. Any performance (memory) differences will be miniscule unless you have a million objects, and will outweighed by the cost of finding the property on the prototype chain. More importantly, if you want multiple sets of objects with the same start/end pair, you'd have to create separate prototypes for each.

Performance

I'm not quite sure why or when we all started worrying quite so much about performance. The bottom line is that the difference in performance (either memory or speed) between any alternatives is going to be miniscule. (The exception is if you are creating millions or tens of millions of these objects.) The guiding principle should be to find an approach which is easy to write, easy to read, and easy to maintain. If you are creating tens of millions of objects, and you determine by profiling your program that this particular area is the bottleneck, then that would be the time to go back and optimize it.

1 Comment

Saving 1 million cycles on one page for one action may seem trivial, and be completely unnoticed, it is but a fraction of a second, who cares?. When multiplied on every computer for every click this does make a difference. As programmers we should be aware that we are consumers and producers of waste and how we code effects this. Just something to keep in mind when coding, because every little bit helps.

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.