在上一篇中我们看到了 一些Intellij Idea对jsdoc的支持, 这篇我们继续看Idea对类型表达式, 类声明, 成员函数, 模板方法的支持.
4. 类型表达式
在上一篇的例子中我们看到可以用@param指定参数的类型, 用@return指定返回值的类型, 用@type指定变量的类型. 下面我们详细的介绍一下指定类型时的表达式可以是什么样子的.
首先类型表达是需要用两个花括号"{...}"包起来, 简单的表达式不用{}也可以, 但为了统一风格, 建议都用{}. 你在代码中对参数, 返回值, 变量的类型表达式写的越精确, 越详细, IDEA对js的支持就越好.
类型名
指定类型的最简单方式
- {boolean}
- {Window}
- {goog.ui.Menu}
集合类型
类似于Java里的泛型, 指定元素的类型
- {Array.<String>} 字符串数组
- {Object.<String,Number>} 对象的key是字符串, 值是数值, 类似于Map
联合类型
指定类型是A或B
{(number|boolean)}
记录(Record)类型
相当于匿名类, 指定类型中应该有的属性及属性类型, 里面那层{}是语法的一部分
- Array.<{length}> Array中的元素需要有一个length属性
- {{myNum: number, myObject}} 指明, 一个对象有一个数值类型的myNum属性, 和一个任意类型的myObject属性
可为null的类型
指明一个类型为A或null, 其实所有的类型(除了function,number, string, boolean)默认都是可以为null的, 即便前面不加问号.
不可为null的类型
指明一个类型为A并且不为null. function,number, string, boolean默认不可为null.
函数类型
指明一个对象是函数, 并指明参数类型
- {function(string, boolean)}
函数返回类型
指明一个对象是函数, 并指明返回值类型(也可以同时指明参数)
- {function(): number}
- {function(string, boolean): number}
函数中this的类型
指明一个对象是函数,并且在函数的context中this指向什么类型
- {function(this:goog.ui.Menu, string)}
例如下面的例子:
/**
*
* @param {function(this:HTMLTableElement, Number)} f
* @param {String} otherData
*/
var doSomething5 = function(f, otherData){
};
在调用doSomething5方法的匿名函数时, 首先我们可以看到函数声明提示
函数中的num变量被识别出是Number类型
函数中的this被识别出是HTMLTableElement
这里面我没有用自定义类型作为this的类型是因为我用IDEA 13.1.2中是乎不支持自定义类型, 不知道是不是Bug
函数中被new出来的类型
指明一个对象是函数,如果在这个函数前用new,会被创造出来的对象的类型
- {function(new:goog.ui.Menu, string)} var menu = new func("str");
很遗憾, IDEA是乎没有找到相关的支持
可变长度的参数的类型
指明某一个参数的个数不定
- {function(string, ...[number]): number} 第一个参数是string, 后面可以任何个Number类型的参数
/**
*
* @param {function(string, ...[number])} f
* @param {String} otherData
*/
var doSomething7 = function(f, otherData){
f("terry");
f("terry", 1);
f("terry", 1, 2, 3);
f("terry", 1, 2, 3, 4);
};
可变长度的参数的类型 (@param)
指明某一个参数的个数不定
- @param {...number} var_args 可以任何个Number类型的参数
/**
*
* @param {...number} args
*/
var doSomething8 = function (args) {
};
调用参数值类型不正确是会有提示
可选参数的类型
指明某一个参数是可选的
- @param {number=} opt_argument
/**
*
* @param {Number} age
* @param {String=} name
*/
var doSomething9 = function (age, name) {
};
任意类型
{*}
不确定类型
{?}
IDEA没有特殊支持
5. 类声明
定义接口和实现
总所周知,JavaScript是没有比较完善的面向对象支持, 所以从语言层面是实现接口和实现是比较困难的. 但是我们可以通过jsdoc让IDE知道这种关系并提供一些错误校验.
主要标签@interface声明一个类是接口, @implements声明一个类是实现了哪个接口, 接口和类都可以通过@extends声明继承关系.
/**
* A shape.
* @interface
*/
function Shape() {
}
Shape.prototype.draw = function () {
};
/**
* A polygon.
* @interface
* @extends {Shape}
*/
function Polygon() {
}
Polygon.prototype.getSides = function () {
};
这里我们声明了一个接口Shape, 一个接口Polygon继承于Shape.
如果我们想要直接实例化这个接口Idea会提示错误:
一个类要实现某个接口但是却没有实现其所有方法, Idea也会给予提示:
上面说getSides方法没有实现, 当我们把这个方法加上以后错误提示就会消失:
并且我们可以看到, 在行号的右侧出现了继承关系的图标, 点击图标可以在接口与实现之间跳转.
继承
标记了@extends标签的类, 在Idea里的代码补全里可以给出父类的方法:
批量添加方法
很多时候我们并不是直接用prototype的方式一个一个的向类里添加方法, 而是一些第三方类型的类型支持功能创建类型. 如著名的John Resig'Class类( http://blog.buymeasoda.com/understanding-john-resigs-simple-javascript-i/)
/**
* Person类
* @class
* @extends {Class}
*/
var Person = Class.extend(
{
init: function (car) {
/** @type {Car} */
this.car = car;
},
canDrive: function () {
return this.car.size > 10;
}
}
);
其中init是构造函数, canDrive是成员函数.上面这种写法,IDEA并不能识别init和canDrive.
我们需要在extend函数的参数值之前用@lends来指定这种关系.@constructs指明那个函数是构造函数.注意@lends后面用的是Person.prototype, 因为其实我们是把函数加到prototype里.
/**
* Person类
* @class
* @extends {Class}
*/
var Person = Class.extend(
/** @lends Person.prototype */
{
/**
* 构造函数
* @constructs
* @param car {Car}
*/
init: function (car) {
/** @type {Car} */
this.car = car;
},
/**
* 能否驾驶
*
* @return {Boolean}
*/
canDrive: function () {
return this.car.size > 10;
}
}
);
尝试调用new Person时, IDEA能把init当构造函数, 并且指明需要一个Car类型的参数.
同时Person的实例中也有相应的方法提示
6. 成员函数
Module模式是javascript里常用的一种设计模式.( http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html)
当我们要声明Module的成员函数时,需要用@memberOf指定一个方法数据某个Module. 而Module本身需要声明为@namespace.
/** @namespace */
var Tools = {};
/**
* @memberof Tools
* @param {String} str
**/
var hammer = function(str) {
};
Tools.hammer = hammer;
其实不写@namespace和@memberof时, IDEA也能很好的解析两者的关系. 因为IDEA看到了
Tools.hammer = hammer;这样显示的调用. 两者的关系没有通过这么明显的方式表示出来, IDEA就无法正确的给出代码补全了.
加了jsdoc的情况
JavaScript没有对成员函数的访问控制支持, 也就是没有类似Java中的private, protected, public等修饰符. 通过jsdoc可以指定这种访问控制.
可以看到, 当尝试访问Tools.sickle方法时,Idea会给出提示.
7. 模板方法
模板方法是Google Closure独有的jsdoc支持, Idea对这个功能提供了很好的支持, 真是让人倍感惊喜. 模板方法具体是什么我就不解释了, 熟悉Java,C++, C#中的泛型的同学看一眼就知道.
下面是一个Foo类, 这个类的set的参数,get的返回值是可以在实例化是确定.
/**
* @constructor
* @template T
*/
Foo = function () {
};
/** @return {T} */
Foo.prototype.get = function () {
};
/** @param {T} t */
Foo.prototype.set = function (t) {
};
/** @type {Foo.<FooBean>} */
var foo = new Foo();
例如上面的最后一句, 我们可以确定, foo的set和get方法的类型为FooBean.
这是Idea的代码补全功能会自动的把FooBean最为set和get的类型,并给出提示
错误提示
代码补全
我用的Intellij Idea的版本是13.1.2, 我不能保证上面写的就是Idea对jsdoc的所有支持, 上面的标签也不是jsdoc的全部标签. 似乎Idea的每次升级都会对js的支持更加丰富, 期待以后的版本中有更加完善的jsdoc支持.