Giter Club home page Giter Club logo

my_blog's People

Watchers

 avatar

my_blog's Issues

ES6之let(理解闭包)和const命令

  最近做项目的过程中,
  我们知道,ECMAScript 6即ES6是ECMAScript的第五个版本,因为在2015年6月正式发布,所以又成为ECMAScript2015。ES6的主要目的是为了是JS用于编写复杂的大型应用程序,成为企业级的开发语言。

  说明:由于有时候我们希望得知es6代码的具体实现原理或者说希望能够转化为es5使用,我们可以使用http://babeljs.io/来实现在线将es6代码转化为es5代码。

第一部分:let命令
  一.块级作用域(重点)。
   我们知道,在javascript中只有全局作用域和函数作用域,并不存在块级作用域。这样,在使用时就会出现一些问题。 下面我们先来举例说明let块级作用域的使用。

   例1:

  代码如下所示: 

    {
        var a=5;
        let b=10;
    }
    console.log(a);
    console.log(b);

  我们在控制台得到的结果如下所示:

  也就是说,var声明的变量由于不存在块级作用域所以可以在全局环境中调用,而let声明的变量由于存在块级作用域所以不能在全局环境中调用。

 
  例2:这个例子是一个非常经典的例子。

  

复制代码
var a=[];
for(var i=0;i<10;i++){
a[i]=function(){
console.log(i);
};
}
a6; //10
复制代码
复制代码
var a=[];
for(let i=0;i<10;i++){
a[i]=function(){
console.log(i);
};
}
a6; //6
复制代码

  我们可以看到,两个例子中,唯一的区别是前者for循环中使用var来定义i,得到的结果是10.而后者使用的是let来定义i,最终得到的结果是6.这是为什么呢?阮一峰老师在书中的解释并不是很清楚,所以下面我会发表个人见解:

  关于这个问题,表面上确实不是很好理解,查询了很多资料,许多人讲到了很多晦涩难懂的知识,似乎很高大上,但是实际上并不难,下面根据我的理解进行解释,如有问题,欢迎批评指正,如果大家能够有些收获就再好不过了。

  例二前者(var i)具体执行过程如下:

var a=[];

var i=0;//由于var来声明变量i,所以for循环代码块不具备块级作用域,因此i认为是全局变量,直接放在全局变量中。
a[0]=function(){
console.log(i);//这里之所以i为i而不是0;是因为我们只是定义了该函数,未被调用,所以没有进入该函数执行环境,i当然不会沿着作用域链向上搜索找到i的值。
}// 由于不具备块级作用域,所以该函数定义就是全局作用域。

var i=1;//第二次循环,这时var i=1;覆盖了前面的var i=0;即现在i为1;
a[1]=function(){
console.log(i);//解释同a[0]函数。
}

var i=2;// 第三次循环,这时 i=2,在全局作用域中,所以覆盖了前面的i=1;
a[2]=function(){
console.log(i);
}

......第四次循环 此时i=3 这个以及下面的i不断的覆盖前面的i,因为都在全局作用域中
......第五次循环 此时i=4
......第六次循环 此时i=5
......第七次循环 此时i=6
......第八次循环 此时i=7
......第九次循环 此时i=8

var i=9;
a[9]=function(){
console.log(i);
}

var i=10;// 这时i为10,因为不满足循环条件,所以停止循环。

紧接着在全局环境中继续向下执行。

   a[6]();//这时调用a[6]函数,所以这时随即进入a[6]函数的执行环境,即a[6]=function(){console.log(i)};执行函数中的代码 console.log(i); 因为在函数执行环境中不存在变量i,所以此时会沿着作用域链向上寻找(可参考我的博文《深入理解作用域和作用域链》),即进入了全局作用域中寻找变量i,而全局作用域中i=10覆盖了前面所有的i值,所以说这时i为10,那么a[6]的值就是10了。

  

  说明:对于例如a[1]=function(){console.log(i)};而不是a[1]=function{console.log(1)},可以在控制台中输出a[1]函数,即可得到验证。

  例二后者(let i)具体执行过程如下:

var a=[];//创建一个数组a;

