Coder Social home page Coder Social logo

关于《Javascript编码规范》的质疑 about edp HOT 5 CLOSED

 avatar commented on August 15, 2024
关于《Javascript编码规范》的质疑

from edp.

Comments (5)

 avatar commented on August 15, 2024

@wurongyao 关于变量即用即声明的观点:

举了 var a=0;这个作为例子的确是钻牛角尖(丢人了),不好意思。

但其实我想表达的是赋值运算某些情况下也能算“用“,不光光是无聊的赋值这么简单,有的时候是为了归类或者理清逻辑。
因为变量之间往往会有关系。
如:
var a = 100 ;
var b = funA( a ) ;

可能b暂时不会被用到,但由于和a有联系,写在这里似乎又是理所应当的。

假设代码很长,当 a 被用来干了很久甚至加班以后,b才出现了。然后很久以后又用到了b的哥们c。

var a = 100 ;
..... 很多很多行用到 a 的代码..............

var b = funA( a );
..... 很多很多行用到 b 的代码..............

var c = funB( b );
..... 很多很多行用到 c 的代码..............

在用到c的过程中会发现,咦?c是干嘛的?然后往上看,哦,那b是?那a呢?结果又得往上找(冒泡),然后看完a再接着来回味b,然后再回到c(捕获)。
这种多次查找比一次查找要更痛苦,尤其对于记性不太好的同学。

还有一种有关联的情况就是某变量的值与传进来的参数有关。
如 function test ( x, y ){
var b = ( x + y ) / 2 ;
....................
}

则无论b会在多么遥远的地方才会被用到,也应该在看得见x和y的地方定义为好,否则又会经历一次冒泡与捕获的痛苦。

而且变量定义在一起还有一个作用,就是防止重复定义,便于维护。
最常见的就是类似

var slice = Array.prototype.slice ;
var concat = Array.prototype.concat ;

我想当人们看到这种代码的时候,也没想过它们定义后会被马上使用。可是你要分开写,就很别扭。
说白了,这些都是全局性的东西。

for while 这种需要定计数器的,是的确不适合在外面定义,
因为它也没想跟外面发生什么关系,它只是个计数器。

如果在最开始定义,var i =0; var j =0 ; 是很奇怪,而且要写注释都不知道该怎么写。总不能写成”某循环计数器“

我甚至见过由于之前定义并赋值了,for循环的定义那直接就省了,
写成了
var i =0;
for ( ; i < len; i++ )

如果之后又来了一个循环,且在看不到上一个for循环代码的遥远地方,再加上记性不好,
也写成 for ( ; i < len; i++ ) , 那基本就完了。

我的观点是:

对于有关联的变量,或者同一种类型(如都是正则表达式),可以声明在一起。
当一个变量的值依赖于传进来参数时,最好在函数最顶上挨着参数写。
对于全局变量,应该在最开始声明。
对于for while 这种需要自己特别定制,且防止被其他地方玷污的变量,必须要即用即声明。 (这种变量往往连个像样的名字都不会有,不是叫 i ,就是叫 j ,也不好意思出去见人)
我在写C#的时候,也是这么写的。

所以,对于 变量的即用即声明 是否要 MUST ,还是应该进一步细化一下,我暂时还是持保留的态度。

from edp.

errorrik avatar errorrik commented on August 15, 2024

对于函数参数列表之前不得(MUST NOT)加上空格这条,是为了和调用保持一致,那么,对于func declartion来说,应该是不允许空格的

function test(param)

但是对于function expr来说,应该是允许空格的吧

export.test = function (param) {
};

from edp.

errorrik avatar errorrik commented on August 15, 2024

js文件应该不允许包含bom

from edp.

 avatar commented on August 15, 2024

rigelfe的宿爽同学关于js编码规范的建议,如下:

【空格】

《JS规范》中说:“除括号外,所有运算符的前后MUST加空格”。

建议:补充说明:单元运算符前后加空格与否?
如 for(i = 0; i < length; i ++),其中一般“++”操作符前可规定加空格,
而“++”操作符后一般不加空格,紧跟”)”。

《JS规范》中提到:函数参数列表之前MUST NOT加上空格

但是未提到catch(e)如何处理

建议:补充说明:catch后加空格否?
可以规定和函数参数列表一致,不加空格写成catch(e)
也可以规定和while、for一致加空格写成catch (e)

【对齐和缩进】

《JS规范》中说“未结束的语句在换行后MUST多一次缩进”。

这是指“未结束语句每回换行都比前一行多加一次缩进”,
还是指“未结束语句换行,比第一行增加一次缩进”?
即如下例:

