安装

1
$ npm install -g json-server

数据库文件

新建一个 json 文件,默认文件名为 db.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
"user": [
{
"id": 10000,
"name": "一韬",
"age": 25,
"gender": "male"
},
{
"id": 10001,
"name": "张三",
"age": 30,
"gender": "female"
}
],
"book": [
{
"id": 10000,
"name": "JavaScript从入门到精通",
"price": 9990,
"owner_id": 10000
},
{
"id": 10001,
"name": "Java从入门到放弃",
"price": 1990,
"owner_id": 10001
}
]
}

Start JSON Server

1
$ json-server --watch db.json -p 3000

get


1
2
3
4
//按照id查
http://localhost:3001/user/10000
//按照属性查
http://localhost:3001/user?age=30

post


1
http://localhost:3001/user?name=killer&age=23

delete


1
http://localhost:3001/user/10000

patch


1
http://localhost:3001/user?name=killer&age=24

注意

  • POST,PUT,PATCH或DELETE请求,则使用lowdb将更改自动安全地保存到db.json。

  • 请求体JSON应该是对象,就像GET输出一样。 (例如{“name”:“Foobar”})

  • Id值不可变。 PUT或PATCH请求的正文中的任何id值将被忽略。 只有在POST请求中设置的值才能被遵守,但只有在尚未占用的情况下才能被遵守。

  • POST,PUT或PATCH请求应包含Content-Type:application / json标头,以在请求正文中使用JSON。 否则将导致200 OK,但不改变数据。

  • 可以用 . 来请求更深层的数据

  • 使用 _page_limit 来分页请求,默认返回10条数据

  • 使用 _sort_order 来对结果排序

更多

查看官方文档

评论和共享

前几天通过掘金投了心知科技的简历,今天收到了电话。
晚些时候约好了和心知科技的长得挺帅的小哥“聊了聊”,
小哥问了我一个这样的问题,当时也没好好分析,下面我总结一下。

三门问题

参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门会否增加参赛者赢得汽车的机率?

我的解法

要是直接按照概率论讲明也太麻烦了,所以,我们来测试它几千万次,看看结果呗。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//换了的正确次数
let resultChange = 0;
//不换正确的次数
let resultUnChange = 0;
//所有正确的结果
let randomList = {};
let doorSize = 3;

for(let i=0;i<10000000;i++){
let randomPrise = Math.floor(Math.random()*doorSize); //真正奖品存在的门
let randomSelect = Math.floor(Math.random()*doorSize);//你第一次选的门
let removeSelect = null;//主持人帮我排除掉的选项

if(randomPrise in randomList){
randomList[randomPrise]++;
}else{
randomList[randomPrise] = 1;
}
/**
* 帮我排除一个选项,
* 如果这个选项是我选的,则不排除
* 如果这个选项不是选的,但是是奖品,不排除
*/
for(let j=0;j<doorSize;j++){
if(j===randomSelect){
continue;
}
if(j===randomPrise){
continue;
}
removeSelect = j;
}
//换了正确
if(randomPrise!==randomSelect&&randomPrise!==removeSelect){
resultChange++;
}
//不换正确
if(randomSelect===randomPrise){
resultUnChange++;
}
}
console.log(randomList);
console.log('换了的正确率',resultChange/10000000);
console.log('不换的正确率',resultUnChange/10000000);

结果如下:

1
2
3
{ '0': 3333844, '1': 3334541, '2': 3331615 }
换了的正确率 0.6666581
不换的正确率 0.3333419

很容易看出来啊,当然换了好。

从概率论方面解读

首先,汽车放到哪个门后如果用一个数组可以表示成这样,[0,1,0]

那么可能的结果会是:

[1,0,0] [0,1,0] [0,0,1]

这三种,其实我们第一次选择哪个都不影响最终的概率,所以,我们就假设:
我们第一次选的数组的第一个元素,那么我们分析一下:

选项 主持人去掉一个后的结果 换能拿到汽车
[1,0,0] [1,0] ×
[0,1,0] [0,1]
[0,0,1] [0,1]

很明显,换的概率是 66.67% ,不换 33.33%。

评论和共享

黑白棋

背景

在开发Reversi_process
( 用Node.js 将黑白棋对局的每一步保存成本地图片的)的时候,遇到这样的需求:
先在 Node-Canvas 画出每一步的图片,然后保存下来。
在绘制下一张图片的时候,要保证前一张图片被保存下来。不然保存的图片可能会乱。

