有道

君子爱才,取之有道


  • 首页

  • 分类

  • 归档

  • 标签

  • 公益404

Function-ES6

发表于 2016-05-13   |  
  • 参数默认值
  • arguments

    • length不包括rese参数(…values)
    • length不包括赋默认值参数(values=1)
      1
      2
      console.log((function A(a,dm,s,...v){}).length) // 3
      console.log((function A(a,dm,s,v=1){}).length) // 3
  • 作用域

  • 必输参数

    1
    2
    3
    4
    5
    6
    function throwMiss(){
    throw new Error('arguments error')
    }
    function gg(x=throwMiss()){ console.log(x)}
    gg(2) // 2
    gg() // arguments error
  • rest参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function add(...values){
    //...values == [2,3,4,5] 从参数末尾索引开始计算,截止到最后一个有效参数索引为止
    let sum = 0;
    for (var val of values){
    sum += val;
    }
    return sum;
    }
    add(2,3,4,5);
  • 扩展运算符…

    1
    2
    3
    4
    5
    	console.log(...[1,5,6]) // 1 5 6
    console.log(...[1,[5,6]]) // 1 [5,6]
    console.log(...[1,...[5,6]]) // 1 5 6
    console.log(...{'0':'12','1':'34',length:2}) // 12 34
    * 凡是涉及到操作32位Unicode字符的函数

    ‘x\uD83D\uDE80y’.length // 4 wrong
    […’x\uD83D\uDE80y’].length // 3 right
    function length(str){ return […str].length}

    1
    2
    3
    4
    5
    6
    7
    * map, generator function可以使用...
    * 如果对没有iterator接口的对象,使用扩展运算符,将会报错
    * 箭头函数
    *函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
    *不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
    *不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
    *不可以使用yield命令,因此箭头函数不能用作Generator函数。

    function Timer () {

    this.s1 = 14;
    this.s2 = 4;
    // 箭头函数
    // this指向定义时所在作用域,即Timer
    setInterval(() => this.s1++, 1000);
    // 普通函数
    // this指向执行时作用域,即window
    setInterval(function () {
      this.s2++;
    }, 1000);
    

    }

    1
    * 箭头函数可以让this指向固定化

    var handler = {

    id: '123456',
    
    init: function() {
        //this指向handler,因为=>没有自己的this(this == undefined),所以指向最近的外部this
      document.addEventListener('click',
        event => this.doSomething(event.type), false);
      // var self = this;
      // document.addEventListener('click',function(){
      //   self.doSomething(event.type)
      // },false)
    },
    
    doSomething: function(type) {
      console.log('Handling ' + type  + ' for ' + this.id);
    }
    

    };

    1
    * call\apply\bind无效,会忽略

    (function() {

    return [
      (() => this.x).bind({ x: 'inner' })()
    ]
    

    }).call({ x: ‘outer’ });
    // function () {
    // var _this = this;
    // return [function () {
    // return _this.x;
    // }.bind({ x: ‘inner’ })()];
    // }.call({ x: ‘outer’ })

    1
    * :: 返回的是原对象

    //上下文::执行方法
    foo::bar;
    // 等同于
    bar.bind(foo);

    foo::bar(…arguments);
    // 等同于
    bar.apply(foo, arguments);

    const hasOwnProperty = Object.prototype.hasOwnProperty;
    function hasOwn(obj, key) {

    return obj::hasOwnProperty(key);
    

    }

    1
    2
    3
    * 尾调=>调用栈
    * stack overflow 栈溢出
    * 尾递归

    //复杂度O(n)
    function factorial(n) {

    if (n === 1) return 1;
    return n * factorial(n - 1);
    

    }
    factorial(5) // 120
    //5 factorial(4)
    //5
    (4 factorial(3))
    //5
    (4 (3 factorial(2)))
    //5 (4 (3 (2 factorial(1))))
    //5 4 3 2 1
    //复杂度O(1)
    function factorial(n, total){

    if(n === 1) return total;
    return factorial(n-1, n * total);
    

    }
    factorial(5,1) // 120
    // 只保留一个调用帧
    // factorial(5,1)
    // factorial(4,5)
    // factorial(3,20)
    // factorial(2,60)
    // factorial(1,120)

    1
    2
    3
    4
    `
    尾递归的实现,往往需要改写递归函数,确保最后一步只调用自身。做到这一点的方法,就是把所有用到的内部变量改写成函数的参数。比如上面的例子,阶乘函数 factorial 需要用到一个中间变量 total ,那就把这个中间变量改写成函数的参数。
    `
    * 闭包记忆 [简书资料](http://www.jianshu.com/p/a2dfa59e70d7)

//cache为function内局部变量,cache被匿名函数引用,匿名函数上下文为全局变量window
//memorize执行完后,cache理应被回收,但是在被匿名函数引用的情况下无法回收
function memorize(sets, f) {
var cache = {};
return function (x) {
console.log(‘cache: %j’, cache);
return x in cache
? cache[x]
: cache[x] = f(sets[x]);
}
}
var g = memorize([1000, 2000, 3000], function (x) { return x * x; });
````

  • console.log(‘%c Hello~how are you’,’font-size:28px; font-family:Microsoft YAHEI;color: blue; font-weight: bold;’)

/Object/

  • Object.assign
  • //除了字符串会以数组形式拷贝入目标对象,数值、布尔、undefined、null都不起效果(这是因为只有字符串的包装对象,会产生可枚举属性)
  • //首参数不能是Null、Undefined :Cannot convert undefined or null to object

Array-ES6

发表于 2016-05-12   |  
  • Array.from(obj, func, this)//类似Array和可Iterator的对象转为Array, func类似于map对每个元素进行处理,将处理后的值放入返回的数组
  • [].slice.call({‘0’:’1’,length:1})// [1]
  • … 扩展运算符 Symbol.iterator
  • 能正确处理Unicode,可以避免JavaScript将大于\uFFFF的Unicode字符,算作两个字符的bug

    1
    2
    3
    function countSymbols(string) {
    return Array.from(string).length;
    }
  • Array.of(‘3’) ,构造数组 new Array(‘3’)

  • Array.prototype.copyWithin(target, start = 0, end = this.length) //在数组内部进行替换元素, 在index为target的起始位置上,从index为start的元素开始复制,截到第end元素为止,数组总长度不变

    1
    2
    3
    4
    5
    6
    7
    let popo = Array.of(3,3,4,5,12);
    console.log(popo.copyWithin(0,2,4)); // [4,5,4,5,12]
    console.log(popo.copyWithin(1,2,4)); // [3,4,5,5,12]
    console.log(popo.copyWithin(1,2,1)); // [3,3,4,5,12]返回元素组
    console.log(popo.copyWithin(1,2,3)); // [3,4,4,5,12]
    console.log(popo.copyWithin(1,2,-1));// [3,4,5,5,12] 负数从末尾算起
    console.log([1, 2, 3, 4, 5].copyWithin(0, -3, -2)); // [3,2,3,4,5] -3代表从末尾索引为-1开始算,-2代表末尾索引为0开始算
  • Array.find

    1
    2
    3
    4
    //当前值,当前索引,当前数组
    [1, 5, 10, 15].find(function(value, index, arr) {
    return value > 9;
    },this) // 10
  • Array.findIndex

    1
    [NaN].findIndex(y => Object.is(NaN, y))
  • Array.fill填充一个数组

    1
    Array.fill(string,start,end) //从start开始到end之前结束
  • Array.entries(),Array.keys(),Array.values()

    1
    2
    3
    4
    5
    let letter = ['a', 'b', 'c'];
    let entries = letter.entries();
    console.log(entries.next().value); // [0, 'a']
    console.log(entries.next().value); // [1, 'b']
    console.log(entries.next().value); // [2, 'c']
  • Array.prototype.includes()
    Map结构的has方法,是用来查找键名的,比如Map.prototype.has(key)、WeakMap.prototype.has(key)、Reflect.has(target, propertyKey)。 Set结构的has方法,是用来查找值的,比如Set.prototype.has(value)、WeakSet.prototype.has(value)。

  • ES5数组对空位的处理

    • forEach(), filter(), every() 和some()都会跳过空位。
    • map()会跳过空位,但会保留这个值
    • join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      // forEach方法
      [,'a'].forEach((x,i) => log(i)); // 1

      // filter方法
      ['a',,'b'].filter(x => true) // ['a','b']

      // every方法
      [,'a'].every(x => x==='a') // true

      // some方法
      [,'a'].some(x => x !== 'a') // false

      // map方法
      [,'a'].map(x => 1) // [,1]

      // join方法
      [,'a',undefined,null].join('#') // "#a##"

      // toString方法
      [,'a',undefined,null].toString() // ",a,,"
  • ES6则是明确将空位转为undefined
    Invalid attempt to destructure non-iterable instance

Number-ES6

发表于 2016-05-12   |  
  • 二进制和八进制表示法

    1
    2
    3
    // 分别用前缀0b、0B和0o、0O表示
    0b111110111 === 503
    0o767 === 503 //八进制
  • Number转为十进制,Number(‘0o10’) == 8

  • 极小误差

    1
    2
    3
    4
    function withinErrorMargin (left, right) {
    return Math.abs(left - right) < Number.EPSILON
    }
    console.log(withinErrorMargin(0.1 + 0.2, 0.3))
  • JavaScript能够准确表示的整数范围在-2^53到2^53之间

  • Math对象扩展
    • Math.trunc()//去除小数部分
    • Math.sign()//判断正数、负数、零
    • Math.cbrt()//返回立方根
    • Math.clz32()//补零
    • Math.imul()//返回两个数以32位带符号整数形式相乘结果
    • Math.fround()// new Float32Array()
    • Math.hypot()//返回所有参数平方和的平方根
    • 对数方法
    • 三角函数
      • Math.sinh()//双曲正弦
      • Math.cosh()//双曲余弦
      • Math.tanh(x) 返回x的双曲正切(hyperbolic tangent)
      • Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine)
      • Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine)
      • Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent)