{ //进入第一次循环
let i=0; //注意:因为使用let使得for循环为块级作用域,此次let i=0在这个块级作用域中,而不是在全局环境中。
a[0]=function(){
console.log(i);
}; //注意:由于循环时,let声明i,所以整个块是块级作用域,那么a[0]这个函数就成了一个闭包。
}// 声明: 我这里用{}表达并不符合语法,只是希望通过它来说明let存在时,这个for循环块是块级作用域,而不是全局作用域。

讲道理,上面这是一个块级作用域,就像函数作用域一样,函数执行完毕,其中的变量会被销毁,但是因为这个代码块中存在一个闭包,闭包的作用域链中包含着(或着说是引用着)块级作用域,所以在闭包被调用之前,这个块级作用域内部的变量不会被销毁。(更多闭包知识,可以看我的博文《JavaScript之闭包》)

{ //进入第二次循环
let i=1; //注意:因为let i=1; 和 上面的let i=0;出在不同的作用域中,所以两者不会相互影响。
a[1]=function(){
console.log(i);
}; //同样,这个a[i]也是一个闭包
}

......进入第三次循环,此时其中let i=2;
......进入第四次循环,此时其中let i=3;
......进入第五次循环,此时其中let i=4;
......进入第六次循环,此时其中let i=5;
......进入第七次循环,此时其中let i=6;
......进入第八次循环,此时其中let i=7;
......进入第九次循环,此时其中let i=8;

{//进入第十次循环
let i=9;
a[i]=function(){
console.log(i);
};//同样,这个a[i]也是一个闭包
}

{
let i=10;//不符合条件,不再向下执行。于是这个代码块中不存在闭包,let i=10;在这次循环结束之后难逃厄运,随即被销毁。
}

a6;//调用a6函数,这时执行环境随即进入下面这个代码块中的执行环境:funcion(){console.log(i)};

{
let i=6;
a[6]=function(){
console.log(i);
}; //同样,这个a[i]也是一个闭包
}

a[6]函数(闭包)这个执行环境中,它会首先寻找该执行环境中是否存在 i,没有找到,就沿着作用域链继续向上到了其所在的代码块执行环境,找到了i=6,于是输出了6,即a[6]();的结果为6。这时,闭包被调用,所以整个代码块中的变量i和函数a[6]()被销毁。

相信大家仔细看完上面的函数执行的过程,对let var 块级作用域 闭包就有一个很好的理解了。我认为重要的是对于函数执行过程的理解!

  二.不存在变量提升
  这里是说使用let不会像使用var一样存在一个变量提升的现象。变量提升是什么呢?在没有接触es6之前我对此也不清楚,但是我想大家一定都听说过函数声明提升:函数声明来定义函数即可实现函数声明提升,这样,我们可以先调用函数,后声明函数;而函数表达式方法不会实现函数声明提升,这样,如果先调用函数,后声明函数,则会抛出错误!!(对于函数声明提升更多知识可以看我的博文《JavaScript函数之美~》)。 那么可以以此类推,var定义变量:可以先使用,后声明;而let定义变量:只可先声明,后使用。

  例3:

  

复制代码
var num1=100;
console.log(num1);

    let num2=200;
    console.log(num2);

    console.log(i);
    var i=10;

    console.log(j);
    let j=5;

复制代码
    我们可以看到结果如下:

即前两个都是先声明后使用,没有问题。而后两个都是先使用,后声明,用var 声明的显示undefined,而 let声明的直接报错。

说明:console.log(i);

   var i=10;

实际上相当于:

   var i;

   console.log(i);

   i=10;

所以会出现undefined的情况。

 

  三.暂时性死区
  暂时性死区即:只要一进入当前作用域,所要使用的变量就已经存在,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

例5:

var tmp=123;
if(true){
    tmp="abc";
    let tmp;
}

结果如下:

也就是说:虽然上面的代码中存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定了块级作用域,所以在let声明变量前,对tmp赋值会报错。此即暂时性死区。

  

  注意:ES6规定暂时性死区和不存在变量提升就是为了减少运行时的错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。

  

  暂时性死区就是: 只要块级作用域内存在let,那么他所声明的变量就绑定了这个区域,不再受外部的影响。

  暂时性死区即 Temperary Dead Zone,即TDZ。

  

   注意:暂时性死区也意味着 typeof 不再是一个百分之百安全的操作。  如下:

