38

Is it possible to create a ES6 class, that assigns a default value to a property if it's not passed in the new method?

class myClass {
    constructor(options) {
        this.a = typeof options.a !== 'undefined' ? options.a : 'default a value';
        this.b = typeof options.b !== 'undefined' ? options.b : 'default b value';
        this.c = typeof options.c !== 'undefined' ? options.c : 'default c value';
    }
}

var myClassWithValue = new myClass({a:'a value', b: 'b value'});

If I try to do this with this code, compiling with babeljs, I get a TypeError: Cannot set property 'c' of undefined.

Maybe I am not getting how classes work in javascript.

3
  • If it cannot read property c of undefined, it means that whatever is supposed to have property c is undefined. Which means in your case options is undefined. Commented Jul 10, 2015 at 13:43
  • But that's what I want. If I don't pass the c value in options, it creates the new object with a default value for c Commented Jul 10, 2015 at 13:47
  • 1
    Your code is OK, you can copy/paste it here and see.
    – Engineer
    Commented Jul 10, 2015 at 13:50

13 Answers 13

49

If you're going to use ES6, why not use all of ES6, i.e. default values for parameters and destructuring assignment

class myClass {
  constructor({a = 'default a value', b = 'default b value', c = 'default c value'} = {a:'default option a', b:'default option b', c:'default option c'}) {
    this.a = a;
    this.b = b;
    this.c = c;
  }
}
var v = new myClass({a:'a value', b: 'b value'});
console.log(v.toSource());
var w = new myClass();
console.log(w.toSource());

http://www.es6fiddle.net/ibxq6qcx/

edit: also tested and confirmed to run on https://babeljs.io/repl/

8
  • Nice. Didn't know about destructuring assignment. This is the solution I was looking for. Commented Jul 10, 2015 at 18:14
  • 4
    @Bergi what is it then? Commented Jul 12, 2015 at 22:52
  • 1
    Whilst steven's explanation is accurate and informative, the behaviour being described IS a case of destructuring assignment. Default values are a feature of destructuring assignment wherever it is used. In this case, it is being used to assign values to parameters. Commented Aug 10, 2018 at 2:12
  • 1
    I think this is fine if you have few properties. I'm in a scenario where I'm dealing with 50+ properties, so this quickly becomes very clunky. I think I'm going to go with the Object.assign method - see further down.
    – JCraine
    Commented Feb 28, 2019 at 0:41
  • 1
    why not do this -> this.config = {...defaults, ...config}; inside the constructor? Commented Mar 2, 2020 at 11:50
33

Object.assign works for me as well

class RenderProperties {
   constructor(options = {}){
        Object.assign(this, {
            fill : false, 
            fillStyle : 'rgba(0, 255, 0, 0.5)',
            lineWidth : 1,
            strokeStyle : '#00FF00'
        }, options);
    }
}
2
  • 1
    I like to Object.seal the default options and assign to that to filter out any unexpected options: assign(this, assign(seal({...}), options))
    – rich remer
    Commented Oct 3, 2018 at 4:37
  • Thanks @richremer, i was using Record from immutable and having a hard time using that! Commented Sep 12, 2019 at 19:57
9

If you would like to have some properties default but some not you can use also use spread operator (work as same as Object.assign answer)

const defaults = {someDefault: true};

class SomeClass {
    constructor(config) {
        this.config = {...defaults, ...config};
    }
}
1
  • In my opinion, the best option. The clearest and most comfortable way to organize "defaults". Additionally, I happened to use it in a project - when you have many variables in the config, it's probably best if you have it arranged this way. It can be even considered as "good practice".
    – TheTanadu
    Commented Oct 26, 2020 at 16:37
6

I would suggest the following:

class myClass {
  constructor(options) {
    const defaults = {
      a: 'default a value',
      b: 'default b value',
      c: 'default c value'
    };
    const populated = Object.assign(defaults, options);
    for (const key in populated) {
      if (populated.hasOwnProperty(key)) {
        this[key] = populated[key];
      }
    }
  }
}

var myClassWithValue = new myClass({a:'a value', b: 'b value'});
3
  • @LeonardoAlves assuming you're using an up-to-date version of Babel, it should convert const to var correctly in the transpiled code. Commented Jul 10, 2015 at 14:38
  • @mathieu shouldn't it be ... if(defaults.hasOwnProperty(key)){ this[key]=populated; } otherwise you are keeping any junk that options give, I could rewrite a class method by doing options.someMethod = null; and my guess would be that it could break the class. I hope it's not too complicated :)
    – Val
    Commented Nov 11, 2015 at 16:49
  • Yes i guess it depends of what you are doing. There might be some options without any default. Commented Nov 12, 2015 at 12:52
6

if there is no parameter passing through the constructor, it is assigned a default value as it was pre-set, I hope I have helped!

class User {
  constructor(fullName = "fooName", lastName, canAccess = false) {
    this.fullName = fullName;
    this.lastName = lastName;
    this.canAccess = canAccess;
  }
}
1
  • 1
    Okay, thanks for the edit fix. Can you explain in the post how the code works? You were flagged in the low-quality answer queue for lack of non-code text, one of the secret ingredients to an awesome answer!
    – ggorlen
    Commented May 4, 2019 at 3:39
4

Shorter and cleaner version based on Default parameters from MDN web docs.

