BFC理解

BFC布局规则

内部的Box会在垂直方向,一个接一个地放置。
Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠。 => 用于清除上下margin重叠
每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。 => 用于左右布局
BFC的区域不会与float box重叠。
BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
计算BFC的高度时,浮动元素也参与计算。 => 用于清除浮动

归纳形成BFC的几个要点

float的值不为none;
position的值为fixed或者absolute;
display的值为 table-cell, table-caption, inline-block, flex, 或者 inline-flex中的其中一个;
overflow的值不为visible。


NodeJs 的几种文件路径

先看一个简单的栗子:

假如我们有这样的文件结构:

app/
-lib/
-common.js
-model
-task.js
-test.js

在 task.js 里编写如下的代码:

var path = require(‘path’);

console.log(dirname);
console.log(
filename);
console.log(process.cwd());
console.log(path.resolve(‘./‘));

在 model 目录下运行 node task.js 得到的输出是:

/Users/guo/Sites/learn/app/model.js
/Users/guo/Sites/learn/app/model.js/task.js
/Users/guo/Sites/learn/app/model.js
/Users/guo/Sites/learn/app/model.js

然后在 app 目录下运行 node model/task.js,得到的输出是:

/Users/guo/Sites/learn/app/model.js
/Users/guo/Sites/learn/app/model.js/task.js
/Users/guo/Sites/learn/app
/Users/guo/Sites/learn/app

那么,不好意思不是问题来了~T_T,我们可以得出一些肤浅的结论了:

dirname: 总是返回被执行的 js 所在文件夹的绝对路径 filename: 总是返回被执行的 js 的绝对路径
process.cwd(): 总是返回运行 node 命令时所在的文件夹的绝对路径
./: 跟 process.cwd() 一样、一样、一样的吗?

只有在 require() 时才使用相对路径(./, ../) 的写法,其他地方一律使用绝对路径,如下:

// 当前目录下
path.dirname(filename) + ‘/test.js’;
// 相邻目录下
path.resolve(
dirname, ‘../lib/common.js’);


ES6 Promise

Pending(进行中)、Resolved(已完成,又称Fulfilled)和Rejected(已失败)

如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve函数的参数除了正常的值以外,还可能是另一个Promise实例,表示异步操作的结果有可能是一个值,也有可能是另一个异步操作

1
2
3
4
5
6
7
8
9
10
11
12
var p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
var p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail

Promise.prototype.then()Promise.prototype.catch()

Promise实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

1
2
3
4
5
6
p.then((val) => console.log("fulfilled:", val))
.catch((err) => console.log("rejected:", err));
// 等同于
p.then((val) => console.log("fulfilled:", val))
.then(null, (err) => console.log("rejected:", err));

Promise.all()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Promise.prototype.all = function(promises) {
let results = []
return new Promise((resolve, rejected) => {
for(let i=0; i<promises.length; i++) {
promises[i].then((res) => {
results.push(res)
if (i === promises.length -1) {
resolve(results)
}
}).catch((err) => {
reject(err)
})
}
})
}

Promise.race()

Promise.resolve()

有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用。

1
2
3
Promise.resolve(42).then(function(value){
console.log(value);
});

Promise.reject()

Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。它的参数用法与Promise.resolve方法完全一致。

手写Promise的polyfill

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
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function MyPromise(executor) {
this.state = PENDING;
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
var self = this;
function resolve(val) {
if (self.state === PENDING) {
self.state = FULFILLED;
self.value = value;
self.onFulfilledCallbacks.forEach(function(fulfilledCallback) {
fulfilledCallback();
});
}
}
function rejected(reason) {
if (self.state === PENDING) {
self.state = REJECTED;
self.reason = reason;
self.onRejectedCallbacks.forEach(function(rejectedCallback) {
rejectedCallback();
});
}
}
try {
executor(resolve, rejected);
} catch (reason) {
reject(reason);
}
}
MyPromise.prototype.then = function(onResolve, onRejected) {
if (self.state === PENDING) {
self.onFulfilledCallbacks.push(() => {
onFuifilled(self.value);
});
self.onRejectedCallbacks.push(() => {
onRejected(self.reason);
});
}
if (this.state === FULFILLED) {
onResolve(this.value)
}
if (this.state === REJECTED) {
onRejected(this.reason)
}
}


Sublime Text快捷键

  • Ctrl+Shift+P:打开命令面板
  • Ctrl+P:搜索项目中的文件
  • Ctrl+G:跳转到第几行
  • Ctrl+W:关闭当前打开文件
  • Ctrl+Shift+W:关闭所有打开文件
  • Ctrl+Shift+V:粘贴并格式化
  • Ctrl+D:选择单词,重复可增加选择下一个相同的单词
  • Ctrl+L:选择行,重复可依次增加选择下一行
  • Ctrl+Shift+L:选择多行
  • Ctrl+Shift+Enter:在当前行前插入新行
  • Ctrl+X:删除当前行
  • Ctrl+M:跳转到对应括号
  • Ctrl+U:软撤销,撤销光标位置
  • Ctrl+J:选择标签内容
  • Ctrl+F:查找内容
  • Ctrl+Shift+F:查找并替换
  • Ctrl+H:替换
  • Ctrl+R:前往 method
  • Ctrl+N:新建窗口
  • Ctrl+K+B:开关侧栏
  • Ctrl+Shift+M:选中当前括号内容,重复可选着括号本身
  • Ctrl+F2:设置/删除标记
  • Ctrl+/:注释当前行
  • Ctrl+Shift+/:当前位置插入注释
  • Ctrl+Alt+/:块注释,并Focus到首行,写注释说明用的
  • Ctrl+Shift+A:选择当前标签前后,修改标签用的
  • F11:全屏
  • Shift+F11:全屏免打扰模式,只编辑当前文件
  • Alt+F3:选择所有相同的词
  • Alt+.:闭合标签
  • Alt+Shift+数字:分屏显示
  • Alt+数字:切换打开第N个文件
  • Shift+右键拖动:光标多不,用来更改或插入列内容
  • 鼠标的前进后退键可切换Tab文件
  • 按Ctrl,依次点击或选取,可需要编辑的多个位置
  • 按Ctrl+Shift+上下键,可替换行

发布订阅模式

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
class EventEmitter {
constructor() {
// 事件对象,存放订阅的名字和事件
this.events = {};
}
// 订阅事件的方法
on(eventName,callback) {
if (!this.events[eventName]) {
// 注意时数据,一个名字可以订阅多个事件函数
this.events[eventName] = [callback]
} else {
// 存在则push到指定数组的尾部保存
this.events[eventName].push(callback)
}
}
// 触发事件的方法
emit(eventName) {
// 遍历执行所有订阅的事件
this.events[eventName] && this.events[eventName].forEach(cb => cb());
}
// 移除订阅事件
removeListener(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(cb => cb != callback)
}
}
// 只执行一次订阅的事件,然后移除
once(eventName,callback) {
// 绑定的时fn, 执行的时候会触发fn函数
let fn = () => {
callback(); // fn函数中调用原有的callback
this.removeListener(eventName,fn); // 删除fn, 再次执行的时候之后执行一次
}
this.on(eventName,fn)
}
}

常用函数实现

new

1
2
3
4
5
6
function new(con, ...args) {
let obj = Object.create({})
obj.__proto__ = con.prototype
let res = con.apply(obj, args)
return typeof res === 'object' ? res : obj
}

instanceof

1
2
3
4
5
6
7
8
9
function instanceof(a, b) {
while(a.__proto__) {
if (a.__proto__ === b.prototype) {
return true
}
a = a.__proto__
}
return false
}

防抖

1
2
3
4
5
6
7
8
9
10
11
function debounce(fn, wait) {
let timer = null
return function(...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, args)
}, wait)
}
}

节流

1
2
3
function throttle(fn, wait) {
}

call

apply

bind

深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
// 递归
function deepClone(obj) {
if(!obj || typeof obj != 'object') return obj
if(obj instanceof RegExp) return new RegExp(obj)
var cloneObj = new obj.constructor
for(let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key])
}
}
return cloneObj
}