if (asdf == ‘asdf’
    && zcvx == ‘2134’
    && qwer == ‘4534’
) {
    // ...
}

或者
var someFun1
= someFun2
= someFun3
= function() { ... }

还是

var someFun1
    = someFun2
        = someFun3
            = function() { ... }

建议:采取“未结束语句换行,比第一行增加一次缩进”,比较合适观看。

【行长度】

###《JS规范》中规定行长度80字符。

但有种情况:JS代码中会出现HTML,容易一行超出80字,
尤其在缩进较深时,以及class较多较长时。
如果HTML块为了80字而要换行,难看不易读。
HTML规范中并不限制行长度,同理。

建议:JS中涉及HTML拼装的代码,不受80字符长度限制,但须符合HTML缩进规则。
如:'' + str + '';

【行长度 -- 函数调用参数过多】

《JS规范》中有提到:“当函数参过多,在一行数显函数调用语句会超过80个字符限制时,SHOULD每个参数单独一行”。

但是未提到:“函数只有一个参数,但是会超过80个字符限制时”,怎么做。(当然,这是SHOULD的部分,不推敲也可)如下例:

baidu.array(arr).each(function(index, item) {
    baidu('#demo_execute_result_1').html(baidu('#demo_execute_result_1').html() + index + ':' + item + ';');
});

(摘自tangram.baidu.com上的范例)

上面这样写符合习惯,但是并不是“每个参数单独一行/块”。如果写成下面这样则符合“每个参数单独一行/块”,但是一个简单遍历,不重要信息占行过多,不好看:

baidu.array(arr).each(
    function(index, item) {
        baidu('#demo_execute_result_1').html(baidu('#demo_execute_result_1').html() + index + ':' + item + ';');
    }
);

这段代码属类似情况

someObj1.funcA(someObj2.funcB( {
    aaa: ‘asdfasdfasdf’,
    bbb: ‘zxcvzxcvzxcvz’,
    ccc: ‘qwerqwerqwer’
});

【行长度 -- 数组和对象初始化的混用】

《JS规范》中有提到:“数组和对象初始化混用”,MUST严格按照每个对象的起始{和结束}在独立一行的风格书写,如:

var array = [
    {
        // ...
    },
    {
        // ...
    }
];

但是,如下情况,这种写法可能带来不便:
[例1],函数中,就最简单的一句话:

function aa() {
    // ...
    var a = [{width: 123}];
    // ...
    bFunc([{someAttr: ‘zxcv’}]);
    // ...
}

如果按照“括号在独立一行”的风格:

function aa() {
    // ...
    var a = [
        {
            width: 123
        }
    ];
    // ...
    bFunc([
        {
            someAttr: ‘zxcv’
        }
    ]);
    // ...
}

则占去了过大空间,使代码比较奇怪,用空间强调了没必要过分强调的东西。

[例2],对象和值的混用:

var a = ['asdf', 'zxcv', {width: 123}, {link: false}, 1221];

如果按照“括号在独立一行”的风格,则写成这样:

var a = [
    'asdf',
    'zxcv',
    {
        width: 123
    },
    {
        link: false
    },
    1231
];

这样占去了过大空间,并不好看。
而且业务逻辑上,往往这些参数并不是需要如此强调而突出关注的。

[例3],多项的初始化:

var someConfig = {
    list: [
        {text: '小时分析', value: 1},
        {text: '日分析', value: 2},
        {text: '月分析', value: 3},
        {text: '周分析', value: 4},
        {text: '季分析', value: 5},
        {text: '年分析', value: 6},
        {text: '五年分析', value: 7}
    ],
    selected: 2
};

如上这种简单的常量配置,这种写法也很清晰,但是不符合“每个大括号占一行”风格。
如果“每个大括号占一行”,那代码就非常冗长,不易读了。

[例4],数组和数组的嵌套混用:

var someConfig = [
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}],
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}],
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}],
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}],
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}],
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}],
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}]
];

同上,如果“每个大扩号占一行”风格,上面代码将非常长,也很不易阅读。

我猜测“每个大括号一行”这条规则的本意,其实是禁止这样风格写(不知我理解对不对):

var array = [
    {
        // ...
    },{
        // ...
    },{
        // ...
    }
];

建议:由于类JSON的常量初始化情况多样,不易都考虑到,
而已经有“每行80字”的约束,也不会出现代码一行很长导致难读,
建议此条描述,改为:
数组和对象混用初始化时,如果某个对象(或数组)垮多行,
则MUST按照此对象(或数组)起始“{”(或“[”)和结束“}”(或“]”)在独立一行的风格书写。