class myClass {
  constructor(
  a = 'default a value',
  b = 'default b value',
  c = 'default c value'
  ) {
      this.a = a;
      this.b = b;
      this.c = c;
    }
}

let myClassWithValue = new myClass('a value','b value');

console.log(myClassWithValue);

With passing an Object.

class myClass {
  constructor({
  a = 'default a value',
  b = 'default b value',
  c = 'default c value'
  }) {
      this.a = a;
      this.b = b;
      this.c = c;
    }
}

let options = {
  a: 'a value',
  b: 'b value'
}

let myClassWithValue = new myClass(options);

console.log(myClassWithValue);

1

I propose this solution with OR operator, this seems more readable and easy to maintain, no need to define default value for option variable.

class myClass {
  constructor(options) {
    this.a = options.a || 'default a value';
    this.b = options.b || 'default b value';
    this.c = options.c || 'default c value';
  }
}

var v = new myClass({
  a: 'a new value',
  b: 'b new value'
});

   // to test 
console.log(v);

0

I just asked a similar question and found this question after I came up with a solution, so I thought I'd post it here as well.

My question was specific to ES6 and using deconstruction to setting up parameter defaults for a class. It seems the spec doesn't allow you to directly deconstruct arguments unless you do something similar to what @Jaromanda X did.

I wanted something shorter and cleaner, and ended up using the following pattern:

class Test {
    constructor(options) {
      let {
        defaultOne   : defaultOne   = 'default one value', 
        defaultTwo   : defaultTwo   = 'default two value', 
        defaultThree : defaultThree = 'default three value'
      } = (options) ? options:{};

      this.defaultOne   = defaultOne;
      this.defaultTwo   = defaultTwo;
      this.defaultThree = defaultThree;

      this.init();
    }

  init() {
    console.log(this.defaultOne);
    console.log(this.defaultTwo);
    console.log(this.defaultThree);
  }
}

new Test({defaultOne: 'Override default one value'});
new Test();

ES6 Babel test

Compiled Babel ES5

All we're doing here is deconstructing the options argument, and we handle the undefined use case with a ternary.

0

I'd just add it to the prototype. ES6 classes are just syntactic sugar, so you can use all the standard prototypal inheritance techniques that were available before the introduction of the class keyword.

const {assign, seal} = Object;

class MyClass {
    constructor(options) {
        assign(seal(this), options);
    }
}

assign(MyClass.prototype, {
    a: "default a value",
    b: "default b value",
    c: "default c value"
});
0

So I've been reading to the answers to this question and I haven't found one that works easily with extended classes, so I came up with this.

First you create a function that checks if an object has the properties of another object:

function checkValues(values = {}, targets) {
  values = Object.keys(values);
  targets = Object.keys(targets);
  return values.every(keyValor => targets.includes(keyValor));
}

Then in a class you define the default values like this:

class Error {
  constructor(options = {}) {
    //Define default values
    var values = {
      title: '',
      status: 500,
      mesagge: ''
    };
    //Check if options has properties of values
    var valid = checkValues(values, options);
    //If options doesn't has all the properties of values, assign default values to options
    if (!valid) {
      options = valores
    }
    //Asign options to the class
    Object.assign(this, options);
  }
}

So now, if you want to have a child class you just need to declare the default values for that child class:

class FormError extends Error{
  constructor (options = {}){
    var values = {
      invalidParams: [{name:'', reason: ''}]
    };
    var valid = checkValues(values, options);
    if (!valid) { options = values}
    super(options);
    Object.assign(this, options);
  }
}

EXAMPLE:

var a = new Error();
var b = new FormError();
var c = new FormError({invalidParams: [{name: 'User', reason: 'It has to be a string!'}]});
console.log(a, b, c);

Note: It only works if you want to have ALL the defaults values and not just some of them, for example:

var e = new Error({message: 'This is an error'});
console.log(e.message);// ''
//Error e will only have the default values, and message would be empty.

var h = new FormError({title: 'FORM ERROR', status: 400});
console.log(h.title);//''
//Error h will only have the defaults because I didn't provide 'invalidParams'
0

I made a small correction: Replaced the large object with Object()

class SomeClass {
  constructor({a = 'defA', b = 'defB', c = 'defC'} = Object()) { 
    this.a = a;
    this.b = b;
    this.c = c; }
}
console.log(new SomeClass ({a:'a', b:'b', c:'c'}))
console.log(new SomeClass ({a:'a', c:'c'}))

(Node REPL) Output:

SomeClass { a: 'a', b: 'b', c: 'c' }
SomeClass { a: 'a', b: 'defB', c: 'c' }
0

class myClass {
  constructor(
  a = 'default a value',
  b = 'default b value',
  c = 'default c value'
  ) {
      this.a = a;
      this.b = b;
      this.c = c;
    }
}

let myClassWithValue = new myClass('a value','b value');

console.log(myClassWithValue);

1
  • 1
    Adding explanation to your code would make it easier to understand you answer.
    – farshad
    Commented Jan 25, 2023 at 16:07
-1

I build this. I think it is the easiest way to read the code in this problem.

class myClass {

    constructor(options){

        let default_values ={
            a: '',
            b: '',
            c: ''
        }
        options = {...default_values, ...options}

        this.a = options.a;
        this.b = options.b;
        this.c = options.c;
    }
}

1

Not the answer you're looking for? Browse other questions tagged or ask your own question.