月盾的博客

婴儿学步牵引绳需不需要?

月盾

我女儿12个月开始学走路,由于她身高还很低,所以需要大人弯着腰搀扶着,时间长了大人的腰比较累,当时就在网上买了个绑带。 牵引绳 本以为能轻松点,结果发现排不上用场,因为实在不好用,宝宝根本不会老实的配合,她一感觉到胸前有个东西扶着她,就整个人爬下来了,所有重点都放在牵引绳上,所以大人就只能提着,而且由于刚学走路,也不能稳当的走,总是东倒西歪,牵引绳其实不能保证宝宝的平衡,反而会过于依赖绑带,结果就导致失去平衡类似于一只脚在地上一只脚翘起来打转,也容易勒到胳膊。

宝宝学走路一般一个月就能自己走了,所以我觉得没必要用这个。

RockMongo使用方法

月盾

RockMongo是一个PHP5写的MongoDB管理工具。

鉴于百度bce的mongodb数据使用了RockMongo来管理数据,就以此来说明。

查询操作

点击某个collection后的默认画面:

rockmongo

查询界面很简单,关键是怎么写查询语句?

rockmongo

点击文本可以查看所有数据字段,查询title为“测试”文档:

array(
	'title' => '测试'
)

模糊查询:

array(
	'title'=> new MongoRegex("/测试/i")
)

字段名要加引号,中间使用=>而不是使用:分割,需要查询的值也需要注意,字符串加引号,数字不加,如果类型不匹配就会查不出数据。查询结果有一条数据

rockmongo

修改操作

动作中选择“modify”

rockmongo

再查询一下

rockmongo

基本操作就是这样,删除操作不用说也应该能知道怎么做了。

nodejs中promise的if流程控制

月盾

Promise虽然解决了原生回调的金字塔写法,但是并不能很好的处理if else流程,相比起Java等同步方式编写还是有点难度,不过还是有方法写出优雅的代码。 下面用了一个不复杂的例子来说明:

假设一个人去银行开户,存钱,如果这个人是普通用户,需要先1.排队等候,2.开户,3.存钱。 如果是VIP用户,直接进行第2,3步操作。 如果普通用户是黑名单用户不给开户办卡。

var fs = require("fs");

function queue(){
	return new Promise((resolve, reject)=>{
		fs.readFile("../queue.txt", "utf8", (err, data)=>{
			if (err) reject(err);
			console.log(data.toString());
			resolve(data.toString());
		})
	})
}
function account(id){
	return new Promise((resolve, reject)=>{
		fs.writeFile('../account.txt', id+'一个新账户', (err) => {
			if (err) reject(err);
			console.log('办卡成功!');
			resolve();
		});
	})
}
function money(id){
	return new Promise((resolve, reject)=>{
		fs.writeFile('../money.txt', id+'存了10万', (err) => {
			if (err) reject(err);
			console.log('存款成功!');
			resolve();
		});
	})
}

//入口
function enter(id) {
	Promise.resolve()
	.then(()=>{
		if (id=="common") {
			return queue()
		} else if (id =="vip") {
			console.log("我是VIP用户,不需要排队等待验证身份直接开户存钱")
		}
	}).then(user=>{
		if (user =="黑名单用户") {
			throw "禁止开户"
		}
		return user;
	}).then(user=>{
		return account(id);
	}).then(()=>{
		return money(id);
	}).catch(err=>{
		console.log("回家")
	})
}

// enter("vip");
enter("common");

不通的用户会有不同的判断,走不同的流程,但是各类用户又有相同的操作,所以有if条件判断的逻辑单独放到一个then中处理,返回处理结果,如果后续then不需要处理结果也可以不返回,当做没有这个then。

typescript泛型的使用方法

月盾

typescript(以下简称ts)中泛型如何使用?(以下代码为ts书写) 个人认为开发语言中一些高级特性如果在不太理解的情况下不使用也无妨,无非是代码写的多一点,烂一点。但是我想作为程序猿大家还是会有所追求的。就以泛型来说,不使用也能正常开发,只不过在个别情况下需要写几份看起来相同的代码。比如在不使用泛型的情况下要求函数参数为number类型,并且返回number。

function a(args: number ): number {
	return args;
}
console.log(a(123));

又有另外一个要求,参数为string类型,并且返回string

function b(args: string ): string {
	return args;
}
console.log(b("sdg"));

如果还有其他类似的要求,就要不停的写类似格式的代码,那么改进一下:

function c(args: any ): any {
	return args;
}
console.log(c("sdg"));