if (true) {
  console.log(typeof x);
  let x;
}

  这里如果没有let x,那么typeof x的结果是 undefined,但是如果使用了let x,因为let不存在变量提升,所以这里形成了暂时性死区,即typeof x也是会报错的。。。 从这里可以理解暂时性死区实际上就是这一部分是有问题的 。

  四.不允许重复声明

  

复制代码
function func (){
let b=100;
var b=10;
}

function add(num){
    let num;
    return num+1;
}

function another(){
    let a=10;
    let a=5;
}

复制代码
上述三个得到的结果均为:

只是前两者为 b和num被声明过了。注意:第二个函数,虽然我们没有明确的声明,但是参数实际上是相当于用var声明的局部变量。

第二部分:const命令
     什么使const命令呢?实际上它也是一种声明常量的方式。const命令用来声明常量,一旦声明,其值就不能改变。初次之外,const和let十分相似。也就是说前者是用于声明常量的,后者是用于声明变量的。

    1.const声明常量,一旦声明,不可改变。

const a=10;
a=100;

    结果如下

    

    2.既然const一旦声明不可改变,所以在声明时必须初始化。

const a;
     结果如下:

    3.const所在的代码块为块级作用域,所以其变量只在块级作用域内使用或其中的闭包使用。

if(true){
    const a=10;
}
console.log(a);

    结果如下:

    

    4.const声明的变量不存在变量提升。

if(true){
    console.log(a);
    const a=10;
}

结果如下:

  

    5.const不可重复声明常量。

var a=10;
const a=5;

结果如下:

    6.const命令只是保证了变量名指向的地址不变,并不保证该地址的数据不变。

  

复制代码
const a={};
a.name="zzw";
console.log(a.name);

const b=[];
b.push("zzw");
console.log(b);

const c={};
c={name:"zzw"};

复制代码
  结果如下:

因此,我们使用const所指向的地址不可变,但是地址的内容是可以变得。

    7.如果希望将对象本身冻结,可以使用Object.freeze()方法。

const a=Object.freeze({});
a.name="zzw";
console.log(a.name); //undefined

    于是通过Object.freeze()方法我们就不可以再改变对象的属性了(无效)。

一种适配方案

了解到一种适配方案(使用 vh vw)

方法一
仅使用 vw 单位作为唯一应用的一种 CSS 单位的这种做法下,遵循

1.对于设计稿的尺寸转换为vw单位,我们使用Sass函数编译

//iPhone 6尺寸作为设计稿基准
$vm_base: 375;
@function vw($px) {
@return ($px / 375) * 100vw;
}
2.无论是文本还是布局高宽、间距等都使用 vw 作为 CSS 单位

.mod_nav {
background-color: #fff;
&_list {
display: flex;
padding: vm(15) vm(10) vm(10); // 内间距
&_item {
flex: 1;
text-align: center;
font-size: vm(10); // 字体大小
&_logo {
display: block;
margin: 0 auto;
width: vm(40); // 宽度
height: vm(40); // 高度
img {
display: block;
margin: 0 auto;
max-width: 100%;
}
}
&_name {
margin-top: vm(2);
}
}
}
}
3.1物理像素线(也就是普通屏幕下 1px ,高清屏幕下 0.5px 的情况)采用 transform 属性 scale 实现。

.mod_grid {
position: relative;
&::after {
// 实现1物理像素的下边框线
content: '';
position: absolute;
z-index: 1;
pointer-events: none;
background-color: #ddd;
height: 1px;
left: 0;
right: 0;
top: 0;
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
-webkit-transform: scaleY(0.5);
-webkit-transform-origin: 50% 0%;
}
}
...
}
4.对于需要保持高宽比的图,应改用 padding-top 实现

.mod_banner {
position: relative;
padding-top: percentage(100/700); // 使用padding-top
height: 0;
overflow: hidden;
img {
width: 100%;
height: auto;
position: absolute;
left: 0;
top: 0;
}
}
方法二
搭配vw和rem,布局更优化

