记一次MongoDB慢查询

项目刚上线,流量突然变大,随之而来的是不断的异常日志,经排查,发现有一处的 Mongo 查询为 9 秒多。

使用 Profiling

设置

db.setProfilingLevel(level, slowms)

level 级别,0 : 关闭不收任何数据,1 : 收集慢查询,默认为 100 ms,2 : 收集所有数据。
slowms 当超过时,收集数据。

因为 profile 会影响到一定的性能,所以推荐 level 为 1,我这边设为 1 500。

返回:
was 当前的 profiling 等级
slowms 记录的毫秒数

确认配置

db.getProfilingStatus()

其他配置方式

  1. 在 MongoDB 启动的时候设置: mongod --profile 1 --slowms 500
  2. 在配置文件中:
    1
    2
    profile = 1
    slowms = 200

重置

profile 默认的大小为 1 MB,如果需要更大的空间,可以自己进行配置。

1
2
3
4
db.setProfilingLevel(0)
db.system.profile.drop()
db.createCollection( "system.profile", { capped: true, size:4000000 } )
db.setProfilingLevel(1)

其他

profile 和 oplog 一样,都是 capped collection,即在一定大小内,会将新的数据覆盖旧的数据。详见

分析

查询

db.system.profile.find().pretty()
查询的过滤条件和普通查询一致,比如:

1
2
db.system.profile.find( { op: { $ne : ‘command‘ } }).pretty()
db.system.profile.find().sort({ millis: {gte: 1000 }}).pretty()

返回结果,具体可以查看 Database Profiler Output

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
{
"op" : "update", //操作,有 command,count,distinct,geoNear 等
"ns" : "test.test1", //操作的表
"query" : {}, //查询
"updateobj" : {}, //在更新操作时会有该字段,超过 50 kb 时,其余会用 ... 代替
"keysExamined" : 9998, //之前为 nscanned 字段,代表数据操作时,扫描过的 key
"locks" : { //锁相关信息
"Global" : { //全局锁
"acquireCount" : { //操作时请求获取锁的次数
"r" : NumberLong(79),
"w" : NumberLong(79)
}
},
"Database" : {
"acquireCount" : {
"w" : NumberLong(79)
}
},
"Collection" : { //集合锁
"acquireCount" : {
"w" : NumberLong(79)
}
}
},
"millis" : 104,
"ts" : ISODate("2017-07-27T14:20:24.117+08:00"),
"client" : "",
"user" : ""
}

注:锁相关资料可以查看 FAQ

优化

  1. 若为查询语句,可以先观察 keysExamined 字段,如果过大,可以适当调整当前集合的索引。
  2. 聚合查询,不知道是业务较复杂,还是当初设计时候未留有较大的空间,较多的使用 aggregation的查询会使用到 $lookup,可以适量通过冗余,以空间换时间不是个坏方法,另外在 pipe 的时候尽量只提取需要的字段。
  3. 负载均衡,读写分离。
  4. 有一个笨方法,在提交代码之前,可以在本地使用 db.xxx.explain(),先分析性能,再进行优化提交。

这篇文章仅仅是基础,在上线之后的更多经验,愿以后可以慢慢分享。

前端路线

写给某辉看的 路线。

css

  1. 熟悉常用的css. http://www.w3school.com.cn/css/index.asp
  2. 熟悉常用的布局和flexBox http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html
  3. 了解 scss或者 less http://www.ruanyifeng.com/blog/2012/06/sass.html
  4. 了解bootstrap

JavaScript

