0

Let's say I have a factory function like this:

function Person(name, age) {
    return {name: name, age: age};
}

Using JSDoc3, I tried to document that function in the following way:

/** Creates a Person object
 * @param {String} name The name of the person
 * @param {Number} age The person's age in years
 * @returns {object} The Person object specified */
function Person() {...}

Obviously, this leads to the generation of the Person function's documentation as a Method and doesn't give any clues about the return object's structure.

Therefore I tried to document it as a class, like so:

/** Represents a person
 * @param {String} name The name of the person
 * @param {Number} age The person's age in years
 * @class*/
function Person(name, age) {
    return /** @lends Person.prototype */ {
        /** The name of the person */
        name: name, 
        /** The person's age in years */
        age: age
    };
}

Unfortunately (apart from the documentation comments not being DRY), documenting it as a class will result in the following text:

Class: Person

Person

new Person(name, age) ...

Notice the new keyword there?

This is not how I intended the Person function to be called, instead I use it as in var joe = Person("Joe", 24);.

How can I get rid of the new keyword in the generated documentation?

Update

Thanks for all the ideas/suggestions in the comments below, here is some additional information to clarify a couple of things:

  • I am aware that it would've probably been better to use real classes (custom objects), which do use new to create. This question however is about documenting these existing factory functions using JSDoc3 in a way that leads to a useful API reference.
  • I am aware that it is technically incorrect to label the function Person with @class, as it is not used as a class (doesn't take new to create an instance). In the same way as it is technically incorrect to use @lends Person.prototype (thanks @nnnnnn for pointing this out), as the prototype of Person is not involved. Again, I did this for documentation purposes to have the fields of a person instance documented in a linkable fashion.
  • As suggested by @Bálint and @nnnnnn, I could document the Person's return type (i.e. the person instance structure) within the Person's @returns tag. This puts me back to my first attempt, in which a person instance documentation is just a textual description and I am not sure I can link to its members from other points in the documentation:

If Person is documented as a @class, I can e.g. create references to a person (and its fields) documentation like so:

/** Produces an array of {@link Person}s, sorted by their {@link Person#age}.
 * @param {Array} persons The persons to sort
 * @returns {Array} The sorted persons*/
function sortPersonsByAge(persons) {
    return persons.sort(function(a, b) {return a.age - b.age;});
}

If I leave the Person's documentation as a method (as the very first attempt above) and put all the information about a person's object structure inside its @returns documentation tag, how can I link to the type and its fields/methods from elsewhere, as in the sortPersonsByAge example?

Community
  • 1
  • 1
FriendFX
  • 2,929
  • 1
  • 34
  • 63
  • Classes are constructed with `new`, so it's not a class, you have a factory function, which raises the question of why the name is capitalized like a constructor, but... – Alexander O'Mara Jun 17 '16 at 02:03
  • factory functions should be named `create_xyz` – Bálint Jun 17 '16 at 02:17
  • @AlexanderO'Mara I wasn't aware of any capitalisation guidelines regarding factory methods - in fact, **[this site](http://javascript.info/tutorial/factory-constructor-pattern)** suggests I am correct, but I am always ready to learn... surely that doesn't affect the question though? – FriendFX Jun 17 '16 at 02:17
  • 1
    Maybe write something like "returns a new person object". Because this is a method, wich returns a new person object. – Bálint Jun 17 '16 at 02:18
  • 1
    Protip: if a site says "like Python" to JavaScript, then avoid it. – Bálint Jun 17 '16 at 02:20
  • @Bálint this matches my first attempt mentioned in my question, but raises the question of how to document the person object's structure? – FriendFX Jun 17 '16 at 02:20
  • Put it in the return description – Bálint Jun 17 '16 at 02:21
  • @Bálint If you could give a complete example, specifically one where I can refer to a person's object (and its fields/methods) from elsewhere in the documentation, I'll be glad to accept your answer. – FriendFX Jun 17 '16 at 02:24
  • One of your documentation comments says `/** @lends Person.prototype */`, but in fact your `Person()` function returns an object that is not in any way connected to `Person.prototype` or `Person`. (Regarding capitalisation, there is a long-standing convention in JS that functions that start with an uppercase letter are intended to be called with `new`. Obviously there is no *technical* requirement for you to follow this convention, and *any* JS function can be called with `new`, but if you don't follow the convention your code could confuse other people.) – nnnnnn Jun 17 '16 at 02:38
  • @nnnnnn I did this in order to document a person instance's object structure. Similarly, decorating the `Person` method as a `@class` is technically incorrect, as it is just a factory method. Again, I haven't found a better way to document both the function *and* its returned type properly - that would exactly be my question... maybe I should edit it to clarify. – FriendFX Jun 17 '16 at 02:46
  • At this point, constructors without `new` is essentially an anti-pattern, and going forward all new native constructors are requiring the use of `new`. If you are making a factory function, that's fine, but you might want to checkout custom objects: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript – Alexander O'Mara Jun 17 '16 at 02:52
  • Can't you say something like `@returns an object with properties: name, age`? (Better than nothing, and better than misusing @class or other options.) – nnnnnn Jun 17 '16 at 02:54

2 Answers2

0

A solution that seems both technically correct and producing nice documentation is provided via the @typedef tag, which can be used to document the return type separately from the factory function.

Within the @typedef, the @property tag can be used to define the fields:

/** Represents a person. Use the {@link Person} function to create one.
 * @typedef PersonObj
 * @property {String} name The name of the person
 * @property {Number} name The person's age in years */

Then, a link to that type can be added to e.g. the factory function's documentation:

/** Creates a {@link PersonObj}
 * @param {String} name The name of the person
 * @param {Number} age The person's age in years
 * @return {PersonObj} The new person object */
function Person(name, age) {
    return {
        name: name, 
        age: age
    };
}

Hope someone else finds this helpful.

FriendFX
  • 2,929
  • 1
  • 34
  • 63
  • This solution doesn't appear to generate documentation for `function` members of the `PersonObj` type. – FriendFX Jun 20 '16 at 04:48
0

Because my previous answer didn't lead to function members of the @typedef being documented at all, I resorted to the @namespace tag instead:

/** Creates a {@link PersonObj}
 * @param {String} name The name of the person
 * @param {Number} age The person's age in years
 * @return {PersonObj} The new person object */
function Person(name, age) {
    /** Represents a person. Use the {@link Person} function to create one.
     * @namespace PersonObj */
    return p = {
        /** The name of the person 
         * @memberof PersonObj# */
        name: name, 
        /** The person's age in years
         * @memberof PersonObj# */
        age: age,
        /** Displays a greeting 
         * @memberof PersonObj# 
         * @param {String} otherName Name of another person */
        greet: function(otherName) {
            alert('Hello '+otherName+', my name is '+p.name);
        }
    };
}

One advantage of this pattern is that the type's member documentation is now closer to where they are actually defined in the source.

One disadvantage is that the @memberof tag seems to be required for the members to show up in the documentation of the namespace.

FriendFX
  • 2,929
  • 1
  • 34
  • 63