这样是可以接受任何类型参数并且返回,但缺点是可以知道能传入任何类型而不知道返回的具体类型是什么,只知道是any类型。将上面的c函数稍作修改:

function c(args: any ): any {
    let n = args + "变成了字符串";
    //甚至更多的处理
	return n;
}
console.log(c(1));

c函数参数是数字,返回的可能是其他类型。 知道前面几种写法的不足再对比一下泛型的写法:

function d<A>(args: A ): A {
    let n = args + "变成了字符串";
    //甚至更多的处理
	return n;
}
console.log(d(1));

FhO7R54TnMePUhwcnry3fo8AJz7_ d函数要求参数是A类型,并且返回A类型,但是实际返回的类型可能由number类型变成了string类型,所以提示错误。其中<>中可以是任意大小写的英文,中文也行,但数字不行,比如:

function d<Az>(args: Az ): Az {
    let n = args + "变成了字符串";
    //甚至更多的处理
	return n;
}
console.log(d(1));

<>中的值是指代某种基础类型或任何自定义类型,它只是一种形式。也可以有下面的形式:

function d<T, U>(args: T,ar: U): U {
	return ar;
}
console.log(d(2, "aaaa"))

其实就是提供一种形式供后面代码使用,没有提供就不能使用。

error TS7010: skipTo, which lacks return-type annotation, implicitly has an any return type.

月盾
node_modules/nodemailer/node_modules/socks/node_modules/smart-buffer/typings/index.d.ts(351,5): error TS7010: 'skipTo', which lacks return-type annotation, implicitly has an 'any' return type.

FswDvibb9SAli1FUGUM_aGgIuEAq

缺少返回类型的注释,隐含地具有’any’返回类型。 tsconfig.json配置文件中noImplicitAny设置为false,该选项的意思是:在表达式和声明上有隐含的’any’类型时报错。

手机丢前必做的两件事

月盾

很多段子手都曾经发过《手机丢后必做的N件事》这类教程,比如:致电运营商挂失手机号;致电银行冻结手机网银;手机解绑支付宝;微信冻结账号;修改微博、微信、QQ等密码;找手机运营商补手机卡;等等等等,实际上,这类教程所要求的事情都不是必要的,真正要做到丢失后手机安全,只需要事前做两件事情:设置指纹密码和SIM卡密码。

以iPhone为例,一般都会设置“查找我的iPhone”功能,由于iPhone有GPS定位功能,小偷如果偷到手机不关机的话,会被GPS定位追踪,因此绝大多数小偷拿到手机后做的第一件事情往往是关机,这正好让指纹锁屏密码和SIM卡PIN码发挥最大作用:如果重新开机,必须要输入手机密码才能开机,同时,SIM卡处于锁定状态,需要输入PIN码才能正常通信,输错3次PIN码后就只能用PUK码来解锁,把SIM卡拔出来查到别的手机上,也需要输入SIM卡PIN码才能通讯,也就是说,不知道PIN码,这个SIM卡就无法接收短信和拨打电话,因此小偷就无法通过这个SIM卡来盗取用户的网银、支付宝、微信上的财产,甚至,小偷连这个手机的手机号码都无法获得,更不用说盗取微信和QQ密码了。

当然,丢失iPhone手机的用户还需要补SIM卡和设置远程抹除iPhone内容最安全,千万不要关闭“查找我的iPhone”功能,忽略那些以苹果名义发来的钓鱼邮件和短信。

以下是iPhone指纹解锁功能设置方法和iPhone的修改SIM卡密码的方法:

指纹密码

指纹天生就是一种个人认证工具,虽然人人都有指纹,但各不相同,据说,现在还没有发现两个指纹完全相同的人。随着年龄的增长,指纹的纹样不会发生变化。因此,使用指纹做为身份验证是非常理想的。

FnASLtfbNS8hUg2Vl1_qOnGtx7R3

iPhone指纹解锁功能设置

iPhone设置指纹密码的具体方法是:

第一步:依次进入iPhone的“设置”,然后再找到,并点击进入“Touch ID与密码”设置。

第二步:接下来需要输入一次四位数字的iPhone6手机密码,这个密码就是手机解锁密码,输入密码后,就可以进入iPhone指纹识别设置界面了。然后我们就可以开始录入指纹了,点击下方的“添加指纹”开始添加。

第三步:接下来我们按照提示开始录入指纹,请将需要设置指纹的手指放置在iPhone屏幕底部的指纹识别Home键上,按照提示放置上去,再移开手指,反复几次,等待指纹录入完成。