这样的页面虽然看起来适配得很好,但是你会发现由于它是利用视口单位实现的布局,依赖于视口大小而自动缩放,无论视口过大还是过小,它也随着视口过大或者过小,失去了最大最小宽度的限制。

当然,你可以不在乎这样微小的不友好用户体验,但我们还是尝试下追求修复这样的小瑕疵吧。

于是,联想到不如结合rem单位来实现布局?rem 弹性布局的核心在于动态改变根元素大小,那么我们可以通过:

给根元素大小设置随着视口变化而变化的 vw 单位,这样就可以实现动态改变其大小。
限制根元素字体大小的最大最小值,配合 body 加上最大宽度和最小宽度
这样我们就能够实现对布局宽度的最大最小限制
代码如下:

// rem 单位换算:定为 75px 只是方便运算,750px-75px、640-64px、1080px-108px,如此类推
$vm_fontsize: 75; // iPhone 6尺寸的根元素大小基准值
@function rem($px) {
@return ($px / $vm_fontsize ) * 1rem;
}
// 根元素大小使用 vw 单位
$vm_design: 750;
html {
font-size: ($vm_fontsize / ($vm_design / 2)) * 100vw;
// 同时,通过Media Queries 限制根元素最大最小值
@media screen and (max-width: 320px) {
font-size: 64px;
}
@media screen and (min-width: 540px) {
font-size: 108px;
}
}
// body 也增加最大最小宽度限制,避免默认100%宽度的 block 元素跟随 body 而过大过小
body {
max-width: 540px;
min-width: 320px;
}

如何更有效率和质量地开发Vue项目

前言
自总结完了上篇前端工程化的**,并在vue全家桶的项目加以实践,趁热给大家总结一篇如何更有效率与质量地开发vue项目,以及其中踩过的一个个坑。。。

基于vue-cli的自定义模板(Custom Templates)
小伙伴们的vue项目应该都是用vue-cli初始化出来的,但是vue-cli只是满足了基础配置和功能,如果你有额外的配置需求或者要迎合团队的业务配置,每新建个项目都得重新安装额外配置,比如说vuex,sass,封装axios,以及相关的文件夹。为了解决上述问题,vue-cli出了一个自定义模板功能,你fork官方的模板下来然后进行修改,然后用 vue-cli 来调用。具体调用的场景有以下两种

直接拉取git源:
当你修改了模板并上传了repo上,可执行以下命令行来初始化

vue init username/repo my-project
拉取本地的模板:
当你clone了官方模板在本地修改,可执行以下命令行来初始化

vue init ~/fs/path/to-custom-template my-project
还可以编写meta.*(js,json)来选择安装哪些配置~

如果大家懒得去编写vuex,sass的配置,封装axios的话,可以来通过我配置完的脚手架来初始化完项目~

vue init duosanglee/vuejs-custom-template
这个模板在repo里
ps:我的这个模板的代码风格是基于standard的

引入sass全局变量,mixin,function等
首先我们考虑下以下场景:当使用rem开发移动端的时候,你定义了一个方法pxToRem的方法来实现px对rem的转换,然后在工程里为每个.vue文件@import 'public.scss',得import很多很多很多次,万一public.scss路径变了的话。。。哭都来不及。
这时候sass-resources-loader就来拯救我们了,他可以省去重复性的引入,还支持LESS,POSTCSS等
具体用法如下:

npm install -D sass-resources-loader

首先得找到项目里的build文件夹,找到util.js
添加一下代码

function resolveResouce(name) {
    return path.resolve(__dirname, '../src/style/' + name);
}
function generateSassResourceLoader() {
    var loaders = [
 cssLoader, 
 // 'postcss-loader',
 'sass-loader',
 {
     loader: 'sass-resources-loader',
     options: {
       // it need a absolute path
       resources: [resolveResouce('common.scss')]
     }
 }
    ];
    if (options.extract) {
 return ExtractTextPlugin.extract({
   use: loaders,
   fallback: 'vue-style-loader'
 })
    } else {
 return ['vue-style-loader'].concat(loaders)
    }
}

然后还是在当前文件里找到

