1

I work with JavaScript (ECMAScript 5). I have a class like this:

My class

function elementClass(htmlType, cssClass, text, value, id) {
    this.htmlType = htmlType;
    this.cssClass = cssClass;
    this.text = text;
    this.value = value;
    this.id = id;
}

Sometimes I need set some properties and I want to use code like below:

var newElement = new elementClass(htmlType ="div",cssClass = "form-group",id = "div1");

I need to pass div1 as id but it passed as third argument.

I can use below code for do it:

var newElement3 = new elementClass();
newElement3.htmlType = "div";
newElement3.cssClass = "form-group";
newElement3.id = "div1";  

But I want to minimize the code numbers like:

var newElement = new elementClass(htmlType ="div",cssClass = "form-group",id = "div1");

fiddle

I read many topics but I couldn't find any solution for doing it.

Is there any way to do it?

Thanks advance.

melpomene
  • 84,125
  • 8
  • 85
  • 148
Ali Soltani
  • 9,589
  • 5
  • 30
  • 55
  • What is the issue with either of the three approaches used at linked jsfiddle? – guest271314 Oct 21 '17 at 13:14
  • @guest271314 I want to use like `new elementClass(htmlType ="div",cssClass = "form-group",id = "div1")` to minimize the code. – Ali Soltani Oct 21 '17 at 13:16
  • The first code example at jsfiddle already does use that pattern. Are you expecting the properties to be assigned to the object automatically within the scope of the default parameter assignment - simply by passing or assigning the parameters? – guest271314 Oct 21 '17 at 13:17
  • @guest271314 Yes exactly. – Ali Soltani Oct 21 '17 at 13:19
  • See also [Can we set persistent default parameters which remain set until explicitly changed?](https://stackoverflow.com/questions/43466657/can-we-set-persistent-default-parameters-which-remain-set-until-explicitly-chang) – guest271314 Oct 21 '17 at 13:23

3 Answers3

2

Pass an object as a parameter:

function ElementClass ( settings ){
  for(var key in settings){
    this[key] = settings[key];
  }     
}

So you can do:

var instance = new ElementClass({
  some:"other"
});

If you want default parameters / prevent extension you could do:

function ElementClass( options ){
  var defaults = {
    some:"value"
    stays:"the same"
  };

  for(var key in defaults){
    this[key] = options[key] || defaults[key];
  }
}

So if you instantiate it:

 new ElementClass({
   some:"stuff", //overridden
   whatever:"unknown" //omitted
 });

It'll result in

{ some:"stuff", stays:"the same"}
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
0

A simple way to do this is by passing in a custom object instead of passing separate different parameters.

Creating an Interface (Optional)

You can add an interface declaration in your typings file for your intellisence.

interface ElementSettings {
    htmlType: string;
    cssClass: string;
    value: string;
    text: string;
    id: string;
}

Changing your Class

/** @param {ElementSettings} settings */
function ElementClass(settings) {
    // Throws an error if function is called without new
    if (!(this instanceof ElementClass)) {
        throw new TypeError("Cannot call a class as a function");
    }
    // Discards invalid input
    settings = settings instanceof Object ? settings : {};
    this.htmlType = settings.htmlType || "Default type";
    this.cssClass = settings.cssClass || "Default class";
    this.value = settings.value || "Default value";
    this.text = settings.text || "Default text";
    this.id = settings.id || "Default id";
}

// This can now be called using: 

new ElementClass({
    cssClass: "item",
    text: "Hello World"
});

Using a default Object

You can instead of having || "Default Value" everywhere have a default settings object and merge it with the settings parameter.

var ElementClass = (function() {
    var defaultSettings = {
        htmlType: "Default Type",
        cssClass: "Default Class",
        value: "Default Value",
        text: "Default Text",
        id: "Default Id"
    };

    /** @param {ElementSettings} settings */
    function ElementClass(settings) {
        // Throws an error if function is called without new
        if (!(this instanceof ElementClass)) {
            throw new TypeError("Cannot call a class as a function");
        }
        // Discards invalid input
        settings = settings instanceof Object ? settings : {};
        // In ES6 you could use Object.assign
        for (var property in defaultSettings) {
            this[property] = settings[property] || defaultSettings[property];
        }
    }

    return ElementClass;    
})();

Example

var ElementClass = (function () {
    var defaultSettings = {
        htmlType: "div",
        cssClass: "Default Class",
        value: "Default Value",
        text: "Default Text",
        id: "Default Id"
    };

    function ElementClass(settings) {
        if (!(this instanceof ElementClass)) throw new TypeError("Cannot call a class as a function");
        settings = settings instanceof Object ? settings : {};
        for (var property in defaultSettings) this[property] = settings[property] || defaultSettings[property];
        this.getDom = function getDom() {
            var dom = document.createElement(this.htmlType);
            dom.className = this.cssClass;
            dom.textContent = this.text;
            dom.value = this.cssClass;
            dom.id = this.id;
            return dom;
        };
    }

    return ElementClass;
})();

var form = document.querySelector("form");
var container = document.querySelector("#item-container");
form.addEventListener("submit", function (evt) {
    var formData = form.querySelectorAll("input:not([type='submit'])");
    var settings = {};
    formData.forEach(function (formItem) {
        var value = formItem.value;
        var name = formItem.getAttribute("name");
        if (value && name) settings[name] = value;
    });
    container.appendChild(new ElementClass(settings).getDom());
    evt.preventDefault();
});
#item-container>* {
background-color: rgba(100, 100, 100, 9.4);
min-width: 20px
min-height: 20px;
}
<form action="#">
  <input type="text" name="htmlType" placeholder="HTML Type" />
  <input type="text" name="cssClass" placeholder="CSS Class" />
  <input type="text" name="value" placeholder="Value" />
  <input type="text" name="text" placeholder="Text" />
  <input type="text" name="id" placeholder="Id" />
  <input type="submit" value="Create Object" />