【语法 -- 变量声明】

《规范》中说,一个var关键字MUST只能声明一个变量

但是,例如这种情况:

var i, len, item;
while () {
    // ...
}

如果按照规定,则不得不这样写:

var i;
var len;
var item;
while () {
    // ...
}

这样占了过多空间,强掉了没必要强调的部分。

“一个var只能声明一个变量”的初衷是为了避免误将逗号写成分号而穿透到全局中去,
这种错误一般在分行写变量声明时出现,如:

var i,
    len,
    item;

这种写法是可以被禁止的,
但是单行声明几个变量,不会出这种错误,最好不限制,造成代码结构不易读。

建议:此处规则改为:var关键字声明变量不得(MUST NOT)跨行。

《规范》中说,变量MUST即用即声明,MUST NOT在函数起始位置统一声明所有变量。

这个规则对写代码风格影响最为大,所以想仔细考虑。
确实有很多开源库的代码是在函数起始位置统一声明所有变量的,
但是好些现代强类型语言编程中又并不鼓励这种风格。
下面辩论下。

正方:即用即声明好。

[好处1] 一个逻辑块的代码都在一处,便于阅读和书写,这个好处是很大的。
[好处2] 如果写在函数头统一声明所有变量,变量多时,
有可能在函数修修改改过程中多声明了变量,或少声明变量导致穿透到全局中去。

反方:即用即声明不好。

[坏处1] 变量复用带来的问题。
变量复用有时是会带来问题的,
然而不同于类C语言,JS没有块级作用域,并且允许重复声明变量,
这样就不容易检查出不同逻辑块变量的复用,从而可能出现问题

例1,重声明上一层作用域的变量引发的问题:

(function() {

    var a = ‘asdf’;

    function ff() {
        // 第一个逻辑块
        alert(a);
        // do something ...

        // ...

        // 第四个逻辑块,
        // 没注意第一个逻辑块中的a是外层作用域的,以为可以重用不会影响第一个逻辑块,
        // 或者没注意到第一个逻辑块中使用了a变量
        var a = ‘ttttt’;
        // do something ...
    }
})();

上面这段逻辑是很典型的alert出undefined而非预想的’asdf’的情况,
因为第四个逻辑块对a做了重新声明,
误以为a可以作为自己逻辑块的局部变量,
没有发现前面逻辑已经使用了的a,是外层作用域的。

例2,有闭包时变量重用引发的问题

function ff() {
    // 第一个逻辑块
    // ...

    // 第二个逻辑块
    var item = ‘asdf’;
    function aFunc() {
        doSomethingA(item);
    }
    // ...

    // 第三个逻辑块
    // 误以为item这种临时变量可以重用,没注意它要用在aFunc中
    // 或者没注意到前面已经声明了item变量
    item = ‘ttttt’;
    function bFunc() {
        doSomethingB(item)
    }
    // ...
}

此例和上一例类似,也是重用变量引发问题,
item看似是“第二个逻辑块”的局部变量,实际上是在函数ff中独占,不能在其他块中修改的。

可能还有其他例子。
有时候,如果要去检查前面逻辑中各个变量的情况,再决定本逻辑块中变量是否能重用之前的命名,会造成编程的困扰,
那么我们是否就应该遵循这样的原则:
[1] “禁止重复声明变量”
[2] “本逻辑块的局部变量,重用之前逻辑块局部变量命名时,要灰常小心”?
但是如果“即用即声明”,
会对这种“搜索函数中其他代码看有没有变量重用”的行为,带来麻烦,
得整函数内查找,就算有开发工具的帮助,也可能遗漏。
如果变量都在函数头部区域声明,则比较清晰,不易漏。
(注:这里的讨论和“函数不应过长”的问题无关)

[坏处2] 不便于重构/代码调整
假设一些简单的变量复用没有问题(比如for循环使用的临时变量会复用等等,这也是常见的)
例3,移动代码引发问题

function funA() {
    // ...

    // 逻辑块三
    var i, len, item;
    for(i = 0, len = 5; i < len; i ++) {
        // do something ...
    }

    // ...

    // 逻辑块五,复用i和len
    for(i = 0, len = 7; i < len; i ++) {
        // do something ...
    };
}

现在编程中我想把代码调整一下,把逻辑块五移动到逻辑块三前面(这也是常有的行为)

function funA() {
    // ...

    // 逻辑块五,移动到前面来了
    for(i = 0, len = 7; i < len; i ++) {
        // do something ...
    };

    // ...

    // 逻辑块三
    var i, len, item;
    for(i = 0, len = 5; i < len; i ++) {
        // do something ...
    }  
}