return {
  css: generateLoaders(),
  postcss: generateLoaders(),
  less: generateLoaders('less'),
  sass: generateLoaders('sass', { indentedSyntax: true }),
  scss: generateLoaders('sass'),
  stylus: generateLoaders('stylus'),
  styl: generateLoaders('stylus')
}

替换成

return {
  css: generateLoaders(),
  postcss: generateLoaders(),
  less: generateLoaders('less'),
  sass: generateSassResourceLoader(),
  scss: generateSassResourceLoader(),
  stylus: generateLoaders('stylus'),
  styl: generateLoaders('stylus')
}

这样就可以在项目里使用sass全局变量,mixin,function了~~

在线 Mock 平台 easy-mock
现在讲都是前后端分离,前后端并行开发来提高开发效率,通过一个api文档来协作,所以一个好的mock工具对于提高效率也至关重要~
这里极力推荐easy-mock工具,支持团队协作编辑,生成模拟数据的在线 mock 服务,还支持导入swagger文档等功能,界面如下
clipboard.png

定义全局变量
在项目会有需要使用全局变量的需求,来处理一些频繁的操作,大家都应该会绑定到window对象上,但是这种方式不适合服务端渲染,因为服务端没有 window 对象, 是 undefined, 当试图去访问属性时会报错.我总结了两个靠谱的方法

代理到Vue的原型对象
由于所有的组件都会从 Vue 的原型对象上继承它们的方法, 因此我们只要

Object.defineProperty(Vue.prototype, '$xxx', { value: xxx });
就可以在所有组件/实例中通过 this.$xxx: 的方式访问插件了而不需要定义全局变量或者手动的引入了
至于为什么要用Object.defineProperty这个方法,是因为通过Object.defineProperty绑定的属性是只读的,以防一起开发项目的协(zhu)作(dui)者(you)去重写或者覆盖该方法的值。

vuex大法
vuex的出现就是vue为了集中式存储管理应用的所有组件的状态,所以说全局变量和方法都可以放到vuex当中~具体用法就不加阐述了,大家可仔细阅读vuex文档

组件设计
大家都知道组件化的**就是分治,几乎任意类型的应用程序界面,都可以抽象为一个组件树,那我们该按照什么规则把应用抽象成组件,来应对复杂多变的业务需求呢。
我们从通信、黑箱,继承这几个角度来看看

通信: vue的父子组件通信机制是props down,events up,尽量保持松耦合,一直保持单向数据流的特点,并加以强约束。需要注意的时候,尽可能减少跨组件通信,例如使用$parent,$root。

继承: 当两个组件存在些许的共性,又存在足够的差异性的时候,就可以用到vue的继承---mixin,他允许你封装一块在应用的其他组件中都可以使用的函数。如果使用姿势正确,他们不会改变函数作用域外部的任何东西。而且mixin还有各种高阶用法,大家可自行查询(我也不会)。

黑箱: 组件的黑箱状态既只暴露易变的接口和方法,渲染给入的数据,组件内部封装不变的逻辑。

设计模式原则: 运用设计模式原则,比如单一职责原则,将组件拆分抽离成更细粒度,保证高内聚性;再如接口隔离原则,采用稳定的服务端接口,将变化模块分离,使得组件得以解耦;里氏替换原则、依赖倒置原则等等。。

目录结构
-- src
-- assets # 私有资源
-- common # 通用组件
-- components # 业务组件
-- api.js # 请求文件
-- config # 环境变量配置
-- env.js # 环境变量文件
-- http.js # 封装axios文件
-- pages # 页面维度
-- pageA # 页面A
-- pageA.vue # 页面A单文件
-- pageA-components # 页面A下的一个组件
-- children # 子页面
-- router # 路由
-- index.js # 路由入口
-- routes.js # 路由配置信息
-- store # vuex
-- modules # vuex模块
-- index.js # vuex入口
-- utils # js通用方法
-- app.vue # 顶层单文件
-- main.js # 入口
大家可以从目录结构中看出我整个项目分割的思维
首先我把组件分为通用组件和业务组件两大类。