</form>
<div id="item-container">

</div>

Some notes

JavaScript Class naming convention is UpperCamelCase so you should have ElementClass instead of elementClass. It is also a good idea to check if the Class has been called with new or as a function. So now if you do ElementClass({}) instead of new ElementClass({}) an Exception will be thrown. Finally, I wrapped the default settings Object in an IIFE with the ElementClass so that it will not be accessed/altered by other scripts.

nick zoum
  • 7,216
  • 7
  • 36
  • 80
0

You code appears to already use default parameters, you can also use shorthand property names and return the object and rest parameter to get any properties not initially defined

function elementClass({
  htmlType = "" // defaults to empty string
, cssClass = "" // defaults to empty string
, text = "" // defaults to empty string
, value = "" // defaults to empty string
, id = "" // defaults to empty string
, ...rest // handle any other properties passed as parameters
}) {
    return {htmlType, cssClass, text, value, id, ...rest}
}

$("#new1").click(function() {
    var newElement1 = new elementClass({
                        htmlType:"div"
                      , cssClass:"form-group"
                      , id:"div1"
                      , otherData:123
                      });
    showElement(newElement1);    
});

jsfiddle https://jsfiddle.net/3r8a6gbq/2/

guest271314
  • 1
  • 15
  • 104
  • 177
  • Tanks for answer. But in this way method 2 and 3 don't work. – Ali Soltani Oct 21 '17 at 13:44
  • @AliSoltani _"But in this way method 2 and 3 don't work."_ Not sure what you mean? The code at Answer defines default parameters to the function, which if not passed are set to the default, else the property value is overwritten by the value passed; and allows for N number of additional parameters to be passed which are also set at the returned object. Why would you need four different patterns to achieve same result? – guest271314 Oct 21 '17 at 13:48
  • I used method 2 and method 3 in my project and I tend to change `elementClass` for supporting this situation. – Ali Soltani Oct 21 '17 at 13:59
  • @AliSoltani _"I used method 2 and method 3 in my project and I tend to change elementClass for supporting this situation."_ ? The code at Answer both defines default expected parameters and allows for parameters that are not explicitly declared to be set as properties, values of the returned object. There cannot be a conclusive Answer to a Question where you decide to change the function arbitrarily, see https://stackoverflow.com/help/how-to-ask. There is no need to "change" the pattern of the function which returns expected result; that leads to confusing viewers of Question and yourself. – guest271314 Oct 21 '17 at 14:08
  • I asked the question to know the opinions of other people. Anyway, I tested your code, but did not work in IE because [Default parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters) don't work in `ES5`. I worked with `ES5`. – Ali Soltani Oct 21 '17 at 15:03
  • @AliSoltani _"I asked the question to know the opinions of other people. Anyway, I tested your code, but did not work in IE"_ The original Question does not mention that you are only seeking opinion or ie. Have you read link at previous comment? – guest271314 Oct 21 '17 at 15:04
  • You are right but I said that "I work with JavaScript (ECMAScript 5)." – Ali Soltani Oct 21 '17 at 15:07
  • @AliSoltani `var def = {htmlType:"", cssClass:"", text:"", value:"", id:""} function elementClass(o) { for (var prop in def) { if (!(prop in o)) { o[prop] = def[prop]; } } return o }; var newElement1 = new elementClass({htmlType:"div",cssClass:"form-group",id:"div1", otherData:123});` https://jsfiddle.net/3r8a6gbq/3/ – guest271314 Oct 21 '17 at 15:22
  • Not able to verify result at ie, though the code at previous comment should meet requirement. – guest271314 Oct 21 '17 at 15:34