利用jsdoc和idea加速javascript开发(二)

本文介绍 IntelliJ IDEA 对 JS Doc 的支持,包括类型表达式、类声明、成员函数和模板方法等内容。深入探讨如何利用这些特性提高 JavaScript 代码的可读性和维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上一篇中我们看到了[url=http://zjumty.iteye.com/blog/2053831]一些Intellij Idea对jsdoc的支持[/url], 这篇我们继续看Idea对类型表达式, 类声明, 成员函数, 模板方法的支持.

[size=large]4. 类型表达式[/size]

在上一篇的例子中我们看到可以用@param指定参数的类型, 用@return指定返回值的类型, 用@type指定变量的类型. 下面我们详细的介绍一下指定类型时的表达式可以是什么样子的.

首先类型表达是需要用两个花括号"{...}"包起来, 简单的表达式不用{}也可以, 但为了统一风格, 建议都用{}. 你在代码中对参数, 返回值, 变量的类型表达式写的越精确, 越详细, IDEA对js的支持就越好.

[b]类型名[/b]
指定类型的最简单方式
[list]
[*]{boolean}
[*]{Window}
[*]{goog.ui.Menu}
[/list]

[b]集合类型[/b]
类似于Java里的泛型, 指定元素的类型
[list]
[*]{Array.<String>} 字符串数组
[*]{Object.<String,Number>} 对象的key是字符串, 值是数值, 类似于Map
[/list]

[b]联合类型[/b]
指定类型是A或B
{(number|boolean)}

[b]记录(Record)类型[/b]
相当于匿名类, 指定类型中应该有的属性及属性类型, 里面那层{}是语法的一部分
[list]
[*]Array.<{length}> Array中的元素需要有一个length属性
[*]{{myNum: number, myObject}} 指明, 一个对象有一个数值类型的myNum属性, 和一个任意类型的myObject属性
[/list]

[b]可为null的类型[/b]
指明一个类型为A或null, 其实所有的类型(除了function,number, string, boolean)默认都是可以为null的, 即便前面不加问号.
[list]
[*]{?Number}
[*]{?Shape}
[/list]

[b]不可为null的类型[/b]
指明一个类型为A并且不为null. function,number, string, boolean默认不可为null.
[list]
[*]{!Object}
[/list]

[b]函数类型[/b]
指明一个对象是函数, 并指明参数类型
[list]
[*]{function(string, boolean)}
[/list]

[b]函数返回类型[/b]
指明一个对象是函数, 并指明返回值类型(也可以同时指明参数)
[list]
[*]{function(): number}
[*]{function(string, boolean): number}
[/list]


[b]函数中this的类型[/b]
指明一个对象是函数,并且在函数的context中this指向什么类型
[list]
[*]{function(this:goog.ui.Menu, string)}
[/list]

例如下面的例子:


/**
*
* @param {function(this:HTMLTableElement, Number)} f
* @param {String} otherData
*/
var doSomething5 = function(f, otherData){

};


在调用doSomething5方法的匿名函数时, 首先我们可以看到函数声明提示

[img]http://dl2.iteye.com/upload/attachment/0096/5065/ede35d4e-766c-3ab9-bbe2-fe0d581f9b39.jpg[/img]

函数中的num变量被识别出是Number类型

[img]http://dl2.iteye.com/upload/attachment/0096/5069/257ed146-b9f0-31d1-be86-dd2ccf5eb3c6.jpg[/img]

函数中的this被识别出是HTMLTableElement

[img]http://dl2.iteye.com/upload/attachment/0096/5071/12abf40e-60b0-39fd-9f20-2bf31bd877bb.jpg[/img]

这里面我没有用自定义类型作为this的类型是因为我用IDEA 13.1.2中是乎不支持自定义类型, 不知道是不是Bug

[b]函数中被new出来的类型[/b]
指明一个对象是函数,如果在这个函数前用new,会被创造出来的对象的类型
[list]
[*]{function(new:goog.ui.Menu, string)} var menu = new func("str");
[/list]
很遗憾, IDEA是乎没有找到相关的支持

[b]可变长度的参数的类型[/b]
指明某一个参数的个数不定
[list]
[*]{function(string, ...[number]): number} 第一个参数是string, 后面可以任何个Number类型的参数
[/list]


/**
*
* @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);
};


[img]http://dl2.iteye.com/upload/attachment/0096/5081/fbc74d5b-3938-3391-9425-3c81834dd2f4.jpg[/img]

[b]可变长度的参数的类型 (@param)[/b]
指明某一个参数的个数不定
[list]
[*]@param {...number} var_args 可以任何个Number类型的参数
[/list]


/**
*
* @param {...number} args
*/
var doSomething8 = function (args) {

};


调用参数值类型不正确是会有提示

[img]http://dl2.iteye.com/upload/attachment/0096/5084/7594600c-0d80-3826-bd33-963ade7096a1.jpg[/img]

[b]可选参数的类型[/b]
指明某一个参数是可选的
[list]
[*]@param {number=} opt_argument
[/list]


/**
*
* @param {Number} age
* @param {String=} name
*/
var doSomething9 = function (age, name) {

};


[img]http://dl2.iteye.com/upload/attachment/0096/5086/b6ca8789-5694-372d-9007-bc12f6424d98.jpg[/img]

[b]任意类型[/b]
{*}

[b]不确定类型[/b]
{?}
IDEA没有特殊支持

[size=large]5. 类声明[/size]

[b]定义接口和实现[/b]

总所周知,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会提示错误:


[img]http://dl2.iteye.com/upload/attachment/0096/5094/d3950257-3493-3e1a-bff1-aa82d5b8751d.jpg[/img]

一个类要实现某个接口但是却没有实现其所有方法, Idea也会给予提示:

[img]http://dl2.iteye.com/upload/attachment/0096/5096/485a9698-a55f-3b47-8b22-36b214a31e25.jpg[/img]

上面说getSides方法没有实现, 当我们把这个方法加上以后错误提示就会消失:


[img]http://dl2.iteye.com/upload/attachment/0096/5098/14c4fbf9-5515-38ab-b42e-9129a28ddca6.jpg[/img]

并且我们可以看到, 在行号的右侧出现了继承关系的图标, 点击图标可以在接口与实现之间跳转.

[b]继承[/b]

标记了@extends标签的类, 在Idea里的代码补全里可以给出父类的方法:
[img]http://dl2.iteye.com/upload/attachment/0096/5100/647bd600-03c4-308c-b5b3-3a836023ff5b.jpg[/img]

[b]批量添加方法[/b]
很多时候我们并不是直接用prototype的方式一个一个的向类里添加方法, 而是一些第三方类型的类型支持功能创建类型. 如著名的John Resig'Class类([url]http://blog.buymeasoda.com/understanding-john-resigs-simple-javascript-i/[/url])


/**
* 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类型的参数.
[img]http://dl2.iteye.com/upload/attachment/0096/5104/0bee38a0-7360-3f75-aea7-7f59ef5a2744.jpg[/img]

同时Person的实例中也有相应的方法提示
[img]http://dl2.iteye.com/upload/attachment/0096/5106/5df961df-5306-3e53-b3b9-1fd87579744c.jpg[/img]

[size=large]6. 成员函数[/size]

Module模式是javascript里常用的一种设计模式.([url]http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html[/url])

当我们要声明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就无法正确的给出代码补全了.
[img]http://dl2.iteye.com/upload/attachment/0096/5108/40296a74-0b63-3a92-9981-b7181c8af220.jpg[/img]

加了jsdoc的情况
[img]http://dl2.iteye.com/upload/attachment/0096/5110/c0de9690-9017-306d-b142-666a7426dedc.jpg[/img]

JavaScript没有对成员函数的访问控制支持, 也就是没有类似Java中的private, protected, public等修饰符. 通过jsdoc可以指定这种访问控制.
[img]http://dl2.iteye.com/upload/attachment/0096/5112/536e1088-e06c-3d5d-b6c3-97f276b8ba6d.jpg[/img]
可以看到, 当尝试访问Tools.sickle方法时,Idea会给出提示.

[size=large]7. 模板方法[/size]

模板方法是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的类型,并给出提示

错误提示
[img]http://dl2.iteye.com/upload/attachment/0096/5116/e31bb061-71ca-3725-a904-070b260a657f.jpg[/img]
代码补全
[img]http://dl2.iteye.com/upload/attachment/0096/5118/38f794ff-1cb9-3a82-b40b-be8fcf623e7c.jpg[/img]

我用的Intellij Idea的版本是13.1.2, 我不能保证上面写的就是Idea对jsdoc的所有支持, 上面的标签也不是jsdoc的全部标签. 似乎Idea的每次升级都会对js的支持更加丰富, 期待以后的版本中有更加完善的jsdoc支持.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值