第四步:以上操作完成就,就可以成功完成一个手指的指纹录入,在返回的指纹设置中,已经可以看到有一个“指纹1”的记录了,如果还想录入多个指纹信息,继续点击下方的“添加指纹”,按照上面的步骤完成其他指纹信息录入即可。

iPhone最多支持设置5个指纹,可以同时录入自己的和自己亲人的,另外录入成功的指纹1和指纹2名称,还可以自行修改名称。指纹信息录入成功后,在iTunes Stor与App Store安装应用开启指纹识别即可。

SIM卡密码

SIM卡是(Subscriber Identity Module 客户识别模块)即用户身份识别卡,是手机的一张个人资料卡。SIM卡通常用来存储用户的电话号码、安全数据、通讯费用以及能让运营商判定用户是否是其公司客户的数据。当用户更换新的手机,可以直接插入旧手机原有的SIM卡,即可继续享受之前的各项手机服务。

SIM卡的PIN码(Personal Identification Number 个人识别码)主要用于保护SIM卡的安全性和隐私性,当手机重启、或者插拔SIM卡后,需要输入最正确的SIM卡PIN码才能使用SIM卡的通讯功能(拨打电话、收发短信等操作),当输入三次失败后,SIM卡则会被锁定,而需要输入PUK码(Personal Unlock Key 个人解锁码)进行解锁,PUK码共有10次输入机会,输错10次后,SIM卡会自动启动自毁程序,使SIM卡失效。

修改设置SIM卡PIN码的步骤是:设置 - 电话 - SIM卡PIN码,将其启用。修改PIN码要先输入原有PIN码,SIM卡的默认PIN码为0000或1234,如果都不对的话,可以输错3次,使用PUK码强制修改PIN码。

Fu-b7LZzy2dWpNEJU1btye-Xehqj

修改SIM卡PIN码

FtLIcdX2Zvb59fvDD9Jt8q4pTM99

修改SIM卡PIN码

PUK码由8位数字组成,这是用户无法更改的。一些SIM卡的PUK码是用户在购卡时随卡附带的,通常在卡套背后附有PUK码,也可以登录运营商的网上营业厅,在自助服务里通常会提供查询PUK码的功能。实在找不到的话,也可以致电SIM卡所属运营商的服务热线,客服人员也可以提供查询PUK码的服务。

SIM卡的PIN码有效地防止了盗用SIM卡的情况发生,使用户的正常通信得到了可靠保障。设置了PIN码之后,当用户手机丢失后,别人无法通过取出SIM卡插入另一部手机,来进行原SIM卡的通讯功能(拨打电话、收发短信等)。因此,建议所有手机用户都立刻启用SIM卡的PIN码。

Fp7LoSQoWDETlTBB3kFgLHg2VhV5

iPhone被偷

iPhone丢失前后的处理流程

1、手机的指纹锁屏(密码锁屏)和SIM卡PIN码应该同时启用,缺一不可。如果手机的SIM卡设置了PIN码,那么这个SIM卡插入另一个手机就需要PIN码解锁,重启手机也要PIN码解锁,不解锁无法进行通讯,这从一定程度上保证了手机通讯的安全性,SIM卡默认的PIN码通常是1234,建议手机用户都修改一下这个PIN码。锁屏密码不要使用简单密码,使用数字字母组合长密码,有条件的话建议选择支持指纹密码的iPhone,其使用体验远远优于普通的密码解锁。

2、开启“查找我的iPhone”功能。iCloud设置强密码。iCloud开通二次验证。

3、支付宝等应用启用指纹密码。

4、发现手机丢失后,立即打电话给移动运营商,挂失SIM卡。登录iCloud的“查找我的iPhone”功能,启用“丢失模式”。

5、登录appleid网站,将该手机从“受信任设备”中移除。

6、忽略那些以苹果名义发来的钓鱼邮件和短信。

7、如果手机确认无法找回,在“查找我的iPhone”里,使用“抹掉iPhone”功能。

8、申请一个新的SIM卡并启用。

总结:iPhone丢失后应该做什么?丢失前:设置指纹锁屏和SIM卡PIN码;开启“查找我的iPhone”功能。iCloud设置强密码。丢失后:打电话给移动运营商,挂失SIM卡。登录iCloud,启用“丢失模式”(无法找回的话就用“抹掉iPhone”)。电话冻结绑定的支付宝和网银。申请一个新的SIM卡并启用。

作者:月光 (williamlong) | 来自:月光博客

使用typescript开发nodejs的环境搭建(二)

月盾