但是如果用同步的方法保存,整个过程下来是相当的费时。
生成一个有60步的对局大约需要 12000ms 。于是有了下面这个解决办法。

环境

  • Node.js: v7.9.0
  • node-canvas: 1.6.5

阅读前需要对 Promise 和 async/await 有一定了解

异步进行 Canvas 保存成图片

首先,将异步 fs.writeFile 封装成一个 Promise 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 将buffer数据保存到指定的文件夹下
* @param name 新图片的名字
* @param dataBuffer 图片数据
* @returns {Promise}
*/
DrawChessboard.prototype.saveImg = function(name, dataBuffer){
"use strict";
return new Promise((resolve, reject)=>{
fs.writeFile(this.path + '/' + name + ".png",dataBuffer, function (err) {
if(err) reject(err);
resolve();
})
});
};

之后,我们用一个数组来存每一步的 Promise 对象,在保存每一步 Canvas 图像的时候
我们创建一个当前 Canvas 的副本。如果直接传入当前 Canvas 的时候,会导致保存后的图片乱掉。

在这里我们使用了 toDataURL() 的异步使用方法

1
canvas.toDataURL('image/png', function(err, png){ });

因为 toDataURL() 将非常耗时,在本例中,平均保存一步对局需要 600ms

toDataURL() 回调中,我们调用上面封装成 Promise 方法的 saveImg(name, dataBuffer)
方法,并将返回的 Promise 对象添加到 promises 数组中。

之后我们只要保证 promises 中的 Promise 全部执行完成就可以了。

只要调用 Promise.all( iterable ); 将 promises 传入即可,等待 promises 方法全部执行完后
返回一个新的 Promise 对象,处理之后便可以进行之后的工作了。

代码如下(使用了 async/await 新特性,NodeJs v7.4.0 后正式加入)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
DrawChessboard.prototype.draw = async function (/*string*/chessString) {
//---
//省略若干步
//---

//保存每一步的Promise对象
let promises = [];

for(let i=0;i<length;i++){
//计算这一步的棋局
this.computeChess(chessArray[i*2],chessArray[i*2+1]);
//在Canvas上绘制这一步棋局
this.drawChess();

//为了在异步操作不导致两次 toDataURl() 有影响,这里创建了一个当前 canvas 副本。
let newCanvas = new Canvas(this.size_x, this.size_y);
let newCtx = newCanvas.getContext('2d');
let imageDate = this.ctx.getImageData(0,0,this.size_x,this.size_y);
newCtx.putImageData(imageDate,0,0);

//此处 toDataURL() 为异步操作,如果同步操作则非常耗时,此处约600ms执行完成。
newCanvas.toDataURL("image/png",(err,png)=>{
let base64Data = png.replace(/^data:image\/\w+;base64,/, "");
let dataBuffer = new Buffer(base64Data, 'base64');
promises.push(this.saveImg(i,dataBuffer));
});
}
try {
await Promise.all(promises);
}catch (err){
console.log(err);
}
return this.canvas.toDataURL();
}

评论和共享

fs(文件系统)

通过 require(‘fs’) 使用该模块。 所有的方法都有异步和同步的形式。

异步版本:

1
2
3
4
5
6
const fs = require('fs');

fs.unlink('/tmp/hello', (err) => {
if (err) throw err;
console.log('successfully deleted /tmp/hello');
});

同步版本:

1
2
3
4
const fs = require('fs');

fs.unlinkSync('/tmp/hello');
console.log('successfully deleted /tmp/hello');

基本方法

readFile

1
2
3
4
5
6
7
fs.readFile(file[, options], callback)

file <String> | <Buffer> | <Integer> 文件名或文件描述符
options <Object> | <String>
encoding <String> | <Null> 默认 = null
flag <String> 默认 = 'r'
callback <Function>

1
2
3
4
fs.readFile('/etc/passwd', (err, data) => {
if (err) throw err;
console.log(data);
});

writeFile

1
2
3
4
5
6
7
8
fs.writeFile(file, data\[, options], callback)
file <String> | <Buffer> | <Integer> 文件名或文件描述符
data <String> | <Buffer>
options <Object> | <String>
encoding <String> | <Null> 默认 = 'utf8'
mode <Integer> 默认 = 0o666
flag <String> 默认 = 'w'
callback <Function>