如上结果,没注意变量声明的位置,逻辑块五使用的i、len穿透到全局去了。
所以,公用的变量,其声明不宜放在某个逻辑块中,而应放在更公用明显的地方,
从这个角度讲,放在函数头声明这些局部变量是有意义的。

例4,重构,抽出代码形成函数的不便
假如一个函数在修修改改过程中,形成个100行以上的函数,一屏都写不完,
那么我们可能要考虑重构,抽离一些逻辑形成子函数。
这个过程我们往往要考察:
将要抽离的代码段中如何使用了其他代码段中声明/赋值过的变量
其他代码段中如何使用了将要抽离的代码段中声明/赋值过的变量
如果变量统一在函数头部声明,按照头部的变量列表统一考察即可。
如果变量是“即用即声明”原则,那需要整篇去检查各处出现的变量声明,不方便,易遗漏。
(尤其是JS不像类C语言能编译时检查重复声明、缺失声明的情况下)

总结下,
JS的变量没有块作用域,看似可以每块私有但仍然可能引发问题,
这种情况下限定“变量MUST即用即声明,MUST NOT在函数起始位置统一声明所有变量”不合适,
既然各有利弊,有些弊端也是不易忽略的,那么

建议:不同情况可以不同处理,
简短的、逻辑块较少的函数可以即用即声明;
而较长、逻辑块较多的函数,变量可以在函数头部声明,从而便于“查找”,避免重用以及重构引发的问题。

【遗留工程/库和规范的冲突】

之前的工程,或者使用的基础库,和《规范》有诸多风格冲突。

然而一个工程只能用一种风格,否则代码混乱不堪,(比如,之前工程的css都是“一个selector全在一行内”的风格,《CSS规范》则是竖写的风格,如果我们改之前的工程代码时,两种风格混用,那么这css就乱了,JS也是同理)

建议:本着“一个工程只能用一种风格”的原则,新的工程必须使用《规范》;

  1. 旧的工程,如果代码风格修改成本较大的话,那么对其修改、新加功能可以延用旧工程的代码风格。
  2. 新工程中,基础库的扩展开发(如控件开发),如果代码风格修改成本较大的话,可以延用基础库本身的风格。

这其中,在《规范》和原有风格没有明显冲突的地方,要遵从《规范》的风格。

【2013年1月1日提交代码不合规范】

这问题主要涉及到如何制约及惩处对《规范》的违背。

如果简单以时间来卡,操作上会遇到这些麻烦:

  1. 如果提交的代码,是修改的别人的代码,自己修改的部分合规范,而别人的代码不和规范,又没法去重构(没时间/没人测),咋办?
  2. 如果提交的代码,是修改的自己以前的代码,自己修改的部分合规范,而之前的代码不和规范,又没法去重构(没时间/没人测),咋办?

建议:在考察一个人提交代码发现不合规范时,注意如下例外情况:

  1. 是否是在遵从遗留工程或原有库的代码风格而不得不与规范相悖
  2. 某人虽提交了含有不合规范代码的文件,但是其中不合规范的代码,是否不是他改动过的(而是遗留代码等)

声明惩罚措施(如评级不予过)时,可对如上例外情况加以说明。

from edp.

errorrik avatar errorrik commented on August 15, 2024

关于宿爽同学的建议,下面是我单独回复他的邮件

分割线

以下回复仅代表我个人意见,具体对每个细则的确定,需要所有技术组同学决议。

关于for(i = 0; i < length; i++)的空格

“(”后面的空格是可以加可以不加的。大多数写法是不加的,加了我觉得阅读性更好(参见jquery的源码),不加也没大影响。
“i++”的话,和上面一样,大多数写法是不加的,加了也可以。

关于catch后的空格

cache后,和左括号之间,是需要+空格的。规范中规定了“以下关键字之后:for、switch、while”,你这么一提,看来确实有漏的。

关于“未结束的语句在换行后MUST多一次缩进”

这句话确实存在疏漏,但是“未结束语句换行,比第一行增加一次缩进”也存在疏漏。因为可能是函数调用,中间某个参数是function,function body的缩进和函数调用相比是不只一次缩进的。这里的描述需要斟酌斟酌。

关于HTML字符串拼接的行长度

确实这种情况应该允许适当放宽。但应该加个限制条件,就是“简单内容标签的长度超过80个字符时”。否则开发者容易偷懒,嵌套标签也写成一行。

关于函数调用时的行长度

如果单一参数也超长,其实只有Object(Array)或Function的可能。你举的例子是function。我觉得“function(index, item) {”是需要断下来并缩进的,因为形参在阅读上更直观。

其次,应当避免一条语句做太多事情,避免重复id的hard code。

baidu.array(arr).each(
    function(index, item) {
        var id = '#demo_execute_result_1';
        var html = baidu(id).html() + index + ':' + item + ';';
        baidu(id).html(html);
    }
);

关于数组和对象初始化的混用时的行长度问题

我觉得这个问题提的很赞。但我对结论不完全赞成。下面以你给出的代码举例:

var someConfig = [
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}],
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}],
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}],
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}],
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}],
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}],
    [{aa: ‘asdf’}, {bb: ‘asdf’}, {dd: ‘asdf’}, {dd: ‘fwqe’}, {ee: ‘ewew’}]
];