RegExp-ES6

发表于 2016-05-10   |  
  • 新增u修饰符
  • new RegExp(/x/i, ‘gi’) // 会覆盖前面定义的修饰符
  • /^.$/.test(‘吉’) //false
  • /^.$/u.test(‘吉’) //true
  • /\u{3}/.test(‘uuu’) //{}被解读为量词
  • /\u{61}/u.test(‘a’) //解读为Unicode
  • 有些Unicode字符的编码不同,但是字型很相近,比如,\u004B与\u212A都是大写的K。
  • y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始
  • 可以重置lastIndex来改变匹配起始位置

    1
    2
    3
    4
    5
    6
    7
    8
    var sd = 'aaa_aa_a';
    var r1 = /a+/g;
    var r2 = /a+/y;

    console.log(r1.exec('aaa_a1a_a'))
    console.log(r1.exec('aaaaaa_')) //即使是匹配不同字符串,相同的正则仍然会保留上一次匹配后的起始位置
    r1.lastIndex=0;
    console.log(r1.exec('a_aaaa_aaaa'))
  • split:aaa_aaaaa 如果以a为分隔符,结果是[‘’,’’,’’,’‘,’’,’’,’‘,’’,’’] //(‘’)a(‘’)a(‘’)a(‘‘)a(‘’)a(‘’)a(‘_’)a(‘’)a(‘’)

  • y修饰符确保匹配必须从剩余的第一个位置开始

    1
    2
    const REGEX = /a/gy; //最后一个a因为不是出现下一次匹配的头部,所以不会被替换
    console.log('aaxa'.replace(REGEX, '-')) // '--xa'
  • sticky // RegExp.sticky判断是否使用粘性修饰符

  • source 返回正则匹配正文
  • flags 返回正则修饰符
  • 字符串必须转义,才能作为正则模式

    1
    2
    3
    4
    5
    6
    7
    8
    //可用于fiddler 匹配*请求中http:\/\/xxx.xxx.cn\/\?v=.*
    function escapeRegExp(str) {
    return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
    }

    let str = '/path/to/resource.html?search=query';
    escapeRegExp(str)
    // "\/path\/to\/resource\.html\?search=query"
  • 后行断言,从右往左执行表达式

    1
    2
    /(?<=(\d+)(\d+))$/.exec('1053') // ["", "1", "053"]
    /^(\d+)(\d+)$/.exec('1053') // ["1053", "105", "3"]