1
2
3
4
fs.writeFile('message.txt', 'Hello Node.js', (err) => {
if (err) throw err;
console.log('It\'s saved!');
});

注意点

不建议在调用 fs.open() 、 fs.readFile() 或 fs.writeFile()
之前使用 fs.access() 检查一个文件的可访问性。 如此处理会造成紊乱情况,
因为其他进程可能在两个调用之间改变该文件的状态。 作为替代,
用户代码应该直接打开/读取/写入文件,当文件无法访问时再处理错误。

评论和共享

终极异步方案

JavaScript异步编程一直是一件很麻烦的事,从最早的回调函数,到 Promise 对象,
再到 Generator 函数,每次都有所改进。但总感觉缺点什么。

直到 async/await 出现,很多人认为它是异步操作的终极解决方案。

async 应该会在 ECMAScript 7 引入。在 NodeJs v7.4.0 正式加入 NodeJs 大家庭。

async 是 Generator 函数的语法糖

1
2
3
4
5
6
7
8
9
10
var fs = require('fs');

var readFile = function (fileName){
return new Promise(function (resolve, reject){
fs.readFile(fileName, function(error, data){
if (error) reject(error);
resolve(data);
});
});
};

Generator 写法:

1
2
3
4
5
6
7
8
var co = require('co');
var gen = function* (){
var f1 = yield readFile('/etc/fstab');
var f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
co(gen);

写成 async 函数,就像下面这样:

1
2
3
4
5
6
var asyncReadFile = async function (){
var f1 = await readFile('/etc/fstab');
var f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};

其实就是把 * 替换成 async ,把 yield 换成 await。而且自带执行器
(Generator 函数的执行必须依靠执行器,所以才有了 co 函数库)。

async 函数用法

  • 同 Generator 函数一样,async 函数返回一个 Promise 对象。
    所以调用 async 函数的函数也要处理 Promise 对象。

  • 用 try…catch 来处理 Promise 对象 rejected

  • await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。

  • 将 forEach 方法的参数改成 async 函数不能够实现继发执行。应该采用 for 循环。

  • 希望多个请求并发执行,可以使用 Promise.all 方法。

评论和共享

CommonJS

首先,最先出现的是 CommonJSCommonJS API 定义很多普通应用程序(主要指非浏览器的应用)使用的API。它的终极目标是提供一个类似 PythonRubyJava 标准库。这样的话,开发者可以使用 CommonJS API 编写应用程序,然后这些应用可以运行在不同的 JavaScript 解释器和不同的主机环境中。

2009年,美国程序员 Ryan Dahl 创造了 node.js 项目,将 javascript 语言用于服务器端编程。这标志 Javascript 模块化编程”正式诞生。

NodeJSCommonJS 规范的实现, webpack 也是以 CommonJS 的形式来书写。NPM 作为 Node 的包管理器,帮助Node 解决依赖包的安装问题,也要遵循 CommonJS

Browserify

browserify 是最常用的 CommonJS 转换工具。

然后看个例子

1
2
3
4
5
6
7
8
// foo.js
module.exports = function(x) {
console.log(x);
};

// main.js
var foo = require("./foo");
foo("Hi");

1
$ browserify main.js > compiled.js

即可打包,Browserify 具体做了什么继续向下看即可:

1
$ npm install browser-unpack -g

然后我们将上面生成的 compiled.js 解包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ browser-unpack < compiled.js

[
{
"id":1,
"source":"module.exports = function(x) {\n console.log(x);\n};",
"deps":{}
},
{
"id":2,
"source":"var foo = require(\"./foo\");\nfoo(\"Hi\");",
"deps":{"./foo":1},
"entry":true
}
]

Browserify 在这里将所有模块放到一个数组里,id 是模块的编号,source 是源码,deps 为依赖,entry 为指定入口。

Browserify 先找到 entry: true 的地方,然后执行 source 中的代码,遇到 require 就去deps 中寻找依赖,并执行所依赖的代码,将结果赋值给 require 前的变量。

AMD

AMD ( Asynchronous Module Definition ),是为了解决 CommonJS 不能异步加载的问题。AMD 采用异步的方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等加载完成后,这个回调函数才会执行。

AMD 也采用 require() 语句加载模块,但是不同于 CommonJS,它要求两个参数:

1
require([module], callback);

第一个参数 [module] ,是一个数组,里面的成员就是要加载的模块;第二个参数 callback ,则是加载成功之后的回调函数。如果将前面的代码改写成 AMD 形式,就是下面这样:

1
2
3
require(['math'], function (math) {
  math.add(2, 3);
});

math.add()math 模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD 比较适合浏览器环境。目前,主要有两个 Javascript 库实现了 AMD 规范: require.jscurl.js

require.js

先去官方网站下载最新版本。

1
然后将它放到项目中即可

如果加载这个文件,也可能造成网页失去响应。解决办法有两个,一个是把它放在网页底部加载,另一个是写成下面这样:

1
<script src="js/require.js" defer async="true" ></script>

async 属性表明这个文件需要异步加载,避免网页失去响应。IE不支持这个属性,只支持 defer,所以把 defer 也写上。

加载 require.js 以后,下一步就要加载我们自己的代码了。假定我们自己的代码文件是 main.js ,也放在 js 目录下面。那么,只需要写成下面这样就行了:

1
<script src="js/require.js" data-main="js/main"></script>

data-main 属性的作用是,指定网页程序的主模块。在上例中,就是js目录下面的 main.js ,这个文件会第一个被 require.js加载。由于 require.js 默认的文件后缀名是js,所以可以把 main.js 简写成 main

主模块的写法:

1
2
3
4
// main.js
require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
  // some code here
});

