JSDoc is great at picking up on methods and properties defined on a class as long as you use the this
keyword, eg:
/** @class */
function Person(name) {
/** This person's name */
this.name = name;
/** Greet someone */
this.greet = function(person) {
return 'Hey there, '+person.name;
};
/** Log a greeting to the browser console */
this.logGreeting = function(person) {
console.log(this.greet(Person));
};
}
This will generate docs with a "Class Person" page listing name
as a Member and greet()
and logGreeting()
as Methods.
But when I have a complex case where I need to use the self = this
pattern, things start to break:
/** @class */
function Person(name) {
var self = this;
/** This person's name */
self.name = name;
/** Greet someone */
self.greet = function(person) {
return 'Hey there, '+person.name;
};
/** Log a greeting to the browser console */
self.logGreeting = function(person) {
console.log(self.greet(Person));
};
/** Log a greeting to the browser console after some delay */
self.logGreetingDelayed = function(person, delay) {
setTimeout(function() { self.logGreeting(person); }, delay);
};
}
This example generates a Class Person
page, but it doesn't have the name
member or any of the methods.
I've seen that you can use @memberof
all over the place to manually attach each member and method to the class, but this is really verbose, and I was wondering whether there's a way to tell JSDoc that self
refers to the class.
(Btw, I'm using JSDoc 3.4, in case that's important.)
The @alias
annotation can be used to tell JSDoc to treat self
as a reference to the class. Actually, technically, you need to alias self
to the class's prototype , not just the name of the class itself (that breaks stuff in ways that I don't fully understand). To do this you need to set @alias
to Person#
1 :
/** @class */
function Person(name) {
/** @alias Person# */
var self = this;
/** This person's name */
self.name = name;
/** Greet someone */
self.greet = function(person) {
return 'Hey there, '+person.name;
};
/** Log a greeting to the browser console */
self.logGreeting = function(person) {
console.log(self.greet(Person));
};
/** Log a greeting to the browser console after some delay */
self.logGreetingDelayed = function(person, delay) {
setTimeout(function() { self.logGreeting(person); }, delay);
};
}
1: Technically, Person#
is equivalent to Person.prototype
, since a trailing #
refers to an object's prototype (as I understand it; open to correction). That being said, this
actually refers to an instance, which is not the same as the prototype, so I recommend against using this notation, since it makes the comments more confusing. Fortunately, there's no difference in the JSDoc output between a prototype method and a true instance method, so don't worry too much about the Person#
notation.
Alternative that works the same, included for completeness, but which should probably be avoided:
// ...
/** @alias Person.prototype */
var self = this;
// ...
You are making this (pardon the pun) more complicated then it needs to be. There are numerous problems with your design.
You don't need to use this
to set prototypes. I greatly prefer to separate object Instantiation
from Initialization
paradigm like this:
/** @class */ function Person() {} Person.prototype = { /** This person's name * @return {Person} */ init: function(name) { this.name = name; return this; }, };
Greet already works on an object, you doesn't need pass in a 2nd object:
/** Greet someone */ greet: function() { return 'Hey there, '+this.name; },
This also simplifies logGreeting
:
/** Log a greeting to the browser console */ logGreeting: function() { console.log(this.greet()); },
However, assuming you do want to pass in a second object (person) to your logGreeting
it is incorrectly passing a Class
and not an Object
. It should be this:
/** Greet someone */ self.greet = function(person) { return 'Hey there, '+person.name; }; /** Log a greeting to the browser console */ self.logGreeting = function(person) { console.log(self.greet(person)); // BUG: was Person instead of person };
You shouldn't be using self
to set prototypes -- you're mis-using the reason for using self
in the first place: They are used for callbacks that need an object to refer to.
For example, let's add a callback when a person is renamed:
rename: function(newName) {
var self = this;
var cbRename = function() {
self.onRename( self.name, newName ); // Why self is needed
self.name = newName;
};
setTimeout( cbRename, 1 );
},
/** Callback triggered on rename */
onRename: function(oldName,newName) {
console.log( "The person formally known as: '" + oldName + "'" );
console.log( "Is now known as: '" + newName + "'" );
},
Putting it all together in an example:
/** @class */ function Person() {} Person.prototype = { /** This person's name * @return {Person} */ init: function(name) { this.name = name; return this; }, /** Greet someone */ greet: function() { return 'Hey there, '+this.name; }, /** Log a greeting to the browser console */ logGreeting: function() { console.log(this.greet()); }, rename: function(newName) { var self = this; var cbRename = function() { self.onRename( self.name, newName ); self.name = newName; }; setTimeout( cbRename, 1 ); }, /** Callback triggered on rename */ onRename: function(oldName,newName) { console.log( "The person formally known as: '" + oldName + "'" ); console.log( "Is now known as: '" + newName + "'" ); }, }; var alice = new Person().init( 'Alice' ); console.log( alice ); alice.logGreeting(); alice.rename( 'Bob' );
Which generates this JSdoc3 html:
<div id="main"> <h1 class="page-title">Class: Person</h1> <section> <header> <h2>Person</h2> </header> <article> <div class="container-overview"> <hr> <h4 class="name" id="Person"><span class="type-signature"></span>new Person<span class="signature">()</span><span class="type-signature"></span></h4> <dl class="details"> <dt class="tag-source">Source:</dt> <dd class="tag-source"><ul class="dummy"><li> <a href="person.js.html">person.js</a>, <a href="person.js.html#line2">line 2</a> </li></ul></dd> </dl> </div> <h3 class="subsection-title">Methods</h3> <hr> <h4 class="name" id="greet"><span class="type-signature"></span>greet<span class="signature">()</span><span class="type-signature"></span></h4> <div class="description"> Greet someone </div> <dl class="details"> <dt class="tag-source">Source:</dt> <dd class="tag-source"><ul class="dummy"><li> <a href="person.js.html">person.js</a>, <a href="person.js.html#line17">line 17</a> </li></ul></dd> </dl> <hr> <h4 class="name" id="init"><span class="type-signature"></span>init<span class="signature">()</span><span class="type-signature"> → {<a href="Person.html">Person</a>}</span></h4> <div class="description"> This person's name return {Person} </div> <dl class="details"> <dt class="tag-source">Source:</dt> <dd class="tag-source"><ul class="dummy"><li> <a href="person.js.html">person.js</a>, <a href="person.js.html#line9">line 9</a> </li></ul></dd> </dl> <hr> <h4 class="name" id="logGreeting"><span class="type-signature"></span>logGreeting<span class="signature">()</span><span class="type-signature"></span></h4> <div class="description"> Log a greeting to the browser console </div> <dl class="details"> <dt class="tag-source">Source:</dt> <dd class="tag-source"><ul class="dummy"><li> <a href="person.js.html">person.js</a>, <a href="person.js.html#line23">line 23</a> </li></ul></dd> </dl> <hr> <h4 class="name" id="onRename"><span class="type-signature"></span>onRename<span class="signature">()</span><span class="type-signature"></span></h4> <div class="description"> Callback triggered on rename </div> <dl class="details"> <dt class="tag-source">Source:</dt> <dd class="tag-source"><ul class="dummy"><li> <a href="person.js.html">person.js</a>, <a href="person.js.html#line38">line 38</a> </li></ul></dd> </dl> </article> </section> </div>
Why use a variable to receive this
instead to use this
with arrow function?
/** @class */
function Person(name) {
/** This person's name */
this.name = name;
/** Greet someone */
this.greet = function(person) {
return 'Hey there, '+person.name;
};
/** Log a greeting to the browser console */
this.logGreeting = function(person) {
console.log(this.greet(person));
};
/** Log a greeting to the browser console after some delay */
this.logGreetingDelayed = function(person, delay) {
setTimeout(() => this.logGreeting(person), delay);
};
}
And if you want to document this
(to use private methods for instance) you can use JSDoc @this
:
/** @class */
function Person(name) {
/** This person's name */
this.name = name;
/** Greet someone */
this.greet = function(person) {
return 'Hey there, '+person.name;
};
/** Log a greeting to the browser console */
this.logGreeting = function(person) {
privateLogGreeting.call(this, person);
};
/** Log a greeting to the browser console after some delay */
this.logGreetingDelayed = function(person, delay) {
setTimeout(() => this.logGreeting(person), delay);
};
}
//Private method
/**
* @this Person
* @param {Person} person
*/
function privateLogGreeting(person) {
console.log(this.greet(person));
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.