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

在上一篇中我们看到了 一些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的, 即便前面不加问号.
  • {?Number}
  • {?Shape}


不可为null的类型
指明一个类型为A并且不为null. function,number, string, boolean默认不可为null.
  • {!Object}


函数类型
指明一个对象是函数, 并指明参数类型
  • {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方法的匿名函数时, 首先我们可以看到函数声明提示

利用jsdoc和idea加速javascript开发(二)_第1张图片

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

利用jsdoc和idea加速javascript开发(二)_第2张图片

函数中的this被识别出是HTMLTableElement

利用jsdoc和idea加速javascript开发(二)_第3张图片

这里面我没有用自定义类型作为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);
};


利用jsdoc和idea加速javascript开发(二)_第4张图片

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


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

};


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

利用jsdoc和idea加速javascript开发(二)_第5张图片

可选参数的类型
指明某一个参数是可选的
  • @param {number=} opt_argument


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

};


利用jsdoc和idea加速javascript开发(二)_第6张图片

任意类型
{*}

不确定类型
{?}
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会提示错误:


利用jsdoc和idea加速javascript开发(二)_第7张图片

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

利用jsdoc和idea加速javascript开发(二)_第8张图片

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


利用jsdoc和idea加速javascript开发(二)_第9张图片

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

继承

标记了@extends标签的类, 在Idea里的代码补全里可以给出父类的方法:
利用jsdoc和idea加速javascript开发(二)_第10张图片

批量添加方法
很多时候我们并不是直接用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类型的参数.
利用jsdoc和idea加速javascript开发(二)_第11张图片

同时Person的实例中也有相应的方法提示
利用jsdoc和idea加速javascript开发(二)_第12张图片

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和idea加速javascript开发(二)_第13张图片

加了jsdoc的情况
利用jsdoc和idea加速javascript开发(二)_第14张图片

JavaScript没有对成员函数的访问控制支持, 也就是没有类似Java中的private, protected, public等修饰符. 通过jsdoc可以指定这种访问控制.
利用jsdoc和idea加速javascript开发(二)_第15张图片
可以看到, 当尝试访问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的类型,并给出提示

错误提示
利用jsdoc和idea加速javascript开发(二)_第16张图片
代码补全
利用jsdoc和idea加速javascript开发(二)_第17张图片

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


你可能感兴趣的:(JavaScript,idea,jsdoc)