明白语法,熟悉dom和bom,能写一些动画或者特效

  1. 教程 看完基本能入门 (看完1-8章(web API)就差不多了) http://javascript.ruanyifeng.com/
  2. 了解ES6(作用域,Promise,Class等 http://es6.ruanyifeng.com/

HTML

  1. 了解一些HTML5 的API
  2. 熟悉Ajax,Fetch API
  3. 能够写一些pc页面,移动页面,响应式的页面

算法

了解一些排序算法(冒泡,快速排序等),这是最常见的
其他的可以以后再学习

框架

Vue.js 或者 React.js 学习一个(也可以都学,vue相对简单,)

  1. 了解如何组件化开发
  2. 熟悉生命周期
  3. 熟悉路由
  4. 了解渲染机制
  5. 服务端渲染(可以跳过)
    vue教程:https://cn.vuejs.org/v2/guide/ (中文的
    了解一些UI框架,比如 基于Vue 的 Element,基于React 的Antd
    学完之后,写一个 后面提到的 社区的客户端就差不多了

Node.js

  1. 入门教程:https://cnodejs.org/topic/581b0c4ebb9452c9052e7acb 这是一个完成的博客系统,可以学习Node.js ,也可以复习一下前端
  2. express.js (中文文档,http://www.expressjs.com.cn/guide/routing.html
    只需了解一下,比如简单的RESTFUL 风格的api, 比如文件服务器
  3. 一些爬虫?简单的页面数据爬取 (可以跳过

工具类

  1. 了解Webpack ,能够搭建babel转译,server就可以,不用了解如何运作的
  2. 了解scss或者less

一些项目

  1. 社区的客户端,比如v2ex,cnodejs的社区,有提供api,可以写一个客户端
    https://cnodejs.org/topic/565c4473d0bc14ae279399fe
    可以照着样子来,可以抄代码。
  2. 移动端的页面,可以写一些在移动端的页面,像天猫,京东里面的活动页面
  3. 上面提到的 博客。

其他

  1. vue 的vuex,react的Redux
  2. 微信小程序
  3. 自己写基础组件库
  4. 算法(有时间就多学
  5. 数据库的简单的增删改查(Mysql,MongoDB等
  6. Hero 搭建一个主页
  7. 基于websocket(socket.io) 的聊天室,在线五子棋等

Meteor.js快速接触

Meteor

基于Node.js的一个框架。特点:实时数据(基于Websocket),客户端和服务端可以共享代码,很重。

本文主要讲几个模块:Collection,Methods,Pub/Sub,DDP
适合人群: 未接触过Meteor或接触不超过一个星期

开始

安装 Meteor

$ curl https://install.meteor.com | sh
创建新的应用
meteor create [APPNAME]
安装包
meteor npm install

Meteor的文件结构

也是限制地比较死,最外层的文件结构
/client 客户端代码
/imports 外部引用代码
/server 服务端代码
/lib 一些全局的可以在这里声明

文件加载顺序

  • 同一层级下,以文件名的字母顺序载入,有时候需要为了这个改文件名。。
  • 先载入/lib下的文件

Core

isClient, isServer来判断时客户端还是服务端,可以分别开来执行
isDevelopment, isProduction来判断开发环境还是线上环境
settings配置文件
这些都是全局的,使用类似
Meteor.settingMeteor.isClient

Collection

一般来说,Collection的声明是共用的,所以不在client也不再server中。
Meteor.js内部集成了MongoDB,好像目前也只支持MongoDB,若要结合其他,可以发起HTTP,DDP,RPC等(后续讲到)
创建一个集合,注意:这里的Mongo是全局变量
Post = new Mongo.Collection('post')
在客户端和服务端中,都有这个概念,虽然定义的语句相同,但是实际的意义却是不同。它们通过(Pub/Sub)来对数据进行同步。
其CURD的操作和MongoDB的语法一致,比如:

1
2
3
4
> db.posts.insert({title: "A new post"});

> db.posts.find();
{ "_id": ObjectId(".."), "title" : "A new post"};

客户端集合
客户端的集合更加有趣。当你在客户端申明 Posts = new Mongo.Collection(‘posts’); 你实际上是创建了一个本地的,在浏览器缓存中的真实的 Mongo 集合。 当我们说客户端集合被”缓存”是指它保存了你数据的一个子集,而且对这些数据提供了十分快速的访问。

有一点我们必须要明白,因为这是 Meteor 工作的一个基础: 通常说来,客户端的集合的数据是你 Mongo 数据库的所有数据的一个子集(毕竟我们不会想把整个数据库的数据全传到客户端来)。

第二,那些数据是被存储在浏览器内存中的,也就是说访问这些数据几乎不需要时间,不像去服务器访问 Posts.find() 那样需要等待,因为数据事实上已经载入了。

在 Meteor 中,find() 返回值是一个游标。游标是一种从动数据源。如果你想输出内容,你可以对游标使用 fetch() 来把游标转换成数组。

发布与订阅

发布与订阅用语客户端与服务端的数据交互,基本上是实时性比较高的数据才使用。
简单的代码如下:

1
2
3
4
5
6
// 在服务器端
Meteor.publish('posts', function() {
return Posts.find({flagged: false});
});
// 在客户端
Meteor.subscribe('posts', 'bob-smith');

如果需要使用多个集合,可以选择使用publish-composite
使用命令 meteor add reywood:publish-composite安装,一个简单的例子:

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
Meteor.publishComposite("userCommentsComposite", function(userId) {
return {
find: function() {
// 根据用户 Id 查询出该用户所有的评论集合,注意这里是集合
return Comment.collection.find({userId: userId});
},
children: [
{
find: function(comment) {
// 遍历查询出来的评论集合中所有信息,根据每一条评论关联的文章 Id 查询文章数据
console.log("Get activityId by commantId : commandId =", comment._id, " commandLinkObjectId =", comment.linkedObjectId);
return Activities.find({_id: comment.linkedObjectId});
},
children: [
{
find: function(activity) {
// 查询该文章所有的点赞信息
console.log("Get like info by activityId :", activity._id);
return Like.collection.find({linkedObjectId: activity._id});
}
},
{
find: function(activity) {
// 查询该文章的作者信息
console.log("Get activity user info by activity.userId :", activity.userId);
return Meteor.users.find({_id: activity.userId});
}
}
]
}
]
}
});

其具体实现有点类似,Promise.then(data=>{})的实现,也是数据一层层传递给children

Methods

Meteor不像普通的web框架是通过req/res的方式来操作数据,也就没有最近流行的RESTFUL的风格,客户端请求服务端的数据,可以通过Methods来实现,可以理解为Req/Res。
在服务端和客户端都可以

创建Methods

1
2
3
4
5
6
7
8
Meteor.methods({
say: () => {

}
walk: () => {

}
});

调用Methods

客户端和服务端都可以调用Methods,只是服务端只能调用服务端的,而客户端可以都可以,并优先调用服务端的(即同时存在服务端声明的Methods和客户端声明的Methods时,调用的时候为服务端的)。

1
2
3
4
5
//Method.call 或者 Method.apply ,区别和js类似,就是参数的不同。
Meteor.apply("test",[1,2],function(err,res){
if(err){
}
});

Meteor.methods中函数的上下文对象 this

  1. this.userId
    调用这个函数的用户的id ,类型:string。如果用户没有登录,值为null。 该属性在 Server的Meteor.methods和Client的Meteor.methods都存在。用官方的描述就是Anywhere。 这个属性依赖Meteor的accounts 模块。
  2. this.unblock()
    On the server, methods from a given client run one at a time. The N+1th invocation from a client won’t start until the Nth invocation returns. However, you can change this by calling this.unblock. This will allow the N+1th invocation to start running in a new fiber.

DDP

这个类只有个方法 DDP.connect(url)。DDP可以认为是一个类似RPC的实现,对于不同的开发语言,也有对应的包。

1
2
3
4
5
6
7
8
var remote =  DDP.connect("http://localhost:3010");//链接远程服务器
remote.methods({
say:function(){ console.log("hello world")}
}) //这就是定义远程调用的存根。

remote.call("say","deputy",function(err,res){
console.log(res)
}) //调用远程方法

其他模块

check

check是一个类型验证的库,如
check(userid, String);

accounts

直接使用的账户功能,可以配合Role,简单地实现权限管理。

HTTP

meteor内置了HTTP的包,使用
Http.call(method,url, [options], [asyncCallback])

小结

Meteor.js这个框架很重,虽然提供了很多功能,但是写起来比较不灵活。
适合一个人干完前端和服务端的工作。
熟悉koa或者express的人可能在刚接触的时候比较难以接受,希望此文能有一定的帮助

参考:
Meteor 组合订阅包 publish-composite 使用 http://www.mycode.net.cn/language/javascript/1195.html
Discover Meteor http://wiki.jikexueyuan.com/project/discover-meteor/publish-and-subscribe.html
实时开发框架Meteor API解读系列<一> Meteor.methods http://blog.csdn.net/a6383277/article/details/18098929

Redis在用户数据统计中的简单应用

前言

在项目的基本功能开发完成之后,有一个对用户的数据的统计需求。用于分析和查看应用的相关数据。正好之前看到过有用Redis统计的例子,便开始慢慢摸索。(可能本篇文章内的并不是最佳实践,总要踩坑,优化嘛。。。)

Redis

Redis是一个优秀的K-V数据库,Redis的所有数据都是存放在内存中的,

Redis是用C语言实现的,一般来说C语言实现的程序“距离”操作系统更近,执行速度相对会更快。

Redis使用了单线程架构,预防了多线程可能产生的竞争问题。

Redis有一些非常实用的数据结构,如:String,Set,Hash,List,Zset,方便使用。

统计的项目需求

  • 每日新注册用户
  • 每日登录用户
  • 每个时段的在线人数
  • 平均在线
  • 最高在线
  • 充值人数
  • 新增充值人数
  • 充值金额
  • N日留存
  • N日ARPU
    等。。

Redis设计

基于上面的一些需求,在数据类型上可以分成两类

  1. 累计类型
  2. 新增数据(涉及到集合的运算)
    在数据统计上,也可以分成两类
  3. 每日的数据
  4. 从开始起就统计的数据

Key设计

于是可以对键进行设计,Redis是内存数据库,所以对于键的长度也不能忽视,可以采用缩写

我这里采用 [areaName]:[date]:[type]的格式

1
2
3
4
5
6
7
8
9
10
11
12
13
/*数据类型*/
const ValueType = {
'newPlayer': 'np',
'chargeAmount': 'cA',...
}
/*生成Redis Key*/
function initRedisKey(area, type, time) {
let date = 'all'
if(time) {
date = moment().format('YYYYMMDD')
}
return `${area}:${date}:${ValueType[type]}`
}

Value设计

  1. 对于累加类型的数据,可以使用String,比如:每日消耗的金币,每日注册人数等

    1
    2
    3
    function *incrbyKey(key, num) {
    cache.incrby(key, num)
    }
  2. 对于需要计算唯一性的数据,比如登录用户,充值人数等,不能继续使用 String,可以选用的有Set,Hashbitmap等。
    考虑到平均下来每个服务器分担的用户可能只在万级左右,并且在线人数统计要频繁地统计长度,最后选择了使Set来对这些数据统计。

    1
    2
    3
    cache.sadd(uid, uid) //用户上线
    cache.srem(uid) //用户下线
    cache.scard(key)//这个key的长度

在计算每小时平均在线的时候,使用String来存储,后续只要对字符串进行格式化,即可方便地计算出每小时平均在线,每小时最高在线,整天的数据。

1
2
3
4
5
6
7
8
9
function *calOnlinePlayerNum() {
let hour = New Date().getHours()
let num = yield cache.scard(key1)
cache.append(key2, `-${hour}:${num}`)
}

schedule(
co(calOnlinePlayerNum)
)

  1. 新增的数据计算
    这一类要记录的数据可能比较多,因为涉及到集合的交集,并集计算,一开始想到的就是Set,又考虑到内存的问题,便将目光转移到bitmap上。
    bitmap有点hash位的感觉,如1000 0001,就可以表示uid为8,0 的用户今天登录过。
    如果uid时1000000开始的,可以对其切割(减去初始uid)。在第一次setbit的时候,Redis要对其开一个较大的内存,可能会影响性能。

比如记录留存率:
记录每天的新注册的id。[area]:[date]:[type]
记录每天的登录用户id。[area]:[date]:[type]

A日的N日留存率=(A日之后第N天登录过游戏的并且这些用户是在A日新登录的用户数)/(A日新登录的玩家用户数)

1
2
3
4
5
6
cache.setbit(key, uid, 1)
//====================//
cache.bitop('and' , destKey, key1, key2) //计算交集
let a = cache.bitcount(key1) || 1
let b = cache.bitcount(destKey) // 交集的长度
per = b/a

持久化数据

虽然Redis本身也有持久化的功能,但显然这是不划算的,对于每天统计的保存在Redis中的数据,可能有些数据第二天就不需要继续存在Redis里面了,所以,可以选择数据库,将数据持久化到磁盘。
对于每天的数据,可以在统计完之后就删除,
对于N天的需要统计计算的数据,可以在最后一天进行删除操作。
本文使用MongoDB,MongoDB的一个优势就是对JSON的友好。在次日对Redis中的数据进行一定的分类,计算,对数据进行持久化。以date为索引,可以方便后台的以日期为单位进行查询。

小节

Redis因其提供的数据结构,使得使用起来非常方便,对于不同的需求,可以有多种的实现方式。
但是对于性能和内存的占用还是要有比较多的考虑,
本文只是结合一些资料的探索,可能还存在一些不足的地方,希望在实践中能够进行优化。

参考

  • 《Redis开发与运维》
  • 《Redis实战》

附: 常见的Redis的操作的复杂度

string

set o(1)

get o(1)

del o(n)

set

sadd o(1)

sismember o(1)

srem o(n) n is the number of members to be removed

smembers O(N) where N is the set cardinality

scard o(1)

zset

zadd O(log(N)) for each item added, where N is the number of elements in the sorted set.

zrange
O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.

zrem
O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.
zrangebyscore
O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).

hash

hset o(1)

hget o(1)

bitmap

bitcount o(n)

bitop o(n)

setbit o(1)

getbit o(1)

JavaScript Puzzlers!

JavaScript Puzzlers!

or: do you really know JavaScript?

(or: how insane do you think JavaScript actually is?)

let’s start

下面是我做题后的一个总结和归纳(可能解释不是很标准)

1

1
2
What is the result of this expression? (or multiple ones)
["1", "2", "3"].map(parseInt)

what you actually get is [1, NaN, NaN] because parseInt takes two parameters (val, radix) and map passes 3 (element, index, array)

2

1
2
What is the result of this expression? (or multiple ones)
[typeof null, null instanceof Object]

答案: [“object”, false]
因为历史遗留问题,typeof null 返回object.
instanceof运算符可以用来判断某个构造函数的prototype属性所指向的對象是否存在于另外一个要检测对象的原型链上。
我们可以通过obj.proto 来进行修改 (自己写的对象
null 是没有prototype 的属性

3

1
2
What is the result of this expression? (or multiple ones)
[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ]

答案: 9, error (当然,会直接报错..)
reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始合并,最终为一个值。
该方法的参数是一个callback, 一个[作为第一次调用callback的参数],
如果arr为空,则直接报错,TypeError
另: reduce可用于数组求和,数组扁平化等.

4

1
2
3
What is the result of this expression? (or multiple ones)
var val = 'smtg';
console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing');

答案: Something
+ 的优先级要高,所以来不及三目运算, 前面这段就相当于一个变量, 所以在计算三目的时候,返回’Something’

5

1
2
3
4
5
6
7
8
9
10
What is the result of this expression? (or multiple ones)
var name = 'World!';
(function () {
if (typeof name === 'undefined') {
var name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();

答案: Goodbye Jack
变量提升,所以在第一句的if时, name 是为undefined,直到var name 才被定义

6

1
2
3
4
5
6
7
8
What is the result of this expression? (or multiple ones)
var END = Math.pow(2, 53);
var START = END - 100;
var count = 0;
for (var i = START; i <= END; i++) {
count++;
}
console.log(count);

别直接在控制台跑这段代码(别问我为什么直到 - -$)
摘自 ruanyifeng(链接)

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
根据国际标准IEEE 754,JavaScript浮点数的64个二进制位,从最左边开始,是这样组成的。

1位:符号位,0表示正数,1表示负数
2位到第12位:储存指数部分
13位到第64位:储存小数部分(即有效数字)
符号位决定了一个数的正负,指数部分决定了数值的大小,小数部分决定了数值的精度。

IEEE 754规定,有效数字第一位默认总是1,不保存在64位浮点数之中。也就是说,有效数字总是1.xx...xx的形式,其中xx..xx的部分保存在64位浮点数之中,最长可能为52位。因此,JavaScript提供的有效数字最长为53个二进制位。

(-1)^符号位 * 1.xx...xx * 2^指数位
上面公式是一个数在JavaScript内部实际的表现形式。

精度最多只能到53个二进制位,这意味着,绝对值小于253次方的整数,即-(2^53-1)到2^53-1,都可以精确表示。

Math.pow(2, 53)
// 9007199254740992

Math.pow(2, 53) + 1
// 9007199254740992

Math.pow(2, 53) + 2
// 9007199254740994

Math.pow(2, 53) + 3
// 9007199254740996

Math.pow(2, 53) + 4
// 9007199254740996
从上面示例可以看到,大于253次方以后,整数运算的结果开始出现错误。所以,大于等于253次方的数值,都无法保持精度。

Math.pow(2, 53)
// 9007199254740992

// 多出的三个有效数字,将无法保存
9007199254740992111
// 9007199254740992000
上面示例表明,大于253次方以后,多出来的有效数字(最后三位的111)都会无法保存,变成0

7

1
2
3
4
What is the result of this expression? (or multiple ones)
var ary = [0,1,2];
ary[10] = 10;
ary.filter(function(x) { return x === undefined;});

答案: []
filter() 方法使用指定的函数测试所有元素,并创建一个包含所有通过测试的元素的新数组。
即所谓的过滤函数.
执行到第二句的时候 , ary = [0, 1, 2, undefined * 7, 10]
Array.prototype.filter is not invoked for the missing elements
另:

1
2
3
4
var ary = [0,1,2, undefined];
ary[10] = 10;
ary.filter(function(x) { return x === undefined;});
// --> [undefined]

8

1
2
3
4
5
6
What is the result of this expression? (or multiple ones)
var two = 0.2
var one = 0.1
var eight = 0.8
var six = 0.6
[two - one == one, eight - six == two]

9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function showCase(value) {
switch(value) {
case 'A':
console.log('Case A');
break;
case 'B':
console.log('Case B');
break;
case undefined:
console.log('undefined');
break;
default:
console.log('Do not know!');
}
}
showCase(new String('A'));

答案: Do not know!
switch uses === internally and new String(x) !== x

10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
What is the result of this expression? (or multiple ones)
function showCase2(value) {
switch(value) {
case 'A':
console.log('Case A');
break;
case 'B':
console.log('Case B');
break;
case undefined:
console.log('undefined');
break;
default:
console.log('Do not know!');
}
}
showCase2(String('A'));

答案: Case A
String(x) does not create an object but does return a string, i.e. typeof String(1) === “string”

11

1
2
3
4
5
6
7
8
9
10
11
12
What is the result of this expression? (or multiple ones)
function isOdd(num) {
return num % 2 == 1;
}
function isEven(num) {
return num % 2 == 0;
}
function isSane(num) {
return isEven(num) || isOdd(num);
}
var values = [7, 4, '13', -9, Infinity];
values.map(isSane);

答案: [true, true, true, false, false]
Infinity % 2 gives NaN, -9 % 2 gives -1 (modulo operator keeps sign so it’s result is only reliable compared to 0)

12

1
2
3
4
5
What is the result of this expression? (or multiple ones)

parseInt(3, 8)
parseInt(3, 2)
parseInt(3, 0)

答案: 3, NaN, 3

13

1
2
'5' + 3
'5' - 3

答案: “53”, 2
javascript 的隐式转换,

14

1
2
What is the result of this expression? (or multiple ones)
1 + - + + + - + 1

答案: 2

15

1
2
3
4
What is the result of this expression? (or multiple ones)
var ary = Array(3);
ary[0]=2
ary.map(function(elem) { return '1'; });

答案: [“1”, undefined × 2]
上面提到过,对为初始化的不起作用

16

1
2
3
4
5
6
7
8
9
10
What is the result of this expression? (or multiple ones)
function sidEffecting(ary) {
ary[0] = ary[2];
}
function bar(a,b,c) {
c = 10;
sidEffecting(arguments);
return a + b + c;
}
bar(1,1,1)

答案: 21
arguments 对象并不是一个真正的Array。它类似于数组,但没有数组所特有的属性和方法,除了 length.
in javascript variables are tied to the arguments object so changing the variables changes arguments and changing arguments changes the local variables even when they are not in the same scope.
在js中,变量和arguments 是绑定在一起的,所以改变了变量就是改变了arguments,改变了arguments 改变了当前的变量 ,及时不是在相同的作用域内.

17

1
2
3
4
What is the result of this expression? (or multiple ones)
var a = 111111111111111110000,
b = 1111;
a + b;

同 第六题

18

1
2
3
What is the result of this expression? (or multiple ones)
var x = [].reverse;
x();

答案: VM1357:2 Uncaught TypeError: Array.prototype.reverse called on null or undefined(…)
暂时不知道怎么解释.

19

1
Number.MIN_VALUE > 0

答案: false

在调试工具中为 5e-324, Number.MIN_VALUE 是最小的大于0的数

20

1
2
What is the result of this expression? (or multiple ones)
[1 < 2 < 3, 3 < 2 < 1]

答案: [true, true]

相同运算符优先级相等,故原来的式子相当于 [(1 < 2) < 3, (3 < 2) < 1]
其中存在隐式转换 true -> 1, false -> 0

21

1
2
3
What is the result of this expression? (or multiple ones)
// the most classic wtf
2 == [[[2]]]

答案: true
如果我们这样赋值 var a = [[[2]]] 相当于 a[0][0] = [2] a[0][0][0] = 2
== 比较的是值 ,故返回 true

22

1
2
3
4
What is the result of this expression? (or multiple ones)
3.toString()
3..toString()
3...toString()

答案: error, “3”, error

摘自stackoverflow: That is because when JavaScript sees a period after an integer, it assumes the value after it will be its decimals. Because of that, simply using 5.toString() will make JavaScript find toString where it expected numbers.
当javascript遇到整数后面带有小数点,为认为后面的是这个数的小数部分,所以3.toString() 当然 当申明一个变量a,赋值为3的时候,a.toString() 是可行的

23

1
2
3
4
5
6
What is the result of this expression? (or multiple ones)
(function(){
var x = y = 1;
})();
console.log(y);
console.log(x);

答案: 1, error

这题在考作用域的时候经常考到, var x = y = 1; 相当于 y = 1; var x = y; 所以在这里y 是全局变量,而x只在这个函数里面,
所以log(y) 为 1, x 就是is not defined

24

1
2
3
4
5
What is the result of this expression? (or multiple ones)
var a = /123/,
b = /123/;
a == b
a === b

答案: false, false

a,b 是正则表达式, 我们使用typeof 会发现它们都是object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
摘自网络:
先说 ===,这个比较简单。下面的规则用来判断两个值是否===相等:
1、如果类型不同,就[不相等]
2、如果两个都是数值,并且是同一个值,那么[相等];(!例外)的是,如果其中至少一个是NaN,那么[不相等]。(判断一个值是否是NaN,只能用isNaN()来判断)
3、如果两个都是字符串,每个位置的字符都一样,那么[相等];否则[不相等]。
4、如果两个值都是true,或者都是false,那么[相等]。
5、如果两个值都引用同一个对象或函数,那么[相等];否则[不相等]。
6、如果两个值都是null,或者都是undefined,那么[相等]。
再说 ==,根据以下规则:
1、如果两个值类型相同,进行 === 比较。
2、如果两个值类型不同,他们可能相等。根据下面规则进行类型转换再比较:
a、如果一个是null、一个是undefined,那么[相等]。
b、如果一个是字符串,一个是数值,把字符串转换成数值再进行比较。
c、如果任一值是 true,把它转换成 1 再比较;如果任一值是 false,把它转换成 0 再比较。
d、如果一个是对象,另一个是数值或字符串,把对象转换成基础类型的值再比较。对象转换成基础类型,利用它的toString或者valueOf方法。 js核心内置类,会尝试valueOf先于toString;例外的是DateDate利用的是toString转换。非js核心的对象,令说(比较麻 烦,我也不大懂)
e、任何其他组合,都[不相等]。

25

1
2
3
4
5
6
7
8
What is the result of this expression? (or multiple ones)
var a = [1, 2, 3],
b = [1, 2, 3],
c = [1, 2, 4]
a == b
a === b
a > c
a < c

答案: false, false, false, true

解释同上, 数组在比较的时候,是按照字典序比较的, 故第四条的值为true,
另: 只要一次分出大小,直接返回结果,不看数组的大小,比如:

1
2
3
var a = [3];
var b = [1, 2];
a > b // log true

26

1
2
3
What is the result of this expression? (or multiple ones)
var a = {}, b = Object.prototype;
[a.prototype === b, Object.getPrototypeOf(a) === b]

答案: [false, true]
Object.getPrototypeOf() 方法返回指定对象的原型(也就是该对象内部属性[[Prototype]]的值)。
a 是一个Object, Object中 除了Functions有prototype属性之外都没有, 所以 a.prototype = undefined;
Every Object instead has an internal property accessible via Object.getPrototypeOf

27

1
2
3
4
What is the result of this expression? (or multiple ones)
function f() {}
var a = f.prototype, b = Object.getPrototypeOf(f);
a === b

答案: false
f.prototype is the object that will become the parent of any objects created with new f while Object.getPrototypeOf returns the parent in the inheritance hierarchy.
在控制台中,a 是一个Object, b 是一个 function

28

1
2
3
4
5
What is the result of this expression? (or multiple ones)
function foo() { }
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name]

答案: [“foo”, “foo”]
function.name 返回函数的名称.
Writable no
Enumerable no
Configurable yes
Note that in non-standard, pre-ES6 implementations the configurable attribute was false as well.
另: 当函数使用new Function(…) 或者 直接Function(…) 来创建的时候,其name属性为””, 如:

1
2
3
4
5
6
7
var f = function() {};
var object = {
someMethod: function() {}
};

console.log(f.name == ''); // true
console.log(object.someMethod.name == ''); // also true

( 更多)

29

1
2
What is the result of this expression? (or multiple ones)
"1 2 3".replace(/\d/g, parseInt)

答案: “1 NaN 3”
String.prototype.replace invokes the callback function with multiple arguments where the first is the match, then there is one argument for each capturing group, then there is the offset of the matched substring and finally the original string itself. so parseInt will be invoked with arguments [1, 0], [2, 2], [3, 4].

replace() 方法使用一个替换值(replacement)替换掉一个匹配模式(pattern)在原字符串中某些或所有的匹配项,并返回替换后的字符串。这个替换模式可以是字符串或者RegExp(正则表达式),替换值可以是一个字符串或者一个函数。
str.replace(regexp|substr, newSubStr|function[, flags])

1
2
3
4
5
6
7
8
9
参数
regexp
一个 RegExp 对象。该正则所匹配的内容会被第二个参数的返回值替换掉。
substr
一个要被 newSubStr 替换的字符串。
newSubStr
替换掉第一个参数在原字符串中的匹配部分。该字符串中可以内插一些特殊的变量名。
function
一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。该函数的参数描述请参考 指定一个函数作为参数 小节。

另:

1
2
"1 'av' 3".replace(/\d/g,1) //-->"1 'av' 1"
"1 2 3".replace(/\d/g,1) //--> "1 1 1"

( 更多)

30

1
2
3
4
5
6
7
What is the result of this expression? (or multiple ones)
function f() {}
var parent = Object.getPrototypeOf(f);
f.name // ?
parent.name // ?
typeof eval(f.name) // ?
typeof eval(parent.name) // ?

答案: “f”, “Empty”, “function”, error
The function prototype object is defined somewhere, has a name, can be invoked, but it’s not in the current scope.

31

1
2
3
What is the result of this expression? (or multiple ones)
var lowerCaseOnly = /^[a-z]+$/;
[lowerCaseOnly.test(null), lowerCaseOnly.test()]

答案: [true, true]
该正则的意思就是符合所有的都是小写
而在函数test中,对于argument存在一个ToString的操作
故原来的式子等价于 .test(‘null’) , .test(‘undefined’)

32

1
2
What is the result of this expression? (or multiple ones)
[,,,].join(", ")

答案: “, , “
join() 方法将数组中的所有元素连接成一个字符串。
原来的数组相当于 [udefined * 3],join(“, “) –> “, , “

33

1
2
3
What is the result of this expression? (or multiple ones)
var a = {class: "Animal", name: 'Fido'};
a.class

答案: (浏览器)
class 是保留字,当在Chrome, Firefox, Opera中的时候,可以被认可,返回Animal, 在IE中的时候..

34

1
2
What is the result of this expression? (or multiple ones)
var a = new Date("epoch")

答案: Invalid Date
Date的四中构造方法, 详见 ()

35

1
2
3
4
What is the result of this expression? (or multiple ones)
var a = Function.length,
b = new Function().length
a === b

答案: flase
It’s false. Function.length is defined to be 1. On the other hand, the length property of the Function prototype object is defined to be 0.

36

1
2
3
4
var a = Date(0);
var b = new Date(0);
var c = new Date();
[a === b, b === c, a === c]

答案: [false, false, false]
a是字符串,b, c 是Object, 在===操作符面前,不用任何考虑,直接都是false

37

1
2
3
What is the result of this expression? (or multiple ones)
var min = Math.min(), max = Math.max()
min < max

答案: false
Math.min() 返回Infinity, 是javascript的全局对象,代表正的无穷大
Math.max() 返回-Infinity 所以…

38

1
2
3
4
5
6
7
8
9
10
11
12
What is the result of this expression? (or multiple ones)
function captureOne(re, str) {
var match = re.exec(str);
return match && match[1];
}
var numRe = /num=(\d+)/ig,
wordRe = /word=(\w+)/i,
a1 = captureOne(numRe, "num=1"),
a2 = captureOne(wordRe, "word=1"),
a3 = captureOne(numRe, "NUM=2"),
a4 = captureOne(wordRe, "WORD=2");
[a1 === a2, a3 === a4]

答案: [true, false]
Regular expressions in JavaScript if defined using the /g flag will carry a state across matches, even if they are actually used on different strings (the lastIndex property). This means a3 will be null as the regular expression was applied starting from the index of the last matched string, even if it was a different one.

39

1
2
3
4
What is the result of this expression? (or multiple ones)
var a = new Date("2014-03-19"),
b = new Date(2014, 03, 19);
[a.getDay() === b.getDay(), a.getMonth() === b.getMonth()]

答案: [fasle, false]
注意b 的构造方法 月份从0开始算,
所以a 返回 Wed Mar 19 2014 08:00:00 GMT+0800 (CST)
b 返回 Sat Apr 19 2014 00:00:00 GMT+0800 (CST)
然后getDay 是返回一个星期中的第几天(从0开始)(这有时候会是个坑)
要想返回一个月中的第几天,请使用getDate

40

1
2
3
4
5
6
What is the result of this expression? (or multiple ones)
if ('http://giftwrapped.com/picture.jpg'.match('.gif')) {
'a gif file'
} else {
'not a gif file'
}

答案: a gif file
传入的不是一个正则表达式对象,所以会隐式转换成一个正则表达式对象, .gif –> /.gif/
匹配的字符串为[“/gif”]

41

1
2
3
4
5
6
7
8
9
10
What is the result of this expression? (or multiple ones)
function foo(a) {
var a;
return a;
}
function bar(a) {
var a = 'bye';
return a;
}
[foo('hello'), bar('hello')]

答案:[“hello”, “bye”]
Variabled declarations are hoisted, but in this case since the variable exists already in the scope, they are removed altogether. In bar() the variable declaration is removed but the assignment remains, so it has effect.

小结

这是一套满满都是坑的题目,可能有些认为是钻牛角尖,但也是在开发过程中不能忽视的问题吧..

koa-微信开发(1)

申请公众号

mp.weixin.qq.com 上根据步骤申请一个账号

图片描述

进入公众号

选择开发–基本配置,进行修改配置

图片描述

服务器编写

根据文档,我们需要做的是

1
2
3
4
接入微信公众平台开发,开发者需要按照如下步骤完成:
1、填写服务器配置
2、验证服务器地址的有效性
3、依据接口文档实现业务逻辑

当你在配置中写完配置,要提交的时候,微信会对你填写的url发送一个get请求, 包括:

1
2
3
4
5
参数             描述
signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
timestamp 时间戳
nonce 随机数
echostr 随机字符串

服务器配置如下:

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
router.get('/wechat', function *() {
// 获取微信的请求,注意是 get
var signature = this.query.signature;
var echostr = this.query.echostr;
var timestamp = this.query.timestamp;
var nonce = this.query.nonce;

// 这里的token 要和你表单上面的token一致
var token = 'MMDBB';

// 根文档上面的,我们需要对这三个参数进行字典序排序
var arr = [token, timestamp, nonce];
arr.sort();
var tmpStr = arr.join('');

// 排序完成之后,需要进行sha1加密, 这里我们使用node.js 自带的crypto模块
var sha1 = crypto.createHash('sha1');
sha1.update(tmpStr);
var resStr = sha1.digest('hex');
console.log(signature, 'resStr: ', resStr);

// 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信,
// 如果匹配,返回echoster , 不匹配则返回error
if (resStr === signature) {
this.body = echostr;
} else {
return false;
}
});

需要注意的点:

  • url 的端口必须是80 或者 443
  • 如果验证失败,可以在浏览器的开发者模式下,查看network中的情况