默认情况下,require.js 假定这三个模块与 main.js 在同一个目录,文件名分别为 moduleA.jsmoduleB.jsmoduleA.js,然后自动加载。

使用 require.config() 方法,我们可以对模块的加载行为进行自定义。require.config() 就写在主模块(main.js)的头部。参数就是一个对象,这个对象的 paths 属性指定各个模块的加载路径。

1
2
3
4
5
6
7
require.config({
  paths: {
    "jquery": "jquery.min",
    "underscore": "underscore.min",
    "backbone": "backbone.min"
  }
});

模块的写法

模块必须采用特定的 define() 函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在 define() 函数之中。

1
2
3
4
5
6
7
8
9
// math.js
define(function (){
 var add = function (x,y){
    return x+y;
  };
  return {
    add: add
  };
});

ES6 module

ES6发布的 module 并没有直接采用 CommonJS ,甚至连 require 都没有采用,也就是说 require 仍然只是 node 的一个私有的全局方法,module.exports 也只是 node 私有的一个全局变量属性,跟标准半毛钱关系都没有。

export 详细用法
import 详细用法

区别

require 的使用非常简单,它相当于 module.exports 的传送门,module.exports 后面的内容是什么,require 的结果就是什么,对象、数字、字符串、函数……再把 require 的结果赋值给某个变量,相当于把 requiremodule.exports 进行平行空间的位置重叠,而且 require 理论上可以运用在代码的任何地方,甚至不需要赋值给某个变量之后再使用。在使用时,完全可以忽略模块化这个概念来使用 require,仅仅把它当做一个 node 内置的全局函数,它的参数甚至可以是表达式:

但是 import 则不同,它是编译时的(require是运行时的),它必须放在文件开头,而且使用格式也是确定的,不容置疑。它不会将整个模块运行后赋值给某个变量,而是只选择 import 的接口进行编译,这样在性能上比 require 好很多。

从理解上,require 是赋值过程,import 是解构过程,当然,require 也可以将结果解构赋值给一组变量,但是 import 在遇到 default 时,和 require 则完全不同: var $ = require('jquery');import $ from 'jquery' 是完全不同的两种概念。

评论和共享

Git 连接 Github

初始化邮箱用户名

1
git config --global user.email "shianqi@imudges.com"
1
git config --global user.name "shianqi"

生成SSH密钥

1
ssh-keygen -t rsa -C "shianqi@imudges.com"

windows 需要在 GitBush 中添加

将密钥添加到 Github 设置

测试连接

1
ssh -T git@github.com

出现下面的文本,则链接成功

1
Hi shianqi! You've successfully authenticated, but GitHub does not provide shell access.

评论和共享

  • 第 1 页 共 1 页
作者的图片

Archie Shi

Nothing to say


Front-End Development Engineer