String-ES6

发表于 2016-05-06   |  
  • string.codePointAt()能处理4个字节
  • string.charCodeAt()处理2个字节

    1
    2
    3
    4
    // 是否由四个字节组成
    function is32Bit(c){
    return c.codePointAt(0) > 0xFFFF;
    }
  • string.fromCharCode(0xFFFF)

  • string.fromCodePoint(0x20BB7)
  • 如果String.fromCharCode方法有多个参数,则它们会被合并成一个字符串返回。String.fromCodePoint(0x78, 0x1f680, 0x79) === “x\uD83D\uDE80y” // true
  • fromCodePoint 方法定义在String对象上,codePointAt方法定义在字符串实例对象上
  • for(let xx of string) 可以识别大于0xFFFF的码点
  • string.at()和string.charAt()
  • string.includes()
  • string.endsWith(sting1,index)
  • string.startsWith()
  • string.padStart(length,string1) //length是拼接string1后总长度,超出部分截出
  • string.padEnd(length,string1)

    templete

  • ``反引号成行成段转义输出
  • let man = 6
  • $(‘#J_main’).append(<b>${man}</b>) // 6
  • ${内部执行的是JavaScript代码}
  • ${‘hello’ + ‘ world’}
  • console.log(${man}+${man});
  • console.log(${man + man});
  • let xx = (x)=>{return x;}
  • console.log(${xx(21)})
  • let lo = (function fk(x){ return x}) // 要用括号把函数体包裹起来
  • let fuc = eval(lo)
  • console.log(fuc(223))
  • string.raw //转义\
  • 简单模板

    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
    var template = `
    <ul>
    <% for(var i=0; i < data.supplies.length; i++) {%>
    <li><%= data.supplies[i] %></li>
    <% } %>
    </ul>
    `;
    function compile(template){
    var evalExpr = /<%=(.+?)%>/g;
    var expr = /<%([\s\S]+?)%>/g;

    template = template
    .replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
    .replace(expr, '`); \n $1 \n echo(`');

    template = 'echo(`' + template + '`);';

    // console.log(template);
    var script =
    `(function partse(data){
    var output = "";

    function echo(html){
    output += html;
    }
    ${ template }

    return output;
    })`;

    return script;
    }

    var parse = eval(compile(template));
    console.log(parse({ supplies: [ "broom", "mop", "cleaner" ] }));
  • 标签模板

    1
    tag`Hello ${main}`

正则表达式基础

发表于 2016-05-03   |  

主要参考陈梓瀚

历史

  • 神经元

    问题概述

  • 如何分析正则表达式
  • 如何扩展NFA以便表达正则表达式的高级功能(预查、捕获等)
    阅读全文 »

解构赋值-ES6

发表于 2016-04-29   |  

主要参考来源阮一峰

解构赋值

默认解构赋值

let [x] = [1];
//x => 1

对象的解构赋值

var {bar , foo} = {foo:'hello', bar:'world'};
阅读全文 »

selenium-webdriver

发表于 2016-04-26   |  
  • 延迟至某条件达成后执行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    driver.wait(function () {
    return driver.isElementPresent(webdriver.By.name("username"));
    }, timeout);
    /*==========*/
    driver.wait(until.elementLocated(By.name('username')), 5 * 1000).then(function(elm) {
    elm.sendKeys(username);
    });
    /*==========*/
    driver.findElement(webdriver.By.id(element)).then(function(webElement) {
    console.log(element + ' exists');
    }, function(err) {
    if (err.state && err.state === 'no such element') {
    console.log(element + ' not found');
    } else {
    webdriver.promise.rejected(err);
    }
    });
    }
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
// Import Selenium Dependency
var Webdriver = require('selenium-webdriver');

// Few Settings
var PAGE_LOAD_TIMEOUT_MS = 10000;
var SCRIPT_LOAD_TIMEOUT_MS = 1000;
var WINDOW_WIDTH_PX = 1024;
var WINDOW_HEIGHT_PX = 768;

// Initialize a webdriver with desired capabilites
var driver = new Webdriver.Builder().withCapabilities(Webdriver.Capabilities.firefox()).build();
// Manage timeouts of the webdriver instance
driver.manage().timeouts().pageLoadTimeout(PAGE_LOAD_TIMEOUT_MS);
driver.manage().timeouts().setScriptTimeout(SCRIPT_LOAD_TIMEOUT_MS);
// Manage window settings of the webdriver instance
driver.manage().window().setSize(WINDOW_WIDTH_PX, WINDOW_HEIGHT_PX);

// Request to render google.com
driver.get("http://google.com").then(function () {
// On Success get the title of the rendered page
driver.getTitle().then(function (title) {
// On Success log it out
console.log(title);
});
});
- See more at: http://itsallabtamil.blogspot.com/2014/10/selenium-webdriver-in-nodejs-javascript.html#sthash.QYKh3mjf.dpuf
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

Wednesday, October 8, 2014
Selenium Webdriver in Nodejs + Javascript
Phewww getting back to blog after almost an year.
So, am back to discuss an interesting node module selenium-webdriver [Ref: Project Doc and npm registry]
Am assuming the reader has prior knowledge on what nodejs, npm and node modules are. And a little bit of selenium fun ;)
What is selenium?
As wiki says, Selenium is a portable software testing framework for web applications.
It is a prominently used tool for browser automation. For further details checkout SeleniumHQ.