完成了最基本的项目框架以后就是配置编辑器和编译选项,在没有特别配置的情况下,根目录下执行tsc会在ts后缀文件同级目录下生成js后缀的文件, 这样也没什么不可以,但是在编辑器列表中看着有点混乱,生成的js文件是不建议直接修改的,就算修改了下次修改ts文件编译后也会重置文件内容。 所以还是单独有个文件夹存放生成的js文件,这时就要配置tsconfig.json文件了。tsconfig.json文件可以通过tsc --init命令生成,自动生成内容比较简单,是可以直接使用的。 以下的配置是经过一些特别需求配置的,可以直接复制一下内容到tsconfig.json文件中,然后根据自己的需求加减内容。 其余参数可以参考:http://www.tslang.cn/docs/handbook/compiler-options.html

typescript编译配置

{
    "compilerOptions": {
        "module": "commonjs",//模块化规范
        "target": "es5",//生成js
        "noImplicitAny": true,//在表达式和声明上有隐含的'any'类型时报错
        "noImplicitReturns": true,//函数没有返回值提示
        "noFallthroughCasesInSwitch": true,//switch没有break提示
        "removeComments": true,//输出文件移除注释
        "noEmitOnError": true,//ts文件错误时不生成js
        "rootDir": "./",//需要编译的根目录
        "outDir": "./build",//编译文件输出目录
        "sourceMap": ture//是否生成.map文件,用于ts debug调试
    },
    "include": [
        "*/**/*.ts"
    ],
    "exclude": [
        //默认排除了node_modules
    ]    
}

为了能抛开在命令行中执行tsc命令,能直接通过vscode编辑器来编译,可以使用ctrl+shift+B快捷方式来编译ts文件。第一次使用会有提示

FrQK9PP8WHyeCNa6kM0Ie64IiN9Q

选择TypeScript - Watch-Mode,会在项目根目录下创建.vscode文件夹和tasks.json文件,内容如下:

vscode编译typescript配置

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "0.1.0",
    "command": "tsc",
    "isShellCommand": true,
    "args": ["-w", "-p", "."],
    "showOutput": "silent",
    "isWatching": true,
    "problemMatcher": "$tsc-watch"
}

这样就不用每次编译了,只要文件有修改就会自动编译

编译输出配置

为了将js文件输出到固定的目录,tsconfig.json中配置了"outDir": "./build"选项,会将ts文件编译到build目录下,得到了下面的文件目录结构:

.
├── app.ts
├── bin
│   └── www.ts
├── build
│   ├── app.js
│   ├── app.js.map
│   ├── bin
│   ├── public
│   ├── routes
│   ├── utils
│   └── views
├── node_modules
├── package.json
├── README.md
├── routes
│   ├── index.ts
│   ├── test.js
│   └── users.ts
├── test
├── tsconfig.json
├── typings
│   ├── globals
│   ├── index.d.ts
│   └── modules
├── typings.json
└── utils
    ├── cover.ts
    └── route.ts

build的目录下包含了public目录和views目录,这并不是编译生成的,因为最后执行的文件是js文件,为了与原来的express框架目录保持一致,手动将public和views等文件夹拷贝进去。

我喜欢的电影之《吸血鬼猎人D》

月盾

FhVgrAON10Nk97pm8lj1X-2Gt28n

在遥远的未来,由于最后一次世界大战破坏了所有科学文明,曾经君临天下的贵族,凌驾于人类的统治者吸血鬼面临着原因不明的物种衰退,但是他们仍然威胁着人类的生存,猎杀吸血鬼的职业猎人便应运而生。其中人与吸血鬼贵族的混血儿由于兼备了吸血鬼和人类两方的优点,理所当然的成为了最理想的猎人。然而不幸的是,这些被称为“丹皮尔”的混血儿,无法过正常人的生活,就连吸血鬼也不欢迎他们。在这群孤独的人中有一位英俊的佼佼者,他就是吸血鬼猎人D。

FhtzqbmnV5wTdpyM85eMX6lDMfVw

故事发生在西历12090年,埃尔邦家族的小姐夏洛特被吸血鬼贵族玛埃尔从家中绑架,于是埃尔邦老爷雇佣了远近闻名,连吸血鬼贵族也害怕的第一吸血鬼猎人D和另一组猎人玛克斯兄妹去救回女儿。