通用组件是与业务耦合低,是有简单状态或者无状态的,数据几乎全部依赖于输入,它只负责渲染给入的数据。比如按钮是一个组件,可能有一个参数决定了它的尺寸,一个参数决定了它是否可以点击,但是点击这个按钮之后会发生什么,就不是按钮这个组件需要知道的事情了。

业务组件是与业务耦合高,可以由多个通用组件和其他的业务组件组成,会拥有一些方法,用来修改持有的数据,对内来看,它自己持有一些数据和方法,用来决定内容的渲染,对外又是一个简单的props接受数据。可以理解为组件树的非叶子节点,通过自身数据变化,进而操纵子组件的内容。

然后config文件夹放置了环境变量文件env.js和封装http库文件http.js

env.js
clipboard.png

http.js
clipboard.png

然后我把路由里的routes.js和api.js请求文件都单独抽离了出来。

自动生成雪碧图
前端项目中自动生成雪碧图节省了我们很多的时间,我们只要把图片扔到文件夹里,webpack-spritesmith就能按照我们设定的规则自动合成css-sprite,安装配置如下:

var SpritesmithPlugin = require('webpack-spritesmith');
...
module.exports = {
...
plugins: [
new SpritesmithPlugin({
src: {
cwd: './src/assets/sp/',
glob: '*.png'
},
target: {
image: './src/assets/sprite/sprite.png',
css: './src/assets/sprite/sprite.css'
},
apiOptions: {
cssImageRef: './sprite.png'
},
spritesmithOptions: {
algorithm: 'top-down',
padding: 100
}
})
]
}
自动修复eslint格式错误
这个功能的建立在小伙伴的开发工具是vscode情况下~
首先在vscode扩展里面安装vscode的eslint插件,然后settings.json里添加如下配置

"eslint.validate": [
"javascript",
"javascriptreact",
{
"language": "html",
"autoFix": true
},
{
"language": "vue",
"autoFix": true
}
],
"eslint.autoFixOnSave": true,
然后会在save文件的时候eslint插件自动根据项目下的.eslintrc来设置代码格式~
sf不支持播放gif..具体效果大家只能自行查看

跨域
在浏览vue-cli的官方文档时候发现了vue-cli自带了API proxy,解决了在项目中后端联调的时候的跨域问题。具体安装配置如下:
首先我们找到config文件下的index.js,再找到dev对象下的proxyTable属性,然后把以下代码添加进去

proxyTable: {
'/api': {
target: '网站名',
pathRewrite: {
'^/api': ''
}
}
}
然后重启本地服务器,这样你发送的/api/a就会代理发送到"网站名/a"了~

开发利器emmet
之所以称emmet为前端开发利器是因为他可以根据我们所输入的缩写来得到相应的内容,大大节省我们的开发html和css的时间,例:

输入ul>li*2>span 按下扩展键

<ul>
    <li><span></span></li>
    <li><span></span></li>
</ul>

输入m0-a-0-0+posa+bgc 按下扩展键

margin: 0 auto 0 0;
position: absolute;
background-color: #fff;

更多方法请看官方文档emmet

这篇文章到此就已经结束了感谢大家能够关注此文章如果这篇文章能帮助到大家的话,麻烦请帮我点个赞~~~

大家有啥想法可在下面评论,也可以加我QQ:757592499来讨论~

参考:

http://www.jianshu.com/p/0375...
https://hopkinson.github.io/2...
https://github.com/dwqs/blog/...
http://jeffjade.com/2017/03/1...
http://www.jianshu.com/p/95b2...
https://segmentfault.com/a/11...

改变input placeholder 的颜色

样式文件中加入以下代码,!important 是在其他插件影响时使用

input::input-placeholder{color:#b8c4ce!important;} 

input::-webkit-input-placeholder, textarea::-webkit-input-placeholder { 
color: #b8c4ce!important; 
} 
input:-moz-placeholder, textarea:-moz-placeholder { 
color: #b8c4ce!important; 
} 
input::-moz-placeholder, textarea::-moz-placeholder { 
color: #b8c4ce!important; 
} 
input:-ms-input-placeholder, textarea:-ms-input-placeholder { 
color: #b8c4ce!important; 
} 

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.