Eric 发布的文章

Node.js heap analysis 内存分析

  1. 这个模块可以帮忙做heap dump: https://github.com/bnoordhuis/node-heapdump
  2. 参考文章列表:
    http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection
    https://blog.risingstack.com/finding-a-memory-leak-in-node-js/

Java SimpleDateFormat 的格式化字符串里的Y

2017年元旦假期三天, 程序员小吴没有出去玩. 早上10点多了, 还在家睡懒觉. 突然电话响起,吵醒了半睡半醒的他. 他心想是推销还是贷款专员? 休假都被叫醒, 很是生气. 拿起手机, 发现竟然是公司打来的电话,顿时感觉不妙. 接通电话, 那头说有个紧急的site issue, 可能跟小吴的代码相关, 让他紧急上线看一下. 小吴一下子清醒了很多, 连忙登录VPN, 检查最新邮件, 确实发现有个问题, 跟自己几个月前上线的代码有关系.

这段代码巨简单, 就是取服务器当前的时间, 然后格式化一下, 显示在页面上. 同时,页面上有些数据统计信息, 会通过Ajax请求的方式去慢慢取,然后展示在页面. 去取的时候, 把本来展示在页面的这个格式化后的日期, 发给后台服务器. 问题就出现在, 本来这天是2017年12月31日, 可是页面上却显示的是 2018/12/31, 默认加了1年, 非常的奇怪.

小吴首先把线上已部署的代码的commit id 找到,然后把这个commit拉下来, 仔细检查了一番, 并没有发现什么情况. 这段代码已经在生产环境运行了8个多月, 之后一直没有人改过. 之前的测试以及线上从来没出现过问题, 真是奇了怪了. 小吴只好本地debug了一下, 发现本地能完美重现... 用当天的日期格式化一把, 格式化之后一直是2018年12月31日. 之前从来没有问题, 难道这天是啥好日子? 经过一番修改测试, 终于发现了问题:

public static void main(String[] args) {
    Calendar cal = Calendar.getInstance();
    cal.set(2017, 11, 29);
    SimpleDateFormat sdf0 = new SimpleDateFormat("YYYY/MM/dd");
    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy/MM/dd");
    Date date = null;
    for (int i = 0; i < 4; i++) {
        date = cal.getTime();
        System.out.println(sdf0.format(date));
        System.out.println(sdf1.format(date));
        cal.add(Calendar.DATE, 1);
    }
}

运行结果:

2017/12/29
2017/12/29
2017/12/30
2017/12/30
2018/12/31
2017/12/31
2018/01/01
2018/01/01

从上面代码片段及运行结果看, 只有2017年12月31日出问题了. 问题出现在格式化的时候, 使用是大写的Y还是小写的y. 那么根据官方文档, 差别是什么呢?

y: Year Year 如: 1996; 96
Y: Week year Year 如: 2009; 09

一个是year, 一个 week year. year 很好理解, 就是我们所说的年, 那么 week year 呢? week year的计算涉及到2个东西, 一个是: 一周的第一天是从周一开始算还是从周日开始算, 有的人/地方从周日开始算, 有的从周一开始算. 第二个是, 我们经常听说这是今年第几周, 试想如果一个week跨过1年, 那么这周算到那年里?
JDK 文档举的例子是: 1998年1月1日是周四.
如果我们设定一周的第一天是周一, 那么这周属于1998年的就有4天(周4,5,6,7), 属于1997年的只有3天, 那么这周算在1998年,这是1998年的第一周. 那么1997年12月29, 30, 31日的 week year 就是1998, 而不是1997了.
如果我们设定一周的第一天是周日, 那么这周属于1998年的就有3天(周4,5,6), 属于1997年的就有4天了. 那么这周算在1997年,这是1997年的最后一周,1998年的第一周从1月4号开始, 那么1998年,1月1,2.3号的 week year 就是1997了.

FirstDayOfWeek 是根据JDK所在的系统设置, 有个默认设置的.

所以这个问题, 只要改回正确的yyyy问题就解决了. 另外还发现有人误以为这是JDK的bug, 去提了bug: https://bugs.openjdk.java.net/browse/JDK-8194625

参考:
https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
https://docs.oracle.com/javase/9/docs/api/java/util/GregorianCalendar.html#week_year

<Exploring ES6> 读书笔记 - Variables and scoping

  1. var is function-scoped, let is block-scoped; //for (const x of ['a', 'b']) {} is ok

  2. let/const 有emporal dead zone (TDZ), 其实这和Java生命使用变量一直, 未声明前不可用. 但var声明但变量一旦进入这个变量声明但function, 即使还没到声明这行代码, 但是这个变量已经有了, 并且赋值为 undefined;

    let tmp = true;
    if (true) { // enter new scope, TDZ starts
    // Uninitialized binding for tmp is created
    console.log(tmp); // ReferenceError

     let tmp; // TDZ ends, `tmp` is initialized with `undefined`
     console.log(tmp); // undefined
    
     tmp = 123;
     console.log(tmp); // 123
    

    }
    console.log(tmp); // true
    例子2

    if (true) { // enter new scope, TDZ starts
    const func = function () {
    console.log(myVar); // OK!
    };

     // Here we are within the TDZ and
     // accessing `myVar` would cause a `ReferenceError`
    
     let myVar = 3; // TDZ ends
     func(); // called outside TDZ
    

    }

  3. always use const, let, 避免 var

<Exploring ES6> 读书笔记 - New number and Math features

  1. New integer literals, 直接可以二进制, 八进制, 十六进制了

    0xFF // ES5: hexadecimal
    255
    0b11 // ES6: binary
    3
    0o10 // ES6: octal
    8

  2. New Number properties

    Number.isInteger(num)
    Number.isSafeInteger(number)
    Number.MIN_SAFE_INTEGER
    Number.MAX_SAFE_INTEGER
    Number.isNaN(num)
    Number.isFinite
    Number.parseFloat
    Number.parseInt

  3. New Math methods

    Math.sign(-8) // -1
    Math.trunc(3.1)
    Math.log10(100)

<exploring ES6> 读书笔记 - Core ES6 features

  1. From var to const/let;

  2. From IIFEs(Immediately-Invoked Function Expression) to blocks;

  3. From concatenating strings to template literals -> ``;

  4. From function expressions to arrow functions, array function没有this;

  5. Handling multiple return values
    --Multiple return values via arrays //const [, year, month, day] = [0, 2012, 2, 2]
    --Multiple return values via objects //const {foo, bar} = {'foo':'full', 'bar':3, 'name':'eric'}

  6. From for and forEach() to for-of;

  7. Handling parameter default values -> function foo(x=0, y=0) {};

  8. From arguments to rest parameters (变长参数) foo(...args);

  9. 通过spread operator (...) 去把Array 变成非数组序列;

  10. From function expressions in object literals to method definitions;

    var obj = {
    foo: function () {
    ···
    },
    bar: function () {
    this.foo();
    }, // trailing comma is legal in ES5
    }

to

const obj = {
    foo() {
        ···
    },
    bar() {
        this.foo();
    },
}
  1. 使用 class 和 extend 定义类和继承关系

    class Employee extends Person {}

  2. From custom error constructors to subclasses of Error

    class MyError extends Error {}

  3. From objects to Maps;

  4. String 和 Array 新方法;