外表高贵俊美,一身漆黑,骑着黑马的“D”,与玛克斯兄妹一路追踪驾马车逃走的吸血鬼。途中D和玛克斯兄妹为争夺奖金发生激斗,并且得知夏洛特并非被绑架,而是爱上了吸血鬼玛埃尔,但他们的爱不被接受,所以他们打算到没有人类和吸血鬼贵族的理想国度“夜之都”去过平静的生活。D受到贵族三杰“影子使者”、“千面人”和“狼人”的阻击,关键时刻D的“阳光症”发作,玛克斯兄妹中的雷拉及时伸出援手,他们约定两人中不管谁先死,另一个一定要到墓上献花。吸血鬼玛埃尔的目的地是让吸血鬼都闻风丧胆,被称为“喋血伯爵夫人”卡蜜拉的旧领地切迪城堡。卡蜜拉称自己理解他们,愿意帮助他们离开纷乱的世界前往理想的国度,并向他们展示了城堡内的飞船。

Fv8PiQ_0oQ3e49g843uiXhZBA2tL

一切都看似顺利,但玛埃尔和夏洛特没有想到卡蜜拉邀请他们来此只是一个险恶的阴谋,卡蜜拉为了复活需要夏洛特的鲜血。一番恶斗后,D彻底消灭了卡蜜拉,被吸血鬼洗礼的夏洛特则昏死在吸血鬼玛埃尔的怀中,等待新的生命。城堡在主人消失后开始崩溃,玛埃尔抱着夏洛特乘上飞船飞上蓝天,飞向他们梦想的地方。很多年过去了,在雷拉的葬礼上,她的小孙女看到远处的黑衣猎人,D遵守了和雷拉的约定。

Mongodb和mongoose模糊查询

月盾

需求说明:在mongdb中使用模糊查询,就像sql中的like查询,在where条件中使用模糊匹配,当然最重要的是需要模糊查询的字符串是动态传入的

以一篇文章为例,content字段为文章内容,我们要查询文章内容中包含Nodejs关键字的文章

mongodb中查询

select * from articles where content like '%Nodejs%';
db.articles.find( { content: /Nodejs/i } )
Article.find({ content: /Nodejs/i}, function (err, docs) {});

这种写法是一种简写操作,需要注意的是不能带双引号,带了双引号就成了字符串。但是这种写法是用/包裹了一个字符串类型的关键字,在实际程序是动态传入的,比如:

var a="Nodejs" ;
Article.find({ content: /a/i}, function (err, docs) {});

此时会把"a"当做要查询的关键字而不是"Nodejs",所以这时还是需要用到完整的查询方式,就是使用$regex关键字,还有$options选项

var text = 'Nodejs';//动态传入的变量
Article.find({ content: { $regex: text, $options: 'i' }}, function (err, docs) {});

$options选项值:

  • i 大小写不敏感
  • m $regex包含正则^,$符号的表达式
  • x 忽略空格
  • s 允许逗点匹配所有字符串

其实$regex就是正则表达式的写法

查找数组中的字段:

contacts:{
    [
        {
            address: "address1",
            name: "张三"
        },
        {
            address: "address2",
            name: "李四"
        },
        .....
    ]
}

可以通过db.collection.find({'contacts.name':{$regex:'张'}})获得

Mongodb和mongoose聚合查询

月盾

mongdb查询某一字段sum值

需求说明:articles有一个字段pv记录了该文章的访问量,现在要统计所有文章访问量,类似于sql中的sum统计

mongodb中查询

select sum(pv) from articles;
db.articles.aggregate([{$group:{_id:null,pv:{$sum:"$pv"}}}]);
结果:{ "_id" : null, "pv" : 2 }

select sum(pv) from articles where createDate <= '2016-10-20';
db.articles.aggregate([{$match:{createDate:{$lte:"2016-10-20"}}},{$group:{_id:null,pv:{$sum:"$pv"}}}]);
结果:{ "_id" : null, "pv" : 9 }

select sum(pv) from articles where category = 'Nodejs';
db.articles.aggregate([{$match:{category:"Nodejs"}},{$group:{_id:null,pv:{$sum:"$pv"}}}]);
结果:{ "_id" : null, "pv" : 7 }

需要注意$match$group的顺序,反了是不行的,因为这是Aggregation Pipeline(管道流)

mongoose实现方式,与上面sql的顺序对应:

Article.aggregate({ $group: { _id: null, pvCount: { $sum: '$pv' }}}, function(err, doc) {
    console.log("1", doc);
});
Article.aggregate([{$match:{createDate:{$lte:"2016-10-20"}}},{$group:{_id:null, pvCount:{$sum:"$pv"}}}], function(err, doc) {
    console.log("2", doc);
});
Article.aggregate([{$match:{category:"Nodejs"}},{$group:{_id:null, pvCount:{$sum:"$pv"}}}], function(err, doc) {
    console.log("3", doc);
});
1 [ { _id: null, pv: 25 } ]
2 [ { _id: null, pv: 9 } ]
3 [ { _id: null, pv: 7 } ]

参考文档: