前端学习之路


  • 首页

  • 归档

  • 公益404

继承

发表于 2017-02-28 |

原型链

实现原型链的基本模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function SuperType() {
this.property = true;
}
SuperType.property.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subproperty = false;
}
// 继承了 SuperType
SubType.property = new SuperType();
SubType.property.getSubValue = function() {
return this.subproperty;
}
var instance = new SubType();
console.log(instance.getSuperValue()); // true

原型链

其实上图中的 SuperType Prototype 是 Object 构造函数的一个实例,上图将其省略了

确定原型和实例的关系

1
2
3
console.log(instance instanceof Object); // true;
console.log(instance instanceof SuperType); // true;
console.log(instance instanceof SubType); // true;
1
2
3
console.log(Object.property.isPropertyOf(instance)); // true
console.log(SuperType.property.isPropertyOf(instance)); // true
console.log(SubType.property.isPropertyOf(instance)); // true

原型链的问题

  1. 引用类型值的原型属性会被所有实例共享。因此要在构造函数中定义属性,而不要在原型中定义属性
  2. 没有办法在不影响所有实例的前提下,给超类的构造函数传递参数

借用构造函数

在子类型构造函数的内部调用超类型构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function SuperType() {
this.colors = ['red', 'blue', 'green'];
}
function SubType() {
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push('black');
console.log(instance1.colors); // red, blue, green, black
var instance2 = new SubType();
console.log(instance2.colors); // red, blue, green

缺点:在超类构造函数中定义的方法无法复用;超类原型中的方法对子类型不可见

组合继承

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
function SuperType() {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.property.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
SubType.property = new SuperType();
SubType.property.constructor = SubType;
SubType.property.sayAge = function() {
console.log(this.age);
}
var instance1 = new SubType('a', 29);
instance1.colors.push('black');
console.log(instance1.colors); // red, blue, green, black
instance1.sayName(); // a
instance1.sayAge(); // 29
var instance2 = new SubType('b', 30);
console.log(instance2.colors); // red, blue, green
instance2.sayName(); // b
instance2.sayAge(); // 30

原型式继承

1
2
3
4
5
function object(o) {
function F() {}
F.prototype = o;
return new F();
}

返回的实例的原型为o

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var person = {
name: 'a',
friends: ['A', 'B', 'C']
}
var anotherPerson = object(person);
anotherPerson.name = 'b';
anotherPerson.friends.push('D');
var yetAnotherPerson = object(person);
yetAnotherPerson.name = 'c';
yetAnotherPerson.friends.push('E');
console.log(person.friends); // A, B, C, D, E

寄生式继承

1
2
3
4
5
6
7
8
function createAnother(original) {
// 使用了 原型式继承 中的object方法
var clone = object(original);
clone.sayHi = function() {
console.log('hi');
}
return clone;
}

寄生组合式继承

组合继承调用了两次超类构造函数的代码
为了解决这个问题,寄生组合式继承借用构造函数来继承属性,通过原型链的混成形式继承方法

基本思路:不必为了指定子类型的原型而调用超类的构造函数

基本模式如下:

1
2
3
4
5
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}

使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function SuperType() {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.property.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.property.sayAge = function() {
console.log(this.age);
}

创建对象的几种方法

发表于 2017-02-28 |

工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function createPersion(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {
console.log(this.name);
};
return o;
}
var person1 = createPersion('a', 29, 'jobA');
var person2 = createPersion('b', 30, 'jobB');
console.log(person1 instanceof createPersion) // false
console.log(person1 instanceof Object) // true

优点:创建多个相似对象
缺点:无法知道对象的类型

构造函数模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() {
console.log(this.name);
}
//等价于 new Function(...)
}
var person1 = new Person('a', 29, 'jobA');
var person2 = new Person('b', 30, 'jobB');
console.log(person1 instanceof Person) // true
console.log(person1 instanceof Object) // false

构造函数以大写字母开头

用new的方式调用构造函数,会经历以下4个步骤:

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象(this指向新对象)
  3. 执行构造函数中的代码
  4. 返回新对象

优点:解决了对象类型识别的问题,可以规定特定的对象类型名称
缺点:每个实例上的同名函数不相等,即每个实例上的方法都是新构造出来的

原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Person() {}
Person.prototype.name = 'a';
Person.prototype.age = 29;
Person.prototype.job = 'jobA';
Person.prototype.sayName = function() {
console.log(this.name);
}
var person1 = new Person();
person1.sayName(); // a
var person2 = new Person();
person2.sayName(); // a
console.log(person1.sayName === person2.sayName) // true

优点:共享了属性
缺点:共享的引用类型的数据(如:Array),也被所有实例共享了

理解原型对象

理解原型对象

[[Prototype]]的链接存在于实例与构造函数的原型之间,不是存在于实例与构造函数之间

1
2
3
4
5
6
7
8
9
10
11
// isPrototypeOf
console.log(Person.prototype.isPrototypeOf(person1)) // true
// getPrototypeOf
console.log(Object.getPrototypeOf(person1) == Person.prototype); // true
// hasOwnProperty
console.log(person1.hasOwnProperty('name')); // false
// in
console.log('name' in person1) // true

在读取对象中的属性时,先搜索实例,后搜索原型。

取得对象上所有可枚举的实例属性

Object.keys()
包括原型中与实例中的属性

更简单的原型语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Person() {}
// 相当于重写了原型
Person.prototype = {
name: 'a',
age: 29,
job: 'jobA',
sayName: function() {
console.log(this.name);
}
}
// 将原型对象的'constructor'属性指向Person构造函数
Object.defineProperty(Person.prototype, 'constructor', {
enumberable: false,
value: Person,
});

组合使用构造函数模式和原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 不应该共享的属性,用构造函数模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.friends = ['A', 'B'];
}
// 需要共享的属性,用原型模式共享
Person.prototype = {
constructor: Person,
sayName: function() {
console.log(this.name);
}
}
var person1 = new Person('a', 29, 'jobA');
var person2 = new Person('b', 30, 'jobB');
person1.friends.push('C');
console.log(person1.friends); // 'A', 'B', 'C'
console.log(person2.friends); // 'A', 'B'
console.log(person1.friends === person2.friends); // false
console.log(person1.sayName === person2.sayName); // true

集合了构造函数模式和原型模式的优点

动态原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
if(typeof this.sayName !== 'function') {
// 同样不能用对象字面量重写原型
Person.prototype.sayName = function() {
console.log(this.name);
};
}
}
var friend = new Person('a', 39, 'jobA');
friend.sayName();

寄生构造函数模式

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function() {
console.log(this.name);
}
return o;
}
var friend = new Person('a', 29, 'jobA');
friend.sayName(); // 'a'

Person构造函数在原有Object的基础上添加了额外的属性和方法,而没有改变Object本身

优点:可以在不改变原有构造函数的前提下,为新构造函数添加方法
缺点:无法使用instanceof确定对象类型

稳妥构造函数模式

要求:

  • 新创建对象的实例方法不引用this
  • 不使用new操作符调用构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(name, age, job) {
var o = new Object();
// 定义私有变量和函数
var name = name;
o.sayName = function() {
console.log(name);
}
return o;
}
var friend = Person('a', 29, 'jobA');
friend.sayName(); // 'a'

优点:除了sayName方法,没有其他方法可以访问传入构造函数中的原始数据
缺点:不能用过instanceof方法确定对象类型

《JavaScript高级程序设计》读书笔记

发表于 2017-02-26 |

第三章 基本概念

数据类型:

  • 5种基本类型: Undefined,Null,Boolean,Number,String
  • 1种复杂类型:Object

isNaN() 方法

数字类型转换

Number() 适用于任何数据类型的转换
parseInt()和parseFloat() 专门用于把字符串转换为数值

parseInt & parseFloat

parseInt(012, 10) 第二个参数表示转换的数字本身为十进制数
parseFloat() 只能转换十进制数

++操作符

1
2
3
4
5
6
var s1 = “2”;
s1++;
console.log(s1) // 3
var s2 = “2”;
s2 = s2 + 1;
console.log(s2) // “21”

比较字符串

比较字符串大小时,比较的是字符编码值的大小

for的作用域

1
for(var i = 0; i < 10; i++) {}

等价于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var i;
for(i = 0; i < 10; i++) {}
```
由于不存在块级作用域, 所以在外面可以访问到
### 函数的参数
用`arguments`对象访问参数数组
```javascript
function howManyArgs() {
console.log(arguments.length);
}
howManyArgs(12, 34) // => 2
howManyArgs() // => 0

没有重载

可以通过检查传入函数的参数作出不同的反应,来模仿方法的重载

第四章 变量、作用域和内存问题

传递参数

访问变量有按值和按引用两种方式,而参数只能按值传递

1
2
3
4
5
6
7
8
9
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
console.log(person.name); // => "Nicholas"

证明参数为按值引用(可以理解为局部变量)

没有块级作用域

1
2
3
4
for (var i = 0; i < 10; i++) {
console.log(i);
}
console.log(i); // 10

第五章 引用类型

常见引用类型

  • Object
  • Array
  • Date
  • RegExp
  • Function
  • 基本包装类型
    • Boolen
    • Number
    • String
  • 单体内置对象
    • Global
    • Math

检测数组

Array.isArray()

支持的浏览器:IE9+、Firefox 4+、Safari 5+、Opera 10.5+和Chrome

数组的迭代方法 ES5

  • every
  • filter
  • forEach
  • map
  • some

以上方法均不改变原数组的值

数组的归并方法 ES5

  • reduce
  • reduceRight

函数声明与函数表达式

1
2
3
4
5
// 函数声明
console.log(sum(10, 20)); // 30
function sum(num1, num2) {
return num1 + num2;
}
1
2
3
4
5
// 函数表达式
console.log(sum(10, 20)); // Unexpected identifier
var sum = function (num1, num2) {
return num1 + num2;
}

arguments.callee

1
2
3
4
5
6
7
8
9
function factorial (num) {
if (num <= 0) return 1;
return num * factorial(num - 1);
}
function factorial (num) {
if (num <= 0) return 1;
return num * arguments.callee(num - 1);
}

第二种方式解除了函数与函数名的耦合

函数的属性

1
2
3
4
function sum (num1, num2) {
return num1 + num2;
}
console.log(sum.length) // 2 即希望收到的命名参数的个数

apply() 方法和 call() 方法

区别:

1
2
3
4
5
sum.apply(context, arguments);
// arguments 可以是数组,也可以是参数的枚举
sum.call(context, arguments);
// arguments 必须是参数的枚举

作用:扩充作用域

1
2
3
4
5
6
7
8
9
10
11
window.color = 'red';
var o = { color: 'blue' };
function sayColor () {
console.log(this.color);
}
sayColor(); // red
sayColor.call(this); // red
sayColor.call(window); // red
sayColor.call(o); // blue

bind() 方法

1
2
var objectSayColor = sayColor.bind(o);
objectSayColor(); // blue

toPrecision() 方法

1
2
3
4
var num = 99;
console.log(num.toPrecision(1)); // 1e+2
console.log(num.toPrecision(2)); // 99
console.log(num.toPrecision(3)); // 99.0

charAt() 与 charCodeAt() 方法

1
2
3
var stringValue = 'hello world';
console.log(stringValue.charAt(1)); // e
console.log(stringValue.charCodeAt(1)) // 101

slice()、substr()、substring() 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log(stringValue.slice(3)); // lo world
console.log(stringValue.substring(3)); // lo world
console.log(stringValue.substr(3)); // lo world
console.log(stringValue.slice(3, 7)); // lo w
console.log(stringValue.substring(3, 7)); // lo w
console.log(stringValue.substr(3, 7)); // lo worl
// substr 第二个参数表示返回字符的个数
console.log(stringValue.slice(-3)); // rld
console.log(stringValue.substring(-3)); // hello world
console.log(stringValue.substr(-3)); // rld
console.log(stringValue.slice(3, -4)); // lo w
console.log(stringValue.substring(3, -4)); // hel
console.log(stringValue.substr(3, -4)); //

replace() 方法

第二个参数为字符串

1
2
3
4
5
6
var text = 'cat, bat, sat, fat';
var result = text.replace('at', 'ond');
console.log(result); // 'cond, bat, sat, fat'
result = text.replace(/at/g, 'ond');
console.log(result); // 'cond, bond, sond, fond'

第二个参数为函数时,传入函数的参数依次为:模式的匹配项,匹配项在字符串中的位置,原始字符串

split() 方法

分隔符可以是字符串,也可以是一个RegExp对象

第二个参数为返回数组的长度

单体内置对象

不需要显示地实例化内置对象,就可以直接使用其实例

Math

1
2
3
4
5
6
7
var max = Math.max(3, 54, 32, 16);
console.log(max); // 54
// 找到数组中的最小值
var values = [3, 54, 32, 14];
var min = Math.min.apply(Math, values);
console.log(min); // 3

第六章 面向对象的程序设计

主要分为两部分,见如下两篇文章

《创建对象的几种方法》

《继承》

第七章 函数表达式

函数声明:
特征: 函数声明提升

1
2
3
function a() {
}

函数表达式:
特征: 相当于赋值

1
var a = function() {}

闭包

概念:指有权访问另一个函数作用域中的变量的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createComparisonFunction(propertyName) {
return function(obj1, boj2) {
var value1 = obj1[propertyName];
var value2 = obj2[propertyName];
if(value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
};
}

在另一个函数(createComparisonFunction)内部定义的函数(匿名函数)会将外部函数的活动对象添加到它的作用域链中。

因此,内部匿名函数的作用域链中,会包含外部函数的活动对象

1
2
3
4
5
6
7
var compareNames = createComparisonFunction('name');
// createComparisonFunction 的活动对象依然没有被销毁,因为其在被内部函数的作用域链引用
var result = compareNames({name: 'a'}, {name: 'b'});
compareNames = null;
//解除对匿名函数的引用(释放内存)

闭包与变量

闭包所保存的是整个变量对象,而不是某个特殊的变量

1
2
3
4
5
6
7
8
9
function createFunctions() {
var result = new Array();
for(var i = 0; i < 10; i++) {
result[i] = function() {
return i;
}
}
return result;
}

最后 result 中的每个值都是 10

可以通过创建另一个匿名函数强制让闭包的行为符合预期

1
2
3
4
5
6
7
8
9
10
11
function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function(num) {
return function() {
return num;
};
}(i);
}
return result;
}

this对象

匿名函数的执行环境具有全局性,因此其this通常指向window

1
2
3
4
5
6
7
8
9
10
11
12
var name = 'The window';
var object = {
name: 'My Object',
getNameFunc: function() {
return function() {
return this.name;
};
}
};
console.log(object.getNameFunc()()); // The window

通过把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了,如下:

1
2
3
4
5
6
7
8
9
10
var name = 'The window';
var object = {
name: 'My Object';
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};

内存泄漏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function assignHandler() {
var element = document.getElementById('abc');
// method 1: element 无法被清除
element.onclick = function() {
alert(element.id);
};
// method 2: element 最后被回收
var id = element.id;
element.onclick = function() {
alert(id);
}
element = null;
}

模仿块级作用域

1
2
3
(function() {
// 块级作用域
})();

多人协作时,可以通过创建私有作用域,来使用自己的变量,而不会扰乱全局作用域

私有变量

私有变量包括:

  • 函数的参数
  • 局部变量
  • 在函数内部定义的其他函数

如:

1
2
3
4
function add(num1, num2) {
var sum = num1 + num2;
return sum;
}

在函数内部有三个私有变量: num1/num2/sum
在函数内部可以访问他们,但是在函数外部则不能访问他们
如果在这个函数内部创建一个闭包,则可以创建用于访问私有变量的公有方法(称为特权方法)

1.在构造函数中定义特权方法

1
2
3
4
5
6
7
8
9
10
function MyObject() {
var privateVariable = 10;
function privateFunction() {
return false;
}
this.publicMethod = function() {
privateVariable++;
return privateFunction();
}
}

2.使用静态私有变量来实现特权方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(function() {
var privateVariable = 10;
function privateFunction() {
return false;
}
MyObject = function() {
};
MyObject.prototype.publicMethod = function() {
privateVariable++;
return privateFunction();
};
})();

模块模式

为单例创建私有变量和特权方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var singleton = function() {
var privateVariable = 10;
function privateFunction() {
return false;
}
return {
publicProperty: true,
publicMethod: function() {
privateVariable++;
return privateFunction();
}
}
}();

增强的模块模式

适合单例必须是某种类型的实例,同时还必须添加某些属性和方法对其加以增强的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var singleton = function() {
var privateVariable = 10;
function privateFunction() {
return false;
}
var object = new CustomType();
object.publicProperty = true;
object.publicMethod = function() {
privateVariable++;
return privateFunction();
}
return object;
}

第八章 BOM

部分内容选自A tale of two viewports

Screen Size

screen size 范围图片(需要科学上网)

screen.width和screen.height分别获得设备宽高(设备像素)

Window Size

window size 范围图片

window.innerWidth和window.innerHeight获得浏览器可视区域的css像素测量

window size 范围图片(Opera中)

但在Opera浏览器中
window.innerWidth和window.innerHeight获得浏览器可视区域的设备像素测量

