HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Add functionality to object then remove those functions on export

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thoseremoveexportthenfunctionsobjectfunctionalityadd

Problem

Outline

I have a form that needs validation. The problem is that the software requires custom validation that depends on multiple parameters.

I decided to pass the object into a function which will dynamically give the object the functions it needs to be validated.
Then once the validation is completed, I remove those functions from the object.

I'm adding the functions so I can chain them together, which I believe will provide me with great composition.

Usage

This is how I add the functions to the object:

let q = this.validator.validate(this.question)


I can then chain the added functions like this:

q.validateString().cleanUp();


That way my functions are composable and reusable.

Implementation

Now for the actual code to be reviewed:

export class Validator {
    validate(question) {
        let q = Object.assign(
            {},
            question,
            validationOptions
        );

        return q;
    }
}

let validationOptions = {
    validateString,
    cleanUp
};

function validateString() {
    return this;
}

function cleanUp() {
    Object.keys(validationOptions).map((i) => delete this[i]);
    return this;
}


I'm forced to use the class because I need Aurelia Dependency Injection. I compose my functions outside of that class Validator because they only need to work with the this binding.

This code works perfectly, and it executes as expected; however, I do have a few concerns:

  • Is there a downside to the way I add these functions?



  • The way I remove these functions works well, but are there better options? I can't use Symbol because I return the q object to another class.

Solution

Mild issue with assigning functions

It's possible that you could overwrite properties of a question if they have the same name as one of your validation functions. For example:

let question = {
  foo: "bar"
};

let validationOptions = {
    foo,
    cleanUp
};


When you execute Object.assign the later object's properties override earlier ones in the arguments array.

Alternative to deleting the functions

I think a better way to "delete" the added functions would be to just keep track of the original question object, and then return it from cleanUp:

export class Validator {
    validate(question) {
        let q = Object.assign(
            {},
            question,
            validationOptions
        );

        q.cleanUp = () => question;    

        return q;
    }
}

let validationOptions = {
    validateString
};

function validateString() {
    // validate
    return this;
}


That way the initial object is unchanged, and you don't have to concern yourself with deleting the validation functions from the question when your done.

Wrapper Class

I think you also ought to consider making the validator a wrapper class for your question. That way your validation functions stay out of your data, you can still chain your methods, and you don't have to assign the validation functions for each object:

export class Validator {
    constructor(question) {
      this.data = question;
    }
    validateString() {
      return this;
    }
    cleanUp() {
      return this.data;
    }
}


If you want the validation functions to be free from an object, classes have prototypes that you can assign the methods to:

function validateString() {
  return this;
}

export class Validator {
    constructor(question) {
      data = question;
    }
}
Validator.prototype.validateString = validateString;


If you have a lot of functions you could do:

let validateOptions = {
  validateString,
  validateDate,
  validateAge
};

Object.keys(validateOptions).forEach((property) => {
  Validator.prototype[property] = validateOptions[property];
});

Code Snippets

let question = {
  foo: "bar"
};

let validationOptions = {
    foo,
    cleanUp
};
export class Validator {
    validate(question) {
        let q = Object.assign(
            {},
            question,
            validationOptions
        );

        q.cleanUp = () => question;    

        return q;
    }
}

let validationOptions = {
    validateString
};

function validateString() {
    // validate
    return this;
}
export class Validator {
    constructor(question) {
      this.data = question;
    }
    validateString() {
      return this;
    }
    cleanUp() {
      return this.data;
    }
}
function validateString() {
  return this;
}

export class Validator {
    constructor(question) {
      data = question;
    }
}
Validator.prototype.validateString = validateString;
let validateOptions = {
  validateString,
  validateDate,
  validateAge
};

Object.keys(validateOptions).forEach((property) => {
  Validator.prototype[property] = validateOptions[property];
});

Context

StackExchange Code Review Q#130091, answer score: 2

Revisions (0)

No revisions yet.