我觉得,如果每个{都换行,确实有跨度过大的嫌疑。但是上面的写法我觉得也是不妥的。我觉得应该是,Array或Object中,项少于3时,可以允许不换行。而你的示例,我觉得下面的形式更合理。

var someConfig = [
    [
        {aa: ‘asdf’}, 
        {bb: ‘asdf’}, 
        {dd: ‘asdf’}, 
        {dd: ‘fwqe’}, 
        {ee: ‘ewew’}
    ],
    [
        {aa: ‘asdf’}, 
        {bb: ‘asdf’}, 
        {dd: ‘asdf’}, 
        {dd: ‘fwqe’}, 
        {ee: ‘ewew’}
    ],
    [
        {aa: ‘asdf’}, 
        {bb: ‘asdf’}, 
        {dd: ‘asdf’}, 
        {dd: ‘fwqe’}, 
        {ee: ‘ewew’}
    ],
    [
        {aa: ‘asdf’}, 
        {bb: ‘asdf’}, 
        {dd: ‘asdf’}, 
        {dd: ‘fwqe’}, 
        {ee: ‘ewew’}
    ]
];


var a = [
    'asdf',
    'zxcv',
    { width: 123 },
    { link: false },
    1231
];

关于一var一变量的问题

你举的确实是一个很能说明问题的临界例子。

在我的编码过程中,以前确实会按你说的这种方式写“var i, len, item;”。但是这种方式到后来我发现,仅仅是写的时候快了,爽,但是后面维护的时候,比如我找一个变量在哪被声明了,往往很容易看漏。所以我的观点还是,一var一变量。

当然,循环体中,你可以这么写:

for (var i = 0, len = xx.length; i < len; i++)

关于即用即声明

对于你的例1,如果不是第一个逻辑块,而是第三个逻辑块alert(a),一样看不见声明。所以这个问题不是该条规范要解决的,应该保证函数的行不要太多,函数的粒度足够细。

例2和例1同理。

关于你举的上下两个for的例子,移动是不会跑到全局的,但是你删除后一个for,上面的一个是会跑到全局的。所以你的例子要表达的意思是没错的。对于这种case,我觉得,应该上下都var,循环控制相关变量在for的括号“()”里面var。重复的var在运行时不会导致任何问题,上线的时候压缩工具会帮你处理,提前。

function funA() {
    for(var i = 0, len = 5; i < len; i++) {
        var item = .....;
        // do something ...
    }

    // ...

    // 逻辑块五
    for(var i = 0, len = 7; i < len; i++) {
        var item = .....;
        // do something ...
    };
}

关于【遗留工程/库和规范的冲突】

这个问题是这样的,对于遗留工程,当我们要开发一个新功能,需要在某个文件上添加东西的时候,需要将该文件按新的code style改好。

确实,这么做在前期会占用一些时间,但是如果不这么做,编码规范就会完全变成空谈。现在大家对编码规范还有印象,过两个月后,估计就淡忘了,然后来了一个新项目,还是按原来自己的习惯搞,规范就真的是一纸空文了。

还有一种情况会让人很痛苦,有的产品线开发时文件和模块划分不好,一个文件几千行,就会很惨。但是这是之前随便滥写的苦果,是需要尝的。这就是为什么习惯越好的同学越不抵制遵循规范,因为他们遵循规范的成本低。

当然,可以有例外。比如暴露的api的命名,这肯定是允许不改的。比如注释,大部分是可以不补的,关键入口函数初始化函数要补。

关于【2013年1月1日提交代码不合规范】

对于修改文件时,上面那条已经说清楚了哈。但是如果是别人写的,又看不懂,可以不进行大重构。但是缩进和空格是需要调整好的。确实需要出一个补充说明,在1-1之前。

发布规范时“无理由不通过”,不是指不让工程师解释哈。如果发现不符合的,肯定会问工程师为什么不符合。“无理由”指的是该日期后写的全新代码。

from edp.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.