What are we doing here?
Basically Selenium provides bindings in many languages like java, python, php and now even nodejs.
Here am planning to run through few code samples of using selenium webdriver in nodejs.

Where to start?

im@mylaptop$ npm install --save selenium-webdriver

A Simple Webdriver Example

// Import Selenium Dependency
var Webdriver = require('selenium-webdriver');

// Few Settings
var PAGE_LOAD_TIMEOUT_MS = 10000;
var SCRIPT_LOAD_TIMEOUT_MS = 1000;
var WINDOW_WIDTH_PX = 1024;
var WINDOW_HEIGHT_PX = 768;

// Initialize a webdriver with desired capabilites
var driver = new Webdriver.Builder().withCapabilities(Webdriver.Capabilities.firefox()).build();
// Manage timeouts of the webdriver instance
driver.manage().timeouts().pageLoadTimeout(PAGE_LOAD_TIMEOUT_MS);
driver.manage().timeouts().setScriptTimeout(SCRIPT_LOAD_TIMEOUT_MS);
// Manage window settings of the webdriver instance
driver.manage().window().setSize(WINDOW_WIDTH_PX, WINDOW_HEIGHT_PX);

// Request to render google.com
driver.get("http://google.com").then(function () {
// On Success get the title of the rendered page
driver.getTitle().then(function (title) {
// On Success log it out
console.log(title);
});
});

// Initialize a webdriver with desired capabilites
What are the default capabilities?
Checkout capabilities.js
It includes
1. BROWSER_NAME
2. SUPPORTS_JAVASCRIPT
3. SUPPORTS_CSS_SELECTORS
4. TAKES_SCREENSHOT


Webdriver.Capabilities.firefox()
Creates a new capabilities object by overriding the default BROWSER_NAME from 'browserName' to 'firefox'.