这意味着当我们放大或者缩小(用zomm的方式而不是拉伸)页面内容的时候
在其他浏览器中innerWidth和innerHeight是变化的,因为固定可视区域容纳的像素点变化了;
而在Opera浏览器中,由于上面两个属性代表设备的像素值,因此固定可视区域所容纳的设备像素点是不变的;

(两者都包含滚动条)

Scrolling offset

scrolling offset 范围图片

window.pageXOffset和window.pageYOffset包含了文档水平和垂直滚动的偏移量
(CSS像素)

Viewport

viewport 范围

document.documentElement.clientWidth和document.documentElement.clientHeight表示视口宽高
(不包括滚动条)

element

<html> 元素范围

document.documentElement.offsetWidth和document.documentElement.offsetHeight表示html元素尺寸

Event coordinates

pageX/Y 示意图

clientX/Y 示意图

间歇调用和超时调用

JavaScript是一个单线程序的解释器,因此一定时间内只能执行一段代码。为了控制要执行的代码,就有一个JavaScript任务队列。这些任务会按照将它们添加到队列的顺序执行。setTimeout()的第二个参数告诉JavaScript再过多长时间把当前任务添加到队列中

如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了以后再执行

调用setTimeout()后,该方法会返回一个数值ID,表示超时调用。这个超时调用ID是计划执行代码的唯一标识符,可以通过它来取消超时调用

一般认为,使用超时调用来模拟间歇调用是一种最佳模式

location对象

属性名 例子
hash “#contents”
host “www.wrox.com:80”
hostname “www.wrox.com”
href “http:/www.wrox.com”
pathname “/WileyCDA/“
port “8080”
protocol “http:”
search “?q=javascript”

decodeURIComponent()用来解码URL中的query值

位置操作

可以通过 location.href = "http://www.baidu.com" 改变当前加载的页面

通过修改location的其他属性也可以改变页面,并且会在历史记录中生成一条新记录

如果要避免生成记录,则可以用location.replace()方法

location.reload() // 重新加载(可能从缓存中加载)
location.reload(true) // 重新加载(从服务器重新加载)

history对象

1
2
3
4
5
6
7
8
9
10
11
12
history.go(-1);
history.go(1);
history.go(2);
history.go("baidu.com") // 跳转到最近的 baidu.com 页面
history.back();
history.forward();
if (history.length == 0) {
// 这是用户打开窗口后的第一个页面
}

第九章 客户端检测

  • 能力检测
  • 怪癖检测
  • 用户代理检测

第十章 DOM

因为NodeList是动态的,所以每一次操作都需要重新查询一遍,十分消耗性能,因此要尽量减少DOM操作

第十二章 DOM2 和 DOM3

元素大小

  1. 偏移量
    • offsetHeight
    • offsetWidth
    • offsetTop
    • offsetLeft
    • offsetParent
  2. 客户区大小
    • clientWidth
    • clientHeight
  3. 滚动大小
    • scrollHeight
    • scrollWidth
    • scrollLeft
    • scrollTop
  4. 确定元素大小
    • getBoundingClientRect()

第十三章 事件

事件流

从页面接收事件的顺序

  • 事件冒泡*
  • 事件捕获

Hexo 记

发表于 2017-02-25 |

第一篇文章就用来记录下Hexo的总结~ (Mac OSX)

安装

Git和Node.js的安装略过

安装Hexo命令行工具

1
$ npm install -g hexo-cli

初始化Hexo项目文件夹

1
2
3
$ hexo init <folder>
$ cd <folder>
$ npm install

配置

在_config.yml文件中查看/更改配置

1
2
// 建议明确设置所需语言
language: zh-Hans // 简体中文

常用命令

init

1
$ hexo init <folder>

new

1
$ hexo new [layout] <title>

layout 默认为 posts

generate

1
$ hexo generate

publish

1
$ hexo publish [layout] <filename>

用来发布主题

server

1
$ hexo server

在本地开一个服务,默认地址为http://localhost:4000

1
2
// 在3000端口开服务
$ hexo server -p 3000

deploy

在配置文件(_config.yml)中找到

1
2
3
deploy:
type:
repo:

更改配置(建议使用Git)

1
$ hexo deploy

clean

1
$ hexo clean

清除缓存

Hello World

发表于 2017-02-24 |

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

123
Tianhao Cui

Tianhao Cui

私、気になります!

15 日志
© 2017 Tianhao Cui
由 Hexo 强力驱动
主题 - NexT.Pisces