So, What are all other browser options?
Checkout Browsers Section

Do all the browsers work by default?
Not really. To initialize a webdriver, we need to make sure that executable of desired browser is present in the system PATH.
For Ex: To use firefox, the following should be checked. If this fails, then appropriate binaries should be installed.


iam@mylaptop$ which firefox
/usr/bin/firefox

Apart from this, few browsers use separate driver executables to abstract the communication via webdriver to actual browser. For Ex: Chrome needs chromedriver. Download and make sure it is available in the system PATH.
Internally chromedriver expects google-chrome to be available in the system PATH.

What if I wish to start chrome or firefox from webdriver with specific options?

var driver = new Webdriver.Builder().withCapabilities(Webdriver.Capabilities.firefox()).setFirefoxOptions({
firefox_profile: "Name"
}).build();

How do I manage exceptions while loading [like pageload timesout]?
Selenium webdriver is full of promises. Almost every interaction with the webdriver [irrespective of whether it is sync or async] returns a promise.
As we can see in our code,

driver.get("http://google.com").then(function success() {
console.log('Successful');
i++;
}, function error(err) {
console.log(err);
}).thenCatch(function (e) {
console.log("Any exceptions in success or err callbacks gets thrown here", e);
// You need to decide the resolution value here
}).thenFinally(function () {
console.log("We can tear down stuffs here");
// You need to decide the resolution value here. If nothing specified undefined will be returned.
});

How do I handle actions that are dependent on some conditions [like may be after some ajax calls you need click some button]?
We can ask driver to wait implicitly on a timeout basis or explicitly on a condition basis.

driver.sleep(MY_SLEEPTIMER_MS).then(function () {
console.log("I'll do my action");
});
driver.wait(function () {
if (document.readyState === "complete") {
return true;
}
}, TIMEOUT_TO_WAIT).then(function () {
console.log("I'll do my action here");
}, function () {
console.log("Either the condn satisfied or the TIMEOUT_TO_WAIT happened; luck is yours");
});
- See more at: http://itsallabtamil.blogspot.com/2014/10/selenium-webdriver-in-nodejs-javascript.html#sthash.QYKh3mjf.dpuf

analyse-requirejs

发表于 2016-04-26   |  

定义常量

req
s
head
baseElement
dataMain
src
interactiveScript
currentlyAddingScript
mainScript
subPath
version
commentRegExp
jsSuffixRegExp
op
ostring
hasOwn
isBrowser
isWebWorker
readyRegExp
defContextName
isOpera
contexts
cfg
globalDefQueue
useInteractive
handlers

阅读全文 »

词法分析基础

发表于 2016-04-26   |  

主要参考陈梓瀚

问题概述

  • 词法分析的重要性
    • 运行命令
    • (1+2)*(3+4)
      • (左括号,”(“) (数字,”1”) (一级操作符,”+”) (数字,”2”) (右括号,”)”) (二级操作符,”*”) (左括号,”(“) (数字,”3”) (一级操作符,”+”) (数字,”4”) (右括号,”)”)
      • 操作符、括号、数字
      • 但是,都是等价的对象,没有层次

        正则表达式

  • 代表字符串集合的规则
    • 规则可以把一个特定的字符或者是空字符串认为是一种类型的记号的全部,如”(“是”左括号”,”[“不会是”左括号”
    • 规则可以进行串联,”hello”是”h”,”e”,”l”,”l”,”o”的记号串联
    • 规则可以进行并联, “if”,”else”,”while”等字符串,称之为关键字
    • 规则是可选的, “abc”,”abcde”可以看做是”abd”的前缀字符串,”de”是可选的
    • 规则可以重复
      阅读全文 »
1…4567
wangtuda

wangtuda

新一天新积累
I Belive I Can.

64 日志
1 分类
55 标签
© Thu Mar 24 2016 08:00:00 GMT+0800 (CST) - 2017 wangtuda
由 Hexo 强力驱动
主题 - NexT.Pisces