Full Code of laihuamin/JS-total for AI

master 6b51cafb3c10 cached
72 files
250.9 KB
97.2k tokens
23 symbols
1 requests
Download .txt
Showing preview only (361K chars total). Download the full file or copy to clipboard to get everything.
Repository: laihuamin/JS-total
Branch: master
Commit: 6b51cafb3c10
Files: 72
Total size: 250.9 KB

Directory structure:
gitextract_p7xxa54u/

├── .gitignore
├── README.md
├── blog/
│   ├── ES6系列——let和const深入理解.md
│   ├── Es2016、2017新特性(上).md
│   ├── Host解析.md
│   ├── JS知识总揽.md
│   ├── JS系列(一).md
│   ├── JS系列(三).md
│   ├── JS系列(二).md
│   ├── JS系列(四).md
│   ├── JavaScript之(a==1 && a==2 && a==3)能输出ture么?.md
│   ├── JavaScript之a==1&&a==2&&a==3能输出ture么?.md
│   ├── []为false,!![]为true,[true] == 'true'为true,傻傻分不清.md
│   ├── ajax简述.md
│   ├── console命令还能这么用.md
│   ├── cookie.md
│   ├── css代码规范.md
│   ├── css层叠关系.md
│   ├── html代码规范.md
│   ├── instance_init.md
│   ├── instanceof你懂吗.md
│   ├── js代码规范.md
│   ├── js的实用小技巧.md
│   ├── keep-alive.md
│   ├── lifecycle.md
│   ├── nodejs几种文件路径及path模块.md
│   ├── node中的EventLoop.md
│   ├── require工作原理.md
│   ├── sticky你了解多少.md
│   ├── this的重新认识.md
│   ├── vdom的vnode.md
│   ├── vue生命周期详解.md
│   ├── webpack中hash和chunkhash是不是很眼熟?.md
│   ├── webpack中的entry和context.md
│   ├── 【译】node js event loop part 1.1.md
│   ├── 【译】nodeJsEventLoopPart1.1.md
│   ├── 一看就懂的JS抽象语法树.md
│   ├── 从babel讲到AST.md
│   ├── 函数.md
│   ├── 前端和后端的发展路径.md
│   ├── 响应式原理.md
│   ├── 定时器和计时器.md
│   ├── 建议使用nvm管理node.md
│   ├── 把session聊清楚.md
│   ├── 模块.md
│   ├── 汇总2017JS项目,总结我们从中学到了什么?.md
│   ├── 浏览器中的eventloop.md
│   ├── 细说Array.prototype.slice.call.md
│   ├── 编写一个分析代码依赖的工具(一).md
│   ├── 编码与解码.md
│   ├── 聊聊数据接口.md
│   └── 🚀述说Parcel:A blazing fast, zero configuration web application bundler 📦.md
├── hilo/
│   ├── flybird/
│   │   ├── index.html
│   │   └── js/
│   │       ├── asset.js
│   │       ├── bird.js
│   │       ├── game.js
│   │       ├── overScene.js
│   │       └── readyScene.js
│   ├── hilo-flash.js
│   └── hilo-standalone.js
├── package.json
└── test/
    ├── es-2016.js
    ├── setTimeout.js
    ├── vdom/
    │   └── vnode.js
    └── websocket/
        ├── CHANGES
        ├── LICENSE
        ├── README.md
        ├── count.sh
        ├── index.html
        ├── node-socket.js
        ├── web-socket.html
        └── websocketd

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
image

ceshi.js

js知识点1.gliffy

node_modules/

package-lock.json

================================================
FILE: README.md
================================================
### 博客

##### [所有文章](https://github.com/laihuamin/JS-total/issues)

#### 【ES6】系列

##### [【ES6】变量声明(整理篇)](https://github.com/laihuamin/JS-total/issues/51)

##### [【ES6】解构赋值(整理篇)](https://github.com/laihuamin/JS-total/issues/52)

##### [【ES6】class基础(整理篇)](https://github.com/laihuamin/JS-total/issues/53)

#### 高质量篇

##### [把 cookie 聊清楚](blog/cookie.md)

##### [你所不知道的 setTimeout、setInterval](blog/定时器和计时器.md)

##### [webpack 中 hash 和 chunkhash 是不是很眼熟?](blog/webpack中hash和chunkhash是不是很眼熟?.md)

##### [【译】node js event loop part 1.1](blog/【译】nodeJsEventLoopPart1.1.md)

##### [编写一个分析代码依赖的工具(一)](blog/编写一个分析代码依赖的工具(一).md)

##### [汇总 2017JS 项目,总结我们从中学到了什么?](blog/汇总2017JS项目,总结我们从中学到了什么?.md)

##### [🚀 述说 Parcel:A blazing fast, zero configuration web application bundler 📦](https://github.com/laihuamin/JS-total/issues/30)

##### [从 babel 讲到 AST](从babel讲到AST.md)

#### 备注:

> 答案有错误的地方可以提交 issues,喜欢我写的博文的,欢迎欢迎 Star。
>
> 在 github 项目的右上角,有三个按钮,分别是 watch、star、fork,新来的同学注意不要用错了,无休止的邮件提醒会给你造成不必要的信息干扰。
>
> 当你选择 Watching,表示你以后会关注这个项目的全部动态,以后只要这个项目发生变动,被别人提交了 pull request、被发起了 issue 等情况你都会收到邮件通知。
>
> star 相当于是点赞或收藏,方便以后查找。
>
> fork 表示你想要补充完善这个项目的内容。

#### css 篇

##### [css3——sticky 深度挖掘](blog/sticky你了解多少.md)

#### JS 篇

##### [JS 知识流程图,详细](blog/JS知识总揽.md)

##### [浏览器中的event loop](blog/浏览器中的eventloop.md)

##### [\[\]为false,!!\[\]为true,\[true\] == 'true'为true,傻傻分不清](blog/\[\]为false,!!\[\]为true,\[true\] == 'true'为true,傻傻分不清.md)

##### [Es2016、2017新特性(上)](blog/Es2016、2017新特性(上).md)

##### [前端和后端的发展路径](blog/前端和后端的发展路径.md)

##### [细说 Array.prototype.slice.call](blog/细说Array.prototype.slice.call.md)

##### [ES6 系列——let 和 const 深入理解](blog/ES6系列——let和const深入理解.md)

##### [模块](blog/模块.md)

##### [函数](blog/函数.md)

##### [this 的重新认识](blog/this的重新认识.md)

##### [你所不知道的 setTimeout、setInterval](blog/定时器和计时器.md)

##### [instanceof 你懂吗](blog/instanceof你懂吗.md)

##### [JS 实用小技巧](blog/js的实用小技巧.md)

##### [编码和解码——你不能不懂](blog/编码与解码.md)

##### [把 cookie 聊清楚](blog/cookie.md)

##### [一篇 ajax 的故事](blog/ajax简述.md)

##### [console 命令还能这么用](blog/console命令还能这么用.md)

##### [一看就懂的 JS 抽象语法树](blog/一看就懂的JS抽象语法树.md)

##### [JavaScript:(a==1 && a==2 && a==3)能输出 ture 么?](blog/JavaScript之a==1&&a==2&&a==3能输出ture么?.md)

##### [从 babel 讲到 AST](从babel讲到AST.md)

#### 面试总结篇

##### [JS 系列(一)](blog/JS系列(一).md)

##### [JS 系列(二)](blog/JS系列(二).md)

##### [JS 系列(三)](blog/JS系列(三).md)

##### [JS 系列(四)](blog/JS系列(四).md)

#### Vue 篇

##### [keep-alive](blog/keep-alive.md)

##### [vdom 的 vnode](blog/vdom的vnode.md)

##### [vue 生命周期详解](blog/vue生命周期详解.md)

#### Web 篇

##### [Host 文件是什么](blog/Host解析.md)

#### nodeJS 篇

##### [node中的EventLoop](blog/node中的EventLoop.md)

##### [require工作原理](blog/require工作原理.md)

##### [建议使用 nvm 管理 node](blog/建议使用nvm管理node.md)

##### [nodejs 几种文件路径及 path 模块](blog/nodejs几种文件路径及path模块.md)

##### [【译】node js event loop part 1.1](blog/【译】nodeJsEventLoopPart1.1.md)

#### webpack 篇

##### [webpack 中 hash 和 chunkhash 是不是很眼熟?](blog/webpack中hash和chunkhash是不是很眼熟?.md)

##### [webpack 中的 entry 和 context](blog/webpack中的entry和context.md)

#### 代码规范

##### [css 代码规范](blog/css代码规范.md)

##### [js 代码规范](blog/js代码规范.md)

##### [html 代码规范](blog/html代码规范.md)

#### 项目

##### [编写一个分析代码依赖的工具(一)](blog/编写一个分析代码依赖的工具(一).md)


================================================
FILE: blog/ES6系列——let和const深入理解.md
================================================
### 前言

在ES6中多了两个变量定义的操作符——let和const,在现在项目中,ES6已经是不可获缺,我打算在掘金上整理一套ES6的系列,会收集常用的知识点,喜欢的可以点个喜欢,关注,或者可以去[github](https://github.com/laihuamin/JS-total)点个star

### ES5没有块级作用域

大家都知道js是没有块级作用域的,我们先了解一下块级作用域。

> 任何一对花括号中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的

了解定义之后,我们👀一个用烂了的例子:
```js
for(var i = 0; i < 10; i++) {
      console.log(1);
    }
console.log(i);
```
上面这个例子,最外面会输出10。显而易见,没有块级作用域。

### ES5可以怎么创建块级作用域

- 立即执行函数

关于这一点我们可以看道面试题就能明白。

```js
var func = [];
for(var i = 0; i < 10; i++) {
  func.push(function(){
    console.log(i)
  });
}
func.forEach((func) => {
  func();
})
//10个10
```
为什么会产生这样的事情呢?因为在循环内部这些i都是用同一个词法作用域的,换言之,这10个i用的都是最后的输出的i,最后的i也就等于10。
而立即执行函数就不一样,他用函数作用域代替块级作用域,强制在循环内部创建副本,以便输出1,2,3,4...

```js
var func = [];
for(var i = 0; i < 10; i++) {
  func.push((function(value){
    return function() {
      console.log(value)
    }
  })(i));
}
func.forEach((func) => {
  func();
})
//会输出1到9
```
对立即执行函数有兴趣的好可以看看这么几篇博文,我在这里就不用大篇幅赘述,我们简单过一下下面几种方法,然后去将我们今天的主角们。
[推荐博文](https://segmentfault.com/a/1190000003985390)
[推荐博文](http://www.cnblogs.com/TomXu/archive/2011/12/31/2289423.html)
- try-catch

try-catch这个创建块级作用域在红皮书中有提到过,我没用过,觉得这个知识点了解就可以,也不常用。

```js
try {
   throw 'myException';
}
catch (e) {
   console.log(e);
}
console.log(e);
//第一个输出myException,第二个输出e is not defined
```

### ES6中的块级作用域

在ES6中提出了let和const,我们可以看一下下面这几个例子,在每次循环中,let会创建一个词法作用域,并与之前迭代中同名变量的值将其初始化。

```js
for(let i = 0; i < 10; i++) {
      console.log(1);
    }
console.log(i);
//报错i is not defined
```

```js
const func = [];
for(let i = 0; i < 10; i++) {
  func.push(function(){
    console.log(i)
  });
}
func.forEach((func) => {
  func();
})
//会输出0到9
```
这个特性同样适用于for in

```js
const funcs = [],
  obj = {
    a: 'lai',
    b: 'hua',
    c: 'min'
  };
for (let key in obj) {
  funcs.push(() => {
    console.log(key)
  })
}
funcs.forEach((func) => {
  func()
});
//输出的是a  b  c
```

### 不能重复声明变量

在一个作用域中,已经用var、let、const声明过某标识符之后,不能在用let、const声明变量,不然会抛出错误

```js
var a = 0;
let a = 10;
// 报错
```
但是在作用域中嵌套一个作用域就不会,看下面这个例子

```js
var a = 0;
if (true) {
  let a = 10;
}
// 不会报错
```
const效果也是一致的,不过const用于定义常量,const还有一下特性

### const声明变量

当你用const声明变量,不初始化的话,就会发生报错

```js
const a;
// 报错
```

而const的本质是声明的,不允许修改绑定,但是允许修改值,所以大多数场景,我们都用const来声明对象,那样对象的指针不会改变,相对来说安全,看一下下面的例子

```js
const person = {
  name = 'laihuamin'
}
person.name = 'lai';
//到这里不会发生报错,只会改变值
person = {};
//这里改变了对象的指针,所以会发生报错
```

而const不止能用于对象指针绑定,还能运用在for in的迭代中,因为每次迭代不会修改已有的绑定,而是会创建新的绑定。看下面的例子

```js
const funcs = [],
  obj = {
    a: 'lai',
    b: 'hua',
    c: 'min'
  };
for (const key in obj) {
  funcs.push(() => {
    console.log(key)
  })
}
funcs.forEach((func) => {
  func()
});
//输出a b c
```
但是在循环中就不能用,循环会修改已有的绑定,而const定义的常量时不能修改绑定的,所以会报错。

### 没有变量提升

对于ES5的变量提升有一个经典的考题。如下:

```js
var a = 10;
(function () {
  console.log(a);
  var a = 1;
})();
// 这个会输出undefined
```
其实这个很好理解,js作用域连是从内向外寻找变量的,那么函数的作用域中有a这个变量,由于var会发生变量提升,就相当于下面这个过程

```js
var a;
console.log(a);
a = 1;
```
所以,这个a变量就是undefined。而let和const就不一样,把var换成let或者const都会报错。

### 暂时性死区

我们先来看例子,再来根据例子解析:

```js
console.log(a);
let a = 10;
//Uncaught ReferenceError: a is not defined
```
let和const定义的变量是存在暂时性死区的,而var没有,我们来了解一下两个操作符的工作原理:
对于var而言,当进入var变量的作用域时,会立即为他创建存储空间,并对它进行初始化,赋值为undefined,当函数加载到变量声明语句时,会根据语句对变量赋值。
而let和const却不一样,当进入let变量的作用域时,会立即给他创建存储空间,但是不会对他进行初始化,所以会抛出如上错误。

而对于typeof操作符来说,结果是一致的,一样会报错:
```js
console.log(typeof a);
let a = 10;
//Uncaught SyntaxError: Identifier 'a' has already been declared
```
所以最佳实践是把声明的变量全部提到作用域的开头,这样既方便管理,又能避免不必要的麻烦
### 全局变量绑定
var声明全局变量的时候,当使用关键词,那么就会覆盖掉window对象上原本拥有的属性,我们看一下下面这个例子:

```js
var RegExp = 'lai';
console.log(window.RegExp);
var a = 'hua';
console.log(window.a);
var Array = 'min';
console.log(window.Array);
var b = new Array();
//lai
//hua
//min
//Uncaught TypeError: Array is not a constructor
```
而换成let和const的时候就不会发生这样的事情,我们用同样的例子来看一看:
```js
let RegExp = 'lai';
console.log(window.RegExp);
let a = 'hua';
console.log(window.a);
let Array = 'min';
console.log(window.Array);
let b = new window.Array();
console.log(b);
//ƒ RegExp() { [native code] }
//undefined
//ƒ Array() { [native code] }
//[]
```
结果和上面一样,我们更可以进一步认证
```js
let RegExp = 'lai';
console.log(RegExp === window.RegExp);
var Array = 'hua';
console.log(Array === window.Array);

//会输出 false 和 true
```

### 总结

根据以上讲的,最佳实践应该是,能用const定义对象的,不要用let,能用let定义变量的,不要用var。至于他的很多特性,了解了能更好的帮助你运用。如果觉得笔者写的可以的点一个喜欢,之后还会持续更新其他板块,希望能给笔者的[github](https://github.com/laihuamin/JS-total)点个star,谢谢支持

================================================
FILE: blog/Es2016、2017新特性(上).md
================================================
### 前言

es2015虽然是主流,但是每年都会有新的东西更新,在这些东西中,有许多东西值得我们去学习,以及使用,本篇文章,将提供一些平常业务开发中经常会用到的方法。希望能对大家的学习有帮助。个人的[github博客](https://github.com/laihuamin/JS-total/issues)


### ECMAScript 2016

#### 1、Array.prototype.includes

includes这个方法,是检测数组中是否含有相应的元素,返回的值是true和false。与indexOf方法功能相似,但是还会有许多差异性。

[![includes.png](https://i.loli.net/2018/07/07/5b40d60aac55d.png)](https://i.loli.net/2018/07/07/5b40d60aac55d.png)

其第二个参数还可以代表查询的位置是否正确

[![includes-other.png](https://i.loli.net/2018/07/07/5b40d7d0dc1eb.png)](https://i.loli.net/2018/07/07/5b40d7d0dc1eb.png)

#### 2、求幂操作符

在es2016里面平方操作变得更加渐变,只要使用操作符`**`就可以实现。

![](https://s1.ax1x.com/2018/07/09/PmxeAS.png)

### ECMAScript 2017

#### 1、Object.value()

Object.value()的功能其实和Object.keys()相似,主要作用是取得对象的值,放入到数组中,同样不包括任何原型链中的值。

![Object.value()](https://s1.ax1x.com/2018/07/09/PmzLdK.png)

#### 2、Object.entries()

Object.entries()也是和Object.key()相关的,该方法是返回一个数组,数组的元素是对象自身的所有可以遍历的键值对数组

![Object.entries()](https://s1.ax1x.com/2018/07/09/PnpxKA.png)

#### 3、String.padStart

该方法的作用就是用自定义的字符补全字符串的长度,比如我们平常在做的,小于10的时候自动补零就可以用这个实现。

**例子1:**

![stringPadStart.png](https://i.loli.net/2018/07/11/5b461df8a69d9.png)

#### 4、String.padEnd

该方法和String.padStart相同,只是前者是从字符串的头部开始补全,后者是从字符串的尾部开始补全。

### 总结

这一篇只是介绍了一些实用的方法,下一篇会具体分析一下async/await。

================================================
FILE: blog/Host解析.md
================================================
## host

### 前言

在实习过程中,这个东西用到的确实多,当初只知道我该怎么去配这个东西,预发调试的时候,这个东西用的很多,等会我会先介绍工具,然后再去了解这个东西的原理,工作中应该都能用的到,我去两家公司实习过,确实都用到了,所以,觉得有用的可以收藏,点个喜欢,或者关注都可以,或者在去我的[github](https://github.com/laihuamin/JS-total)点个赞也是极好的,谢谢支持。


### host是什么

hosts文件(域名解析文件)是一个用于储存计算机网络中各节点信息的计算机文件。这个文件负责将主机名称映射到相应的IP地址。hosts文件通常用于补充或取代网络中DNS的功能。和DNS不同的是,计算机的用户可以直接对hosts文件进行控制。
或许可以举个例子,讲的通俗点,我们就拿百度举例子好了,`www.baidu.com`的ip地址长久不发生变化的话,你就可以将百度的网址写到host里面,那样的话我们,以后访问百度这个地址就不在需要DNS解析了,当你输入www.baidu.com之后,会直接去host文件中寻找,直接进行访问。

### host文件位置

![host文件位置](http://laihuamin.oss-cn-beijing.aliyuncs.com/host-location.png)

### 神器推荐——switchHosts

这个工具真的好用,和charles一样,如果已经在用了的朋友可以跳过这一段,还没有用的,我来安利一下,没有接触这款工具的时候,我们修改host文件有以下几种方法:

#### 覆盖法

先将host文件是不允许直接修改的,先将host文件复制出来,然后修改后在复制进去,覆盖掉。

#### mac修改host
[mac修改host](http://www.52mac.com/soft/5966-1-1.html)

#### switchHosts
但是switchHosts用起来就比较简单,按钮切换就行

![switchHosts](http://laihuamin.oss-cn-beijing.aliyuncs.com/switchHost.png)

### host的文件格式

其实host的书写格式很简单,如下:
```
IP地址   主机或者域名   [主机的别名] [主机的别名]
```
host文件的注释格式,如下:
```
# ...
```
我们可以看一下以下这个例子
![host](http://laihuamin.oss-cn-beijing.aliyuncs.com/host.png)

### host文件能干嘛
能干嘛是我们最想要了解到,知道是什么之后,主要作用有以下几点:
- 加速DNS

正如他是什么里面书写的

- 将域名映射到本地

一般我们不改变host文件的时候,我文件中会有以下内容
```
127.0.0.1  localhost localhost.localdomain 
```
这个是代表什么呢,其实localhost大家在开发中也经常用到,而127.0.0.1这个是本地的ip地址,我们可以用localhost:8080访问本地,我们也可以用127.0.0.1:8080访问本地是一个道理.那么我们在本地调试的时候,可以把线上的域名映射到本地,那么一些页面路由的跳转,调试起来会方便很多

### 总结

写这篇文章的目的一来是介绍一个工具switchHosts,二来是让大家了解以下hosts文件,因为很多公司都会自己配一些host。这些host我觉得第一可以方便调试,还有就是让一些域名不受到污染,因为域名绑定的dns是错误的,而host中的ip地址才是正确的。如果觉得我写的还可以的给我的github点个star,谢谢支持

================================================
FILE: blog/JS知识总揽.md
================================================
### JS知识导图
![](../image/js知识点1.png)

================================================
FILE: blog/JS系列(一).md
================================================
> 学前端也快一年了,最近想试试大公司的面试,然后这里把所有的知识点都整理出来,然后慢慢消化。该片总字数:1494,速读3分钟,普通阅读5分钟。有兴趣的可以关注一下我的[blog](https://github.com/laihuamin/JS-total/issues)


### 总的知识点概览

语法、数据类型、运算、对象、function、继承、闭包、作用域、原型链、事件、RegExp、JSON、Ajax、DOM、BOM、内存泄漏、跨域、异步加载、模板引擎、前端MVC、前端MVVM、路由、模块化、Http、Canvas、jQuery、EMCAScript、ES6、NodeJS、Vue、React

### 语法篇

- 变量声明

有三种:var、let、const(具体的知识点放到后面总结)

- 变量名

区分大小写,必须以字母、下划线(_)或者美元符号($)开头,后续可以是数字或字母

- 变量的作用域

声明在所有函数之外的叫全局作用域,可以被这个模块中的所有代码访问。
声明在函数内部的叫局部作用域,只能被该函数内部访问

- 变量声明提升

```js
console.log(a);//undefined
var a = 1;
```
这里就是变量提升的效果,其实相当于发生了以下过程:

```js
var a;
console.log(a)
a = 1;
```
仅对var有效

- 函数提升

定义一个函数有两种方式,一个是函数声明,还有一个是函数表达式。而只有函数声明会被提升到顶部,不包括函数表达式。

```js
// 函数声明

foo();  //bar
function foo() {
  console.log('bar')
}

//函数表达式

var baz = function() {
  console.log('baz');
}
```

### 动态类型

JavaScript是一种弱类型或者说动态语言,你不用提前设定变量类型,在运行的时候会自动确定。

```js
var foo = 2; //foo is a Number now
var foo = 'baz' //foo is a String now
var foo = true //foo is Boolean now
```

### 数据类型

加上es6的symbol之后就是七个:**六个原始类型和一个复杂数据类型**

- 原始类型:
Null、Undefined、Boolean、Number、String、Symbol
- 复杂类型:
Object

### 另外的区分方式

- 值类型:五种原始类型(string,number,boolean,null,undefined)
- 引用类型:数组、函数、对象等

### 按值传递

对于JavaScript中,对于参数传递、构造函数带return等情况都是按值传递的,对于引用类型,其实传的是对象的地址。我们可以看一下以下这个例子:

```js
//函数内部参数的改变并没有影响到外部变量
function foo(a) {
      a = a * 10;
}

var num = 10;
foo(num);
console.log(num); // 10  没有变化

function bar(b) {
    b.item = "new_value";         // 参数b得到了obj1的地址,也叫"指向obj1"
    console.log(b === obj1) ;     // true
}

var obj1 = {item: "old_value"};
bar(obj1);

console.log(obj1.item);            // new_value
```

================================================
FILE: blog/JS系列(三).md
================================================
> 学前端也快一年了,最近想试试大公司的面试,然后这里把所有的知识点都整理出来,然后慢慢消化。该片总字数:2585,速读四分半,普通阅读7分钟。有兴趣的可以关注一下我的[blog](https://github.com/laihuamin/JS-total/issues)

### 总的知识点概览

语法、数据类型、运算、对象、function、继承、闭包、作用域、原型链、事件、RegExp、JSON、Ajax、DOM、BOM、内存泄漏、跨域、异步加载、模板引擎、前端MVC、前端MVVM、路由、模块化、Http、Canvas、jQuery、EMCAScript、ES6、NodeJS、Vue、React

### JS对象和属性

一个JS的对象可以有很多属性。我们来举个例子:

```js
var myCar = new Object();
myCar.make = "Ford";
myCar.model = "Mustang";
myCar.year = 1969;
```
当一个属性未赋值时值为undefined;

```js
console.log(myCar.noProperty)  //undefined
```

当然属性的访问不仅只有点的形式,还可以使用方括号

```js
myCar["make"] = "Ford";
```

### 枚举一个对象的所有属性

原生的三种方法:

- for...in循环:遍历对象中可枚举的属性。
- Object.keys(o):返回一个对象o自身含有(不包括原型中)的所有属性的名称的数组。
- Object.getOwnPropertyNames:该方法返回一个数组,它包含了对象o所有的属性名称(包括不可枚举)。

### 创建一个新的对象

- 使用构造函数

定义构造函数
```js
function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
```
new创建一个新的实例对象

```js
var mycar = new Car("Eagle", "Talon TSi", 1993);
```

- 直接定义

```js
var myCar = {
  make: 'Eagle',
  model: 'Talon TSi',
  year: 1993
}
```
- 使用Object.create方法

```js
//创建一个原型为null的空对象
var o = Object.create(null);
//创建一个原型为空对象的,拥有属性值p,值为24
var o = Object.create({}, {p: {value: 24}})
console.log(o.p);  //24
//该属性值是只可读,不可写、不可枚举、不可配置

//以下对象的p属性是可读可写可配置可枚举。
var o2 = Object.create({}, {
  p: {
    value: 24,
    writable: true,
    enumerable: true,
    configurable: true 
  }
})

```

### 用this来引用对象

this在这里指的是上下午指针,很多情况我们都用它来代表一个对象,我们可以看一段下面的代码:

```js
//方法
function validate(obj, lowval, hival) {
  if ((obj.value < lowval) || (obj.value > hival)) {
    alert("Invalid Value!");
  }
}
```
```html
<input type="text" name="age" size="3"
  onChange="validate(this, 18, 99)">
```
这里的this代表的就是input这个dom节点。当然它还有好多方面,以后我们细讲。

### getters和setters

getter是一个获取对象某个值的方法,而setters是一个设置对象某个值的方法。我们来看一下他们的使用:

```js
var o = {
  a: 7,
  get b() {
    return this.a + 1;
  },
  set c(d) {
    this.a = d / 2;
  }
}

console.log(o.a); //7
console.log(o.b); //8
o.c = 50;
console.log(o.a); //25
```

上述过程就是:

先输出o中a的属性值,
在输出o中b的属性值,等于a + 1,
再设定o中a的值,
最后输出设定过的a的值。

- 当我们想用getter和setter来设定对象的某一个值的时候我们可以使用Object.defineProperty这个方法。我们看下面这个例子:

```js
var d = Date.prototype;
Object.defineProperty(d, "year", {
  get: function() { return this.getFullYear() },
  set: function(y) { this.setFullYear(y) }
});
```
例子中的getFullYear和setFullYear,指的是其他的方法。

当然我们还可以用Object.defineProperties方法来把第一个例子改的更具可读性。

```js
var o = { a:0 }

Object.defineProperties(o, {
    "b": { get: function () { return this.a + 1; } },
    "c": { set: function (x) { this.a = x / 2; } }
});

o.c = 10;
console.log(o.b);
```


### 比较对象

对象是一个引用类型,两个独立声明的对象永远不可能相等,即使值相同也是,我们看下面的例子:

```js
var fruit = {name: "apple"};
var fruitbear = {name: "apple"};

fruit == fruitbear // return false
fruit === fruitbear // return false
```

只有两个对象的引用相同的时候才会返回true。

```js
// 两个变量, 同一个对象
var fruit = {name: "apple"};
var fruitbear = fruit;  // 将fruit的对象引用(reference)赋值给 fruitbear
                        // 也称为将fruitbear“指向”fruit对象
// fruit与fruitbear都指向同样的对象
fruit == fruitbear // return true
fruit === fruitbear // return true
```

================================================
FILE: blog/JS系列(二).md
================================================
> 学前端也快一年了,最近想试试大公司的面试,然后这里把所有的知识点都整理出来,然后慢慢消化。该片总字数:1770,速读三分半,普通6分钟。有兴趣的可以关注一下我的[blog](https://github.com/laihuamin/JS-total/issues)

### 总的知识点概览

语法、数据类型、运算、对象、function、继承、闭包、作用域、原型链、事件、RegExp、JSON、Ajax、DOM、BOM、内存泄漏、跨域、异步加载、模板引擎、前端MVC、前端MVVM、路由、模块化、Http、Canvas、jQuery、EMCAScript、ES6、NodeJS、Vue、React

### 运算符

运算符分类:赋值运算符、比较运算符、算数运算符、位运算符、逻辑运算符、字符串运算符、条件运算符、逗号运算符、一元运算符、关系运算符

### 赋值运算符

- 简单赋值运算符只有一个`=`。
- 复合赋值运算符:

|名字|简单操作符|
|:---:|:---:|
|加法赋值|x += y|
|减法赋值|x -= y|
|乘法赋值|x *= y|
|除法赋值|x /= y|
|求余赋值|x %= y|
|求幂赋值|x **= y|
|左位移赋值|x <<= y|
|右位移赋值|x >>= y|
|无符号右移位赋值|x >>>= y|
|按位与赋值|x &= y|
|按位异或赋值|x ^= y|
|按位或赋值|x |= y|

- 解构:这是es6中出现的一种更加复杂的赋值运算。

```js
var foo = ["one", "two", "three"];
// 不使用解构
var one   = foo[0];
var two   = foo[1];
var three = foo[2];
// 使用解构
var [one, two, three] = foo;
```

### 比较运算符

|运算符|描述|
|:---:|:---:|
|等于(==)|操作数两边相等返回true|
|不等于(!=)|操作数两边不想等返回true|
|全等(===)|操作睡两边相等且数据类型相同返回true|
|不全等(!==)|于上述相反|
|大于(>)|左边操作数大于右边操作数返回true|
|大于等于(>=)|左边操作数大于等于右边操作数返回true|
|小于(<)|左边操作数小于右边操作数返回true|
|小于等于(<=)|左边操作数小于等于右边操作数返回true|


### 算术运算符
算术运算符除了普通的加减乘除(+-*/)
|运算符|例子|
|:----:|:--:|
|求余(%)|12 % 5 = 2|
|自增(++)|var a = 3;a++;console.log(a);//4|
|自减(--)|var a =3;a--;console.log(a);//2|
|一元负值符(-)|var a = 3; console.log(-a);//-3|
|一元正值符(+)|var a = '3'; console.log(+a);//3|
|指数运算符(**)|2 ** 3 = 8|

### 位运算符

|符号|使用|描述|
|:--:|:--:|:--:|
|与|`a & b`|在a和b的二进制表示中,每位都是1的时候才返回1|
|或|`a | b`|在a和b的二进制表示中,只要有一位为1就可以返回1|
|异或|`a ^ b`|在a和b的二进制表示中,只要a和b的相同位不同就返回1|
|非|`~a`|对a的二进制表示,求反|
|左移|`a << b`|把a的二进制串向左移b位,右边移入0|
|右移|`a >> b`|把a的二进制串向右移b位,左边移入0|
|无符号右移|`a >>> b`|把a的二进制表示向右移动b位,丢弃被移出的所有位,并把左边空出的位都填充为0|


### 逻辑运算符

我们先介绍一下有三个逻辑运算符,然后讲几个运用技巧

|运算符|范例|描述|
|:--:|:--:|:----:|
|与|`a && b`|a和b全为true的时候返回true|
|或|`a || b`|a和b有一个为true,就返回true|
|非|`!a`|如果a为true。那么返回false|

- 短路操作

`false` && anything  //返回false
`true` || anything //返回true
`true` && anything //anything
`false` || anything //anything`

### 条件运算符

> 条件 ? 值1 : 值2

例如:

`var status = (age >= 18) ? "adult" : "minor";`

这样会比if-else简便的多。

================================================
FILE: blog/JS系列(四).md
================================================
> 学前端也快一年了,最近想试试大公司的面试,然后这里把所有的知识点都整理出来,然后慢慢消化。该片总字数:1861,速读三分半钟,普通阅读五分钟。有兴趣的可以关注一下我的[blog](https://github.com/laihuamin/JS-total/issues)

### 总的知识点概览

语法、数据类型、运算、对象、function、继承、闭包、作用域、原型链、事件、RegExp、JSON、Ajax、DOM、BOM、内存泄漏、跨域、异步加载、模板引擎、前端MVC、前端MVVM、路由、模块化、Http、Canvas、jQuery、EMCAScript、ES6、NodeJS、Vue、React

### 描述

一般来说,一个函数是可以供外部调用的“子程序”(或者供内部调用比如递归),在JS中,函数是一等公民,因为它可以像任何对象一样拥有方法和属性。与其他对象的区别是可以被调用。

### 函数声明
函数的定义中有两种函数声明和函数表达式,而函数声明就是其中的一种。

```js
function name([param[,param[,...param]]]){statements}
```

- name 函数名
- param 传递给函数的参数名称,一个函数最多可以有255个
- statements 组成函数的语句

### 函数表达式
不以function开头的就是函数表达式。

```js
var myFunction = function() {
  statements
}
```

当函数只使用一次时,一般使用IIFE,如以下这种形式
```js
(function() {
    statements
})();
```

IIFE是在函数声明后立即调用的函数表达式。

### 函数声明vs函数表达式
我们先来举个例子:
```js
//函数声明
function foo() {}
//函数表达式
var foo = function () {}
```

方法一和方法二都是创建一个函数,且都是命名为foo,但是两者还是有区别的,js解析器中存在一种变量(函数)声明被提升的机制,也就是说变量(函数)声明会被提升到最前端,即使写在最后面的也会被提升到最前方。

我们可以看个例子:

```js
console.log(foo); // function foo() {}
console.log(bar); // undefined
function foo() {}
var bar = function bar_fn() {};
console.log(foo); // function foo() {}
console.log(bar); // function bar_fn() {}
```

### 箭头函数

箭头函数表达书存在着更简短的语法。

```js
//第一种
([param][, param]) => {statements}
//第二种
param => expression
```

- param

参数名称,零参数需要用()表示,只有一个参数的时候不需要用括号

- statements or expression

多个声明statements需要用大括号扩起来,单个的时候不需要。expression也表示隐式返回值。

举两个例子:
```js
//第一种
(a, b) => {
  var c = a + b;
  return c;
}
//第二种
n => n + 1
```
### 参数(arguments)对象

- arguments:一个包含了传递给当前执行函数参数的类似于数组的对象——类数组对象
- arguments.callee:只当前正在执行的函数(不过不建议使用)
- arguments.length:传递参数的数目

### block-level函数

从ES6开始,在严格模式下禁止使用块里的函数作用域。举个例子

```js
'use strict';

function f() {
  return 1;
}
//block-level函数
{
  function f() {
    return 2;
  }
}

f() === 1;// true

```

在非严格模式下,也不要用。块中的函数声明表现奇怪。

```js
if (shouldDefineZero) {
   function zero() {     // DANGER: 兼容性风险
      console.log("This is zero.");
   }
}
```
在ES6中,shouldDefineZero为false,那么zero永远不会被定义,但是存在历史遗留问题,ES6是新的标准,但是在以前,无论这个块是否执行,一些浏览器都会定义zero。

更加安全的方式是使用函数表达式:

```js
var zero;
if (0) {
   zero = function() {
      console.log("This is zero.");
   };
}
```

================================================
FILE: blog/JavaScript之(a==1 && a==2 && a==3)能输出ture么?.md
================================================
> 如果你能确切的答出可以,那恭喜你,你可以绕道了

### 前言
有人会说,这个问题好奇葩,放在别的语言里,这要是能输出true,估计是见鬼了,但是你别说,放在js中好真有可能。最近在一个人的推特上提了一个问题:

- 问题:Can (a==1 && a==2 && a==3) ever evaluate to true?
- 答案:yes

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/twitter1.png)

在这篇文章中,我将解释这段代码的原理:

```js
const a = {
  num: 0,
  valueOf: function() {
    return this.num += 1
  }
};
const equality = (a==1 && a==2 && a==3);
console.log(equality); // true
```

你可以打开chorme浏览器,然后打开开发者模式,在console中输入这段代码,你就可以看到输出结果([windows]: Ctrl + Shift + J [mac]: Cmd + Opt + J)

### 有什么窍门呢?

其实也没有,能有的就是js中的两个概念:

- 隐式转换
- object的valueOf函数

### 隐式转换

注意:这题里面我们用的是==而不是===,在js中==代表的是等于而不是全等,那么就存在变量的隐式转化问题。这就意味着结果会比我们所期望的更多的可能性。对于js的隐式转化,真的有很多文章,我推荐一下以下几篇博客,如果你想要了解,可以点进去:

[推荐博客](https://github.com/jawil/blog/issues/1)

### valueOf

JavaScript提供了一种将对象转化为原始值的方法:Object.prototype.valueOf(),默认情况下,返回正在被调用的对象。

我们举个例子:

```js
const a = {
  num: 0
}
```

我们可以对上述对象使用valueOf方法,他会返回一个对象。

```js
a.valueOf();
// {num: 0}
```
是不是很酷,我们可以用typeOf来检测一下这个输出结果的类型:

```js
typeof a.valueOf();
// "object"
```

为了让valueOf可以更方便将一个对象转化成原始值,我们可以重写他,换种说法就是我们可以通过valueOf来返回一个字符串、数字、布尔值等来代替一个对象,我们可以看以下代码:

```js
a.valueOf = function() {
  return this.num;
}
```

我们已经重写了原生的valueOf()方法,当我们调用valueOf的时候,他会返回a.num。那我们现在运行以下:

```js
a.valueOf();
// 0
```

我们得到0了,这很合理,因为0就是赋给a.num的值。那我们可以来做几个测试:

```js
typeof a.valueOf();
// "number"

a.num == a.valueOf()
// true
```

很好,**但为什么这个很重要呢?**

这很重要,因为当你两种不同类型的遇到相等操作符的时候,js会对其进行类型转化——它企图将操作数的类型转化为类似的。

在我们的问题中:`(a==1 && a==2 && a==3)`JavaScript会企图将对象转化成数字的类型,进行比较。**当要转化的是一个Object的时候,JavaScript会调用valueOf()方法。**

自从我们改变了valueOf()方法之后,我们能不能做到以下几点呢:

```js
a == 0

// true
```
我们做到了,异常轻松。

**现在我们需要做的的一点是:当我们每次去调用a的值的时候,能改变它。**

幸运的是,在JavaScript中有`+=`符号。

`+=`这个运算符可以轻松的去改变一个的值,我们可以举个简单的例子:

```js
let b = 1
console.log(b+=1); // 2
console.log(b+=1); // 3
console.log(b+=1); // 4
```

正如你所见的,我们每次使用加法赋值运算符,可以让我们的变量增加。

所以我们可以将这个观念用到valueOf()中。

```js
a.valueOf = function() {
  return this.num += 1;
}
```

当我们每次调用valueOf的时候,他会将变量增加1返回给我们。

随着这个改变,我们来运行下面的代码:

```js
const equality = (a==1 && a==2 && a==3);
console.log(equality); // true
```

这就是它的工作原理。

**记住下面两点:**

- 使用相等操作符,js会做强制类型转化
- 我们的对象每次调用valueOf()它的值会增加1

所以比较的时候我们每次都能得到true。

- 补充第二点的运算过程

```js
a                     == 1   -> 
a.valueOf()           == 1   -> 
a.num += 1            == 1   -> 
0     += 1            == 1   ->
1                     == 1   -> true
a                     == 2   -> 
a.valueOf()           == 2   -> 
a.num += 1            == 2   -> 
1     += 1            == 2   ->
2                     == 2   -> true
a                     == 3   -> 
a.valueOf()           == 3   -> 
a.num += 1            == 3   -> 
2     += 1            == 3   ->
3                     == 3   -> true
```

### 总结

谢谢你观看这个小实验,希望你能从中学到东西,有兴趣的朋友也可以去我的[github](https://github.com/laihuamin/JS-total)点个star,你的支持是我持续输出的动力,谢谢!!!

================================================
FILE: blog/JavaScript之a==1&&a==2&&a==3能输出ture么?.md
================================================
> 如果你能确切的答出可以,那恭喜你,你可以绕道了

### 前言
有人会说,这个问题好奇葩,放在别的语言里,这要是能输出true,估计是见鬼了,但是你别说,放在js中好真有可能。最近在一个人的推特上提了一个问题:

- 问题:Can (a==1 && a==2 && a==3) ever evaluate to true?
- 答案:yes

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/twitter1.png)

在这篇文章中,我将解释这段代码的原理:

```js
const a = {
  num: 0,
  valueOf: function() {
    return this.num += 1
  }
};
const equality = (a==1 && a==2 && a==3);
console.log(equality); // true
```

你可以打开chorme浏览器,然后打开开发者模式,在console中输入这段代码,你就可以看到输出结果([windows]: Ctrl + Shift + J [mac]: Cmd + Opt + J)

### 有什么窍门呢?

其实也没有,能有的就是js中的两个概念:

- 隐式转换
- object的valueOf函数

### 隐式转换

注意:这题里面我们用的是==而不是===,在js中==代表的是等于而不是全等,那么就存在变量的隐式转化问题。这就意味着结果会比我们所期望的更多的可能性。对于js的隐式转化,真的有很多文章,我推荐一下以下几篇博客,如果你想要了解,可以点进去:

[推荐博客](https://github.com/jawil/blog/issues/1)

### valueOf

JavaScript提供了一种将对象转化为原始值的方法:Object.prototype.valueOf(),默认情况下,返回正在被调用的对象。

我们举个例子:

```js
const a = {
  num: 0
}
```

我们可以对上述对象使用valueOf方法,他会返回一个对象。

```js
a.valueOf();
// {num: 0}
```
是不是很酷,我们可以用typeOf来检测一下这个输出结果的类型:

```js
typeof a.valueOf();
// "object"
```

为了让valueOf可以更方便将一个对象转化成原始值,我们可以重写他,换种说法就是我们可以通过valueOf来返回一个字符串、数字、布尔值等来代替一个对象,我们可以看以下代码:

```js
a.valueOf = function() {
  return this.num;
}
```

我们已经重写了原生的valueOf()方法,当我们调用valueOf的时候,他会返回a.num。那我们现在运行以下:

```js
a.valueOf();
// 0
```

我们得到0了,这很合理,因为0就是赋给a.num的值。那我们可以来做几个测试:

```js
typeof a.valueOf();
// "number"

a.num == a.valueOf()
// true
```

很好,**但为什么这个很重要呢?**

这很重要,因为当你两种不同类型的遇到相等操作符的时候,js会对其进行类型转化——它企图将操作数的类型转化为类似的。

在我们的问题中:`(a==1 && a==2 && a==3)`JavaScript会企图将对象转化成数字的类型,进行比较。**当要转化的是一个Object的时候,JavaScript会调用valueOf()方法。**

自从我们改变了valueOf()方法之后,我们能不能做到以下几点呢:

```js
a == 0

// true
```
我们做到了,异常轻松。

**现在我们需要做的的一点是:当我们每次去调用a的值的时候,能改变它。**

幸运的是,在JavaScript中有`+=`符号。

`+=`这个运算符可以轻松的去改变一个的值,我们可以举个简单的例子:

```js
let b = 1
console.log(b+=1); // 2
console.log(b+=1); // 3
console.log(b+=1); // 4
```

正如你所见的,我们每次使用加法赋值运算符,可以让我们的变量增加。

所以我们可以将这个观念用到valueOf()中。

```js
a.valueOf = function() {
  return this.num += 1;
}
```

当我们每次调用valueOf的时候,他会将变量增加1返回给我们。

随着这个改变,我们来运行下面的代码:

```js
const equality = (a==1 && a==2 && a==3);
console.log(equality); // true
```

这就是它的工作原理。

**记住下面两点:**

- 使用相等操作符,js会做强制类型转化
- 我们的对象每次调用valueOf()它的值会增加1

所以比较的时候我们每次都能得到true。

- 补充第二点的运算过程

```js
a                     == 1   -> 
a.valueOf()           == 1   -> 
a.num += 1            == 1   -> 
0     += 1            == 1   ->
1                     == 1   -> true
a                     == 2   -> 
a.valueOf()           == 2   -> 
a.num += 1            == 2   -> 
1     += 1            == 2   ->
2                     == 2   -> true
a                     == 3   -> 
a.valueOf()           == 3   -> 
a.num += 1            == 3   -> 
2     += 1            == 3   ->
3                     == 3   -> true
```

### 总结

谢谢你观看这个小实验,希望你能从中学到东西,有兴趣的朋友也可以去我的[github](https://github.com/laihuamin/JS-total)点个star,你的支持是我持续输出的动力,谢谢!!!

================================================
FILE: blog/[]为false,!![]为true,[true] == 'true'为true,傻傻分不清.md
================================================
### 先看看官方对于隐式转换的定义
![abstract-equality.png](https://i.loli.net/2018/08/21/5b7b624412c35.png)

注意图片中的第7条:If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
第9条:If Type(x) is Object and Type(y) is either String or Number,return the result of the comparison ToPrimitive(x) == y.

那么我们以[] == false为例,先对x进行ToPrimitive(x),在对y进行ToNumber(y).

这里我们先来了解一下ToPrimitive()和ToNumber()的源码

### ToPrimitive和ToNumber

ToPrimitive
```js
// ECMA-262, section 9.1, page 30. Use null/undefined for no hint,
// (1) for number hint, and (2) for string hint.
function ToPrimitive(x, hint) {  
  // Fast case check.
  if (IS_STRING(x)) return x;
  // Normal behavior.
  if (!IS_SPEC_OBJECT(x)) return x;
  if (IS_SYMBOL_WRAPPER(x)) throw MakeTypeError(kSymbolToPrimitive);
  if (hint == NO_HINT) hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT;
  return (hint == NUMBER_HINT) ? DefaultNumber(x) : DefaultString(x);
}

// ECMA-262, section 8.6.2.6, page 28.
function DefaultNumber(x) {  
  if (!IS_SYMBOL_WRAPPER(x)) {
    var valueOf = x.valueOf;
    if (IS_SPEC_FUNCTION(valueOf)) {
      var v = %_CallFunction(x, valueOf);
      if (IsPrimitive(v)) return v;
    }

    var toString = x.toString;
    if (IS_SPEC_FUNCTION(toString)) {
      var s = %_CallFunction(x, toString);
      if (IsPrimitive(s)) return s;
    }
  }
  throw MakeTypeError(kCannotConvertToPrimitive);
}

// ECMA-262, section 8.6.2.6, page 28.
function DefaultString(x) {  
  if (!IS_SYMBOL_WRAPPER(x)) {
    var toString = x.toString;
    if (IS_SPEC_FUNCTION(toString)) {
      var s = %_CallFunction(x, toString);
      if (IsPrimitive(s)) return s;
    }

    var valueOf = x.valueOf;
    if (IS_SPEC_FUNCTION(valueOf)) {
      var v = %_CallFunction(x, valueOf);
      if (IsPrimitive(v)) return v;
    }
  }
  throw MakeTypeError(kCannotConvertToPrimitive);
}
```

大致的代码逻辑是:
- 如果变量为字符串,直接返回
- 如果!IS_SPEC_OBJECT(x),直接返回
- 如果IS_SYMBOL_WRAPPER(x),则抛出异常
- 否则会根据传入的hint来调用DefaultNumber和DefaultString,比如如果为Date对象,会调用DefaultString
  - DefaultNumber:首先x.valueOf,如果为primitive,则返回valueOf后的值,否则继续调用x.toString,如果为primitive,则返回toString后的值,否则抛出异常
  - DefaultString:和DefaultNumber正好相反,先调用toString,如果不是primitive再调用valueOf
  
ToNumber

```js
// ECMA-262, section 9.3, page 31.
function ToNumber(x) {  
  if (IS_NUMBER(x)) return x;
  // 字符串转数字调用StringToNumber
  if (IS_STRING(x)) {
    return %_HasCachedArrayIndex(x) ? %_GetCachedArrayIndex(x) : %StringToNumber(x);
  }
  // 布尔型转数字时true返回1,false返回0
  if (IS_BOOLEAN(x)) return x ? 1 : 0;
  // undefined返回NAN
  if (IS_UNDEFINED(x)) return NAN;
  // Symbol抛出异常,例如:Symbol() + 1
  if (IS_SYMBOL(x)) throw MakeTypeError(kSymbolToNumber);
  return (IS_NULL(x)) ? 0 : ToNumber(DefaultNumber(x));
}
```

ToString

```js
// ECMA-262, section 9.8, page 35.
function ToString(x) {  
  if (IS_STRING(x)) return x;
  // 数字转字符串,调用内部的_NumberToString
  if (IS_NUMBER(x)) return %_NumberToString(x);
  // 布尔型转字符串,true返回字符串true
  if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
  // undefined转字符串,返回undefined
  if (IS_UNDEFINED(x)) return 'undefined';
  // Symbol抛出异常
  if (IS_SYMBOL(x)) throw MakeTypeError(kSymbolToString);
  return (IS_NULL(x)) ? 'null' : ToString(DefaultString(x));
}
```
那么[].valueOf()的结果是[],在对[]调用toString的方法,得到的结果是""。
而true调用ToNumber的方法得到的结果是1。
"" == 1 返回的是false,所以[] == true,返回false。

那么我们再来看看!![] == true是怎么解读的?
根据优先级,先进行!操作。

![Logical-NOT-Operator.png](https://i.loli.net/2018/08/21/5b7b694fb3c5f.png)

所以!![]相当于!!(ToBoolean([]))

ToBoolean源码

```js
// ECMA-262, section 9.2, page 30
function ToBoolean(x) {  
  if (IS_BOOLEAN(x)) return x;
  // 字符串转布尔型时,如果length不为0就返回true
  if (IS_STRING(x)) return x.length != 0;
  if (x == null) return false;
  // 数字转布尔型时,变量不为0或NAN时返回true
  if (IS_NUMBER(x)) return !((x == 0) || NUMBER_IS_NAN(x));
  return true;
}
```
从源码中,我们得知,null,undefined,0,"",false,NaN,返回false。其余都是返回true。

那么!![],转换之后等于!!true,即true。

那其余的一个就留给大家自己分析啦!!!

================================================
FILE: blog/ajax简述.md
================================================
## 一篇关于ajax的故事
### 前言
我为什么要写这个呢,以前面试的时候问过这些,还有就是我个人来看,学习前端其实闭包啊,原型啊,等等的问题,被写烂了,但是关于数据交互这一块的很少,我们在业务中,数据交互用的并不占少数,整理一篇给大家,也给我自己,希望喜欢的点一个关注[GitHub](https://github.com/laihuamin/JS-total)
### ajax

#### 什么是ajax

其实呢,说起ajax,大家都不陌生,但是这里我还是详细的介绍一下,也好为我下一篇博文做基础,下一篇内容是和数据交互相关的,ajax全称Asynchronous JavaScript and XML(异步的javascript和XML),为什么会有这么一种技术的出现呢,因为前端时常会有这样的需求,我们只要局部刷新,不需要整一个刷新的时候,便吹生了这样的技术ajax,具体它是怎么实现的我们下面娓娓道来。

#### ajax实现的基本流程

其实以前看到过一个变态的面试题,让你自己写一个原生的ajax,如果你让我查接口我能写的出来,但是让我默写我办不到,因为现在用的基本都是jquery封装的ajax,确实人家封装的很好,所以我们只要懂得了ajax的怎么实现的基本流程,我觉得对于像我这样的应届生够了。

ajax的基本流程:

- 页面js脚本实例化一个XMLHttprequest对象
- 设置好服务端给定的url、必要的查询参数、回调函数等
- 向服务端发起请求,服务端处理请求之后的结果返回给页面
- 触发原先订好的回调函数,来获取数据

ajax实现局部刷新的流程也是这样,因为我们可以发出ajax向服务器获取这个局部相关的少量数据,然后运用这部分数据来更新页面

#### ajax追本溯源
其实呢,ajax是在2005年由google的Jesse James Garrett发表了一篇文章中提出的,它依赖于XMLHttp实现的,XMLHttp是1998年由微软提出的,google用ajax开发Google Maps等产品,运用若干年之后,才在文章中发表,那么其实ajax是给Google Maps这样的复杂应用而生的,但是,我想谈谈ajax带来的副产品,表单提交

#### ajax在数据交互中的应用
我觉得ajax用于数据交互,对于我这些初学者更应该把握好两点,一点是GET和POST的区别,重中之重,还有一点,可以了解一下什么是RESTFUL风格,其他更加深人的可以结合promise规范,看一下jquery的ajax是怎么封装的等等,这篇博文不会写这些,我打算后期出一篇promise规范相关的,把现在的一些fetch等新出来的数据交互手段进行归纳,举个ajax应用的例子:
```js
$.ajax({
	type: "post",  //数据传输的方法
	url: ...,  //一般这里会是后端给你的接口路径
 	data: {data},//这里是一个提交的数据内容
	success: function(){}	//成功后进行的操作
	error: function(){}		//数据审核不通过,后端一般会返回false然后进行的操作
```


#### GET和POST的区别
碰到一样东西时,我们应该先去查文档,把基本概念搞清楚,然后在开始分析利和弊

- 基本概念

其实在MDN的解释中就可以清楚的发现两者的区别:

> HTTP GET 方法请求指定的资源。使用 GET 的请求应该只用于获取数据。

> HTTP POST 方法 发送数据给服务器. 

- 安全性

关于两者的安全性,这个毋庸置疑,但是你只知道POST是比GET安全,但你不知道,为什么?

![](http://mmbiz.qpic.cn/mmbiz/VUGnGjllRE5vZcld02bjOjWPPBRXYdhLVXZkPZibSibtVZoIkDcBTQJ3mFibpNtqOSNTLDs01s2rmB6PyCoibjczxQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1)

深入了解一点的会发现,get方式的http请求是这样的

![GET](http://laihuamin.oss-cn-beijing.aliyuncs.com/GET.png)

你的参数会被拼接到url上,然后进行传输,这样就会又一个问题,参数都是可见的,就像我截图的一样,而POST就不一样

![POST](http://laihuamin.oss-cn-beijing.aliyuncs.com/POST.png)

涂了点,因为接口是公司的,POST方式url后面是不带参数的,POST放在Request body中(这点是来自W3C)

- 书签

GET可作为书签收藏,因为参数是拼在URL上的,POST就不可以了

- 历史

GET请求的参数能存在浏览器历史中,而POST不能,原因也在于一个参数是拼接在url中,而一个不是

- 对数据长度的限制

这一点我想要好好谈谈,GET的数据长度限制来源于哪里,还是上面讲的一点,GET的参数是拼接在URL中的,理论上URL中的参数是可以无限加长的,但是这样势必会给浏览器和服务器带来很重的负担,所以业界有一种不成文的规定,大多数浏览器在URL的限制是2K,而大多数服务器的限制在64K。
在这点上很多人都会有误区,我们得明白一下几点:

1.http是没有限制GET和POST的传递数据长度的
> The HTTP protocol does not place any a priori limit on the length of a URI. Servers MUST be able to handle the URI of any resource they serve, and SHOULD be able to handle URIs of unbounded length if they provide GET-based forms that could generate such URIs. A server SHOULD return 414 (Request-URI Too Long) status if a URI is longer than the server can handle (see section 10.4.15). 

>  Note: Servers ought to be cautious about depending on URI lengths above 255 bytes, because some older client or proxy implementations might not properly support these lengths.

2.规定是来源于浏览器和服务器

- 对数据的编码

这点也是跟GET是拼接在URL上一样,那么GET的参数只能进行URL编码,而POST就不一样,可以进行二进制编码等

- 性能方面

这个方面就是GET比较出色了,这一点自己也是在整理的时候才关注起来的,很多知识来源于网络,所以,大家可以看,如果和你实际情况不符,可以在评论中提出:
> GET产生一个TCP数据包;POST产生两个TCP数据包。
对于GET请求来说,是把http header和data一起发送给服务器,然后服务器会返回200。
但是POST就不一样,是先发送http header,然后服务器响应100后,在将data发送给服务器,然后服务器响应200

对于性能而言,一个走了一趟,一个走了两趟,明显是走一趟的来的快捷

- 总结

全部使用POST明显是不合理的,在一些数据不敏感,请求频繁,数据量小于浏览器限制2K,这样的情况还是选择GET会比较合理。

#### 结语
关于RESTful风格,笔者也在探索阶段,现在只会书写,但是为什么是这样写的,这样的格式是怎么来的,自己也不清楚,哈哈哈,不过,可以给大家推荐几本书,有兴趣的可以看一下《REST in Practice》,还有一个是REST风格的提出者发表的[论文(英文版)](http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm)

#### 文章中参考的博文
[来源知乎](https://zhuanlan.zhihu.com/p/22536382)
[来源知乎](https://www.zhihu.com/question/28586791)

#### 备注
希望喜欢的朋友点个喜欢,也可以关注,要是能给博主的[GitHub](https://github.com/laihuamin/JS-total)点个star就更好了,大家一起努力,🙏🙏🙏

	

================================================
FILE: blog/console命令还能这么用.md
================================================
### 前言
这篇文章是网上看到的,自己实验了一遍,把过程和结果都分享给大家,网上的那片我也会注明出处,希望对大家真实过程中有用,喜欢的点个赞,或者给我的[github](https://github.com/laihuamin/JS-total)点个star谢谢.

### 平常我们用的console.log

```js
let a = 10;
console.log(a);
```
这个使用在正常不过,确实方便,而且还可以用alert一样的效果。不过下面和大家介绍的,对大家应该有用。

### 显示信息

```js
console.log(10); //10
console.info('信息');
console.warn('警告');
console.error('错误');
```
后面几个的输出结果如下:

![console.info](http://laihuamin.oss-cn-beijing.aliyuncs.com/consoleInfo.png)

![console.warn](http://laihuamin.oss-cn-beijing.aliyuncs.com/consoleWarn.png)

![console.error](http://laihuamin.oss-cn-beijing.aliyuncs.com/consoleError.png)

### 占位符

先看例子:

![placeholder](http://laihuamin.oss-cn-beijing.aliyuncs.com/palceholder.png)

占位符不止这些,还有字符串%s,浮点型%f和对象%o。不过这些功能现在应该用不到了,从es6出了模版字符串之后,字符串的拼接已经不是什么难事。

### 输出信息组

```js
console.group("第一组");
  console.log("第一组第一条:github:github.com/laihuamin");
  console.log("第一组第二条:github:github.com/laihuamin");
console.groupEnd();
console.group("第二组信息");
  console.log("第二组第一条:github:github.com/laihuamin");
  console.log("第二组第二条:github:github.com/laihuamin");
console.groupEnd();
```

输出结果:

![console.group](http://laihuamin.oss-cn-beijing.aliyuncs.com/consoleGroup.png)

### 输出对象

console.dir可以输出对象的所有属性和方法,console.log可以输出对象内容

```js
console.log(document.body);
console.dir(document.body);
```

输出结果:

![console.dir](http://laihuamin.oss-cn-beijing.aliyuncs.com/consoleDir.png)


### 输出计时时间

console.time是输出时间的函数
```js
console.time("控制台计时器一");
  for(var i=0;i<10000;i++){
    for(var j=0;j<10000;j++){}
  }
console.timeEnd("控制台计时器一");
```

输出结果:
![console.time](http://laihuamin.oss-cn-beijing.aliyuncs.com/consoleTime.png)


### 追踪函数的调用轨迹
console.trace是追踪函数的轨迹

```js
function add(a,b){
  console.trace(); 
  return a+b; 
}
let x = add3(1,1); 
function add3(a,b){
  return add2(a,b);
} 
function add2(a,b){
  return add1(a,b);
} 
function add1(a,b){
  return add(a,b);
}
```

输出结果:
![console.trace](http://laihuamin.oss-cn-beijing.aliyuncs.com/console.trace.png)


这篇文章出于猎奇心里0.0

================================================
FILE: blog/cookie.md
================================================
### 前言

cookie在web开发中时常被用到,也是面试官喜欢问的一块技术,很多人或许和我以前一样,只知其一不知其二,谈起web存储,都会答localStorage、sessionStorage、还有就是cookie,然后一些区别啊什么的倒背如流,cookie的优缺点也了然于心,但是当你看完这块内容之后,你会对cookie有另外独到的见解,希望以后问到这块技术,或者项目中遇到这个你都会处理,我在实习的过程中,一直在用,所以它真的不是口头说说的那么简单,让我们进入cookie的世界

### cookie是什么

这个讲起来很简单,了解http的同学,肯定知道,http是一个不保存状态的协议,什么叫不保存状态,就是一个服务器是不清楚是不是同一个浏览器在访问他,在cookie之前,有另外的技术是可以解决,这里简单讲一下,就是在请求中插入一个token,然后在发送请求的时候,把这个东西带给服务器,这种方式是易出错,所以有了cookie的出现

![cookie](
http://laihuamin.oss-cn-beijing.aliyuncs.com/cookie.png)

cookie是什么,cookie就是一种浏览器管理状态的一个文件,它有name,也有value,后面那些看不见的是Domain、path等等,我们后面会介绍

### cookie原理

![cookieSend](http://laihuamin.oss-cn-beijing.aliyuncs.com/cookieSend.png)


第一次访问网站的时候,浏览器发出请求,服务器响应请求后,会将cookie放入到响应请求中,在浏览器第二次发请求的时候,会把cookie带过去,服务端会辨别用户身份,当然服务器也可以修改cookie内容

### cookie不可跨域

我就几个例子你就懂了,当我打开百度的网页,我要设置一个cookie的时候,我的指令如下
```js
javascript:document.cookie='myname=laihuamin;path=/;domain=.baidu.com';
```
```js
javascript:document.cookie='myname=huaminlai;path=/;domain=.google.com';
```
当我将这两个语句都放到浏览器控制台运行的时候,你会发现一点,注意,上面两个cookie的值是不相同的,看清楚
![cookieDontDomain](http://laihuamin.oss-cn-beijing.aliyuncs.com/cookieDonDomain.png)
显而易见的是,真正能把cookie设置上去的只有domain是.baidu.com的cookie绑定到了域名上,所以上面所说的不可跨域性,就是不能在不同的域名下用,每个cookie都会绑定单一的域名

### cookie的属性
cookie的属性众多,我们可以来看一下下面这张图,然后我们一个一个分析

![cookieAttr](http://laihuamin.oss-cn-beijing.aliyuncs.com/cookieAttr.png)

#### name
这个显而易见,就是代表cookie的名字的意思,一个域名下绑定的cookie,name不能相同,相同的name的值会被覆盖掉,有兴趣的同学可以试一试,我在项目中切实用到过

#### value
这个就是每个cookie拥有的一个属性,它表示cookie的值,但是我在这里想说的不是这个,因为我在网上看到两种说法,如下:<br/>
1.cookie的值必须被URL编码<br/>
2.对cookie的值进行编码不是必须的,还举了原始文档中所说的,仅对三种符号必须进行编码:分号、逗号和空格

这个东西得一分为二来看,先看下面的图

![cookievalue](http://laihuamin.oss-cn-beijing.aliyuncs.com/cookieValue.png)

我在网上看到那么一种说法:
> 由于cookie规定是名称/值是不允许包含分号,逗号,空格的,所以为了不给用户到来麻烦,考虑服务器的兼容性,任何存储cookie的数据都应该被编码。

#### domain
这个是指的域名,这个代表的是,cookie绑定的域名,如果没有设置,就会自动绑定到执行语句的当前域,还有值得注意的点,统一个域名下的二级域名也是不可以交换使用cookie的,比如,你设置www.baidu.com和image.baidu.com,依旧是不能公用的

#### path
path这个属性默认是'/',这个值匹配的是web的路由,举个例子:
```
//默认路径
www.baidu.com
//blog路径
www.baidu.com/blog
```
我为什么说的是匹配呢,就是当你路径设置成/blog的时候,其实它会给/blog、/blogabc等等的绑定cookie

#### cookie的有效期
![cookieMaxAge](http://laihuamin.oss-cn-beijing.aliyuncs.com/cookieMaxAge.png)

什么是有效期,就是图中的Expires属性,一般浏览器的cookie都是默认储存的,当关闭浏览器结束这个会话的时候,这个cookie也就会被删除,这就是上图中的——session(会话储存)。

如果你想要cookie存在一段时间,那么你可以通过设置Expires属性为未来的一个时间节点,Expires这个是代表当前时间的,这个属性已经逐渐被我们下面这个主人公所取代——Max-Age

Max-Age,是以秒为单位的,Max-Age为正数时,cookie会在Max-Age秒之后,被删除,当Max-Age为负数时,表示的是临时储存,不会生出cookie文件,只会存在浏览器内存中,且只会在打开的浏览器窗口或者子窗口有效,一旦浏览器关闭,cookie就会消失,当Max-Age为0时,又会发生什么呢,删除cookie,因为cookie机制本身没有设置删除cookie,失效的cookie会被浏览器自动从内存中删除,所以,它实现的就是让cookie失效。

#### secure

![cookieSecure](http://laihuamin.oss-cn-beijing.aliyuncs.com/cookieSecure.png)

这个属性译为安全,http不仅是无状态的,还是不安全的协议,容易被劫持,打个比方,你在手机端浏览网页的时候,有没有中国移动图标跳出来过,闲言少叙,当这个属性设置为true时,此cookie只会在https和ssl等安全协议下传输

- 提示:这个属性并不能对客户端的cookie进行加密,不能保证绝对的安全性

#### HttpOnly

这个属性是面试的时候常考的,如果这个属性设置为true,就不能通过js脚本来获取cookie的值,能有效的防止xss攻击,看MDN的官方文档:
![httpOnly](http://laihuamin.oss-cn-beijing.aliyuncs.com/cookieHttpOnly.png)

#### 关于js操作cookie
document.cookie可以对cookie进行读写,看一下两条指令:
```
//读取浏览器中的cookie
console.log(document.cookie);
//写入cookie
document.cookie='myname=laihuamin;path=/;domain=.baidu.com';
```

#### 服务端如何去设置cookie

关于怎么设置cookie,我们只要打开控制台,看一个http的请求头和响应头中的东西即可明白:
![setCookie](http://laihuamin.oss-cn-beijing.aliyuncs.com/setCookie.png)

服务端就是通过setCookie来设置cookie的,注意点,要设置多个cookie时,得多写几个setCookie,我们还可以从上图看到,请求可以携带cookie给后端。


### 总结
cookie讲了这么多,自己也收获了很多,也希望分享给大家,或许写的不够好,请见谅,如果觉得我写的好的朋友,给个star,[github地址](https://github.com/laihuamin/JS-total)

================================================
FILE: blog/css代码规范.md
================================================
### 前言

下周是我们公司的代码规范考试,所以我把一些相关的代码规范整理一遍,然后还可以一起看几个例子。

### css代码规范

我司氛围建议和强制两个部分:

### 建议

- css文件使用无BOM的UTF-8编码。

理由: UTF-8编码更具有广泛的适应性,BOM文件在不同的工具上容易造成不必要的干扰

- 属性名和之后的:之间不允许有空格,:和之后的属性值之间必须有空格。

```
margin: 0
```

- 建议列表性属性书写的时候单行用, 。,后面必须跟一个空格

- 建议超长的样式,在样式值空格后或者,后换行,按逻辑分组 

- 选择器嵌套层级最好不要大过三级

- 能使用缩写的时候,尽量使用缩写

```
.post {
    font: 12px/1.5 arial, sans-serif;
}
```

-  使用 `border` / `margin` / `padding` 等缩写时,应注意隐含值对实际数值的影响,确实需要设置多个方向的值时才使用缩写。

- 同一 rule set 下的属性在书写时,应按功能进行分组,并以 **Formatting Model(布局方式、位置) > Box Model(尺寸) > Typographic(文本相关) > Visual(视觉效果)** 的顺序书写,以提高代码的可读性。

> 解释: 

> Formatting Model 相关属性包括:`position` / `top` / `right` / `bottom` / `left` / `float` / `display` / `overflow` 等

> Box Model 相关属性包括:`border` / `margin` / `padding` / `width` / `height` 等

> Typographic 相关属性包括:`font` / `line-height` / `text-align` / `word-wrap` 等

> Visual 相关属性包括:`background` / `color` / `transition` / `list-style` 等

- 当元素需要撑起高度以包含内部的浮动元素时,通过对伪类设置 `clear` 或触发 `BFC` 的方式进行 `clearfix`。尽量不使用增加空标签的方式。
- 尽量不使用 `!important` 声明。
- 当需要强制指定样式且不允许任何场景覆盖时,通过标签内联和 `!important` 定义样式。
- 将 `z-index` 进行分层,对文档流外绝对定位元素的视觉层级关系进行管理。
- 在可控环境下,期望显示在最上层的元素,`z-index` 指定为 `999999`。
- 在第三方环境下,期望显示在最上层的元素,通过标签内联和 `!important`,将 `z-index` 指定为 `2147483647`。
- 当数值为 0 - 1 之间的小数时,省略整数部分的 `0`。
- `url()` 函数中的路径不加引号。
- 颜色值中的英文字符采用小写。如不用小写也需要保证同一项目内保持大小写一致。
- 需要在 Windows 平台显示的中文内容,不要使用除 `normal` 外的 `font-style`。其他平台也应慎用。
- `font-weight` 属性必须使用数值方式描述。
- `line-height` 在定义文本段落时,应使用数值。
- 尽可能给出在高分辨率设备 (Retina) 下效果更佳的样式。
- 需要添加 `hack` 时应尽可能考虑是否可以采用其他方式解决。
- 尽量使用 `选择器 hack` 处理兼容性,而非 `属性 hack`。
- 尽量使用简单的 `属性 hack`。

### 强制

- 使用4个空格作为一层缩进,不允许使用2个空格活着Tab字符

- 选择器与{间必须有空格

```
.selector {
}
```
- 每行不得超过120个字符,除非不可分割的场景

常见的场景:
背景图中的url

- 当一个rule包含多个选择器的时候,每个选择器占一行

```
.post,
.get,
.comment {
    color: #000
}
```

- `>、+、-`选择器两边都需要带空格

```
main > nav {
    color: #000;
}
main + nav {
    color: #000;
}
main - nav {
    color: #000;
}
```

- 属性的定义必须另起一行

```
.selector {
    margin: 0;
    padding: 0;
}
```

- 属性值必须已分号结尾,如上面的例子所示

- 如果没有必要,不得为id、class选择器添加类型的限

解释:在性能和维护性上,都有一定的影响

```
#error,
.danger {
  font-color: #c00;
}
/* bad */
div#error,
p.danger {
}
```

- 文本内容必须用双引号包围。

解释:
文本类型的内容可能在选择器、属性值等内容中
示例:
```
/* good */
html[lang|="zh"] q:before {
    font-family: "Microsoft YaHei", sans-serif;
    content: "“";
}
```

- 长度为 `0` 时须省略单位。 (也只有长度单位可省)
- RGB颜色值必须使用十六进制记号形式 `#rrggbb`。不允许使用 `rgb()`。 
- 颜色值可以缩写时,必须使用缩写形式。
- 颜色值不允许使用命名色值。
- 必须同时给出水平和垂直方向的位置。
- `font-family` 属性中的字体族名称应使用字体的英文 `Family Name`,其中如有空格,须放置在引号中。

示例:

```css
h1 {
    font-family: "Microsoft YaHei";
}
```
- `font-family` 按「西文字体在前、中文字体在后」、「效果佳 (质量高/更能满足需求) 的字体在前、效果一般的字体在后」的顺序编写,最后必须指定一个通用字体族( `serif` / `sans-serif` )。
- `font-family` 不区分大小写,但在同一个项目中,同样的 `Family Name` 大小写必须统一。
- 需要在 Windows 平台显示的中文内容,其字号应不小于 `12px`。
- 使用 `transition` 时应指定 `transition-property`。
- 尽可能在浏览器能高效实现的属性上添加过渡和动画。
- `Media Query` 不得单独编排,必须与相关的规则一起定义。

示例:

```css
/* Good */
/* header styles */
@media (...) {
    /* header styles */
}

/* main styles */
@media (...) {
    /* main styles */
}

/* footer styles */
@media (...) {
    /* footer styles */
}


/* Bad */
/* header styles */
/* main styles */
/* footer styles */

@media (...) {
    /* header styles */
    /* main styles */
    /* footer styles */
}
```
- `Media Query` 如果有多个逗号分隔的条件时,应将每个条件放在单独一行中。

示例:

```css
@media
(-webkit-min-device-pixel-ratio: 2), /* Webkit-based browsers */
(min--moz-device-pixel-ratio: 2),    /* Older Firefox browsers (prior to Firefox 16) */
(min-resolution: 2dppx),             /* The standard way */
(min-resolution: 192dpi) {           /* dppx fallback */
    /* Retina-specific stuff here */
}
```
- 带私有前缀的属性由长到短排列。
- 禁止使用 `Expression`。

================================================
FILE: blog/css层叠关系.md
================================================
## css层叠关系

### 前言
css的层叠关系,发现很少人去关注它,我以前看书的时候关注过,但是现在和别人讨论这一块的时候,我们之间观念发生了偏差,我去网上看了很多博文,大部分的论调都是我以前的观念,所以,我要写下这篇文章

### 

================================================
FILE: blog/html代码规范.md
================================================
### 前言

前两篇书写了css和js的,html其实也有一些规范,我司给出了相应的规范,在规范中,我们和前面一样,将其分为建议和强制

### 建议
- 每行不得超过 `120` 个字符。
- `class` 必须代表相应模块或部件的内容或功能,不得以样式信息进行命名。
- `id` 建议单词全字母小写,单词间以 `-` 分隔。`class` 建议单词全字母小写,单词间以 `-` 分隔,用于JavaScript选择的class则建议用`J_class-name`格式。
- `id`、`class` 命名,在避免冲突并描述清楚的前提下尽可能短。
- `HTML` 标签的使用应该遵循标签的语义。

解释:
- 下面是常见标签语义:
  - p - 段落
  - h1,h2,h3,h4,h5,h6 - 层级标题
  - strong,em - 强调
  - ins - 插入
  - del - 删除
  - abbr - 缩写
  - code - 代码标识
  - cite - 引述来源作品的标题
  - q - 引用
  - blockquote - 一段或长篇引用
  - ul - 无序列表
  - ol - 有序列表
  - dl,dt,dd - 定义列表


- 在 `CSS` 可以实现相同需求的情况下不得使用表格进行布局。
- 布尔类型的属性,建议不添加属性值。
- 自定义属性建议以 `xxx-` 为前缀,推荐使用 `data-`。
- 启用 IE Edge 模式。
- `HTML` 文件使用无 `BOM` 的 `UTF-8` 编码。
- 引入 `CSS` 时必须指明 `rel="stylesheet"`。
- 引入 `CSS` 和 `JavaScript` 时无须指明 `type` 属性。
- 展现定义放置于外部 `CSS` 中,行为定义放置于外部 `JavaScript` 中。
- 在 `head` 中引入页面需要的所有 `CSS` 资源。
- `JavaScript` 应当放在页面末尾,或采用异步加载。
- 移动环境或只针对现代浏览器设计的 Web 应用,如果引用外部资源的 `URL` 协议部分与页面相同,建议省略协议前缀。
- 若页面欲对移动设备友好,需指定页面的 
- 避免为 `img` 添加不必要的 `title` 属性。
- 为重要图片添加 `alt` 属性。
- 有下载需求的图片采用 `img` 标签实现,无下载需求的图片采用 `CSS` 背景图实现。
- 尽量不要使用按钮类元素的 `name` 属性。
- 负责主要功能的按钮在 `DOM` 中的顺序应靠前。
- 当使用 `JavaScript` 进行表单提交时,如果条件允许,应使原生提交功能正常工作。
- 在针对移动设备开发的页面时,根据内容类型指定输入框的 `type` 属性。
- 当在现代浏览器中使用 `audio` 以及 `video` 标签来播放音频、视频时,应当注意格式。
- 在支持 `HTML5` 的浏览器中优先使用 `audio` 和 `video` 标签来定义音视频元素。
- 使用退化到插件的方式来对多浏览器进行支持。
- 只在必要的时候开启音视频的自动播放。
- 在 `object` 标签内部提供指示浏览器不支持该标签的说明。

示例:

```html
<object width="100" height="50" data="something.swf">DO NOT SUPPORT THIS TAG</object>
```
- 模板代码的缩进优先保证 `HTML` 代码的缩进规则。
- 模板代码应以保证 `HTML` 单个标签语法的正确性为基本原则。

### 强制
- 使用 `4` 个空格做为一个缩进层级,不允许使用 `2` 个空格 或 `tab` 字符。
- `html` 文件名必须单词全字母小写,单词间以 `-` 分隔。
- `class` 必须单词全字母小写,单词间以 `-` 分隔。
- 元素 `id` 必须保证页面唯一。
- 禁止使用有样式的 `class`作为JavaScript选择器。
- 同一页面,应避免使用相同的 `name` 与 `id`。
- 标签名必须使用小写字母。
- 对 `HTML5` 中规定允许省略的闭合标签,不允许省略闭合标签。
- 标签使用必须符合标签嵌套规则。
- 属性名必须使用小写字母。
- 属性值必须用双引号包围。
- 使用 `HTML5` 的 `doctype` 来启用标准模式,建议使用大写的 `DOCTYPE`
- 页面必须使用精简形式,明确指定字符编码。指定字符编码的 `meta` 必须是 `head` 的第一个直接子元素。
- 页面必须包含 `title` 标签声明标题。
- `title` 必须作为 `head` 的直接子元素,并在 `charset` 声明之后。
- 保证 `favicon` 可访问。
- 禁止 `img` 的 `src` 取值为空。延迟加载的图片也要增加默认的 `src`。
- 有文本标题的控件必须使用 `label` 标签将其与其标题相关联。
- 使用 `button` 元素时必须指明 `type` 属性值。

================================================
FILE: blog/instance_init.md
================================================
## instance中的init.js
- 其实我们可以从一个小例子来看一下整体
```js
new Vue({
    el: '#app',
    data: {
        a: '1',
        b: '2'
    }
})
```
> 这个例子我们在一开始写项目的时候也是打头阵的,那么根据这个例子,我们来看一下instance中的init.js的源码

- 全局变量uid
```
let uid = 0
```
> 这是一个全局变量,后面我们可以看到每个vue的实例都是有相应的uid的

- initMixin
```js
export function initMixin (Vue: Class<Component>) {
  //传入的是一个Vue的类
  Vue.prototype._init = function (options?: Object) {
    //在Vue的原型上定义了一个init方法,传入的是一些Vue实例的一些选项
    const vm: Component = this
    //用一个vm也就是Vue实例指向这个对象
    // a uid
    vm._uid = uid++
    //vm中定义一个uid,用完这个uid之后,uid++可供后续使用
    let startTag, endTag
    //定义一个开始的tag和结束的tag
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      startTag = `vue-perf-init:${vm._uid}`
      endTag = `vue-perf-end:${vm._uid}`
      mark(startTag)
    }

    // a flag to avoid this being observed
    vm._isVue = true
    //vm中有一个_isVue为ture,我觉得代表语义化是,代表这是一个Vue的对象
    // merge options
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    // expose real self
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      vm._name = formatComponentName(vm, false)
      mark(endTag)
      measure(`${vm._name} init`, startTag, endTag)
    }

    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}

```

================================================
FILE: blog/instanceof你懂吗.md
================================================
### 简单介绍——instanceof
> instanceof的发明是为了弥补typeof所带来的缺陷,因为typeof在检测object类型的时候,总是会返回object,所以js提供了另外一个接口来实现对对象类型的判断,那就是我们的instanceof,我们先来看一下他的用法:
```js
const obj = new Object();
console.log(obj instanceof Object) //true
```
### 其实他没那么简单——instanceof
> 其实他的功能还很强大,我们可以看一下下面的例子
```js
function Foo(){};
const foo = new Foo();
console.log(foo instanceof Foo);
```
> 不仅可以检测原有的object类型,还可以检测自定义你构造函数类型
### instanceof和继承也有关哦
> instance不仅可以检测实例本身的构造函数,他还可以检测,实例的父类型继承,我们看一下下面这个例子
```js
function Foo() {};
function Bar() {};
Bar.prototype = new Foo();
Bar.prototype.constructor = Bar;
const bar = new Bar();
console.log(bar instanceof Bar); //true
console.log(bar instanceof Foo); //true
```
### 你又觉得你了解了吗——instanceof复杂用法
> 其实instanceof还有很多复杂用法,我们看一下下面的例子
```js
function Foo() {};
console.log(Object instanceof Object); //true
console.log(Function instanceof Function); //true
console.log(Number instanceof Number); //false
console.log(String instanceof String); //false
console.log(Function instanceof Object); //true
console.log(Foo instanceof Function); //true
console.log(Foo instanceof Foo); //false
```
- 一脸懵逼?理解上述的例子还得从两个方面入手,instanceof操作符的机制和js继承的机制
### 来聊聊instanceof操作符的机制
> 从网上的一篇博客中找到一段自己实现instanceof的代码,如下
```js
function  instance_of (L, R){
    var O = R.prototype;    //右边表达式的显示原型
    var L = L._proto_;  //左边的表达式的隐式原型
    while(true){
        if(L === null){
            return false;
        };
        if(O === L){
            return true;
        };
        L = L._proto_;
    }
}
```
### 关键——js的原型继承机制
> 原型和原型链是js中很重要的一部分,我们用流程图来看一下
![image](https://user-images.githubusercontent.com/28126886/30860374-874bef70-a28c-11e7-82eb-7a399f34413d.png)
> 有几点需要注意:
1.Object是构造函数,他的_proto_指向Function.prototype
2.Function.prototype的_proto_指向Object.prototype
3.Object.prototype的_proto_指向null
### 看完了你还懂么
- Object instanceof Object
> 左边的Object的_proto_指向Function.prototype,Function.prototype指向Object.prototype
- console.log(Function instanceof Function);
> 左边的Function的_proto_指向Function.prototype
- console.log(Number instanceof Number);
> 左边的Number的_proto_指向Function.prototype,所以原型链向上已经回不到Number.prototype,所以返回false
- console.log(String instanceof String);
> 和上面同理
- console.log(Function instanceof Object);
> 左边的Function的_proto_指向Function.prototype,而Function.prototype的_proto_指向Object.prototype,所以返回true
- console.log(Foo instanceof Function);
> Foo是构造函数,所以他的_proto_是指向Function.prototype,所以返回true
- console.log(Foo instanceof Foo); //false
> 上面已经解释了指向,这个肯定为false
### 总结
- instanceof搞懂两点很重要,一个是原型链,还有一个是instanceof操作符的机制,与君共勉,希望能给个star

================================================
FILE: blog/js代码规范.md
================================================
### 前言

和前面一样,上面一篇是和样式相关的,这一篇是和js相关的,我们也一起来过一遍,然后还可以看几个例子。我们在代码规范中将分为建议和强制两个篇章

### 建议
- `JavaScript` 文件使用无 `BOM` 的 `UTF-8` 编码。
- 在文件结尾处,保留一个空行。
- `if / else / for / while / function / switch / do / try / catch / finally` 关键字后,必须有一个空格。
- 在对象创建时,属性中的 `:` 之后必须有空格,`:` 之前不允许有空格。
- 函数声明、具名函数表达式、函数调用中,函数名和 `(` 之间不允许有空格。
- `,` 和 `;` 前不允许有空格。
- 在函数调用、函数声明、括号表达式、属性访问、`if / for / while / switch / catch` 等语句中,`()` 和 `[]` 内紧贴括号部分不允许有空格。
- 单行声明的数组与对象,如果包含元素,`{}` 和 `[]` 内紧贴括号部分不允许包含空格。
- 行尾不得有多余的空格。
- 运算符处换行时,运算符必须在行的行尾。

示例:

```javascript
// good
if (user.isAuthenticated() &&
    user.isInRole('admin') &&
    user.hasAuthority('add-admin') ||
    user.hasAuthority('delete-admin')) {
    // Code
}

var result = number1 + number2 + number3 +
    number4 + number5;
```
- 在函数声明、函数表达式、函数调用、对象创建、数组创建、for语句等场景中,不允许在 `,` 或 `;` 前换行。
-  不同行为或逻辑的语句集,使用空行隔开,更易阅读。
-  在语句的行长度超过 `120` 时,根据逻辑条件合理缩进。
-  对于 `if...else...`、`try...catch...finally` 等语句,推荐使用在 `}` 号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。
-  `函数名` 使用 `动宾短语`。
-  `boolean` 类型的变量使用 `is` 或 `has` 开头。
-  `Promise对象` 用 `动宾短语的进行时` 表达。
-  避免使用 `/*...*/` 这样的多行注释。有多行注释内容时,使用多个单行注释。
-  为了便于代码阅读和自文档化,以下内容必须包含以 `/**...*/` 形式的块注释中。
-  文档注释前必须空一行。
-  自文档化的文档说明 what,而不是 how。
-  类型定义都是以`{`开始, 以`}`结束。
-  对于基本类型 {string}, {number}, {boolean},首字母必须小写。

类型定义 | 语法示例 | 解释 |
| ------- | ------- | --- |
|String|{string}|--|
|Number|{number}|--|
|Boolean|{boolean}|--|
|Object|{Object}|--|
|Function|{Function}|--|
|RegExp|{RegExp}|--|
|Array|{Array}|--|
|Date|{Date}|--|
|单一类型集合|{Array.&lt;string&gt;}|string 类型的数组|
|多类型|{(number|boolean)}|可能是 number 类型, 也可能是 boolean 类型|
|允许为null|{?number}|可能是 number, 也可能是 null|
|不允许为null|{!Object}|Object 类型, 但不是 null|
|Function类型|{function(number, boolean)}|函数, 形参类型|
|Function带返回值|{function(number, boolean):string}|函数, 形参, 返回值类型|
|参数可选|@param {string=} name|可选参数, =为类型后缀|
|可变参数|@param {...number} args|变长参数,  ...为类型前缀|
|任意类型|{*}|任意类型|
|可选任意类型|@param {*=} name|可选参数,类型不限|
|可变任意类型|@param {...*} args|变长参数,类型不限|

- 文件顶部必须包含文件注释,用 `@file` 标识文件说明。
- 文件注释中可以用 `@author` 标识开发者信息.
- 命名空间使用 `@namespace` 标识。
- 使用 `@class` 标记类或构造函数。
- 使用 `@extends` 标记类的继承信息。
- 细节注释遵循单行注释的格式。说明必须换行时,每行是一个单行注释的起始。
- `var` 声明多个变量。
- 尽可能使用简洁的表达式。
- 按执行频率排列分支的顺序。
- 如果函数或全局中的 `else` 块后没有任何语句,可以删除 `else`。
- 对有序集合进行遍历时,缓存 `length`。
- 对有序集合进行顺序无关的遍历时,使用逆序遍历。
- 类型检测优先使用 `Object.prototype.toString.call`。对象类型检测使用 `instanceof`。
- 转换成 `string` 时,使用 `+ ''`。
- 转换成 `number` 时,通常使用 `+`。
- `string` 转换成 `number`,要转换的字符串结尾包含非数字并期望忽略时,使用 `parseInt`。
- 使用 `parseInt` 时,必须指定进制。
- 转换成 `boolean` 时,使用 `!!`。
- `number` 去除小数点,使用 `Math.floor / Math.round / Math.ceil`,不使用 `parseInt`。
- 使用 `数组` 或 `+` 拼接字符串。
- 复杂的数据到视图字符串的转换过程,选用一种模板引擎。
- 属性访问时,尽量使用 `.`。
- 不因为性能的原因自己实现数组排序功能,尽量使用数组的 `sort` 方法。
- 清空数组使用 `.length = 0`。
- 一个函数的长度控制在 `50` 行以内。
- 一个函数的参数控制在 `6` 个以内。
- 通过 `options` 参数传递非数据输入型参数。
- 在适当的时候将闭包内大对象置为 `null`。
- 使用 `IIFE` 避免 `Lift 效应`。
- 空函数不使用 `new Function()` 的形式。
- 对于性能有高要求的场合,建议存在一个空函数的常量,供多处使用共享。
- 类的继承方案,实现时需要修正 `constructor`。
- 属性在构造函数中声明,方法在原型中声明。
- 设计自定义事件时,应考虑禁止默认行为。
- 避免修改外部传入的对象。
- 具备强类型的设计。
- 对于单个元素,尽可能使用 `document.getElementById` 获取,避免使用`document.all`。
- 对于多个元素的集合,尽可能使用 `context.getElementsByTagName` 获取。其中 `context` 可以为 `document` 或其他元素。指定 `tagName` 参数为 `*` 可以获得所有子元素。
- 遍历元素集合时,尽量缓存集合长度。如需多次操作同一集合,则应将集合转为数组。
- 获取元素的直接子元素时使用 `children`。避免使用`childNodes`,除非预期是需要包含文本、注释和属性类型的节点。
- 获取元素实际样式信息时,应使用 `getComputedStyle` 或 `currentStyle`。
- 尽可能通过为元素添加预定义的 className 来改变元素样式,避免直接操作 style 设置。

### 强制
-  使用 `4` 个空格做为一个缩进层级,不允许使用 `2` 个空格 或 `tab` 字符。
-  `switch` 下的 `case` 和 `default` 必须增加一个缩进层级。

示例:

```javascript
// good
switch (variable) {

    case '1':
        // do...
        break;

    case '2':
        // do...
        break;

    default:
        // do...

}

// bad
switch (variable) {

case '1':
    // do...
    break;

case '2':
    // do...
    break;

default:
    // do...

}
```
- 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。
- 用作代码块起始的左花括号 `{` 前必须有一个空格。
- 每个独立语句结束后必须换行。
- 每行不得超过 `120` 个字符。
- 不得省略语句结束的分号。
- 在 `if / else / for / do / while` 语句中,即使只有一行,也不得省略块 `{...}`。
- 函数定义结束不允许添加分号。
- `IIFE` 必须在函数表达式外添加 `(`,非 `IIFE` 不得在函数表达式外添加 `(`。
- `变量` 使用 `Camel命名法`。
- `常量` 使用 `全部字母大写,单词间下划线分隔` 的命名方式。
- `函数` 使用 `Camel命名法`。
- 函数的 `参数` 使用 `Camel命名法`。
- `类型` 使用 `Pascal命名法`。
- 类的 `方法 / 属性` 使用 `Camel命名法`。
- `枚举变量` 使用 `Pascal命名法`,`枚举的属性` 使用 `全部字母大写,单词间下划线分隔` 的命名方式。
- `命名空间` 使用 `Camel命名法`。
- 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致。
- `类名` 使用 `名词`。
- 必须独占一行。`//` 后跟一个空格,缩进与下一行被注释说明的代码一致。
- 使用包装方式扩展类成员时, 必须通过 `@lends` 进行重新指向。

示例:

```javascript
/**
 * 类描述
 *
 * @class
 * @extends Developer
 */
function Fronteer() {
    Developer.call(this);
    // constructor body
}

util.extend(
    Fronteer.prototype,
    /** @lends Fronteer.prototype */{
        _getLevel: function () {
            // TODO
        }
    }
);
```
- 有时我们会使用一些特殊标记进行说明。特殊标记必须使用单行注释的形式。下面列举了一些常用标记:

解释:

1. TODO: 有功能待实现。此时需要对将要实现的功能进行简单说明。
2. FIXME: 该处代码运行没问题,但可能由于时间赶或者其他原因,需要修正。此时需要对如何修正进行简单说明。
3. HACK: 为修正某些问题而写的不太好或者使用了某些诡异手段的代码。此时需要对思路或诡异手段进行描述。
4. XXX: 该处存在陷阱。此时需要对陷阱进行描述。

- 变量在使用前必须通过 `var` 定义。
- 在 Equality Expression 中使用类型严格的 `===`。仅当判断 null 或 undefined 时,允许使用 `== null`。
- 不要在循环体中包含函数表达式,事先将函数提取到循环体外。

示例:

```javascript
// good
function clicker() {
    // ......
}

for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    addListener(element, 'click', clicker);
}


// bad
for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    addListener(element, 'click', function () {});
}
```

- 对循环内多次使用的不变值,在循环外用变量缓存。

示例:

```javascript
// good
var width = wrap.offsetWidth + 'px';
for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    element.style.width = width;
    // ......
}


// bad
for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    element.style.width = wrap.offsetWidth + 'px';
    // ......
}
```
- 字符串开头和结束使用单引号 `'`。
- 使用对象字面量 `{}` 创建新 `Object`。
- 对象创建时,如果一个对象的所有 `属性` 均可以不添加引号,则所有 `属性` 不得添加引号。
- 对象创建时,如果任何一个 `属性` 需要添加引号,则所有 `属性` 必须添加 `'`。
- 不允许修改和扩展任何原生对象和宿主对象的原型。

示例: 

```javascript
// 以下行为绝对禁止
String.prototype.trim = function () {
};
```
- `for in` 遍历对象时, 使用 `hasOwnProperty` 过滤掉原型中的属性。

示例:

```javascript
var newInfo = {};
for (var key in info) {
    if (info.hasOwnProperty(key)) {
        newInfo[key] = info[key];
    }
}
```
- 使用数组字面量 `[]` 创建新数组,除非想要创建的是指定长度的数组。
- 遍历数组不使用 `for in`。
- 自定义事件的 `事件名` 必须全小写。
- 自定义事件只能有一个 `event` 参数。如果事件需要传递较多信息,应仔细设计事件对象。
- 避免使用直接 `eval` 函数。
- 尽量不要使用 `with`。
- 减少 `delete` 的使用。
- 通过 style 对象设置元素样式时,对于带单位非 0 值的属性,不允许省略单位。
- 操作 `DOM` 时,尽量减少页面 `reflow`。

================================================
FILE: blog/js的实用小技巧.md
================================================
## 前言
每一门语言都有一些奇技淫巧,JS也不例外,一直想总结这么篇文章,我包括一些新手,都会有这么一个疑问,每次面对一张空白的页面,不知从何下手,没有思路,高手有的是设计模式,但是在这里讲一些设计模式,我可能不够格,这些书籍都有可以自己去翻阅,我能给的就是,总结我写代码的时候,会优化的一些技巧
## 实用篇
### 1.立即执行
我为什么把立即执行放在第一个,因为一般做业务的时候都会有一个入口函数,比如一下这种格式
```js
function init(){
    //...
}
init()
```
我一开始也跟上面这样写,但是后来看了《你所不知道的javaScript》,我是这么写的
```js
(function init(){
    //...
})()
```
立即执行的好处:
作用域隔离,因为init这个函数名是没有必要在全局作用域中展示的
### 2.常量解耦
这个技巧我在业务中也是经常使用的,没学会之前是这么写的
```js
function(){
    console.log(12345678901)
}
```
但是学会之后,是这样的
```js
const TEL = 12345678901
function(){
    console.log(TEL)
}
```
可能这里有人会问,你这不是多此一举,而且还添加了一个全局常量,但是事实是这样的,好处如下:
- 当多处引用这个常量的时候,当你修改的时候,只要修改一处代码即可
- 这样写出来的代码更具语义话,举个例子,有些请求回调中ERROR常量一般为1,摆在那别人很难看懂
### 3.递归的解耦
这个方法在红宝书中有写到,我不知道常不常用,如有错误,请纠正我,因为我递归一直在用,面试写算法题也在用,没学会之前的写法
```js
function a(){
    //...
    a();
}
```
学会之后,我的递归一般是这么写的
```js
function a(){
    //...
    argument.callee();
}
```
解耦的好处:
修改函数名即可,不影响里面的代码
### 4.整数的转换之加法篇
你可能以前看到过parseInt和parseFloat这两个方法来转换成number类型的整数型和浮点型,其实,一开始我也是用这么笨的方法的,为什么说笨呢,举个例子
```js
let str = '123'
console.log(typeof parseInt(str));    //number
```
接下来是加号操作符
```js
let str = '123'
console.log(typeof +str);    //number
```
好处就不多说了吧,你打字打得累死,不如加号来的快,而且加号实现方式还优雅
### 5.短路操作
没学会这个方法之前我的代码是这样的:
```js
if(!foo){
   foo = bar
}
```
但是我学会短路操作之后的是这样的:
```js
foo = foo || bar
```
好处:
1. 代码量减少
2. 书写优雅

缺点:
代码可读性降低

短路原理:
在js中&&和||这两个操作符有一个特性,比如&&前后有两个表达式,前者为false,后者不会执行,||会反过来

### 6.条件表达式
条件表达式或许在每门语言中都会用到,你没学会之前你会这么写
```js
if(a === true){
   b = c;
}else{
    b = d;
}
```
但是你学会之后,你会这么写
```
let b = a ? c : d
```
好处:
1.减少代码量
2.代码优雅

缺点:
代码可读性降低
### 7.调试之alert
我没有实习之前不喜欢用alert的,现在也不怎么用,都是console.log,这样就可以在浏览器的控制台中看到页面数据的输出,但是h5不一样,h5有时候pc端没什么问题,但是手机端就是各种bug,想调试一个数据很麻烦,但是我们有个宝贝,alert这个东西在手机端调试比console.log棒的地方就是我们能看见我们的数据,他会以一个弹框的形式显示给我们
### 8.优雅的向下取整
向下取整有很多种方式,做常用的,是调用Math的方法,如下图的例子
```
let num = 1.23
let num1 = Math.floor(num);
console.log(num1); //1
```
但是下面有一种更加优雅的方式
```js
let num = 1.23
let num1 = num | 0;
console.log(num1)  // 1
```
这种方式更加简洁,这种方式的原理是来源于js的位运算,这边的 | 不是逻辑或,是按或运算
- 注意点:
    - Math.floor(NaN)返回的是NaN,但是或零返回的是0;
    - Math.floor(Infinity)返回的是Infinity,但是或零返回的是0;
### 9.单声明的形式
在编程中,最好养成一个习惯,一个函数的参数,声明在函数的顶部,然后用这个声明操作符来完成,例如:
```js
let a = 0,
     b = 0,
     c = 0,
     d = 0;
```
- 这样的好处有:
    - 变量名不散乱,比较容易寻找
    - 少代码量
    - 防止你声明变量的时候出错
### 10.绑定this
这个怎么解释呢?其实es6箭头函数已经解决了,我也当一个技巧跟大家絮叨絮叨,我们在设计整个js代码怎么书写的时候,常常会把一块功能相同的代码放到一块,看看下面的例子,你就懂了
```js
function bindEvent(){
    let _this = this;
    function a () {
        //可以在a中使用_this;
    }
    function b () {
        //可以在b中使用_this;
    }
}
```

- 总结:
上面很多技巧,我平时也在用,如果有错,请你们纠正,如果有受益,麻烦点个👍,谢谢。

================================================
FILE: blog/keep-alive.md
================================================
## 前言
- vue中有一个内置组件就是keep-alive组件,这个组件自身不会被渲染成一个Dom对象,也不会监听任何事件
## 流程图
![](../image/keep-alive.png)
## keep-alive的实现
- 既然是内置组件,就应该有组件的生命周期等一些特性,在源码中是这样的,我们一部分一部分的看
```js
export default {
  name: 'keep-alive',   //名字keep-alive
  abstract: true,   //不渲染成dom元素,也不监听任何事件,抽象组件的意思。

  props: {
    ...
  },

  created () {
    ...
  },

  destroyed () {
    ...
  },

  watch: {
    ...
  },

  render () {
    ...
  }
}
```
- props
```js
props: {
//从父组件传递进来的数据
include: patternTypes,
//字符串或正则表达式。只有匹配的组件会被缓存。
exclude: patternTypes
//字符串或正则表达式。任何匹配的组件都不会被缓存。
},
```
> 这里用的是TypeScript的语法,意思是include和exclude必须是patternType类型的,那什么是patternTypes类型呢?源码中是这样解释的

```js
const patternTypes: Array<Function> = [String, RegExp, Array]    // 定义一个数组,数组中的元素是函数的指针
```
> 这样我们就清晰很多,字符串,正则,数组中的一个,所以,官网给出的keep-alive中有这么一段

```html
<!-- 逗号分隔字符串 -->
<keep-alive include="a,b">
  <component :is="view"></component>
</keep-alive>
<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
  <component :is="view"></component>
</keep-alive>
<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
  <component :is="view"></component>
</keep-alive>
```
- created
```js
created () {
//创建一个空对象
    this.cache = Object.create(null)
},
```
> created的生命钩子是数据挂载的时候,尤大在这个钩子里面创建了一个cache对象,Object.create(null)这是在js中经常使用的手段,应为这个创建的对象,比{}还要空,有兴趣的可以自己谷歌一下

- destroyed
```js
destroyed () {
//遍历缓存的数据,并调用pruneCacheEntry,去销毁this.cache对象
    for (const key in this.cache) {
        pruneCacheEntry(this.cache[key])
    }
},
```
> 这个生命钩子是组件被销毁的生命钩子,这里面有一个pruneCacheEntry函数,我们可以来看一下,他是实现什么功能的

```js
function pruneCacheEntry (vnode: ?VNode) {
  //传入一个虚拟dom对象
  if (vnode) {
    //如果有这个对象
    vnode.componentInstance.$destroy()
    //调用对象组件中销毁接口
  }
}
```
> 这个函数中传入的是一个对象调用对象destroy的生命钩子,可以去销毁他

> 所以我们可以知道当keep-alive这个组件别销毁之后,缓存的对象也会随之销毁

- watch
```js
watch: {
  //当include中的值发生变化时,会触发prunecache函数
  include (val: string | RegExp | Array<string>) {
    pruneCache(this.cache, this._vnode, name => matches(val, name))
  },
  exclude (val: string | RegExp | Array<string>) {
    pruneCache(this.cache, this._vnode, name => !matches(val, name))
  }
},
```
> watch这个是检测props中的数据的变化的,当include和exclude的值发生变化时,值的类型还是上述三种,我们会调用pruneCache函数,但是调用之前,我们先来看一下matches函数

```js
function matches (pattern: string | RegExp | Array<string>, name: string): boolean {
  //传入pattern,类型是字符串或正则或者字符串数组,传入名字字符串,返回布尔值
  if (Array.isArray(pattern)) {
    //检查pattern变量是否是数组
    return pattern.indexOf(name) > -1
    //如果是,就看name是否在数组中,在的返回true,不再返回false
  } else if (typeof pattern === 'string') {
    //检测类型是否是字符串
    return pattern.split(',').indexOf(name) > -1
    //将其分割,存入数组中,之后查找那么是否在分割后的数组中,在的返回true,不再返回false
  } else if (isRegExp(pattern)) {
    //判断他是否是正则
    return pattern.test(name)
    //测试name是否在其中
  }
  /* istanbul ignore next */
  return false
  //如果都不是返回false
}
```
> matches函数的作用就是判断name是否在pattern中,如果在其中返回的是true,不在其中,返回的是false

```js
function pruneCache (cache: VNodeCache, current: VNode, filter: Function) {
  //传入的第一个参数是缓存对象,原本的对象,一个filter函数,其实就是上述的matches函数
  for (const key in cache) {
    //遍历缓存对象
    const cachedNode: ?VNode = cache[key]
    //cacheNode是缓存对象中的一项
    if (cachedNode) {
      //如果cachedNode不为空
      const name: ?string = getComponentName(cachedNode.componentOptions)
      //获取组件的名字
      if (name && !filter(name)) {
        //这里的filter其实是match函数,因为可以从下面可以看出,返回的是一个布尔值
        if (cachedNode !== current) {
          //如果缓存的不等于正确的,就是组件已经更新了,就把原来的销毁掉
          pruneCacheEntry(cachedNode)
        }
        //将全局变量cache变成null
        cache[key] = null
      }
    }
  }
}
```

> 这个函数的大致作用就是判断缓存组件中的每一项,当缓存中的每一项,又和正确是是否相同,如果不相同的话调用pruneCacheEntry函数销毁他,这里面有一个getComponentName函数,我们来看一下这个是用来干什么的?

```js
function getComponentName (opts: ?VNodeComponentOptions): ?string {
    // 接收的类型是VNodeComponentOptions,返回的是字符串类型
  return opts && (opts.Ctor.options.name || opts.tag)
  //这里面时一个短路操作,当opts为真时,里面又是一个短路操作,当opts.Ctor.options.name为真时,返回opts.Ctor.options.name,当opts.Ctor.options.name为假时,返回opts.tag。
  // componentOptions包含五个元素{ Ctor, propsData, listeners, tag, children }
}
```
- render
```js
render () {
  const vnode: VNode = getFirstComponentChild(this.$slots.default)
  //getFirstComponentChild这个函数等会再说,他的主要作用就是获取this.$slots.default的第一个子元素
  const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
  //这是一个js的短路操作,当vnode为真时,componentOptions等于vnode.componentOptions
  if (componentOptions) {
    // check pattern
    const name: ?string = getComponentName(componentOptions)
    //获取opts.Ctor.options.name元素
    if (name && (
      (this.include && !matches(this.include, name)) ||
      (this.exclude && matches(this.exclude, name))
      //如果匹配到exclude中,或者没有匹配到include中,就直接返回,不缓存
    )) {
      return vnode
    }
    //接下来进行缓存操作
    const key: ?string = vnode.key == null
      ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
      : vnode.key
      //当vnode.key为null时,key等于前者,否则等于后者,前者有分两种情况,componentOptions.tag为真时,componentOptions.Ctor.cid::componentOptions.tag的值,否则为componentOptions.Ctor.cid
    if (this.cache[key]) {
      //如果缓存中有这个健值对,就把对象中元素的componentInstance等于缓存中componentInstance
      vnode.componentInstance = this.cache[key].componentInstance
    } else {
      //否者,缓存虚拟对象
      this.cache[key] = vnode
    }
    vnode.data.keepAlive = true
    //吧对象中一个keepAlive设置为true代表以缓存
  }
  return vnode
}
```

- 总结,源码看完了,我们总结一下
1、在自己的代码中,可以用短路操作的,尽量用短路操作,这样可以美化代码
2、当组件销毁时,记得吧组件中的变量一并销毁,这样可以保证安全
3、从源码中,我们可以看出vue文档中为什么只接受String, RegExp, Array,String还需要用逗号隔开
4、写代码的时候,要把方法函数单独拎出来写,这样可以强化代码的复用


================================================
FILE: blog/lifecycle.md
================================================
## lifecycle.js源码
### 我们先来看一张网上的图
- [vue生命周期](https://cn.vuejs.org/images/lifecycle.png)
### 接下来是我画的源码流程图

### 各个击破
- activeInstance
```js
export let activeInstance: any = null
//全局变量,初始化为null,类型可以为任意
```
- isUpdatingChildComponent
```js
export let isUpdatingChildComponent: boolean = false
//全局变量,初始化为false,类型为boolean
```
- initLifecycle


================================================
FILE: blog/nodejs几种文件路径及path模块.md
================================================
### 前言

最近在写一篇weex的webpack配置,刚刚踩坑了,weekpack中会用到path模块,而对于这个模块,我想抽离出来看一下,因为这个用到的还是比较多的,喜欢的朋友可以点个喜欢,或者去我的[github](https://github.com/laihuamin/JS-total)点个star也行,谢谢支持,举起小手指点一点哦😯。

### node中的路径分类

node中的路径大致分5类,__dirname,__filename,process.cwd(),./,../,其中前三个都是绝对路径

我们先来看一个简单点的例子

假如,我有一个文件的目录结构如下:

```
editor/
  - dist/
  - src/
      - task.js
```
然后我们在task.js文件中写入一下代码

```js
const path = require('path');
console.log(__dirname);
console.log(__filename);
console.log(process.cwd());
console.log(path.resolve('./'));
```

在editor目录下运行node src/task.js,我们可以看到结果如下:

```
/Users/laihuamin/Documents/richEditor/editor/src
/Users/laihuamin/Documents/richEditor/editor/src/task.js
/Users/laihuamin/Documents/richEditor/editor
/Users/laihuamin/Documents/richEditor/editor
```

然后我们有可以在src目录下运行这个文件,node task.js,运行结果如下:

```
/Users/laihuamin/Documents/richEditor/editor/src
/Users/laihuamin/Documents/richEditor/editor/src/task.js
/Users/laihuamin/Documents/richEditor/editor/src
/Users/laihuamin/Documents/richEditor/editor/src
```

对比两个输出结果,我们可以归纳一下几点:
1.__dirname:返回的是这个文件所在文件夹的位置
2.__filename:你运行命令代表的是文件所在的位置,不管你运行什么命令,都是指向文件
3.process.cwd():你运行node命令所在文件夹的位置,比如你在src目录下运行,那么就是输出到src为止,下面的同理。

### path

讲完前面三个绝对路径,我倒是挺想来聊聊path这个模块的,这个node模块在很多地方都有应用,所以,对于我们来说,掌握他,对我们以后的发展更有利,不用每次看webpack的配置文件还要去查询一下这个api是干什么用的,很影响我们的效率

[nodeJS/path](https://nodejs.org/api/path.html)

上面那个网站有详细的api,但是我们这里不用都掌握吧,我就讲几个我遇到过的,我觉得webpack等工程配置中会用到的

### path.normalize

这个方法就是把不规范的路径规范化,比如看下面的例子

```js
const path = require('path');
console.log(path.normalize('/foo/bar//baz/asdf/quux/..'));
```
输出结果:
```
/foo/bar/baz/asdf
```

### path.join

```js
const path = require('path');
console.log(path.join('src', 'task.js'));

const path = require('path');
console.log(path.join('dist', 'task.js'));

const path = require('path');
console.log(path.join(''));
```

这么两个的输出结果是:

```
src/task.js
dist/task.js
.
```

他的作用也就显而易见,他有一下几条规则:
1.传入的参数是字符串的路径片段,可以是一个,也可以是多个

2.返回的是一个拼接好的路径,但是根据平台的不同,他会对路径进行不同的规范化,举个例子,Unix系统是”/“,Windows系统是”\“,那么你在两个系统下看到的返回结果就不一样。

3.如果返回的路径字符串长度为零,那么他会返回一个'.',代表当前的文件夹。

4.如果传入的参数中有不是字符串的,那就直接会报错

### path.parse
我们先来看个例子,在src目录下的task.js写入
```js
const path = require('path');
console.log(path.parse('/Users/laihuamin/Documents/richEditor/editor'));
```
然后运行node src/task.js之后,输出的结果如下:
```js
{ 
  root: '/',
  dir: '/Users/laihuamin/Documents/richEditor',
  base: 'editor',
  ext: '',
  name: 'editor' 
}
```
他返回的是一个对象,那么我们来把这么几个名词熟悉一下:

```
┌────────────────────—————————————————————─┬───────────┐
│          dir                             │    base   │
├────┬                                     ├─────—─┬───┤
│root│                                     │ name  │ext│
"  /  Users/laihuamin/Documents/richEditor / editor ''
└────┴────────——————————————————————───—───┴───—───┴───┘
```

![path.parse](http://laihuamin.oss-cn-beijing.aliyuncs.com/path.parse.png)

这个表格应该展示的很形象,但是我们还是来解释一下这些名词:
1.root:代表根目录
2.dir:代表文件所在的文件夹
3.base:代表整一个文件
4.name:代表文件名
5.ext: 代表文件的后缀名

那我们根据下面的规则,来看一下下面这个例子,最好自己脑子做一遍

```js
const path = require('path');
console.log(path.parse('/Users/laihuamin/Documents/richEditor/editor/src/task.js'));
```

输出的结果:
```
{ 
  root: '/',
  dir: '/Users/laihuamin/Documents/richEditor/editor/src',
  base: 'task.js',
  ext: '.js',
  name: 'task' 
}
```
你做对了么?0.0

### path.basename

那有了前面这个铺垫,想必这个接口猜也能猜的到了。。。。我们看下面这个例子

```js
const path = require('path');
console.log(path.basename('/Users/laihuamin/Documents/richEditor/editor/src/task.js'));
```

输出的结果是:
```
task.js
```

我们还是简单介绍一下,接收两个参数,一个是path,还有一个是ext(可选参数).
```js
const path = require('path')
console.log(path.basename('/Users/laihuamin/Documents/richEditor/editor/src/task.js', '.js'));
```

输出结果:
```
task
```

### path.dirname

这个接口比basename还要简单,我就不多说了,看例子,看结果
```js
const path = require('path');
console.log(path.basename('/Users/laihuamin/Documents/richEditor/editor/src/task.js'));
```
输出的结果:

```
/Users/laihuamin/Documents/richEditor/editor/src
```
注意一下,接收的参数是字符串类型
### path.extname
这个就是展示文件的扩展名,我们得注意几种情况
```js
const path = require('path');
path.extname('index.html');
path.extname('index.coffee.md');
path.extname('index.');
path.extname('index');
path.extname('.index');
```

输出的结果是:
```
.html
.md
.
''
''
```
自己注意一下这几个情况

### path.resolve

我们通过下面这几个例子先来熟悉一下:

```js
const path = require('path');
console.log(path.resolve('/foo/bar', '/bar/faa', '..', 'a/../c'));
```

输出的结果是

```
/bar/c
```

他就相当于一堆cd操作,我们一步一步看

```
cd /foo/bar/    //这是第一步, 现在的位置是/foo/bar/
cd /bar/faa     //这是第二步,这里和第一步有区别,他是从/进入的,也就时候根目录,现在的位置是/bar/faa
cd ..       //第三步,从faa退出来,现在的位置是 /bar
cd a/../c   //第四步,进入a,然后在推出,在进入c,最后位置是/bar/c
```
但是这个操作和cd还是有区别的,这个路径不一定要存在,而且最后的可以是文件


### path.relative

这个返回的是from到to的相对路径,什么意思呢,我们看下面的例子就知道了.

```js
const path = require('path');
console.log(path.relative('src/bar/baz', 'src/aaa/bbb'));
```
输出的结果是:

```
../../aaa/bbb
```

### 总结
这些比较实用的方法,分享给大家,自己还是老老实实去看weektool的webpack的配置文件了,喜欢的朋友可以点个喜欢,或者去我的[github](https://github.com/laihuamin/JS-total)点个star也行,谢谢支持,举起小手指点一点哦😯。

================================================
FILE: blog/node中的EventLoop.md
================================================
关于事件这一块在《深入浅出的nodejs》中很少讲到,书里面只是在第三章提及了4个API方法,比如两个定时器(setTimeout和setInterval),process.nextTick()和setImmediate。

[浏览器中的event loop](https://github.com/laihuamin/JS-total/issues/43)

### 前篇回顾

```js
setTimeout(()=>{
    console.log('timer1')

    Promise.resolve().then(function() {
        console.log('promise1')
    })
}, 0)

setTimeout(()=>{
    console.log('timer2')

    Promise.resolve().then(function() {
        console.log('promise2')
    })
    setTimeout(() => {
    	console.log('timer3')
    }, 0)
}, 0)

Promise.resolve().then(function() {
    console.log('promise3')
})

console.log('start')
```

浏览器中输出结果:

```js
start
promise3
timer1
promise1
timer2
promise2
timer3
```
这个输出结果的原因我们已经在上一篇文章中说明,本章就不多加赘述。

在nodejs中,运行却能得到不同的结果,让我们先来过一下node的事件模型。
```js
start
promise3
timer1
timer2
promise1
promise2
timer3
```
### node中的事件循环模型

node的事件循环分为6个阶段

[源代码地址](https://github.com/libuv/libuv/blob/v1.x/src/unix/core.c#L348-L397)有兴趣的可以去看一下源代码

![node-phase.png](https://i.loli.net/2018/09/03/5b8c84aca9792.png)

六个阶段的功能如下:

- timers:这个阶段执行定时器队列中的回调,比如setTimeout()和setInterval()
- I/O callbacks:这个阶段执行几乎所有的回调。但是不包括close事件,定时器和setImmediate的回调。
- idle,prepare:仅在内部使用。
- poll:等待新的I/O事件,node会在一些特殊的情况下使用
- check:setImmediate()的回调会在这个中执行。
- close callbacks:例如socket.on('close', ...)执行close的回调。


#### poll阶段

当有数据或者连接传入事件循环的时候,先进入的是poll阶段,这个阶段,先检查poll queue中是否有事件,有任务就按先进先出的顺序执行回调,如果队列为空,那么会先检查是否有到期的setImmdiate,如果有,将其的callback推入check队列中,同时还会检查是否有到期的timer,如果有,将其callback推入到timers队列中。如果前面两者都为空,那么直接进入I/O callback,并执行这个事件的callback。

#### check阶段

check阶段专门用来执行setImmidate的回调函数。

#### close阶段

用于执行close事件的回调函数

#### timer阶段

用于执行定时器设置的回调函数

#### I/O callback阶段

用于执行大部分I/O事件的回调函数。

#### process.nextTick

这个钩子在node的事件循环模型中没有提及,但是node中有一个特殊的队列,nextTick queue。在node事件循环进入到下一个阶段的时候,都会去检测nextTick queue中有没有清空,如果没有清空,那么就会去清空nextTick queue中的事件。


### 循环过程

在[官网的文档](https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/)里面有那么一段话:

> When Node.js starts, it initializes the event loop, processes the provided input script (or drops into the REPL, which is not covered in this document) which may make async API calls, schedule timers, or call process.nextTick(), then begins processing the event loop.

- 循环开始之前

1、所有同步任务
2、脚本任务中发送的api请求
3、规划定时器同步任务的生效时间
4、执行process.nextTick()

- 开始循环

> 第一种情况

1、清空当前循环内的 Timers Queue,清空NextTick Queue,清空Microtask Queue
2、清空当前循环内的 I/O Queue,清空NextTick Queue,清空Microtask Queue
3、进入poll阶段
4、清空当前循环内的 Check Queue,清空NextTick Queue,清空Microtask Queue
5、清空当前循环内的 Close Queue,清空NextTick Queue,清空Microtask Queue

> 第二种情况

1、先进入poll阶段
2、清空当前循环内的 Check Queue,清空NextTick Queue,清空Microtask Queue
3、清空当前循环内的 Close Queue,清空NextTick Queue,清空Microtask Queue
4、清空当前循环内的 Timers Queue,清空NextTick Queue,清空Microtask Queue
5、清空当前循环内的 I/O Queue,清空NextTick Queue,清空Microtask Queue

- setTimeout 和 setImmediate 的区别

```js
setTimeout(() => {
  console.log('timeout')
}, 0);

setImmediate(() => {
  console.log('immediate')
});
```

直接运行脚本,输出的结果是

```js
timeout
immediate
```

当我们把他放在同一个I/O循环中运行

```js
const fs = require('fs');

fs.readFile(__filename, () => {
    setTimeout(() => {
        console.log('timeout')
    }, 0)
    setImmediate(() => {
        console.log('immediate')
    })
})
```

输出的结果是

```js
immediate
timeout
```

- process.nextTick和microtask

```js
process.nextTick(() => {
    console.log('nextTick')
})

Promise.resolve().then(() => {
    console.log('promise')
})
```

输出的结果是

```js
nextTick
promise
```

nodejs中的实现方式:microtask queue的任务通过runMicrotasks将microtask queue中的task放入到nextTick中,所以microtask的任务会在nextTick queue之后执行。

- process.nextTick() 和 setImmediate()

> 书本中是推荐使用setImmediate(),用户如果递归调用process.nextTick()的时候,会造成I/O被榨干。而使用setImmediate,只会在check中执行,不至于异步调用的时候无法执行。

================================================
FILE: blog/require工作原理.md
================================================
在平常开发中,当我们需要用一个模块的时候,只需要require一下就行了,但是对于其内部的原理,不一定都清楚。读《深入浅出的nodejs》的时候,我们会发现,书中提到,每一个模块在编译过程中,node都会在模块外面封装一层,(function(exports, require, module, \_\_filename, \_\_dirname){})。但是真正调用require的时候,发生了什么,必须得细究一下。

### require大致过程

- require方法

```js
Module.prototype.require = function(id) {
  if (typeof id !== 'string') {
    throw new ERR_INVALID_ARG_TYPE('id', 'string', id);
  }
  if (id === '') {
    throw new ERR_INVALID_ARG_VALUE('id', id,'must be a non-empty string');
  }
  return Module._load(id, this, /* isMain */ false);
};
```
- _load方法

```js
Module._load = function(request, parent, isMain) {
  if (parent) {
    debug('Module._load REQUEST %s parent: %s', request, parent.id);
  }

  var filename = Module._resolveFilename(request, parent, isMain);

  var cachedModule = Module._cache[filename];
  if (cachedModule) {
    updateChildren(parent, cachedModule, true);
    return cachedModule.exports;
  }

  if (NativeModule.nonInternalExists(filename)) {
    debug('load native module %s', request);
    return NativeModule.require(filename);
  }

  // Don't call updateChildren(), Module constructor already does.
  var module = new Module(filename, parent);

  if (isMain) {
    process.mainModule = module;
    module.id = '.';
  }

  Module._cache[filename] = module;

  tryModuleLoad(module, filename);

  return module.exports;
};
```
从上述两个函数中,require的大致过程如下:

1、先检测传入的id是否有效。
2、如果有效,则调用Module._load方法,该方法主要负责加载新模块和管理模块的缓存,而require本身就是对该方法的一个封装。
3、然后会调用Module._resolveFilename去取文件地址。
4、判断是否有缓存模块,如果有返回缓存模块的exports。
5、如果没有缓存,在检测文件名是否是核心模块,如果是调用核心模块的require。
6、如果不是核心模块,那么,创建新的一个module对象。
7、在 Module._cache中缓存该对象,
8、返回模块本身的exports对象。


上述的解读中,我们抛开了两个没有谈,一个是Module._resolveFilename()方法,还有一个是tryModuleLoad()方法。

### filename的获取


```js
Module._resolveFilename = function(request, parent, isMain, options) {
  if (NativeModule.nonInternalExists(request)) {
    return request;
  }

  var paths;

  if (typeof options === 'object' && options !== null &&
      Array.isArray(options.paths)) {
    const fakeParent = new Module('', null);

    paths = [];

    for (var i = 0; i < options.paths.length; i++) {
      const path = options.paths[i];
      fakeParent.paths = Module._nodeModulePaths(path);
      const lookupPaths = Module._resolveLookupPaths(request, fakeParent, true);

      if (!paths.includes(path))
        paths.push(path);

      for (var j = 0; j < lookupPaths.length; j++) {
        if (!paths.includes(lookupPaths[j]))
          paths.push(lookupPaths[j]);
      }
    }
  } else {
    paths = Module._resolveLookupPaths(request, parent, true);
  }
  var filename = Module._findPath(request, paths, isMain);
  if (!filename) {
    var err = new Error(`Cannot find module '${request}'`);
    err.code = 'MODULE_NOT_FOUND';
    throw err;
  }
  return filename;
};
```

_resolveFilename的大致流程:

1、查询文件名是否是核心模块,如果是直接返回传入的id
2、因为option没有参数传入,所以会调用 Module.\_resolveLookupPaths方法去获取路径
3、调用Module.\_findPath方法

我们可以写如下测试代码:

```js
console.log(require('module')._resolveFilename('./lodash'));
let paths = require('module')._resolveLookupPaths('./lodash');
console.log(paths);
console.log(require('module')._findPath("./lodash", paths[1]));
```
输出的结果:
```js
/Users/laihuamin/Documents/learn-record/node_modules/lodash/lodash.js
[ './lodash',
  [ '.',
    '/Users/laihuamin/Documents/learn-record/node_modules',
    '/Users/laihuamin/Documents/node_modules',
    '/Users/laihuamin/node_modules',
    '/Users/node_modules',
    '/node_modules',
    '/Users/laihuamin/.node_modules',
    '/Users/laihuamin/.node_libraries',
    '/Users/laihuamin/.nvm/versions/node/v6.9.1/lib/node' ] ]
/Users/laihuamin/Documents/learn-record/node_modules/lodash/lodash.js
```
\_resolveLookupPaths:其实就是node解析模块中的路径查找,他会向父目录查找,直到根目录为止。
\_findPath:其实就是将\_resolveLookupPaths查找出来的文件名和文件id向匹配,返回一个文件地址。

### tryModuleLoad
```js
function tryModuleLoad(module, filename) {
  var threw = true;
  try {
    module.load(filename);
    threw = false;
  } finally {
    if (threw) {
      delete Module._cache[filename];
    }
  }
}
```
在拿到文件地址之后,module会调用这个方法取tryModuleLoad,去尝试加载模块,如果报错,那么清除模块的缓存。

================================================
FILE: blog/sticky你了解多少.md
================================================
## sticky你了解多少
### 前言
以前呢position只有4种属性,但是到了css3之后呢,又添了这个家伙,刚接触css的时候也不知道,最近业务中碰到的次数越来越多了,就想写一篇自己研究的,让大家参考参考,可能写的不够好,不过,看了之后觉得喜欢的可以点个喜欢,或者点个关注,博文中有几篇高赞的也可以看一看,或者觉得博主写的不错,可以给我的[github](https://github.com/laihuamin/JS-total)点个star

### css定位
在讲什么是sticky之前,我们先来补一下css中的三个定位,知道的朋友可以跳过这一段,我们先来看一下没有定位的默认状态,一下都会以这个举例:

![position-static](http://laihuamin.oss-cn-beijing.aliyuncs.com/position-static.png)

#### 相对定位
相对于其正常位置进行定位,但是不影响其他元素的偏移。元素的位置通过 left, top, right 以及 bottom 属性进行规定。
![position-relative](http://laihuamin.oss-cn-beijing.aliyuncs.com/position-relative2.png)
#### 绝对定位
相对定位的元素并未脱离文档流,而绝对定位的元素则脱离了文档流。在布置文档流中其它元素时,绝对定位元素不占据空间。绝对定位元素相对于最近的非 static 祖先元素定位。当这样的祖先元素不存在时,则相对于根级容器定位。举个例子:
![position-absolute](http://laihuamin.oss-cn-beijing.aliyuncs.com/position-absolute.png)

#### 固定定位
固定定位与绝对定位相似,但元素的包含块为 viewport 视口。该定位方式常用于创建在滚动屏幕时仍固定在相同位置的元素。
![position-fixed](http://laihuamin.oss-cn-beijing.aliyuncs.com/position-fixed.png)

### 什么是sticky
MDN给出的解释是
> 粘性定位是相对定位和固定定位的混合。元素在跨越特定阈值前为相对定位,之后为固定定位。

MDN中的说法以及例子

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/MDN.png)

可以,我的实践结果告诉我,MDN给的那个解释有错误,可能是我理解的不够深,但是我把我的探索过程和大家分享,要是大家觉得我哪里说错了,一定要在评论中纠正我,这样我可以及时修改,以免误导大家.

首先我们来看一下,relative和fixed定位下,滚动会有什么样的效果,我们来看几张图片

![relative](http://laihuamin.oss-cn-beijing.aliyuncs.com/relative.png)

![fixed](http://laihuamin.oss-cn-beijing.aliyuncs.com/fixed.png)

我来大致介绍一下这两个情况下滚动会发生些什么,fixed是根据html定位的,他会一直呆在原地,而relative就不一样,他会跟着滚上去,有兴趣的可以自己去jsbin上试一试,亲身体验一下,接下来我要和大家讲讲我们今天的主角,sticky,我们也来看几张图,我们先来研究他到底是基于什么定位的。

![未滚动](http://laihuamin.oss-cn-beijing.aliyuncs.com/sticky1.png)

![滚动](http://laihuamin.oss-cn-beijing.aliyuncs.com/sticky2.png)

笔者自己玩过了,总结出来一套:

1、其实sticky是根据窗口定位的,但是这个有个前提,不能脱离body的范围,我们可以从1和2看到,当top小于body的margin的时候,它的top是不起作用的,只有当向上滚了50以上之后,你会发现,sticky是基于窗口定位的,笔者很巧的用了100和50这两个值,其实我为什么说和MDN上有区别,就是这个阈值范围是错的,你们可以自己体验一把,可以将margin设成100,top设置成30,你就可以看出阈值是70.

2、第二点,我在放两张图,是sticky和fixed的区别。

![fixed](http://laihuamin.oss-cn-beijing.aliyuncs.com/fixed1.png)

![sticky](http://laihuamin.oss-cn-beijing.aliyuncs.com/sticky3.png)

你会发现,其实fixed属于脱离文档流,sticky不属于脱离文档流,这点和relative很像,不影响布局,但是会根据位置偏移,但是也没有MDN中所说的阈值变化一说,只能说很像。

- 注意:

这两点是我自己实践出来的,和权威有区别,你们也可以试试,然后我们在评论中可以探讨。

### 兼容性
关注新东西时,兼容性总是关键,掌握他也得从这点入手,这样才能挑战移动端的各种机型等等。
![sticky兼容性](http://laihuamin.oss-cn-beijing.aliyuncs.com/stickyUse.png)

我们先看pc端的,IE是完全不支持的,在后面我会讲一种js来实现sticky的效果,其他兼容性还可以,我们可以用到浏览器前缀,来更好的兼容它。何为浏览器前缀,我们举个例子:

```
.sticky { 
	position: -webkit-sticky; 
	position:sticky; 
	top: 15px;  
}
```

### 不兼容的情况
我们先要搞清楚sticky这个属性是怎么来的,sticky的提出是因为屏幕越做越大,但是屏幕大了不适宜阅读,网页主体大小没有多大变化,这样就会导致很多空白区,这些空白区用来干什么呢,打广告呗,还有就是用来做导航条,不需要考虑内容已开始就被导航条遮盖的情况,导航条的情况属于大多数,我们可以用js来模拟一下:

HTML
```html
<div class="header"></div>  
```

CSS
```css
.sticky { 
	position: fixed;
	top: 0;
} 
.header {
	width: 100%; 
	background: red; 
	height:100px;}
```

JS
```js
const header = document.querySelector('.header'); 
const origOffsetY = header.offsetTop; 
function onScroll(e) { 
	window.scrollY >= origOffsetY ? header.classList.add('sticky') : header.classList.remove('sticky'); 
} 
document.addEventListener('scroll', onScroll);
```

这个原理就是监听滚动事件,然后给元素添加position:fixed属性,我模拟的这个有两点做的不够好:

1、滚动事件触发的太频繁,没有用节流函数,也算我懒吧,大家用的时候要注意
2、还有一点就是那个我上面指出的第二点,未滚动时,那个类似于relative的状态我模拟不出来,或者就是那个第二点我理解错误,有什么想法,可以一起在评论中交流。

### 总结
这种属性,自己摸索一边真的有助于掌握,这篇博文,是我摸索的结果,我信我自己没有出错,如果有意见不符可以在评论中探讨,本来还有几个例子的,但是我觉得直接放代码晦涩难懂,又没有一种好的方式,不知道掘金允不允许将jsbin嵌到页面中,自己也没尝试,不过,我觉得我已经把特性搞懂了,也尽最大努力把它讲明白,还是建议自己实践一遍,如果喜欢给个喜欢,关注,或者去我的[github](https://github.com/laihuamin/JS-total)点个star,对我的支持是我在前端探索的最大动力,谢谢。

================================================
FILE: blog/this的重新认识.md
================================================
## 原来写的this,最近看书又有了新的认识
[原来的博客](https://github.com/laihuamin/program-blog/issues/19)
## 对this的重新认识
### 默认绑定的情况
- 在严格模式下,this绑定的是undefined
- 在非严格模式下,this绑定的是全局作用域
### 隐形绑定
> 这种情况就是我们所说的调用时决定的绑定,举个例子
```
function thisBind() {
    console.log(this.name);
}
const obj = {
    name: 'laihuamin',
    fn: thisBind
}
obj.fn(); // laihuamin
const bar = obj.fn;
let name = 'huaminlai';
bar(); // undefined
setTimeout(obj.fn, 1000); //undefined
```
> 是不是很奇怪,不应该绑定到全局么,基础不扎实的我也是这么认为的,那我们看下面这个例子

```
function thisBind() {
    console.log(this.name);
}
const obj = {
    name: 'laihuamin',
    fn: thisBind
}
obj.fn(); // laihuamin
const bar = obj.fn;
var name = 'huaminlai';    //改了操作符哦
bar(); // huaminlai
setTimeout(obj.fn, 1000); //huaminlai
```
> 其实到这里,我们已经大致知道了,是let这里出了问题,跑个题继续往下看,我们在看一个例子

```
window.a = 1;
console.log(a) // 1
var a = 2;
window.a // 2
```
> 其实在这里,有两个概念,顶层对象和全局变量,顶层对象即window对象,全局变量在ES5中是顶层对象的属性,但是这就会产生很大的问题,第一个,顶层对象的属性是可读可写的,这个不利于模块化编程,第二个,window对象是实体含义,但是可以随意改变属性就不是这个味道了。

```
var a = 1;
window.a // 1

let b = 1;
window.b // undefined
```
> 在ES6中对这个就有所改观,es6中规定,let,const,class定义的全局变量不属于顶层对象,但是还保留了var,function定义的变量为顶层对象的属性

- 跑题结束,不过,补了一个不起眼的知识点
### 显性绑定
> 显示绑定是用apply和call方法将this绑定到指定的

- 这两种方法的工作机制
> 他们的第一个参数都是要绑定的对象,第二个参数call和apply有区别,apply需要传入的是一个数组,而call传入的是一个个变量

- 例子
```
function foo() {
    console.log(this.a);
}
const obj = {
    a: 2
}
foo.call(obj); //2
```
- 新问题——当你第一个对象传入的是string,boolean,number这么三个基础类型时,又会发生什么呢
> call和apply会把这些基本累心转化成new String(...),new Boolean(...),new Number(...)来处理
### new绑定
> new操作符,对于大家来说并不陌生,那么,他是进行了什么样的操作呢?

- new操作符做了什么?
1、他会先创建一个原型为空的对象
2、将这个函数的prototype赋值给对象的`_proto_`
3、将这个对象当作this传入到函数中,如果没有return,直接返回函数中this的属性,如果有return,直接返回return中的内容
- 接下来,我们来用函数模拟一下这个行为
```
function NewFunc(){
    const obj = {};
    if(func.prototype !== null){
        obj._proto_ = func.prototype;        
    }
    var ret1 = func.apply(obj, Array.prototype.slice.call(arguments, 1))
    if((typeof ret1 === 'object' || typeof ret1 === 'function') && ret1 !== null ){
        return ret1
    }
    return obj;
}
```
- 举几个例子
```
function Person1(name) {
    this.name = name;
}
function Person2(name) {
    this.name = name;
    return this.name;
}
function Person3(name) {
    this.name = name;
    return new Array();
}
function Person4(name) {
    this.name = name;
    return new String(name);
}
function Person5(name) {
    this.name = name;
    return function() {};
}


const person1 = new Person1('xl');
const person2 = new Person2('xl');
const person3 = new Person3('xl');
const person4 = new Person4('xl');
const person5 = new Person5('xl');
```
- 结果
```
xl
xl
[]
xl
function(){}
```
### 硬绑定
> 介绍完上面这几个,还遗漏了一个,便是bind,这个就是我们这里将的硬绑定,bind会把this强制绑定到对象上

```
function thisBind(){
    console.log(this.name)
}
obj={
    name: 'laihuamin'
}
var bar = thisBind.bind(obj);
bar(); //laihuamin
```
- 我们还可以实现一个简单的bind函数
```
function bind(fn, obj){
    fn.apply(obj, arguments)
}
```
- ES5中内置的bind函数
```
if(!Function.prototype.bind) {
    Function.prototype.bind = function(oThis){
        if(typeof this !== 'function') {
            throw new TypeError(
                "Function.prototype.bind what is trying to be bound is not callable"
            );
        }
        var aArgs = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP = function(){},
        fBound = function () {
            return fToBind.apply((this instanceof fNOP && oThis ? this : oThis), aArgs.concat(Array.prototype.call(arguments)))
        }
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
        return fBound;
    }
}
```
### 几个绑定的优先级
- 隐形绑定和显性绑定的优先级判断
```
function thisBind(){
    console.log(this.name);
}
obj1 = {
    name: 'laihuamin',
    fn: thisBind
}

obj2 = {
    name: 'huaminlai',
    fn: thisBind
}

obj1.fn(); //laihuamin
obj1.fn.apply(obj2) //huaminlai
```
> 这里明显可以看出显性绑定优先级高于隐性绑定

- new绑定和显性绑定、硬绑定
> 硬绑定和显性绑定的优先级应该是一致的,因为bind在显性绑定的基础上封装了一层,那我们就比较显性绑定是不能与new绑定进行比较的,因为new没有办法和apply、call一起使用那么我们有什么更好的方法呢,可以让new绑定和硬绑定进行比较

```
function thisBind(){
    this.name = name
}
obj = {}
var bar = thisBind.bind(obj);
bar(2);
var obj2 = new bar(3);
console.log(obj.name);  //2
console.log(obj2.name);  //3
```
> 从这个例子我们可以看出,new绑定可以改变硬绑定的this的指向,所以,优先级是比硬绑定高的

- this绑定优先级排序
```
new绑定 > 硬绑定 = 显示绑定 > 隐性绑定 > 默认绑定
```
- 如何去看this的绑定情况
```
1、先看有没有new关键字
2、在看是否有显性绑定和硬绑定
3、再次,你可以看是否隐形绑定
4、最后在根据默认绑定来判断
```
### 安全的使用方式
- 忽略this的情况
> 如果你把null和undefined作为this的绑定,这些值就会被忽略,实际应用的是默认的绑定

```
function foo() {
    console.log(this.a)
}
var a = 2;
foo.call(null); //2
```
- 这样的操作可以进行什么呢?有两个作用,第一个可以传入参数,第二个是可以进行函数柯里化
```
function foo() {
    console.log(this.a + 'is' + this.b);
}
//展开参数
foo.apply(null, [2, 3]);
//类似与es6中的扩展运算符
foo(...[2, 3]);
//函数柯里化
var bar = foo.bind(null, 2);
bar(3); // 2+3
```
- 更安全的this
> 忽略this绑定我们有更安全的方式,传入一个DMZ对象

```
//DMZ对象
Object.create(null)
//展开对象
var a = Object.create(null);
function foo() {
    console.log(this.a + 'is' + this.b);
}
//展开参数
foo.apply(a, [2, 3]);
//类似与es6中的扩展运算符
foo(...[2, 3]);
//函数柯里化
var bar = foo.bind(a, 2);
bar(3); // 2+3
```
### 软绑定

================================================
FILE: blog/vdom的vnode.md
================================================
### 前言

对于vdom而言,更新有3个步骤,一个是create,第二个是diff,第三个是patch。这是他的大格局,但是看这个东西之前,我们可以先来看看vnode,这是虚拟的节点。

### vnode

vnode是一个虚拟的节点,他是一个对象,它有很多方法和属性,从源码中我们可以知道:

```js
constructor (tag, data, children, text, elm, context, componentOptions, asyncFactory) {
  this.tag = tag
  this.data = data
  this.children = children
  this.text = text
  this.elm = elm
  this.ns = undefined
  this.context = context
  this.functionalContext = undefined
  this.key = data && data.key
  this.componentOptions = componentOptions
  this.componentInstance = undefined
  this.parent = undefined
  this.raw = false
  this.isStatic = false
  this.isRootInsert = true
  this.isComment = false
  this.isCloned = false
  this.isOnce = false
  this.asyncFactory = asyncFactory
  this.asyncMeta = undefined
  this.isAsyncPlaceholder = false
}
```
对每一个做解释的话就是:
tag:每个节点的标签名
children:该节点的子节点,是一个数组类型
text:当前节点的文本,一般是文本节点和注释节点才会有这个属性
elm:该虚拟节点对应的真实节点
ns:当前节点的namespace
context:上下文,也可称作环境,或者编译作用域
functionalContext:函数组件的上下文
key:该节点的key,也是该节点的标识,有利于在patch的时候优化
componentOptions:创建组件的时候的options选项
componentInstance:当前节点对应的组件实例
parent:该节点的父节点
raw:是否为原生HTML或只是普通文本,innerHTML的时候为true,textContent的时候为false
isStatic:是不是静态节点
isRootInsert:是不是作为根节点插入
isComment:是不是注释节点
isCloned:是不是克隆节点
isOnce:是否有v-once指令
asyncFactory:
asyncMeta:
isAsyncPlaceholder:
data:每个节点的数据对象,VNodeData是在vue中有接口定义的,我们可以看一下

### VNodeData

在types文件夹中的vnode.d.ts中有定义

```js
export interface VNodeData {
  key?: string | number;
  slot?: string;
  scopedSlots?: { [key: string]: ScopedSlot };
  ref?: string;
  tag?: string;
  staticClass?: string;
  class?: any;
  staticStyle?: { [key: string]: any };
  style?: Object[] | Object;
  props?: { [key: string]: any };
  attrs?: { [key: string]: any };
  domProps?: { [key: string]: any };
  hook?: { [key: string]: Function };
  on?: { [key: string]: Function | Function[] };
  nativeOn?: { [key: string]: Function | Function[] };
  transition?: Object;
  show?: boolean;
  inlineTemplate?: {
    render: Function;
    staticRenderFns: Function[];
  };
  directives?: VNodeDirective[];
  keepAlive?: boolean;
}
```

data的类型是vnodeData,我们看一下其中的属性值:

### 最后我们再来看看vnode的类型

![vnode类型](http://laihuamin.oss-cn-beijing.aliyuncs.com/vnode%E7%B1%BB%E5%9E%8B.png)

看完上面几种类型,我们来看一下vnode.js文件中的几个创建节点和克隆节点的函数

- createEmptyVNode

```js
const createEmptyVNode = (text = '') => {
  const node = new VNode()
  node.text = text
  node.isComment = true
  return node
}
```
创建一个空的节点,text是空字符串,isComment为true代表是注释节点。

- createTextVNode

```js
function createTextVNode (val) {
  return new VNode(undefined, undefined, undefined, String(val))
}
```
创建一个普通的文本节点,传入的tag,data,children都是undefined,然后text的值转化成字符串类型传入。

- cloneVNode

```js
function cloneVNode (vnode, deep) {
  const cloned = new VNode(
    vnode.tag,
    vnode.data,
    vnode.children,
    vnode.text,
    vnode.elm,
    vnode.context,
    vnode.componentOptions,
    vnode.asyncFactory
  )
  cloned.ns = vnode.ns
  cloned.isStatic = vnode.isStatic
  cloned.key = vnode.key
  cloned.isComment = vnode.isComment
  cloned.isCloned = true
  if (deep && vnode.children) {
    cloned.children = cloneVNodes(vnode.children)
  }
  return cloned
}
```
该函数是创建一个新的节点然后,将传入的vnode的tag,data,children,text,elm,context,componentOptions,asyncFactory几个参数传入,之后再将vnode的ns,isDtatic,key,isComment,isCloned赋值给cloned,在检测传入的deep是否为true,或者vnode是否有children数组,如果有,那么调用下面的cloneVNodes克隆全部子节点。最后返回cloned。


- cloneVNodes

```js
function cloneVNodes (vnodes, deep){
  const len = vnodes.length
  const res = new Array(len)
  for (let i = 0; i < len; i++) {
    res[i] = cloneVNode(vnodes[i], deep)
  }
  return res
}
```
这个函数很简单,创建一个和vnodes长度相等的数组,然后循环调用一下cloneVNode函数就可以了。

================================================
FILE: blog/vue生命周期详解.md
================================================
### 前言

最近在写业务的时候,总是会遇到一些和vue的生命周期相关的问题,比如:
你用ajax请求数据,然后将数据props到子组件的时候,因为ajax是异步的,然后会发生没有数据。然后查找原因还是自己对这个东西理解不够深入。

### 生命周期图

![](https://i.loli.net/2018/05/11/5af5638adcc6b.png)

### 生命钩子函数

什么是生命周期函数?

比如:

```js
mounted: function() {
}

// 或者

mounted() {
}
```

- 注意点,Vue的所有生命周期函数都是自动绑定到this的上下文上。所以,你这里使用箭头函数的话,就会出现this指向的父级作用域,就会报错。

错误的形式:

```js
mounted:() => {
}
```

### beforeCreate
![](https://i.loli.net/2018/05/16/5afb97018f74d.png)
在实例初始化之后,数据观测和暴露了一些有用的实例属性与方法。

实例初始化——`new Vue()`

数据观测——在vue的响应式系统中加入data对象中所有数据,这边涉及到vue的双向绑定,可以看官方文档上的这篇深度响应式原理
[深度响应式原理](https://cn.vuejs.org/v2/guide/reactivity.html)

暴露属性和方法——就是vue实例自带的一些属性和方法,我们可以看一个官网的例子,例子中带$的属性和方法就是vue实例自带的,可以和用户定义的区分开来

```js
var data = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data
})

vm.$data === data // => true
vm.$el === document.getElementById('example') // => true

// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
  // 这个回调将在 `vm.a` 改变后调用
})
```

### created

- el属性对生命周期的影响

![](https://i.loli.net/2018/05/16/5afb9827155b4.png)

```js
// 有el属性的情况下
new Vue({
el: '#app',
beforeCreate: function() {
  console.log('调用了beforeCreate')
},
created: function() {
  console.log('调用了created')
},
beforeMount: function() {
  console.log('调用了beforeMount')
},
mounted: function() {
  console.log('调用了mounted')
}
})

// 输出结果
// 调用了beforeCreate
// 调用了created
// 调用了beforeMount
// 调用了mounted
```

```js
// 在没有el属性的情况下,没有vm.$mount

new Vue({
beforeCreate: function() {
  console.log('调用了beforeCreate')
},
created: function() {
  console.log('调用了created')
},
beforeMount: function() {
  console.log('调用了beforeMount')
},
mounted: function() {
  console.log('调用了mounted')
}
})

// 输出结果
// 调用了beforeCreate
// 调用了created
```

```js
// 在没有el属性的情况下,但是有vm.$mount方法

var vm = new Vue({
beforeCreate: function() {
  console.log('调用了beforeCreate')
},
created: function() {
  console.log('调用了created')
},
beforeMount: function() {
  console.log('调用了beforeMount')
},
mounted: function() {
  console.log('调用了mounted')
}
})

vm.$mount('#app')

// 输出结果
// 调用了beforeCreate
// 调用了created
// 调用了beforeMount
// 调用了mounted
```

- template属性对生命周期的影响

![](https://i.loli.net/2018/05/16/5afbe8c47c4cb.png)

这里面分三种情况:

1、在实例内部有template属性的时候,直接用内部的,然后调用render函数去渲染。
2、在实例内部没有找到template,就调用外部的html。实例内部的template属性比外部的优先级高。
3、要是前两者都不满足,那么就抛出错误。

我们来看以下几个例子:

```js
new Vue({
  el: '#app',
  template: '<div id="app">hello world</div>'
})

//页面上渲染出了hello world
```

```
<div id="app">hello world</div>

new Vue({
  el: '#app'
})

// 页面上渲染出了hello world
```

```
//两者都存在的时候

<div id="app">hello world2</div>

new Vue({
  el: '#app',
  template: '<div id="app">hello world1</div>'
})
// 页面上渲染出了hello world1
```
从上述的例子可以看出内部的优先外部的。


- 关于这个生命周期中的一些问题:

1、为什么el属性的判断在template之前?
因为el是一个选择器,比如上述例子中我们用到的最多的是id选择器app,vue实例需要用这个el去template中寻找对应的。

2、实际上,vue实例中还有一种render选项,我们可以从文档上看一下他的用法:

```js
new Vue({
  el: '#app',
  render() {
    return (...)
  }
})
```

3、上述三者的渲染优先级:render函数 > template属性 > 外部html

4、vue编译过程——把tempalte编译成render函数的过程。

### beforeMount和mounted

![life-mounted.png](https://i.loli.net/2018/05/16/5afc189db1e78.png)

我们先来看一个例子:
```
<div id="app">
  <p>{{message}}</p>
</div>

new Vue({
  el: '#app',
  data: {
    message: 1
  },
  beforeMount: function() {
    console.log('调用了beforeMount');
    console.log(this.message)
    console.log(this.$el)
  },
  mounted: function() {
    console.log('调用了mounted');
    console.log(this.message)
    console.log(this.$el)
  }
})

// 输出的结果:
// 调用了beforeMount
// 1
// <div>
// </div>

// 调用了mounted
// 1
// <div id="app">
//  <p>1</p>
// </div>
```

创建vue实例的$el,然后用它替代el属性。

### beforeUpdate和updated

![](https://i.loli.net/2018/05/16/5afc189db7517.png)

这个过程中,我们会发现,当一个数据发生改变时,你的视图也将随之改变,整个更新的过程是:数据改变——导致虚拟DOM的改变——调用这两个生命钩子去改变视图

- 重点:这个数据只有和模版中的数据绑定了才会发生更新。

```js
// 没绑定的情况

var vm = new Vue({
  el: '#app',
  template: '<div id="app"></div>',
  beforeUpdate: function() {
    console.log('调用了beforeUpdate')
  },
  updated: function() {
    console.log('调用了uodated')
  },
  data: {
    a: 1
  }
})

vm.a = 2
//这种情况在控制台中是什么都不会输出的。
```

```js
var vm = new Vue({
  el: '#app',
  template: '<div id="app">{{a}}</div>',
  beforeUpdate: function() {
    console.log('调用了beforeUpdate')
  },
  updated: function() {
    console.log('调用了uodated')
  },
  data: {
    a: 1
  }
})

vm.a = 2

// 输出结果:
// 调用了beforeUpdate
// 调用了uodated
```

### beforeDestory和destoryed

![](https://i.loli.net/2018/05/16/5afc189db3505.png)

在beferoDestory生命钩子调用之前,所有实例都可以用,但是当调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

### 几个不常用的生命钩子

- activated:当组件激活的时候调用
- deactivated:当组件停用的时候调用
- errorCaptured:这个生命钩子可以看官网,2.5.0之后才有的。当捕获一个来自子孙组件的错误时被调用。

### 最后我们用一个例子来过一遍生命周期

```
let vm = new Vue({
  el: '#app',
  data: {
    message: 1
  },
  template: '<div id="app"><p>{{message}}</p></div>',
  beforeCreate() {
    console.log('调用了beforeCreate')
    console.log(this.message)
    console.log(this.$el)
  },
  created() {
    console.log('调用了created')
    console.log(this.message)
    console.log(this.$el)
  },
  beforeMount() {
    console.log('调用了beforeMount')
    console.log(this.message)
    console.log(this.$el)
  },
  mounted() {
    console.log('调用了mounted')
    console.log(this.message)
    console.log(this.$el)
  },
  beforeUpdate() {
    console.log('调用了beforeUpdate')
    console.log(this.message)
    console.log(this.$el)
  },
  updated() {
    console.log('调用了updated')
    console.log(this.message)
    console.log(this.$el)
  },
  beforeDestory() {
    console.log('调用了beforeDestory')
    console.log(this.message)
    console.log(this.$el)
  },
  destoryed() {
    console.log('调用了Destoryed')
    console.log(this.message)
    console.log(this.$el)
  }
})

vm.message = 2
```

- 输出的结果:

```js
// 调用了beforeCreate
// undefined
// undefined
// 调用了created
// 1
// undefined
// 调用了beforeMount
// 1
// <div></div>
// 调用了mounted
// 1
// <div id="app"><p>1</p></div>
// 调用了beforeUpdate
// 2
// <div id="app"><p>2</p></div>
// 调用了updated
// 2
// <div id="app"><p>2</p></div>
```

================================================
FILE: blog/webpack中hash和chunkhash是不是很眼熟?.md
================================================
### 前言
最近在研究webpack的东西,然后也陆陆续续整理了几篇文章,从里面可以学到许多有用的插件和node的一些模块,今天我要介绍的就是hash和chunkhash,这个在webpack打包的过程中经常见到,有兴趣的可以关注一下我的[github](https://github.com/laihuamin/JS-total),点个star,关注和喜欢都行,🙏

### 两者的使用

```js
//使用hash的情况
output: {
  path: path.resolve(__dirname, '../dist/static'),
  publicPath: '/',
  filename: 'static/js/[name].[hash].js',
  chunkFilename: 'static/js/[id].[hash].js'
}
//使用chunkhash的情况
output: {
  path: config.prod.assetsRoot,
  filename: 'static/js/[name]-[chunkhash:16].js',
  chunkFilename: 'static/js/[id]-[chunkhash:16].js',
  publicPath: '/'
},
```
一般使用的时候,会用在文件的产出中,css和js都有,用于打包产出的文件
### hash和chunkhash是什么
大家都知道,用于优化页面性能的常用方法就是利用浏览器的缓存,文件的hash值,就相当于一个文件的身份证一样,很适用于前端静态资源的版本管理,前端实现增量更新的方案之一,那为什么会有两个东西呢,我们娓娓道来,先看两者的定义

- hash

> [hash] is replaced by the hash of the compilation.

compilation的hash值

- chunkhash

> [chunkhash] is replaced by the hash of the chunk.

chunk的hash值

后者很容易理解,因为chunk在webpack中的含义就是模块,那么chunkhash根据定义来就是模块内容计算出来的hash值。
在理解前者之前我们先来看一下compilation有什么作用

### compilation的浅析

webpack的官网文档中[HOW TO WRITE A PLUGIN](http://webpack.github.io/docs/how-to-write-a-plugin.html#compiler-and-compilation)中对这个有一段文字的解析

> A compilation object represents a single build of versioned assets. While running Webpack development middleware, a new compilation will be created each time a file change is detected, thus generating a new set of compiled assets. A compilation surfaces information about the present state of module resources, compiled assets, changed files, and watched dependencies. The compilation also provides many callback points at which a plugin may choose to perform custom actions.

翻译:

> compilation对象代表某个版本的资源对应的编译进程,当你跑webpack的development中间件,每当检测到一个文件被更新之后,一个新的comilation对象会被创建,从而引起新的一系列的资源编译。一个compilation含有关于模块资源的当前状态、被编译的资源,改变的文件和监听依赖的表面信息。compilation也提供很多回调方法,在一个插件可能选择执行制定操作的节点

而与compilation对应的还有一个compiler对象,我们也来介绍一下,这样能够更方便理解compilation

> The compiler object represents the fully configured Webpack environment. This object is built once upon starting Webpack, and is configured with all operational settings including options, loaders, and plugins. When applying a plugin to the Webpack environment, the plugin will receive a reference to this compiler. Use the compiler to access the main Webpack environment.

翻译:

> compiler对象代表的是整个webpack的配置环境,这个对象只在webpack开始的时候构建一次,且所有的操作设置包括options,loaders,plugin都会被配置,当在webpack中应用插件时,这个插件会接受这个compiler对象的引用。通过webpack的主环境去使用这个compiler。

简单的说,compiler是针对webpack的,是不变的webpack环境,而compilation这个就是每次有一个文件更新,然后会重新生成一个,那么当你一个文件的更新,所有的hash字段都会发生变化,这就很坑了,本来我们做增量更新就是想改的那个文件发生变化,但是如果全部都发生变化就没有意义了,我们来看一下实际操作中的例子:

- 修改前文件的hash

![修改前文件的hash](http://laihuamin.oss-cn-beijing.aliyuncs.com/before-hash.png)

- 修改后文件的hash

![修改后文件的hash](http://laihuamin.oss-cn-beijing.aliyuncs.com/after-hash.png)

而且从上图中可以看出,每次有文件更新,会产生一个新的compilation,从而会用新的compilation来计算得出新的hash,而且每个文件带有的hash值还是一样的,这样的肯定达不到我们的要求,那么如何避免这个问题呢?——chunkhash

### chunkhash

chunkhash是由chunk计算的得出的hash值,chunk指的是模块,这个hash值就是模块内容计算出来的hash值

- 修改单个文件前的chunkhash

![修改前的chunkhash](http://laihuamin.oss-cn-beijing.aliyuncs.com/before-chunkhash.png)

- 修改后的文件的chunkhash

![修改后的chunkhash](http://laihuamin.oss-cn-beijing.aliyuncs.com/after-chunkhash.png)

这里我们还得提一个问题,比如像vue这些框架,把js和css共同放在一个里面会时,我们一般会用一个插件叫extract-text-webpack-plugin,这样我们就能把css单独打包,但是这样就会产生一个问题,这样打包出来的css的chunkhash和js的chunkhash会不会是一样的呢,其实我这么问了,当然是会的啦。我们可以看一下下面两张图片。

![chunkhash计算出的js](http://laihuamin.oss-cn-beijing.aliyuncs.com/chunkhash-js.png)
![chunkhash计算出的css](http://laihuamin.oss-cn-beijing.aliyuncs.com/chunkhash-css.png)

其实也很简单,webpack的理念就是为了js的打包,style标签也会视为js的一部分,那么这我们会发现,还是有坑,当我们只改css的时候,js也会同时发生改变,那么我们还是没有做到严格意义上的增量更新,那么我们又该怎么解决呢?

### contenthash

使用方式如下:
```js
new ExtractTextPlugin({
  filename: 'static/css/[name]-[contenthash:16].css',
  allChunks: true
})
```
这样我们看打包后的效果。

![chunkhash计算出来的js](http://laihuamin.oss-cn-beijing.aliyuncs.com/chunhash-js.png)
![contenthash计算出来的css](http://laihuamin.oss-cn-beijing.aliyuncs.com/content-css.png)

### 总结

静态资源的管理是前端很重要的一块,最近由于业务转型,自己也在尝试换个架子,那么肯定得从研究webpack入手,现在webpack已经是必不可少的工具之一,这篇博文有借鉴网上的,如有侵权删,但是研究得出的结论我会记忆一生,所以建议看完这篇的小伙伴自己动手配置一边,喜欢的可以去github上点个star,喜欢和关注都行,最近有点忙,但是我还是每天会写一点博文。谢谢大家

================================================
FILE: blog/webpack中的entry和context.md
================================================
### 前言
配置webpack的环节中,entry是必不可少的一个环节,我最近做了一篇关于webpack的分享,然后也想做一个关于webpack的一个系列,讲讲我对于webpack的理解,以及我对于我们工程架构的理解。有兴趣的也可以关注一下我的[github](https://github.com/laihuamin/JS-total),可以点个关注,喜欢或者收藏谢了0.0

### entry和context——是什么

在能有什么用之前,然我们前去了解一下他们两个到底是什么,其实entry和context从英文层面上我们就可以清晰的知道,一个是入口的意思,还有一个是上下文的意思,那么我们想知道他们到底有什么用,请看下面。

### context

[关于context的demo](https://github.com/laihuamin/webpack-domes/tree/master/context)

其实这个目录结构是这样的,有兴趣的可以去看一下这个小demo,是这个上下文的示意。

![文件的目录结构](http://laihuamin.oss-cn-beijing.aliyuncs.com/context.png)

目录中的dist是我打包出来的文件,而app中的文件是我书写的原文件,而webpack.config.js中的代码是这样的

```
const path = require('path');

module.exports = {
    context: path.resolve(__dirname, 'app'),
    entry: './main.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }
}
```
看这段代码,其实我们就可以猜到context的作用了,如果没有这个context,那我们的entry应该怎么写,是不是应该书写成
```
'./app/main.js'
```
还好我app中就一个main.js,要是来10个或者100个,那要多写多少个app,所以我们可以看到context的作用。他就是会将entry的根路口指向app这个文件夹

### entry

讲完context,那么我们应该要来讲讲什么呢,当然是entry入口文件啦,对于一个webpack打包配置文件来说,entry是我们最初配置的时候就该解决的问题。

对于entry这个配置项来说,我们可以把他分类型来看,entry一共可以分为string、array和object。其他的都是这几种的组合。


#### string

对于字符串,其实这是一个单一的入口,是一个一对一或者一对多的关系,在现在的单页应用SPA上面用的很多,我们可以来看一下我们的[demo](https://github.com/laihuamin/webpack-domes/tree/master/entry/string)。

在demo中我们的目录结构是这样的

![目录结构](http://laihuamin.oss-cn-beijing.aliyuncs.com/entry-string.png)

我们可以看一下webpack.config.js中的代码:

```
module.exports = {
    entry: './main.js',
    output: {
        path: __dirname,
        filename: 'bundle.js'
    }
}
```

这是个一对一的关系,一个入口,产出一个bundle.js,所以这个是举个例子,比较简单,但是在真实的单页应用中,不会这么简单,他会是一个一对多的关系,一个入口,然后webpack会解析依赖然后将其打包多个文件。


#### array

讲完字符串我们来讲讲数组是个什么关系呢,数组其实是多对一的关系,就是入口可以是多个文件,但是出口会是一个模块,这个可以用在哪里呢,比如,我们项目中肯定会有很多公用的模块,那么这些模块有必要打包到很多个文件中么,答案当然是没有这个必要的,放一个里面,我们只要在html中引入一个就行了,多省事,还会用在这种情况上,比如jquery和lodash这样的不相互依赖的文件可以放到一个模块中。那我们来看看具体的[demo](https://github.com/laihuamin/webpack-domes/tree/master/entry/array)

在demo中我们的目录结构是这样的

![目录结构](http://laihuamin.oss-cn-beijing.aliyuncs.com/entry-array.png)

我们可以看一下webpack.config.js中的代码:

```
module.exports = {
    entry: ['./main1.js', './main2.js'],
    output: {
        path: __dirname,
        filename: 'bundle.js'
    }
}
```

到这里我们可以打开index.html文件看一下,是不是main1.js和main2.js中的两条代码都在bundle.js中了,所以,这个多对一的关系可以很好的用到真实的项目中。


#### object

最后一个对象,不用说大家应该也猜得到,当然是多对多的关系。也是在多页面应用中我们经常用到的,我这里举一个简单点的例子,具体的[demo](https://github.com/laihuamin/webpack-domes/tree/master/entry/object)


在demo中我们的目录结构是这样的

![目录结构](http://laihuamin.oss-cn-beijing.aliyuncs.com/entry-object.png)

我们可以看一下webpack.config.js中的代码:

```
module.exports = {
    entry: {
        one: './main1.js',
        two: './main2.js',
        'path/to/main': './main3.js'
    },
    output: {
        path: __dirname,
        filename: '[name].js'
    }
}

```

我们可以看到,前两个entry对象的键值对还是比较正常的,但第三个的时候我却把它换成了一个路径名,但是webpack很聪明,看到目录结构你就知道,其实webpack会解析这个路径,然后他会帮我们创建相对应的路径,比如,path文件夹中有to文件夹,在to文件夹中还有main.js。

但是在实际的项目中,entry肯定不会变的这么简单,我可以给大家提供几个例子,关于entry对象的收集,因为如果是数组和字符串,在单页应用中算简单的,但是在多页面应用中却是困难重重。

### 实际项目中的entry

![待收集资源的目录结构](http://laihuamin.oss-cn-beijing.aliyuncs.com/edu_h5.png)

收集entry对象的函数

```
const glob = require('glob')
const path = require('path')

const GLOB_FILE_PATH = './src/pages/**/index.js'
const CUT_PATH = './src/pages/'

exports.getEntries = function(argv){
    let  paths = glob.sync(GLOB_FILE_PATH)
    let  entries = {}
    for (let i = 0; i < paths.length; i++){
        let pathName = path.dirname(paths[i]).replace(new RegExp('^' + CUT_PATH), '')
        entries[pathName] = paths[i]
    }
    return entries
}
```

这里用正则来提取的键值对,有兴趣的同学也可以去了解一下glob这个文件读取模块。

### 总结

这算是我的第二篇webpack的文章,也是希望能给我个人的webpack系列文章有一个很好的开始,写写大家的支持,我会将webpack的配置项一个一个拿出来讲,然后配上demo这样希望能让大家更好的理解,这期讲了比较简单点的context和entry。下期将会带来output,希望喜欢的可以去我的[github](https://github.com/laihuamin/JS-total)上面点个star,文章属于纯原创,转载请注明出处谢谢

================================================
FILE: blog/【译】node js event loop part 1.1.md
================================================
[原文](https://jsblog.insiderattack.net/event-loop-and-the-big-picture-nodejs-event-loop-part-1-1cb67a182810)

先说1.1总揽:
- Reactor模式
- Reactor模式中的协调机制Event Loop
- Reactor模式中的事件分离器Event Demultiplexer
- 一些Event Demultiplexer处理不了的复杂I/O接口比如File I/O、DNS等
- 复杂I/O的解决方案
- 未完待续

### 前言
nodejs和其他编程平台的区别在于如何去处理I/O接口,我们听一个人介绍nodejs,总是会说是一个**基于v8引擎,没有堵塞,事件驱动**的语言,那这些又意味着什么呢?什么叫‘没有堵塞’和‘事件驱动’?所有的答案都在nodejs的核心——**Event Loop**。
在这一系列的帖子中,我们将一起去描述什么是Event Loop,它是如何工作的,它是如何影响我们的应用的,如何充分的利用他甚至更多。为什么不用一篇代替一个系列的帖子呢,因为这样的话,他就会变成一个很长很长的帖子,我会肯定会错过很多东西,因此我才把它写成一个系列,在第一篇帖子中,我将讲述nodejs如何工作,如何通过I/O,他如何与其他平台一起工作等。

### Reactor Pattern

nodejs工作在一个事件驱动的模型中,这个模型涉及一个事件分离器和事件循环,所有的I/O请求最终将会生成一个事件完成、事件失败或者唤醒其他事件的结果。这些事件将会根据以下规则做处理:

- 1.事件分离器收到I/O请求,之后将这些请求委托给相应的硬件

- 2.曾经被处理过的请求(比如来自可读取文件的数据,来自可读取接口的数据),事件分离器会为要进行的特殊操作添加注册回调程序。

- 3.如果事件可以在事件循环中被处理,那么将有序的被执行,直到循环为空

- 4.如果没有事件在事件循环中,或者事件分离器没有添加任何请求,这个 程序将被完成,否则,程序将从第一步在开始,进行循环操作。

这整个工程的协调机制我们叫做Event Loop

![Event Loop](http://laihuamin.oss-cn-beijing.aliyuncs.com/Event-loop%281%29.jpeg)

Event Loop其实是一个单线程的半无限循环,为什么会说是半无限呢?因为在没有工作需要完成的时候程序会退出。从开发者的角度来说,这些是程序退出的点。

> 注意:不要把Event Loop和Event Emitter弄混淆,Event Emitter和这个机制完全是不同的概念,在最后一篇帖子,我会解释Event Emitter是如何通过Event Loop影响事件的处理。

上面的图是对NodeJs如何工作以及展示一种被叫做Reactor Pattern的主要组件的设计模式的高级概览。
但是真正的复杂度远超于它,那它有多复杂呢?

> Event demultiplexer不是一个在所有os平台中解析所有I/O类型的单一组件。
Event queue在这里展示出来的不是一个单一的队列,在其中所有类型的事件会在队列中排队或者从队列中移除,并且I/O不是唯一一个要排队的事件类型

让我们继续深挖

### Event Demultiplexer

Event Demultiplexer并不是一个现实存在的组件,而是在reactor pattern中的一个抽象概念。

在现实中,Event Demultiplexer 在不同的系统中以不同的名字被实现,比如在linux中叫做epoll, 在MacOS中叫做kqueue,在Solaris中叫event post,在window系统下叫做IOCP等。

nodeJS可以使用Event Demultiplexer提供的底层非阻塞、异步硬件I/O功能。

### Complexities in File I/O

但是令人苦恼的是,不是所有类型的I/O都可以使用Event Demultiplexer被执行,甚至是在相同的操作系统中,支持不同类型的I/O也是很复杂的。

通常来说,epoll, kqueue, event ports和IOCP可以使用非阻塞的方式执行网络I/O。

但是文件I/O就复杂多了,某些系统,比如Linux不支持完全异步的方式去访问文件系统,在MacOS系统中文件系统对事件的发送和接收会有限制(你可以在[这里](http://blog.libtorrent.org/2012/10/asynchronous-disk-io/)了解更多)。

为了提供完全异步而去解决所有文件系统的复杂性是非常复杂的,几乎是不可能的。

### Complexities in DNS

和文件I/O一样,由node API提供某些DNS的功能也存在一定的复杂性。

比如dns.lookup等Node DNS功能访问系统的一些配置文件,例如nsswitch.conf、resolv.conf和/etc/hosts。

上面描述的文件系统复杂性也适用于dns.resolve函数。

### The solution?

因此,引入了一个线程池来支持I/O功能,这些功能不能由硬件异步I/O实用程序(如epoll / kqueue / event ports或IOCP)直接解决。

现在我们知道不是所有的I/O功能都可以在线程池中运行。nodeJS已经尽最大努力来使用非阻塞和硬件的异步I/O方式来完成大部分I/O功能,但是对于一些复杂的、阻塞的I/O还是通过引入一个线程池的方式来解决

### 未完待续
该篇先翻译到这,有些地方翻译的不好请指出,过几天我会继续出第二篇。

================================================
FILE: blog/【译】nodeJsEventLoopPart1.1.md
================================================
[原文](https://jsblog.insiderattack.net/event-loop-and-the-big-picture-nodejs-event-loop-part-1-1cb67a182810)

先说1.1总揽:
- Reactor模式
- Reactor模式中的协调机制Event Loop
- Reactor模式中的事件分离器Event Demultiplexer
- 一些Event Demultiplexer处理不了的复杂I/O接口比如File I/O、DNS等
- 复杂I/O的解决方案
- 未完待续

### 前言
nodejs和其他编程平台的区别在于如何去处理I/O接口,我们听一个人介绍nodejs,总是会说是一个**基于v8引擎,没有堵塞,事件驱动**的语言,那这些又意味着什么呢?什么叫‘没有堵塞’和‘事件驱动’?所有的答案都在nodejs的核心——**Event Loop**。
在这一系列的帖子中,我们将一起去描述什么是Event Loop,它是如何工作的,它是如何影响我们的应用的,如何充分的利用他甚至更多。为什么不用一篇代替一个系列的帖子呢,因为这样的话,他就会变成一个很长很长的帖子,我会肯定会错过很多东西,因此我才把它写成一个系列,在第一篇帖子中,我将讲述nodejs如何工作,如何通过I/O,他如何与其他平台一起工作等。

### Reactor Pattern

nodejs工作在一个事件驱动的模型中,这个模型涉及一个事件分离器和事件循环,所有的I/O请求最终将会生成一个事件完成、事件失败或者唤醒其他事件的结果。这些事件将会根据以下规则做处理:

- 1.事件分离器收到I/O请求,之后将这些请求委托给相应的硬件

- 2.曾经被处理过的请求(比如来自可读取文件的数据,来自可读取接口的数据),事件分离器会为要进行的特殊操作添加注册回调程序。

- 3.如果事件可以在事件循环中被处理,那么将有序的被执行,直到循环为空

- 4.如果没有事件在事件循环中,或者事件分离器没有添加任何请求,这个 程序将被完成,否则,程序将从第一步在开始,进行循环操作。

这整个工程的协调机制我们叫做Event Loop

![Event Loop](http://laihuamin.oss-cn-beijing.aliyuncs.com/Event-loop%281%29.jpeg)

Event Loop其实是一个单线程的半无限循环,为什么会说是半无限呢?因为在没有工作需要完成的时候程序会退出。从开发者的角度来说,这些是程序退出的点。

> 注意:不要把Event Loop和Event Emitter弄混淆,Event Emitter和这个机制完全是不同的概念,在最后一篇帖子,我会解释Event Emitter是如何通过Event Loop影响事件的处理。

上面的图是对NodeJs如何工作以及展示一种被叫做Reactor Pattern的主要组件的设计模式的高级概览。
但是真正的复杂度远超于它,那它有多复杂呢?

> Event demultiplexer不是一个在所有os平台中解析所有I/O类型的单一组件。
Event queue在这里展示出来的不是一个单一的队列,在其中所有类型的事件会在队列中排队或者从队列中移除,并且I/O不是唯一一个要排队的事件类型

让我们继续深挖

### Event Demultiplexer

Event Demultiplexer并不是一个现实存在的组件,而是在reactor pattern中的一个抽象概念。

在现实中,Event Demultiplexer 在不同的系统中以不同的名字被实现,比如在linux中叫做epoll, 在MacOS中叫做kqueue,在Solaris中叫event post,在window系统下叫做IOCP等。

nodeJS可以使用Event Demultiplexer提供的底层非阻塞、异步硬件I/O功能。

### Complexities in File I/O

但是令人苦恼的是,不是所有类型的I/O都可以使用Event Demultiplexer被执行,甚至是在相同的操作系统中,支持不同类型的I/O也是很复杂的。

通常来说,epoll, kqueue, event ports和IOCP可以使用非阻塞的方式执行网络I/O。

但是文件I/O就复杂多了,某些系统,比如Linux不支持完全异步的方式去访问文件系统,在MacOS系统中文件系统对事件的发送和接收会有限制(你可以在[这里](http://blog.libtorrent.org/2012/10/asynchronous-disk-io/)了解更多)。

为了提供完全异步而去解决所有文件系统的复杂性是非常复杂的,几乎是不可能的。

### Complexities in DNS

和文件I/O一样,由node API提供某些DNS的功能也存在一定的复杂性。

比如dns.lookup等Node DNS功能访问系统的一些配置文件,例如nsswitch.conf、resolv.conf和/etc/hosts。

上面描述的文件系统复杂性也适用于dns.resolve函数。

### The solution?

因此,引入了一个线程池来支持I/O功能,这些功能不能由硬件异步I/O实用程序(如epoll / kqueue / event ports或IOCP)直接解决。

现在我们知道不是所有的I/O功能都可以在线程池中运行。nodeJS已经尽最大努力来使用非阻塞和硬件的异步I/O方式来完成大部分I/O功能,但是对于一些复杂的、阻塞的I/O还是通过引入一个线程池的方式来解决

### 未完待续
该篇先翻译到这,有些地方翻译的不好请指出,过几天我会继续出第二篇。

================================================
FILE: blog/一看就懂的JS抽象语法树.md
================================================
## 前言
babel是现在几乎每个项目中必备的一个东西,但是其工作原理避不开对js的解析在生成的过程,babel有引擎babylon,早期fork了项目acron,了解这个之前我们先来看看这种引擎解析出来是什么东西。不光是babel还有webpack等都是通过javascript parser将代码转化成抽象语法树,这棵树定义了代码本身,通过操作这颗树,可以精准的定位到赋值语句、声明语句和运算语句

## 什么是抽象语法树

我们可以来看一个简单的例子:

```js
var a = 1;
var b = a + 1;
```

我们通过这个[网站](http://esprima.org/demo/parse.html#),他是一个esprima引擎的网站,十分好用.画成流程图如下:

![AST](http://laihuamin.oss-cn-beijing.aliyuncs.com/ast.png)

而他的json对象格式是这样的:

```js
{
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "a"
                    },
                    "init": {
                        "type": "Literal",
                        "value": 1,
                        "raw": "1"
                    }
                }
            ],
            "kind": "var"
        },
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "b"
                    },
                    "init": {
                        "type": "BinaryExpression",
                        "operator": "+",
                        "left": {
                            "type": "Identifier",
                            "name": "a"
                        },
                        "right": {
                            "type": "Literal",
                            "value": 1,
                            "raw": "1"
                        }
                    }
                }
            ],
            "kind": "var"
        }
    ],
    "sourceType": "script"
}
```

## 众多的引擎
chrome有v8,firefix有spidermonkey.还有一些常用的引擎有:
- esprima
- acron
- Traceur
- UglifyJS2
- shift

下面是一些引擎的速度对比,以及用不同的框架,引擎们的加载速度:

![jq-parser](http://laihuamin.oss-cn-beijing.aliyuncs.com/jq-parser.png)

![ng-parser](http://laihuamin.oss-cn-beijing.aliyuncs.com/ng-parser.png)

![react-parser](http://laihuamin.oss-cn-beijing.aliyuncs.com/react-parser.png)

我个人认为,封装的越完美的,其实解析的时间更长,引擎之间也是acron这个速度比较优秀,babel引擎前身就是fork这个项目的。

## AST的三板斧

- 通过esprima生成AST
- 通过estraverse遍历和更新AST
- 通过escodegen将AST重新生成源码

我们可以来做一个简单的例子:

1.先新建一个test的工程目录
2.在test工程下安装esprima、estraverse、escodegen的npm模块
```js
npm i esprima estraverse escodegen --save
```
3.在目录下面新建一个test.js文件,载入以下代码:
```js
const esprima = require('esprima');
let code = 'const a = 1';
const ast = esprima.parseScript(code);
console.log(ast);
```
你将会看到输出结果:
```js
Script {
  type: 'Program',
  body:
   [ VariableDeclaration {
       type: 'VariableDeclaration',
       declarations: [Array],
       kind: 'const' } ],
  sourceType: 'script' }
```

4.再在test文件中,载入以下代码:
```js
const estraverse = require('estraverse');

estraverse.traverse(ast, {
    enter: function (node) {
        node.kind = "var";
    }
});

console.log(ast);
```
输出的结果:
```js
Script {
  type: 'Program',
  body:
   [ VariableDeclaration {
       type: 'VariableDeclaration',
       declarations: [Array],
       kind: 'var' } ],
  sourceType: 'script' }
```

5.最后在test文件中,加入以下代码:
```js
const escodegen = require("escodegen");
const transformCode = escodegen.generate(ast)

console.log(transformCode);

```
输出的结果:
```js
var a = 1;
```

- 通过这三板斧:我们将`const a = 1`转化成了`var a = 1`

有没有babel的感觉0.0

## 推荐网站

[esprima源码](https://github.com/jquery/esprima)
[acron源码](https://github.com/ternjs/acorn)
[speed comparison](http://esprima.org/test/compare.html)
[AST explorer](https://astexplorer.net/)
[esprima可视化](http://esprima.org/demo/parse.html#)
[在线可视化AST](http://resources.jointjs.com/demos)

## 总结

抽象树在前端用的很多很多,现在流行的工具,不管是webpack还是babel都会通过那个三板斧的流程,这里我只是大致介绍一下,过段时间,会出一篇抽象树的语法,有兴趣的也可以把esprima的源码看一下,为什么是esprima呢,因为esprima的资料比较多,而acron比较轻量级。有兴趣的可以关注一下我的[github](),记得点个star,就当是对笔者的支持,谢谢。

================================================
FILE: blog/从babel讲到AST.md
================================================
### 前言

最近给团队分享了一篇babel原理,然后我把他整理成一篇blog,本篇总字数6059(含代码),速读3分钟,普通阅读5分钟,有兴趣的可以关注一下我的[github博客](https://github.com/laihuamin/JS-total)

### babel

我们来看一段代码:

```js
[1,2,3].map(n => n + 1);
```
经过babel之后,这段代码变成了这样:

```js
[1, 2, 3].map(function (n) {
  return n + 1;
});
```

### babel的背后

babel的过程:解析——转换——生成。

![](https://i.loli.net/2018/03/21/5ab1b8e1a8abf.png)

这边又一个中间的东西,是抽象语法树(AST)

### AST的解析过程

一个js语句是怎么被解析成AST的呢?这个中间有两个步骤,一个是分词,第二个是语义分析,怎么理解这两个东西呢?

- 分词

什么叫分词?

比如我们在读一句话的时候,我们也会做分词操作,比如:“今天天气真好”,我们会把他切割成“今天”,“天气”,“真好”。

那换成js的解析器呢,我们看一下下面一个语句`console.log(1);`,js会看成`console`,`.`,`log`,`(`,`1`,`)`,`;`。

所以我们可以把js解析器能识别的最小词法单元。

![](https://i.loli.net/2018/03/20/5ab0b7b69cc95.png)

当然这样的分词器我们可以简易实现一下。

```js
//思路分析:传入的是字符串的参数,然后每次取一个字符去校验,用if语句去判断,然后最后结果存入一个数组中,对于标识符和数字进行特殊处理
function tokenCode(code) {
    const tokens = [];
    //字符串的循环
    for(let i = 0; i < code.length; i++) {
        let currentChar = code.charAt(i);
        //是分号括号的情况
        if (currentChar === ';' || currentChar === '(' || currentChar === ')' || currentChar === '}' || currentChar === '{' || currentChar === '.' || currentChar === '=') {
            // 对于这种只有一个字符的语法单元,直接加到结果当中
            tokens.push({
              type: 'Punctuator',
              value: currentChar,
            });
            continue;
        }
        //是运算符的情况
        if (currentChar === '>' || currentChar === '<' || currentChar === '+' || currentChar === '-') {
            // 与上一步类似只是语法单元类型不同
            tokens.push({
              type: 'operator',
              value: currentChar,
            });
            continue;
        }      
        //是双引号或者单引号的情况
        if (currentChar === '"' || currentChar === '\'') {
            // 引号表示一个字符传的开始
            const token = {
              type: 'string',
              value: currentChar,       // 记录这个语法单元目前的内容
            };
            tokens.push(token);
      
            const closer = currentChar;
      
            // 进行嵌套循环遍历,寻找字符串结尾
            for (i++; i < code.length; i++) {
              currentChar = code.charAt(i);
              // 先将当前遍历到的字符无条件加到字符串的内容当中
              token.value += currentChar;
              if (currentChar === closer) {
                break;
              }
            }
            continue;
          }
        if (/[0-9]/.test(currentChar)) {
            // 数字是以0到9的字符开始的
            const token = {
              type: 'number',
              value: currentChar,
            };
            tokens.push(token);
      
            for (i++; i < code.length; i++) {
              currentChar = code.charAt(i);
              if (/[0-9\.]/.test(currentChar)) {
                // 如果遍历到的字符还是数字的一部分(0到9或小数点)
                // 这里暂不考虑会出现多个小数点以及其他进制的情况
                token.value += currentChar;
              } else {
                // 遇到不是数字的字符就退出,需要把 i 往回调,
                // 因为当前的字符并不属于数字的一部分,需要做后续解析
                i--;
                break;
              }
            }
            continue;
          }
      
          if (/[a-zA-Z\$\_]/.test(currentChar)) {
            // 标识符是以字母、$、_开始的
            const token = {
              type: 'identifier',
              value: currentChar,
            };
            tokens.push(token);
      
            // 与数字同理
            for (i++; i < code.length; i++) {
              currentChar = code.charAt(i);
              if (/[a-zA-Z0-9\$\_]/.test(currentChar)) {
                token.value += currentChar;
              } else {
                i--;
                break;
              }
            }
            continue;
          }
          
          if (/\s/.test(currentChar)) {
            // 连续的空白字符组合到一起
            const token = {
              type: 'whitespace',
              value: currentChar,
            };      
            // 与数字同理
            for (i++; i < code.length; i++) {
              currentChar = code.charAt(i);
              if (/\s]/.test(currentChar)) {
                token.value += currentChar;
              } else {
                i--;
                break;
              }
            }
            continue;
          }
          throw new Error('Unexpected ' + currentChar);
        }
    return tokens;
}
```

- 语义分析

语义分析的话就比较难了,为什么这么说呢?

因为这个不像分词这样有个标准,有些东西都要靠自己去摸索。

其实语义分析分为两块,一块是语句,还有一块是表达式。

什么叫语句?什么叫表达式呢?

表达式,比如:`a > b; a + b;`这一类的,可以嵌套,也可以运用在语句中。

语句,比如:`var a = 1, b = 2, c =3;`等,我们理解中的一个语句。类似于语文中的一个句子一样。

当然,有人会问,`console.log(1);`这个算什么呢。

其实这种情况可以归为一类,单语句表达式,你既可以看作表达式,也可以看作语句,一个表达式单成一个语句。

既然分完了,我们也可以尝试这来写一下,简单点的语句分析。
比如var定义语句,或者复杂点的if语句块。

生成AST的形式可以参考这个[网站](),AST的一些语法可以从这个网站试出个大概

```js
//思路分析:既然分三种情况,那么我们也从语句,表达式,单语句表达式入手,我们先定义一个方法用来分析表达式,在定义一个方法来分析语句,最后在定义一个方法分析单语句表达式。整个过程也是分为那么几步。就多了对于指针的管控。

function parse (tokens) {
    // 位置暂存栈,用于支持很多时候需要返回到某个之前的位置
    const stashStack = [];
    let i = -1;     // 用于标识当前遍历位置
    let curToken;   // 用于记录当前符号

    // 暂存当前位置  
    function stash () {
        stashStack.push(i);
    }
      // 往后移动读取指针
    function nextToken () {
        i++;
        curToken = tokens[i] || { type: 'EOF' };;
    }

    function parseFalse () {
      // 解析失败,回到上一个暂存的位置
      i = stashStack.pop();
      curToken = tokens[i];
    }

    function parseSuccess () {
      // 解析成功,不需要再返回
      stashStack.pop();
    }
  
    const ast = {
        type: 'Program',
        body: [],
        sourceType: "script"
    };

  // 读取下一个语句
  function nextStatement () {
    // 暂存当前的i,如果无法找到符合条件的情况会需要回到这里
    stash();
    
    // 读取下一个符号
    nextToken();

    if (curToken.type === 'identifier' && curToken.value === 'if') {
      // 解析 if 语句
      const statement = {
        type: 'IfStatement',
      };
      // if 后面必须紧跟着 (
      nextToken();
      if (curToken.type !== 'Punctuator' || curToken.value !== '(') {
        throw new Error('Expected ( after if');
      }

      // 后续的一个表达式是 if 的判断条件
      statement.test = nextExpression();

      // 判断条件之后必须是 )
      nextToken();
      if (curToken.type !== 'Punctuator' || curToken.value !== ')') {
        throw new Error('Expected ) after if test expression');
      }

      // 下一个语句是 if 成立时执行的语句
      statement.consequent = nextStatement();

      // 如果下一个符号是 else 就说明还存在 if 不成立时的逻辑
      if (curToken === 'identifier' && curToken.value === 'else') {
        statement.alternative = nextStatement();
      } else {
        statement.alternative = null;
      }
      parseSuccess();
      return statement;
    }
    // 如果是花括号的代码块
    if (curToken.type === 'Punctuator' && curToken.value === '{') {
      // 以 { 开头表示是个代码块
      const statement = {
        type: 'BlockStatement',
        body: [],
      };
      while (i < tokens.length) {
        // 检查下一个符号是不是 }
        stash();
        nextToken();
        if (curToken.type === 'Punctuator' && curToken.value === '}') {
          // } 表示代码块的结尾
          parseSuccess();
          break;
        }
        // 还原到原来的位置,并将解析的下一个语句加到body
        parseFalse();
        statement.body.push(nextStatement());
      }
      // 代码块语句解析完毕,返回结果
      parseSuccess();
      return statement;
    }
    
    // 没有找到特别的语句标志,回到语句开头
    parseFalse();

    // 尝试解析单表达式语句
    const statement = {
      type: 'ExpressionStatement',
      expression: nextExpression(),
    };
    if (statement.expression) {
      nextToken();
      return statement;
    }
  }

  // 读取下一个表达式
  function nextExpression () {
    nextToken();
    if (curToken.type === 'identifier' && curToken.value === 'var') {
      // 如果是定义var      
        const variable = {
          type: 'VariableDeclaration',
          declarations: [],
          kind: curToken.value
        };
        stash();
        nextToken();
        // 如果是分号就说明单句结束了
        if(curToken.type === 'Punctuator' && curToken.value === ';') {
          parseSuccess();
          throw new Error('error');
        } else {
          // 循环
          while (i < tokens.length) {
            if(curToken.type === 'identifier') {
              variable.declarations.id = {
                type: 'Identifier',
                name: curToken.value
              }
            }
            if(curToken.type === 'Punctuator' && curToken.value === '=') {
              nextToken();
              variable.declarations.init = {
                type: 'Literal',
                name: curToken.value
              }
            }
            nextToken();
            // 遇到;结束
            if (curToken.type === 'Punctuator' && curToken.value === ';') {
              break;
            }
          }
        }
        parseSuccess();
        return variable;
    }
      // 常量表达式    
    if (curToken.type === 'number' || curToken.type === 'string') {
      const literal = {
        type: 'Literal',
        value: eval(curToken.value),
      };
      // 但如果下一个符号是运算符
      // 此处暂不考虑多个运算衔接,或者有变量存在
      stash();
      nextToken();
      if (curToken.type === 'operator') {
        parseSuccess();
        return {
          type: 'BinaryExpression',
          operator: curToken.value,
          left: literal,
          right: nextExpression(),
        };
      }
      parseFalse();
      return literal;
    }

    if (curToken.type !== 'EOF') {
      throw new Error('Unexpected token ' + curToken.value);
    }
  }


  // 逐条解析顶层语句
  while (i < tokens.length) {
    const statement = nextStatement();
    if (!statement) {
      break;
    }
    ast.body.push(statement);
  }
  return ast;
}
```

关于转换和生成,笔者还在研究,不过生成其实就是解析过程的反向,转换的话,还是挺值得深入的,因为AST这东西在好多方面用到,比如:

- eslint对代码错误或风格的检查,发现一些潜在的错误
- IDE的错误提示、格式化、高亮、自动补全等
- UglifyJS压缩代码
- 代码打包工具webpack

这篇文章讲完了,其实不理解代码没关系,把整体思路把握住就行。

================================================
FILE: blog/函数.md
================================================
## 函数
### 函数声明与表达式
- 函数声明
> 语法
```
// MDN中有说明一个函数最多拥有255个参数
function functionName(arg){
    //函数体
}
```
> 示例
```
function square(number) {
    return number * number;
}
```
- 函数表达式
> 语法
```
const functionName = function() {
    //函数体
}
```
> 示例
```
const square = function(number) {
    return number * number;
}
```
- 两者的区别
> 函数声明有变量提升,函数表达式没有变量提升
```
square1(1) //可行
square2(1)  //报错
function square1(number) {
    return number * number;
}
const square2 = function(number) {
    return number * number;
}
```
- Function构造函数
> 语法
```
const functionName = new Function('arg', 'functionBody');
```
> 示例
```
const sum = new Function('a', 'b', 'return a+b')
sum(1, 2);
```
- 函数内部引用自身的方式
> 例子
```
const foo = function bar() {}
```
> 语法
1.foo
2.函数名bar
3.argument.callee
### 作用域与作用域链
- 作用域
> 作用域控制着变量可访问的范围,有全局作用域和局部作用域之分,作用域也控制着变量的声明周期
- 作用域链
> 每个函数内部都有一个[[Scope]],包含了这个函数所有能访问的对象集合
```
function add(num1, num2){
    return num1 + num2;
}
```
> 下列图片只列举了部分

![](http://7xnxzw.com1.z0.glb.clouddn.com/js%E4%BD%9C%E7%94%A8%E5%9F%9F%E9%93%BE_01.jpg)
- 模仿块级作用域——立即执行
> 语法
(function(){
    ...
})();
> 例子
```
//未立即执行
for(var i = 0; i < 3; i++){
    setTimeout(function(){
        console.log(i);
    }, i*1000)
}
//立即执行
for(var i = 0; i < 3; i++){
    (function(){
        setTimeout(function(){
            console.log(i);
        }, i*1000); 
    })();
}
```
### 构造函数
- 构造函数的写法
```
function People(name, age){
    this.name = name;
    this.age = age;
}
```
- 注意点
> 函数首字母大写
### 闭包
- 定义
> 闭包是指那些能够访问自由变量的函数
- 自由变量
> 可以在函数内使用的,既不是函数的参数,也不是局部变量
- 举例
```
const a = 1;
function () {
    console.log(a);
}
foo();
```
> 讲道理a既不是函数参数,也不是局部变量,就是自由变量
- 理论角度的闭包——所有js函数
- 实践角度的闭包
1.函数上下文已销毁,但是函数仍然存在
2.代码中引用自由变量
```
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
var foo = checkscope();
foo();
```
> 可以上面一段代码执行的上下文改变:
1.checkscope()的时候返回的是一个f(),将一个函数的引用给了foo
2.foo变成了全局函数,不会在checkscope()运行完后销毁
- 闭包面试题
```
var data = [];
for (var i = 0; i < 3; i++) {
  data[i] = function () {
    console.log(i);
  };
}
data[0]();
data[1]();
data[2]();
```
> 答案都是3,因为var是没有块级作用域,所以所有函数访问的都是一个i,也就是3
```
var data = [];

for (var i = 0; i < 3; i++) {
  data[i] = (function (i) {
        return function(){
            console.log(i);
        }
  })(i);
}

data[0]();
data[1]();
data[2]();
```
> 立即执行函数可以产生块级作用域,将i的值存在function匿名函数中
### 柯里化
### ES6函数新特性
[整理的第一篇](https://github.com/laihuamin/program-blog/issues/2)
[整理的第二篇](https://github.com/laihuamin/program-blog/issues/3)
### this,apply,call,bind
[整理的第一篇](https://github.com/laihuamin/program-blog/issues/19)

================================================
FILE: blog/前端和后端的发展路径.md
================================================
### 前言

看到medium上面的一篇外文,就来转发一下,写的主要是前端和后端改如何发展,感觉路线上还是可以的,只是细化部分做的不够,需要自己学到那一块的时候满满细化。

### Font-end

![1_V7TMAzvhW7_cn9FbkKqOcQ.png](https://i.loli.net/2018/07/05/5b3e2462c7e8c.png)

### Back-end

![back-end.png](https://i.loli.net/2018/07/05/5b3e2a5d382e8.png)

### 总结

终于毕业了,前段时间被毕业设计搞得头昏脑胀,现在所有事情都搞定了,又要开始定期更新博客了,从这边总结开始,后期挑每个板块好好研究。

================================================
FILE: blog/响应式原理.md
================================================


================================================
FILE: blog/定时器和计时器.md
================================================
## setTimeout和setInterval
- setTimeout的使用
```
setTimeout(cb, time);
```
> setTimeout传入的是两个参数,第一个参数是cb代表的是回调函数callback,第二个代表的是时间,以ms计算

- setInterval的使用
```
setInterval(cb, time);
```
> setInterval传入的也是两个参数,第一个参数是cb代表的是回调函数callback,第二个代表的也是时间,以ms计算

## setTimeout和setInterval的区别和注意点
- 区别
> setTimeout含义是定时器,到达一定的时间触发一次,但是setInterval含义是计时器,到达一定时间触发一次,并且会持续触发

- 相互之间的转换
```js
function run() {
    //其他代码
    setTimeout(function(){
        run();
    }, 10000);
}
setInterval(function(){
    run();
}, 10000);

```
> *上面的代码还是有区别的:*
第一段代码使用的是setTimeout来实现的,这个实现就有一个缺点,就是setTimeout是在代码的执行时间上加10秒,比如run()执行了100s,而整个过程可能是110s,
第二段代码就不一样了,setInterval是当run()跑了不到10s,那么就是10s走一回,如果setInterval大于10s,我们后面详解。

## 你真的了解么————setInterval
```js
setInterval(function(){
    // ...
}, 100)
```
- 我们先思考两种情况,第一种func的执行时间小于100ms,第二种情况func的执行时间大于100ms
> 第一个当执行时间小于100ms的时候

![image](https://user-images.githubusercontent.com/28126886/30782756-ed3c8e66-a16a-11e7-8dda-a7f88899eddd.png)
- 第二个当100ms时,还得分两种情况,因为可能有150ms的,还可能时500ms的等等
> 我们先看类似于150ms的,当执行完后他会立即触发第二次

![image](https://user-images.githubusercontent.com/28126886/30782800-6fc8cf8e-a16b-11e7-9939-27b51d900464.png)
> 那我们来看一下第三种情况,其实根据setInterval的机制,他会抛弃掉中间所发生的,我们用图表来看一看就明白了

![image](https://user-images.githubusercontent.com/28126886/30782840-019f928a-a16c-11e7-9daa-c2e018cee5d3.png)
## 你真的了解——setTimeout
- 第一个,经常会出错的问题就是setTimeout中的this
```js
var i = 0;
const o = {
    i: 1;
    fn: function(){
        console.log(this.i);
    }
}
setTimeout(o.fn, 1000); //0
```
> 这里可以看出,如果是o对象调用的话,就会是1,但是他输出的确实0,因为有两点原因:
1.setTimeout是运行在全局环境下的
2.其实他是发生了下面的步骤:
```js
var a = o.fn;
a();
//只有这样,this才会被绑定到全局上去
```
- 第二个,setTimeout还能干什么?
> 其实不是的,我们先来看一下,setTimeout的一个面试中经常会问到的问题
```js
setTimeout(function(){
    console.log(1);
},0);
console.log(2);
```
> 其实这个特性说来话长,输出的是先2后1,因为setTimeout会把第一个函数放进任务队列,然后走一个event loop,所以会先输出的是2,才会输出1

> 那我们试想一下,这个特性我们可以用来做什么?当事件冒泡的时候,会正常情况下,会先触发子元素,然后在触发父元素,那么我们使用这个特性是不是能让其先触发父元素,在触发子元素,(题主没试过)

这篇文章就写到这儿,后面会出一篇定时器和事件循环的博客总结


================================================
FILE: blog/建议使用nvm管理node.md
================================================
### 前言

为什么题目是这个呢,因为自己最近在用npm包的时候被mac的权限恶心到了。

在mac中全局安装npm包,需要用sudo,因为npm包安装的位置在/usr/local/bin中,然后自己在安装weex-toolkit的时候,用了sudo命令,然后用weex命令创建文件夹的时候也要用sudo,导致创建的文件夹权限也是只读,不能写入。

### 如何改变这种现状

产生这种现状是因为我们没有对node这个东西进行管理,首推nvm,其次是brew。nvm是mac下的node管理工具,当然你手头上有好几个不同的项目,依赖不同的node版本,这个东西也可以很好的解决。


### 如果你已经安装了node,怎么办

- 全局卸载

```
sudo rm /usr/local/bin/npm
sudo rm /usr/local/share/man/man1/node.1
sudo rm /usr/local/lib/dtrace/node.d
sudo rm -rf ~/.npm
sudo rm -rf ~/.node-gyp
sudo rm /opt/local/bin/node
sudo rm /opt/local/include/node
sudo rm -rf /opt/local/lib/node_modules
```

### 安装nvm

```
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.0/install.sh | bash
```

### 安装nodejs

- 安装固定版本的
```
nvm install 8.9.1
```
- 安装最近稳定版本
```
nvm install stable
```

### 使用不同版本的nodejs

使用命令nvm use加版本号

```
nvm use 8.9.1
```

### 在项目中使用不同版本的node

我们可以通过创建项目目录中的`.nvmrc`文件来指定要使用的nodejs版本。之后在项目目录中执行`nvm use`即可。

### 对于不同版本的npm怎么办呢

其实每个版本的node,都会自带一个不同版本的npm,可以用`npm -v`来查看npm的版本。全局安装的npm,并不会在不同的node版本中共享。

================================================
FILE: blog/把session聊清楚.md
================================================
## 把session聊清楚
### 前言
前一篇写了关于[cookie](https://github.com/laihuamin/JS-total/issues/12)的,想必session大家也不陌生,我对这个东西初印象是什么,cookie是客户端的,session是服务端的,他们的优缺点为了应付面试也是了然于心,但是,这里我们来细究一下,其实,当你和面试官聊到这一块的时候,确实可以加分,因为他会觉得你这个知识点讲的不错,让我们来进入session的世界

### 什么是session


================================================
FILE: blog/模块.md
================================================
# 模块
## 模块化的好处
- 可维护性
> 可维护性显而易见,未模块化之前,你要改代码,可能得改好几处,模块化之后,代码复用了,改一处就可以了
- 命名空间
> 什么叫命名空间,简单点讲就是变量容易重名,你叫张三,他也叫张三,这就很尴尬。

> 还有就是模块化可以防止污染全局变量,为什么这么说,因为打包过之后,是没有全局变量这一个概念的,模块顶级作用域定义的也会被封装到局部作用域中
- 重用代码
> 这点基本不用讲,模块化的根本就是复用,
## commonJS
- commonJS核心
> commonJS有三部分分别是模块的定义,模块的引用和模块的标识
- commonJS的导出和引入
> 导出: [module.export和export](https://github.com/laihuamin/node-learning/tree/master/node%E5%AD%A6%E4%B9%A0/%E7%AC%AC%E4%B8%80%E8%AF%BE),[module.export和export的区别](https://github.com/laihuamin/node-learning/tree/master/node%E5%AD%A6%E4%B9%A0/%E7%AC%AC%E4%BA%8C%E8%AF%BE)

> 导入:require
```
const fs = require('fs');
```
> 注意点:commonJS是同步加载的,具体后面会讲到
## AMD与CMD
- AMD简介
> AMD又称异步模块加载,非阻塞性的模块加载
- AMD的格式
```
define(['myModule', 'otherModule'], function(myModule, otherModule){
    console.log(myModule.hello());
    console.log(otherModule.hello());
})
```
- AMD的原理
> AMD是通过appendChlid插入到DOM的,在模块加载成功之后,define会调用第二个回调函数
- CMD简介
> 和AMD原理一样
- CMD格式
```
define(function(require, export, module){
    var a = require('./a');
    module.export = {
        fn: a.doSomeThing()
    }
})
```
- 两者的区别
1.AMD推崇提前执行,CMD推崇延后执行
2.AMD推崇依赖前置,CMD推崇就近依赖
3.AMD推崇API一个多用,而CMD推崇API单一,举个例子,AMD中的require没有全局和局部之分,而CMD中只有局部没有全局,职责单一
## ES6模块
### 为什么要有模块?
> 模块与script有很大的不同:
- 模块代码自动运行在严格模式之下,并且没有任何办法跳出严格模式
- 在模块的顶级作用域添加全局变量,不会自动添加到共享作用域的全局中,只会在共享作用域函数内部存在
- 在模块顶级作用域添加this会变成undefined
- 模块不允许在代码中使用HTML风格的注释
- 如果想让外部访问模块必须导出
- 允许其他模块导入绑定
### 导出
> 利用export关键字将模块的部分代码导出,导出部分可以是常量,变量,函数,类等
- 语法及解析
```
//直接导出——不能匿名,但是可以是任何类型
export let color = "red";
//接口导出——跟上面直接导出类似
const b = (c) => c;
export {b};
//接口名修改导出——函数名是张三,我们非要把他导出叫李四
const a = (b) => b;
export { a as b};   //注意——引用也要引用李四
//默认值导出——对于匿名的我们可以这么做
export default function (){
    ...
}
//默认值导出2——和上面的类似,就是导出放在下面了
export {xxx as default}
```
### 导入
- 语法解析
```
// 完全导入
import * as xx from './example.js';
// 部分导入
import {xx, yy} from './example.js';
// 重命名导入
import {yy as xx} from './example.js';
```
- 多个模块导入
> 举个例子
```
import {sum} from "./example.js"
import {multiply} from "./example.js"
import {magicNumber} from "./example.js"
```
> 在这个例子中example文件只会被执行一次,其余的都是读取缓存来进行的
### 注意点
- export和import必须置于顶层代码中
```
//错误
function(){
    export.xxx;
    import xx from xx
}
```
- import导入的模块是只读的
```
//错误
import {obj} from xx;
obj = {} //错误
```
- ES6支持循环依赖
> 原因在与import是只读的,不可改变
```
//------ a.js ------
import {bar} from 'b'; // (i)
export function foo() {
    bar(); // (ii)
}

//------ b.js ------
import {foo} from 'a'; // (iii)
export function bar() {
    if (Math.random()) {
        foo(); // (iv)
    }
}
//foo永远是a.js中的foo,无法被改变
```
- 导入时不能使用解构
```
//错误
import {foo: {bar}} from './example.js'
```
## 面试题——commonJS和(AMD或者ES6)有什么区别
- 前者是同步加载的,后者是按需加载的,如果浏览器中也引用同步加载容易引起阻塞
- ES6 Module 中导入模块的属性或者方法是强绑定的,包括基础类型;而 CommonJS 则是普通的值传递或者引用传递。


================================================
FILE: blog/汇总2017JS项目,总结我们从中学到了什么?.md
================================================
![](http://laihuamin.oss-cn-beijing.aliyuncs.com/frameTotal.png)

### 当红辣子鸡——vue

和去年一样,vue是js项目中点赞数增加最多的,我们可以看下图:

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/vue-github.png)

这并不代表是最受欢迎项目,在项目总的点赞数量上依旧是react(86102 stars),但是确实是增长速度最快的,如果按照这个速率,接下来有可能会超过react。

- 个人感悟:自己公司也有项目在用,学vue的难点在于vuex,其他的话上手挺快的,而且是尤大写的,中文文档也很完整,很适合新手。

### 强生态——react

如果vue是成功的,那么无疑react表现的更加不错。

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/react-github.png)

虽然react点赞增加速率不很快(对于一个老项目来说,我们应该满足),但是react的成功更倾向于看他的生态圈,如下图:

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/react-other.png)

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/vue-other.png)

前五个项目的总star数超过67.9K,和vue的44.4K比无疑是成功的。

react经历了许可证的闹剧之后,它的最大的障碍也不复存在了,我们可以一起来见证它在2018能够带来什么新的东西。


- 个人感悟:react自己做项目的时候很倾向于用,但是公司的话没有在用,因为一些业务组件不完全等原因,不过,后面应该会一点点推行。就像上述我说的,react是facebook团队在维护的,生态圈也不错,而且react一直引领着前端的发展,很赞,个人也很喜欢!!!

### 新星——Parcel

parcel在一个月内增加的点赞数量可以超过大多数项目一年增加的。

你可能不能想象,一个12月份才推出的项目,点赞数的增加量可以排到年度的第12名,哈哈哈

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/parcel-author.png)
![](http://laihuamin.oss-cn-beijing.aliyuncs.com/parcel.png)

甚至超过了webpack,就单单一个月哈,我已经抑制不住好奇心了!!!

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/pacel-webpack.png)


- 个人感悟:这个打包工具我是在前端早读课这个公众号中了解到的,建议大家也可以了解一下,毕竟是前端的新东西,自己也没怎么用过,暂时不评价哈,不过口碑好像不错,听说打包速度比webpack快。到时候自己去尝试一下!!!

### 战争——前端

三大框架的战争应该已经告一段落,总结所有框架一年的star增长量,如下图:

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/allFrame.png)

位列前三的框架(Vue、React和Angular),还有一些更小的框架比如preact(这个在react许可证闹剧的时候听说),还有一些自己没有接触没有用过的(Hyperapp、dva等)。

虽然三大框架各有千秋,但是从流行度和生态圈来讲,已经不能像以前一样同日而语了。

但是你还是可以选择使用任何一门框架,但是他们不在具有一样的势头了。

优胜劣汰,或许也是另一种美好,至少不用像我学前端的时候一样,需要去考虑先学哪个,哪个才是潮流。

- 个人感悟:我觉得框架是另一回事,有一些原理是共同的,比如vdom、依赖收集等,框架会变,只有抓住不变的东西才是我们程序员的核心竞争力,我现在也在一点一点的啃源码,收获还是挺大的。对于新东西的好奇也是我们的动力源泉,但是不能忘了本质性的东西。

### 编译工具——VSCode

2017年最成功编译工具无疑是vscode。vscode在编辑器中star增加量毫无悬念是第一名,然后他在受欢迎度排行中排到了第六名,很不错的成绩。

这也证实了VSCode成为我们前端开发者的头号编辑器。

VScode是成功的,但也不是完美的,他有需要改进的地方,按网上的说法,他在用户界面的渲染速度是一个很大缺陷,会导致初始化的时候速度变慢

当然用一句话总结:VSCode比webstorm更轻、比sumlime开源、比atom更快。

- 个人感悟:以前在老师那边做项目的时候,用的是IDEA,但是那个比webstorm更笨重,后来转向了webstorm,但是用了VSCode之后,感觉那个启动速度,瞬间就爱上了,也用过一段事件的sumline,sumline给我的感觉太个性化了,当时就不怎么会配,后来就没用,用到现在,还是vscode最合心意。

### 展望——2018

- GraphQL

GraphQL是facebook在2012年的时候提出,然后在2015年的时候开源,和RESTful对比有优点也有缺点。这是一门查询语言,可能不是主流,但是它强大的功能可能能激起不小的浪花,还是挺期待的,虽然要许久才能用到。
[推荐博客](http://imweb.io/topic/58499c299be501ba17b10a9e)

- Parcel

新的打包工具,自己一开始的时候接触过gulp,前端时间搭建公司新项目架子的时候研究过webpack的配置,对于webpack还是挺了解的,webpack需要自己去配置,比如整理entry,output,module,还有一系列的插件,有些许麻烦,不过,webpack有很好的中文文档,但是,新的工具来的势头很猛。看了下面这片博文,知道了点parcel的好处,但是他在2018年还有好多问题要解决,希望我们能用上一个更好的工具。
[推荐博客](https://segmentfault.com/a/1190000012612891)

- Prettier

这个工具在2017年也取得了不小的成就,自动格式化代码,让团队具备统一的代码风格,听起来比eslint还厉害,但是有优点也有缺点,或许在2018年能更智能一点,在配置方面能够更灵活等问题,希望它能变得更好,那我们就又有一个神器了!!!
[推荐博客](http://react-china.org/t/prettier/11498)

- Puppeteer

这个是google发行的,时间好像是8月份,但是还是受到大家的追捧,想了解的也可以了解一下,因为在4个月时间里就可以增加20000stars,

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/puppeteer.png)

### 数据来源
[bastof.js.org](https://risingstars.js.org/2017/en/#section-all)

### 总结

希望2018前端变得更美好,希望自己在2018年还能有过多的产出,希望自己对技术还满怀好奇,有探索的心,希望自己抓住一些不变的东西,巩固自己,充实自己,输出给大家,最后希望学习前端的同志们一起加油!!![个人博客](https://github.com/laihuamin/JS-total)喜欢的可以点个赞,谢谢!!

================================================
FILE: blog/浏览器中的eventloop.md
================================================
最近一直在研究event loop相关的,首先我们可以从[HTML standard](https://html.spec.whatwg.org/multipage/webappapis.html#task-queue),标准中对于event loop的介绍。

> To coordinate events, user interaction, scripts, rendering, networking, and so forth, user agents must use event loops as described in this section. There are two kinds of event loops: those for browsing contexts, and those for workers.

为了协调事件,用户交互,脚本,渲染,网络请求,等等,必须用到event loop。而event loop有两种类型,一种browsing contexts和另一种workers。


### task

一个event loop中会有一个或者多个task队列。而来源不同的task将会放入到不同的task中。

典型的任务源:

- DOM操作
- 用户交互(点击事件之类的)
- 网络请求
- script代码
- setTimeout/setInterval
- I/O
- UI交互
- setImmediate(nodejs环境中)

大致有这么几种,关于macrotask这个说法,并没有在标准中被提及。

### Microtask

一个事件循环只有一个Microtask,以下几种任务被认为是microtask

- promise
- promise回调(then和catch)
- MutationObserver
- process.nextTick(nodejs环境中)


### 关于EL的机制

可以观看标准的8.1.4.2 processing model中的解释,过程很长,很复杂。

但是其实总结的来说就是以下这段话:

`先运行一个task,然后再去清空Microtask队列,在运行一个task,然后再去清空Microtask队列`

那么我们来看一个例子:

```js
setTimeout(()=>{
    console.log('timer1')

    Promise.resolve().then(function() {
        console.log('promise1')
    })
}, 0)

setTimeout(()=>{
    console.log('timer2')

    Promise.resolve().then(function() {
        console.log('promise2')
    })
    setTimeout(() => {
    	console.log('timer3')
    }, 0)
}, 0)

Promise.resolve().then(function() {
    console.log('promise3')
})

console.log('start')
```

当然,答案不重要,其中的过程能理解最重要。

- 循环1

运行过程:
script脚本被当作一个task,放入到task队列中

【task队列】:
1、将定时器set1和set2放入到task队列中。
2、将Promise放入到microtask队列中
3、输出start

【microtask队列】:
1、执行promise的回调,输出promise3

- 循环2

运行过程:
在task队列中,将set1当作一个task。

【task队列】:
1、输出timer1
2、将promise放入到microtask队列中

【microtask队列】
1、执行promise的回调,输出promise1

- 循环3

运行过程:
在task队列中,将set2当作一个task。

【task队列】:
1、输出timer2
2、将promise放入到microtask中
3、将set3放入到task队列中

【microtask】
1、执行promise的回调,输出promise2

- 循环4

运行过程:
在task队列中,将set3当作一个task。

【task队列】:
1、输出timer3
2、经过 microtask checkpoint检测,microtask队列为空,跳过。

- 答案:

```
start
promise3
timer1
promise1
timer2
promise2
timer3
```

================================================
FILE: blog/细说Array.prototype.slice.call.md
================================================
### 前言

总觉得这个方法比较眼熟,但是不知道这个方法是干什么用的,那我们今天就来剖析一下这个方法

### 基本语法的讲解

- Array.prototype.slice()
Array是一个构造函数,原型中带有的slice方法就是我们平常用来切割数组用的,之后会返回一个数组,不清楚的朋友可以去MDN上看一下,[MDN之slice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)

- call和apply
call和apply、bind这已经是老生常谈的问题了,是可以改变this指向的,这里就简单点打个比方吧。

```js
function Food(){}
Food.prototype.say = function(){
  console.log(this.color)
}
var fish = new Food()
var o = {
  color: 'red'
}
fish.say.call(o);  //red
```

### 答疑解惑

Array.prototype.slice.call那么这个有什么用呢,我们来看个例子:

```js
function test(a,b,c,d) { 
  var arg = Array.prototype.slice.call(arguments,1); 
  console.log(arg); 
} 
test("a","b","c","d"); //b,c,d
```
看到这儿,可能有些人会说那能不能写arguments.slice(1),当然不能,这样的话会报错,因为arguments是类数组对象,并没有slice这个方法。
[关于arguments对象](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments)

### 真正的机制

Array.prototype.slice.call()能把类数组对象转化成数组,当然像传统的dom对象和js对象都是行不通的,我们接下来举个例子:
```js
var a={length:2,0:'lai',1:'hua'};//类数组,有length属性,长度为2,第0个是lai,第1个是hua
console.log(Array.prototype.slice.call(a,0));// ["lai", "hua"],调用数组的slice(0);
```
这个例子作证了我们的观点
接下来看一个实用的例子,这个例子看懂了,这个知识点也就懂了:
```
function bind(func, thisArg) {
    var nativeBind = Function.prototype.bind;
    var slice = Array.prototype.slice;
    if (nativeBind && func.bind === nativeBind) {
        return nativeBind.apply(func, slice.call(arguments, 1));
    }

    var args = slice.call(arguments, 2);
    return function () {
        return func.apply(thisArg, args.concat(slice.call(arguments)));
    };
}
```

上面这个式自己对bind的封装,可以兼容比较老的浏览器。

### 补充

最后补充一个通用的将类数组转化成数组的方法,因为考虑到可能有些不支持Array.prototype.slice,或者环境冲突的情况将这个原生方法覆盖:

```js
var toArray = function(s){
    try{
        return Array.prototype.slice.call(s);
    } catch(e){
        var arr = [];
        for(var i = 0,len = s.length; i < len; i++){
               arr[i] = s[i];
        }
         return arr;
    }
}
```

================================================
FILE: blog/编写一个分析代码依赖的工具(一).md
================================================
一个源码中,理不清的依赖是最烦的,让我们继续往下看,如何实现它,__工具的[github](https://github.com/laihuamin/JS-structure)地址__,觉得可以的可以点个star,__[博客](https://github.com/laihuamin/JS-total)的地址__,喜欢的也可以点个star,谢谢。

__先让我们来看看最后的效果如何:__

![all-js-analysis](http://laihuamin.oss-cn-beijing.aliyuncs.com/all-js-analysis.png)
![one-analysis](http://laihuamin.oss-cn-beijing.aliyuncs.com/one-analysis.png)

选中的情况下,会把其余的都隐藏,显示它引入的依赖。

## 怎么实现它

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/%E9%80%80%E5%90%8E%E6%88%91%E8%A6%81%E8%A3%85%E9%80%BC.jpeg)
![](http://laihuamin.oss-cn-beijing.aliyuncs.com/%E7%AC%AC%E4%B8%80%E6%AD%A5%E9%97%AE.png)

__我们先了解echart和node的fs和path模块,这是编写的基础。fs的几个处理文件的方法,path几个处理路径的方法,还有echart中的和弦图,我们要整理出来的nodes节点和links依赖关系。__

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/%E9%BC%93%E6%8E%8C.jpg)

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/%E7%AC%AC%E4%BA%8C%E6%AD%A5%E9%97%AE.png)

__第二步,先定义一些常量,然后我们要用fs模块去读取文件夹中的文件的filename和pathname,我们还要判断一个文件是不是文件夹,如果是文件夹,我们要递归调用的个函数,继续读取,直到所有的都遍历完毕。代码如下:__

- 定义常量

```js
// 该文件是这个npm包用到的常量

// 需要忽略的文件夹
module.exports.IGNORE_DIR = ['node_modules', '.git', 'dist', 'build', 'test', '.DS_Store', '.gitignore', 'package-lock.json', 'README.md'];

// 符合标准的文件的扩展名
module.exports.INCLUDE_EXT = ['.js', '.json', '.node']
```

- 收集文件的filename和pathname
```js
var fs = require('fs');
var path = require('path');
// 引入我们定义好的常量
var extName = require('./constant.js').INCLUDE_EXT,
    ignoreFile = require('./constant.js').IGNORE_DIR,
    res = {
        filename: [],
        pathname: []
    };

function getFileName(dir, addIgnore) {
    var files = fs.readdirSync(dir),
        ignoreList = [];

    // 判断不需要的文件
    if(Array.prototype.isPrototypeOf(addIgnore)) {
        ignoreList = addIgnore.concat(ignoreFile);
    } else {
        ignoreList = ignoreFile;
    }

    // 收集文件名称和所属路径

    files.forEach(function(item) {
        var extname = path.extname(item),
            currentPath = path.join(dir, item),
            isFile = fs.statSync(currentPath).isFile(),
            isDir = fs.statSync(currentPath).isDirectory();
        
        // 先在ignore的列表中寻找,如果找到直接return
        if (ignoreList.indexOf(item) !== -1) {
            return;
        } else {
            // 判断他是不是我们需要的文件名
            if(isFile && extName.indexOf(extname) !== -1) {
                res.filename.push(item);
                res.pathname.push(currentPath);
            } else if (isDir) {
                // 如果是文件夹,调用函数继续处理
                getFileName(currentPath);
            }
        }
    })
    return res;
}

```
![](http://laihuamin.oss-cn-beijing.aliyuncs.com/%E9%BC%93%E6%8E%8C1.jpg)

你会发现这里的输出结果整理一下已经可以作为echarts的节点了。

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/%E7%AC%AC%E4%B8%89%E6%AD%A5%E9%97%AE.png)
__第三步的话,我倾向于把links这个关系整理出来,那么我们要做的活就是用fs读取每一个文件,然后在用正则,将import和require的文件整理到target中,这样我们就得未经处理的links。那我就直接上代码了!!!__

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/%E9%BC%93%E6%8E%8C2.png)

```js
var fs = require('fs'),
    path = require('path'),
    reqReg = /require\(['|"](.*?)['|"]\)/g,
    impReg = /import\s.*?['|"](.*?)['|"]/g,
    resDep = [];

function getDepend(res, dir) {
    // 根据上一个文件res获得的pathname数组进行依赖收集
    res.pathname.forEach(function(item, index) {
        // 读取文件
        var data = fs.readFileSync(item, 'utf-8'),
            results = [];
            // 正则匹配require
        while((results = reqReg.exec(data)) !== null) {
            var link = {
                source: res.pathname[index],
                target: results[1],
                weight: 1,
                name: '依赖'
            };
            resDep.push(link);
        }
        // 正则匹配import
        while((results = impReg.exec(data)) !== null) {
            var link = {
                source: res.pathname[index],
                target: results[1],
                weight: 1,
                name: '依赖'
            };
            resDep.push(link);
        }
    });
    return resDep;
}
```
 
![](http://laihuamin.oss-cn-beijing.aliyuncs.com/%E7%AC%AC%E5%9B%9B%E6%AD%A5%E9%97%AE.png)
__第四步的话,下回在讲吧,要跑去要饭了__


![](http://laihuamin.oss-cn-beijing.aliyuncs.com/%E8%A6%81%E9%A5%AD.jpg)

## 总结
最后在死皮赖脸的推荐一下[工具](https://github.com/laihuamin/JS-structure)和[博客](https://github.com/laihuamin/JS-total),欢迎star,谢谢

================================================
FILE: blog/编码与解码.md
================================================
## 前言
### 说说我为什么要写关于编码的一篇博文?有两个原因
1.艺龙的面试面试官问到了,让我知道了你想扩展的你的基础,你不能放弃任意一个知识点
2.就是今天做业务碰到了这个bug,肯定有人想知道,这个会产生什么bug,bug一般都是不注意细节,才会导致的,业务中有一个分享到朋友圈和微信好友的一个业务模块,大家都知道,一般这种模块都是要利用客户端给你提供一个hybrid的一个接口,然后,你通过调用这个接口,来完成分享这一个举动,但是在测试的时候并没有发现问题,但是到了线上问题来了,我们一个主管的账号分享不了,但是很多人的账号却可以分享,这种问题是最烦人的,而且更气人的是安卓机都可以分享,那我们是不是该分分锅,ios的锅?还是账号的锅?又或者是前端的锅?故事发展到最后,很多人都忙了一下午来查找bug,我来写这篇文章,肯定说明是前端的锅啊,有人奇怪,这安卓的,ios的部分都是可以的,说明功能没问题,我当时也是这么想的,但是结果却出乎意料,先说说为什么部分ios可以,因为url里面带了昵称参数,一部分人的昵称是英文,一部分是中文,这就是结果——在url中的中文字符都要用base64转成ASCII字符进行传递。


## 了解篇——怎么用
很多不了解的人,应该更关心怎么用,这里我会讲几个web的api,后面一部分博文,可能会深入点,无聊点,程序员写作能力欠缺0.0
### encodeURIComponent和encodeURI
这两个接口是我们经常用的,他们的用途一致,是对URL进行编码,但是他们之间还是有区别
- encodeURI不能对下列字符进行编码,ASCII字母、数字、~!@#$&*()=:/,;?+'
- encodeURIComponent不能对下列字符进行编码,ASCII字母、数字、~!*()'
> 总结,encodeURIComponent的编码范围比encodeURI大,我的问题就是用encodeURIComponent解决的
### escape
 上面的两个对URL进行编码,这个接口就是对字符串进行编码,让字符串可以在所有计算机上被读取,编码之后的效果是%XX或者%uXXXX这种形式。
- 注意,它针对的是字符串,不适用于URL,不会对 ASCII字母、数字、@*/+
```
//举个例子
let str = '来铧敏';
let encodeStr = escape(str);
console.log(encodeStr);//%u6765%u94E7%u654F
```
### 前三个的用法
- 不针对URL的那么就用escape
- 针对整个http的,用encodeURI
- 针对http的参数的,就用encodeURIComponent

### unescape、decodeURI和decodeURIComponent
这三者是解码用的,分别对应上面的三个,看下面的例子就知道了
```
let str = '来铧敏';
let encodeStr = escape(str);
console.log(encodeStr);
console.log(unescape(encodeStr));
let _encodeStr = encodeURIComponent(str);
console.log(_encodeStr);
console.log(decodeURIComponent(_encodeStr));
```
输出结果我就不写了,有几点需要注意,编码和解码需一一对应,默写场景encodeURI进行编码的decodeURIComponent可以解码,但是escape编码的decodeURIComponent解码会报错
## 了解篇——有什么用
URL中允许的只有英文字符,阿拉伯数字,某些标志,你肯定没有看过这样的网站:
```
http://github.com/laihuamin/来点赞
```
为什么会上述网站不行呢,因为网络标准RFC 1738做了硬性规定:
> 原文:"...Only alphanumerics [0-9a-zA-Z], the special characters "$-_.+!*'()," [not including the quotes - ed], and reserved characters used for their reserved purposes may be used unencoded within a URL."
- 所以综上所述,编码是为了一些不被规定的字符在URL中传递变得合法

用编码可以对前端的一些重要信息进行加密,其实前端并没有上面严格意义上的加密,这里说的加密更多偏向与混淆,我们来实现一个简单点的加密:
```js
//这里实现的是对js代码的加密,这是个例子
console.log('我要加密这个语句');
//下面是加密和解密处理
escape('console.log("我要加密这个语句")')
//先进行编码获得加密后的
const code = unescape(console.log%28%22%u6211%u8981%u52A0%u5BC6%u8FD9%u4E2A%u8BED%u53E5%22%29)
eval(code)
```
- 总结,第二点功能,没有第一点功能来的实用,其实可以不用记忆,如果还有其他上面功能的欢迎补充
## 基础篇——有哪些
说了这么多编码,那到底有多少种编码形式呢?熟悉计算机的朋友应该都听过这样的词汇:ASCII、Unicode、UTF-8、UTF-16等等,那我们就来絮叨絮叨常见的
### ASCII
ASCII码我们在熟悉不过,它是使用8位二进制来表示英文字符和符号,但是吹生出一个问题,中华文化博大精深,光汉字就有几万个,256个字符哪里够用呢。。。。
### 非ASCII编码
一个英文字母一个字节足够表示,但是10万个汉字,一个字节怎么够,那就两个呗,256x256才足以表示,中文编码就是GB2312,但在日本或者其他地方就不能使用,所以众多的编码方式,没有统一性
### Unicode
众多的编码方式困扰这大家,电子邮件也时常出现乱码,如果有一种字符集可以把所有的字符都收录进来,不就可以解决问题,Unicode应运而生,Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号。
### Unicode的问题
- Unicode只是一个字符集,并没有规定二进制码该怎么储存,这样就会引发一个问题,一个汉字两个字节,一个英文字符一个字节,计算机把一个汉子当成两个英文字符来读写怎么办
- 前者还会导致一种结果,为了解决前面的问题,我们势必多添加几个000去表示到底是几个字节的字符,这样存储空间有了巨大的浪费
### UTF-8
UTF-8是Unicode的一种实现,它是一种可变的编码规则,当在ASCII范围内时,用一个字节读取,如果在范围之外,就用多个字节读取,注意,中文字符在Unicode中是两个字节,在UTF-8中是3个字节,Unicode到UTF-8的转换规则有相应的算法
## 总结篇
聊了这么多关于编码的,我简单总结几点:
- 现有ASCII编码,但是对于中文和多国语言,不够用了,中国人民对ASCII进行扩展,产生了GB2312编码,但是还是不够用,之后出现了GBK编码
- 很多国家编码规则不统一,吹生出了Unicode编码方式,但是由于Unicode的缺陷,所以UTF-8应运而生,所以我们经常会在网页的head标签中看到
```
<meta charset="utf-8">
```
- UTF这么方便,为什么国内还有人使用GBK编码呢,就是因为UTF的占用体积过大,如果仅仅面向国人的,还是建议GBK编码

> 知识来源网络,实践获得真知,借鉴网络的地方过多,无法著名出处,侵删

================================================
FILE: blog/聊聊数据接口.md
================================================
## 聊聊数据接口
其实我挺想聊聊这方面的东西的,前有ajax今有fetch、axios,其实前端发展的挺迅猛,有时候掌握原理,追本溯源是很重要的,而且,我觉得书写数据接口,其实在前端项目中的比重不可小觑,所以让我们进入接口的世界,讲讲它的流
程

================================================
FILE: blog/🚀述说Parcel:A blazing fast, zero configuration web application bundler 📦.md
================================================
> 这篇文章总字数:1214,普通阅读4分钟,速读2分钟,主要讲的是新的打包工具parcel的一些新特性,谢谢,有兴趣朋友可以关注一下我的[github](https://github.com/laihuamin/JS-total)上面有30多篇文章,喜欢的可以watch或者star。你的支持是我输出的动力。

### 前言

今天很高兴来讲一下新的打包工具parcel,一个快速的,零配置的打包工具。可以点击这里看他的[github](https://github.com/parcel-bundler/parcel).

![](http://laihuamin.oss-cn-beijing.aliyuncs.com/parcel1.png)

为了解决现有webpack等打包工具存在的问题:**性能和配置复杂度**。我开始研究parcel。

> 以webpack举例,我认为打包工具是用来减轻前端负担的,但是在webpack上我并没有感觉到,除了需要学习webpack外,配置的时候还是要查询中文文档,因为太多的插件根本不适合记忆,还有性能方面,当页面足够多时,打包的速度开始变得很慢,我记得我们公司一个运营中心的项目,打包时间惨目忍睹。

### 特点

- 🚀打包**速度快**——多核编译,以及文件系统缓存,即使在重新启动后也能快速重建。
- 📦支持JS,CSS,HTML,图像,文件资产等等——**不安装插件**
- 🐠当我们需要时使用Babel,PostCSS和PostHTML**自动转换模块**——甚至是node_modules包
- ✂️使用动态import()语句进行**零配置代码的分割**。
- 🔥编译项目的时候支持**热更新模块**。
- 🚨友好地错误日志体验——语法高亮有助于我们追踪问题

### 性能

首先我想说的就是性能,我上面已经说了,当一个项目有好多个页面的时候,你的打包速度真的是慢,webpack也是一样,一个项目要打包上线,这里花的时间,都可以早点下班了,开发过程中也是一样,不过本地的话,你还可以给打包单一几个页面。

很多打包工具都注重于能快速的重新构建,这是伟大的,但是,初次构建的性能对于开发和生产来说是很重要的

parcel解决了这个问题,在编译过程中,并行的编译代码,并使用现代的多核处理器解决这个问题。上述原因影响了初步构建的速度。他也有文件缓存系统,以便于快速的重建。

![Based on a reasonably sized app, containing 1726 modules, 6.5M uncompressed. Built on a 2016 MacBook Pro with 4 physical CPUs.](http://laihuamin.oss-cn-beijing.aliyuncs.com/parcel-compare.png)

### 零配置体验

第二个原因就是帮助我们减轻配置的负担,大多数打包工具是围绕着配置文件建立起来的,配置文件有很多的插件。一个webpack的打包工程500行代码,已经不是什么稀罕的事情了。

这种配置不仅繁琐耗时,而且你不能保证你一定是正确的,还得参照规范改,这可能导致优化应用程序而影响正常的生产

parcel被设计为零配置:只需要应用程序入口给它,他就可以正确的打包。Parcel支持JS,CSS,HTML,图像,文件资产等等 - 不需要任何插件。

parcel的零配置体验还体现在不局限于文件格式,当parcel检测到一个`.babelrc, .postcssrc`等文件,就会自动转化相应模块,比如Babel, PostCSS和PostHTML。这甚至适用于仅用于该模块的node_modules中的第三方代码,因为应用的使用者,不需要知道构建的时候每一个模块是如何导入的。并且这次构建也没有必要让Babel在每一个文件上的运行

最后,parcel也很好的支持一些先进的打包功能,像代码拆分,热更新等。在生产模式中,还支持自动压缩,未来也可能加入像tree-shaking等优化

### 未来发展的前景

开启新项目的好处就是,parcel可以使用现代的体系结构,没有历史包袱,并且在这个体系结构上扩展,更加灵活,并支持代码拆分和热更新等功能。

现在主流的打包工具还是主要关注JavaScript,比如webpack,其他类型的文件也要通过loader将其变成JavaScript来进行打包。

但在parcel中,任何类型的文件都有机会成为一等公民,很容易可以添加一种新类型输入文件,并将类似类型的文件组合到输出文件中

你可以在这个网站上了解更多关于[parcel如何工作的](https://parceljs.org/how_it_works.html)

### 尝试一下吧!!

parcel才刚刚开始就收到大众追捧,尝试一下吧

================================================
FILE: hilo/flybird/index.html
================================================
<!doctype html>
<head>
<title>Flappy Bird</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1, maximum-scale=1" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" href="images/icon.jpg"/>
<link rel="apple-touch-startup-image" href="images/icon.jpg" />

<style type="text/css">
body, div, canvas{
    image-rendering: optimizeSpeed;
    -webkit-image-rendering: optimizeSpeed;
    -webkit-interpolation-mode: nearest-neighbor;
}
body{padding:0; margin:0; font-size:12px; background-color:#fff;}
body, html{height:100%;}
</style>

<script type="text/javascript" src="../hilo-standalone.js"></script>
<script type="text/javascript" src="../hilo-flash.js" data-auto="true"></script>
<script type="text/javascript" src="js/game.js"></script>
<script type="text/javascript" src="js/asset.js"></script>
<script type="text/javascript" src="js/readyScene.js"></script>
<script type="text/javascript" src="js/overScene.js"></script>
<script type="text/javascript" src="js/bird.js"></script>
<!-- <script type="text/javascript" src="js/Holdbacks.js"></script> -->

</head>

<body>
</body>

</html>

================================================
FILE: hilo/flybird/js/asset.js
================================================
// 该模块主要是资源的整合和加载
(function(game) {
    var Asset = game.asset = Hilo.Class.create({
        Mixes: Hilo.EventMixin,
        queue: null,
        bg: null,
        ground: null,
        birdAtils: null,
        ready: null,
        over: null,
        load: function() {
            var resources = [{
                id: 'bg', src: 'images/bg.png'
            }, {
                id: 'bird', src: 'images/bird.png'
            }, {
                id: 'ground', src: 'images/ground.png'
            }, {
                id: 'holdback', src: 'images/holdback.png'
            }, {
                id: 'icon', src: 'images/icon.jpg'
            }, {
                id: 'number', src: 'images/number.png'
            }, {
                id: 'over', src: 'images/over.png'
            }, {
                id: 'ready', src: 'images/ready.png'
            }]
            this.queue = new Hilo.LoadQueue();
            this.queue.add(resources);
            this.queue.on('complete', this.onComplete.bind(this))
            this.queue.start();
        },
        onComplete: function(e) {
            this.bg = this.queue.get('bg').content;
            this.ground = this.queue.get('ground').content;
            this.ready = this.queue.get('ready').content;
            this.over = this.queue.get('over').content;
            this.birdAtils = new Hilo.TextureAtlas({
                image: this.queue.get('bird').content,
                frames: [
                    [0, 120, 86, 60],
                    [0, 60, 86, 60],
                    [0, 0, 86, 60]
                ],
                sprites: {
                    bird: [0, 1, 2]
                }
            })
            // 删除下载图片的监听
            this.queue.off('complete');
            // 发送complete事件
            this.fire('complete');
        }
    })
})(window.game)

================================================
FILE: hilo/flybird/js/bird.js
================================================
(function(game) {
    var Bird = game.Bird = {
        Extends: Hilo.Sprite,
        construtor: function() {
            Bird.superclass.construtor
        }
    }
})(window.game)

================================================
FILE: hilo/flybird/js/game.js
================================================
(function() {
    window.onload = function() {
        game.init();
    }
    var game = window.game = {
        // 初始化数据
        width: 0,
        height: 0,
        scale: 0,
        ground: null,
        tick: null,
        readyGame: null,
        overScene: null,
        // 初始化游戏
        init: function() {
            this.asset = new game.asset();
            this.asset.on('complete', function(e) {
                this.asset.off('complete');
                this.initStage();
                this.initBackground();
                // this.initReady();
                this.initOverScene();
            }.bind(this));
            this.asset.load();
        },
        // 初始化舞台
        initStage: function() {
            this.width = 720;
            this.height = 1280;
            this.scale = 0.5;
            // 舞台
            this.stage = new Hilo.Stage({
                width: this.width,
                height: this.height,
                scaleX: this.scale,
                scaleY: this.scale
            })
            document.body.appendChild(this.stage.canvas);
            // 设置定时器
            this.tick = new Hilo.Ticker(60);
            // 将舞台元素加入到定时器队列
            this.tick.addTick(this.stage);
            // 启动定时器
            this.tick.start();

        },
        // 设置游戏背景
        initBackground: function() {
            var bgWidth = this.width * this.scale;
            var bgHeight = this.height * this.scale;
            document.body.insertBefore(Hilo.createElement('div', {
                id: 'bg',
                style: {
                    position: 'absolute',
                    background: 'url(images/bg.png) no-repeat',
                    backgroundSize: bgWidth + 'px, ' + bgHeight + 'px',
                    width: bgWidth + 'px',
                    height: bgHeight + 'px' 
                }
            }), this.stage.canvas)
            // 创建地面
            this.ground = new Hilo.Bitmap({
                id: 'ground',
                image: this.asset.ground
            }).addTo(this.stage)
            this.ground.y = this.height - this.ground.height;
            Hilo.Tween.to(this.ground, {x: -60}, {duration:300, loop:true})
            this.tick.addTick(Hilo.Tween)
        },
        // 准备开场动画
        initReady: function() {
            this.readyGame = new game.ReadyScene({
                width: this.width,
                height: this.height,
                image: this.asset.ready
            }).addTo(this.stage)
        },
        // 初始化结束场景
        initOverScene: function() {
            this.overScene = new game.OverScene({
                width: this.width,
                height: this.height,
                image: this.asset.over
            }).addTo(this.stage)
        }
    }
})()

================================================
FILE: hilo/flybird/js/overScene.js
================================================
(function(game) {
    var OverScene = game.OverScene = Hilo.Class.create({
        Extends: Hilo.Container,
        constructor: function(protypes) {
            OverScene.superclass.constructor.call(this, protypes);
            this.init(protypes);
        },
        init: function(protypes) {
            //Game Over图片文字
            var gameover = this.gameover = new Hilo.Bitmap({
                id: 'gameover',
                image: protypes.image,
                rect: [0, 298, 508, 158]
            });

            //结束面板
            var board = this.board = new Hilo.Bitmap({
                id: 'board',
                image: protypes.image,
                rect: [0, 0, 590, 298]
            });

            //开始按钮
            var startBtn = this.startBtn = new Hilo.Bitmap({
                id: 'start',
                image: protypes.image,
                rect: [590, 0, 290, 176]
            });

            //等级按钮
            var gradeBtn = this.gradeBtn = new Hilo.Bitmap({
                id: 'grade',
                image: protypes.image,
                rect: [590, 176, 290, 176]
            });
            // 玩家当前得分
            // var scoreLabel = this.scoreLabel = new Hilo.BitmapText({
            //     id: 'score',
            //     image: protypes.
            // })
            //白色的遮罩效果
            var whiteMask = this.whiteMask = new Hilo.View({
                id: 'mask',
                width: this.width,
                height: this.height,
                alpha: 0
            })
            board.x = this.width - board.width >> 1;
            board.y = this.height - board.height >> 1;
            gameover.x = this.width - gameover.width >> 1;
            gameover.y = board.y - gameover.height - 20;
            startBtn.x = board.x - 5;
            startBtn.y = board.y + board.height + 20 >> 0;
            gradeBtn.x = startBtn.x + startBtn.width + 20 >> 0;
            gradeBtn.y = startBtn.y;
            this.addChild(gameover, board, startBtn, gradeBtn, whiteMask);
        }
    })
})(window.game);

================================================
FILE: hilo/flybird/js/readyScene.js
================================================
(function(game) {
    var ReadyScene = game.ReadyScene = Hilo.Class.create({
        Extends: Hilo.Container,
        constructor: function(properties) {
            ReadyScene.superclass.constructor.call(this, properties);
            this.init(properties);
        },
        init: function(properties) {
            var getReady = new Hilo.Bitmap({
                image: properties.image,
                rect: [0, 0, 508, 158]
            })
            var tap = new Hilo.Bitmap({
                image: properties.image,
                rect: [0, 158, 286, 246]
            })
            // 定位提示
            tap.x = this.width - tap.width >> 1
            tap.y = this.height - tap.height + 40 >> 1
            getReady.x = this.width - getReady.width >> 1
            getReady.y = tap.y - getReady.height >> 0
            this.addChild(tap, getReady)
        }
    })
})(window.game)

================================================
FILE: hilo/hilo-flash.js
================================================
;(function(){

/**
 * @class Flash渲染器。将可视对象以flash方式渲染出来。
 * @augments Renderer
 * @param {Object} properties 创建对象的属性参数。可包含此类所有可写属性。
 * @module hilo/flash/FlashRenderer
 * @requires hilo/core/Class
 * @requires hilo/core/Hilo
 * @requires hilo/renderer/Renderer
 */
var FlashRenderer = (function(){

var _stageStateList = ["x", "y", "scaleX", "scaleY", "rotation", "visible", "alpha"];
var _stateList = _stageStateList.concat(["pivotX", "pivotY", "width", "height", "depth"]);
var _textStateList = _stateList.concat(["text", "color", "textAlign", "outline", "lineSpacing", "font"]);
var n = 0;

var state = {
    View: _stateList,
    Stage: _stageStateList,
    Graphics: _stateList,
    Text: _textStateList
};

function createFid(target){
    return target.id + (n++);
}

return Hilo.Class.create(/** @lends FlashRenderer.prototype */{
    Extends: Hilo.Renderer,
    constructor: function(properties){
        FlashRenderer.superclass.constructor.call(this, properties);

        this.stage._ADD_TO_FLASH = true;
        this.stage.fid = createFid(this.stage);
        this.stage.flashType = "Stage";

        this.commands = properties.commands || [];
        this.commands.push("create", this.stage.fid, "Stage");
        this.commands.push("stageAddChild", this.stage.fid);
    },

    /**
     * @private
     * @see Renderer#startDraw
     */
    startDraw: function(target){
        if(target == this.stage){
            return true;
        }

        target._lastState = target._lastState || {};
        //create
        if(!target._ADD_TO_FLASH){
            target._ADD_TO_FLASH = true;
            target.fid = createFid(target);

            if(target._drawTextLine){
                target.flashType = "Text";
            }
            else if(target.beginLinearGradientFill){
                target.flashType = "Graphics";
            }
            else if(target == this.stage){
                target.flashType = "Stage";
            }
            else{
                target.flashType = "View";
            }
            this.commands.push("create", target.fid, target.flashType);
        }

        return true;
    },

    /**
     * @private
     * @see Renderer#draw
     */
    draw: function(target){
        if(target == this.stage){
            return;
        }

        target._lastState = target._lastState || {};

        var lastParent = target._lastState.parent;
        var parent = target.parent;

        if(parent){
            if(!lastParent || parent.fid != lastParent.fid){
                this.commands.push("addChild", parent.fid, target.fid, target.depth);
            }
        }
       
        target._lastState.parent = target.parent;

        switch(target.flashType){
            case "Graphics":
                if(target.isDirty && target.flashGraphicsCommands && target.flashGraphicsCommands.length > 0){
                    this.commands.push("graphicsDraw", target.fid, target.flashGraphicsCommands.join(";"));
                    target.isDirty = false;
                }
                break;
            case "Text":
                break;
        }
    },

    /**
     * @private
     * @see Renderer#transform
     */
    transform: function(target){
        var stateList = state[target.flashType];
        var lastState = target._lastState = target._lastState||{};
        
        if(stateList){
            for(var i = 0,l = stateList.length;i < l;i ++){
                var prop = stateList[i];
                var lastValue = lastState[prop];
                var value = target[prop];
                
                lastState[prop] = value;

                if(lastValue != value){
                    this.commands.push("setProp", target.fid, prop, value);
                }
            }

            //画图
            if(target.drawable && target.drawable.image){
                var image = target.drawable.image;
                var rect = target.drawable.rect;

                var lastRect = lastState.rect||[];
                var lastImage = lastState.image||{};

                if(rect && rect.join(",")!= lastRect.join(",")){
                    this.commands.push("setProp", target.fid, "rect", rect.join(","));
                }

                if(image && (image.src != lastImage.src)) {
                    this.commands.push("setImage", target.fid, image.src);
                }

                lastState.rect = rect;
                lastState.image = image;
            }
        }
    },

    /**
     * @private
     * @see Renderer#remove
     */
    remove: function(target){
        var parent = target.parent;
        if(parent){
            this.commands.push("removeChild", target.parent.fid, target.fid);
            if(target._lastState){
                target._lastState.parent = null;
            }
        }
    }
});

})();

/**
 * @class FlashAdaptor
 * @module hilo/flash/FlashAdaptor
 * @requires hilo/core/Hilo
 * @requires hilo/view/Text
 * @requires hilo/view/Graphics
 * @requires hilo/media/WebAudio
 * @requires hilo/media/WebSound
 * @requires hilo/view/Stage
 * @requires hilo/flash/FlashRenderer
*/
var FlashAdaptor = (function(){

var scripts = document.scripts;
var selfScript = scripts[scripts.length - 1];
var scriptDir = selfScript.src.substring(0, selfScript.src.lastIndexOf('/') + 1);
var defaultSwf = scriptDir + 'hilo.swf';

var defaultOption = {
    url: defaultSwf,
    id: "hiloFlash",
    width: "100%",
    height: "100%",
    color: "#ffffff",
    fps: 60
};

var imageCallBacks = {};
var isFlashReady = false;
var flashCommands = []; 

var Adaptor = {
    /**
     * 初始化flash
     * @public
     * @method init
     * @param {Object} option 参数option定义
     *      option.url flash网址
     *      option.fps  flash fps, 默认60
     *      option.forceFlash 强制falsh模式
     *      option.id  flash id,默认 hiloFlash    
     */
    init:function(option){
        option = option || {};
        var that = this;

        if(!Hilo.browser.supportCanvas || option.forceFlash || location.search.indexOf("forceFlash") > -1){
            Hilo.isFlash = true;
            this._addFlashCallback();
            this._flashShim(option);
        }
        else{
            Hilo.View.prototype.release = function(){
                this.removeFromParent();
            };
        }
    },
    setFps:function(fps){
        if(this._fps != fps){
            this._fps = fps;
            flashCommands.push("setFps", fps);
        }
    },
    _flashShim:function(option){
        var that = this;
        option = Hilo.copy(defaultOption, option||{});

        Array.prototype.indexOf = Array.prototype.indexOf||function(a){
            for(var i = 0, l = this.length;i > l;i ++){
                if(this[i] === a){
                    return i;
                }
            }
            return -1;
        };

        Hilo.Stage.prototype._initRenderer = function(properties){
            var canvas = this.canvas;
            if(typeof canvas === 'string') canvas = Hilo.getElement(canvas);

            var container = properties.container;
            if(typeof container === 'string') container = Hilo.getElement(container);
            if(!container) container = document.body;
            
            if(canvas && canvas.parentNode){
                container = container||canvas.parentNode;
                canvas.parentNode.removeChild(canvas);
            }

            this.canvas = container;
            var width = this.width, height = this.height, 
            viewport = this.updateViewport();
            if(!properties.width) width = (viewport && viewport.width) || 320;
            if(!properties.height) height = (viewport && viewport.height) || 480;

            that._insertSwf(Hilo.copy(option, {
                container:container,
                width:width * (this.scaleX||1),
                height:height * (this.scaleY||1)
            }));

            var props = {canvas:container, stage:this, commands:flashCommands};
            this.renderer = new FlashRenderer(props);
        };

        Hilo.Stage.prototype.addTo = function(domElement){
            var swf = this._swf;
            if(swf && swf.parentNode !== domElement){
                domElement.appendChild(swf);
            }
            return this;
        };

        var enableDOMEvent = Hilo.Stage.prototype.enableDOMEvent;
        Hilo.Stage.prototype.enableDOMEvent = function(type, enabled){
            var canvas = this.canvas;
            if(!canvas.addEventListener){
                canvas.addEventListener = function(type, handler){
                    canvas.attachEvent('on' + type, handler);
                };
                canvas.removeEventListener = function(type, handler){
                    canvas.detachEvent('on' + type, handler);
                };
            }

            return enableDOMEvent.call(this, type, enabled);
        };

        var onDOMEvent = Hilo.Stage.prototype._onDOMEvent;
        Hilo.Stage.prototype._onDOMEvent = function(e){
            onDOMEvent.call(this, e || fixEvent());
        };

        Hilo.View.prototype.release = function(){
            this.removeFromParent();
            if(this.fid){
                flashCommands.push("release", this.fid);
            }
        };

        Hilo.Text.prototype.render = function(renderer){
            renderer.draw(this);
        };

        Hilo.Graphics.prototype.render = function(renderer){
            renderer.draw(this);
        };

        var graphicsFuncs = [
            "lineStyle", "beginFill", "endFill",
            "beginBitmapFill", "beginPath", "closePath", "moveTo", "lineTo", "quadraticCurveTo", "bezierCurveTo",
            "drawRect", "drawRoundRectComplex", "drawRoundRect", "drawCircle", "drawEllipse", "cache", "uncache", "clear"
        ];

        //flashGraphicsCommands  command由";"分割 参数由","分割 参数中数组由":"分割
        for(var i = 0;i < graphicsFuncs.length;i ++){
            var funcName = graphicsFuncs[i];
            Hilo.Graphics.prototype[funcName] = function(funcName){
                return function(){
                    var args = Array.prototype.slice.call(arguments);
                    var arr = [funcName].concat(args).join(",");

                    this.flashGraphicsCommands = this.flashGraphicsCommands||[];
                    this.flashGraphicsCommands.push(arr);
                    this.isDirty = true;
                    return this;
                }
            }(funcName);
        }

        Hilo.Graphics.prototype.beginRadialGradientFill = function(x0, y0, r0, x1, y1, r1, colors, ratios){
            var cmd = ["beginRadialGradientFill", x0, y0, r0, x1, y1, r1, colors.join(":"), ratios.join(":")].join(",");
            this.flashGraphicsCommands = this.flashGraphicsCommands||[];
            this.flashGraphicsCommands.push(cmd);
            this.isDirty = true;
            return this;
        };

        Hilo.Graphics.prototype.beginLinearGradientFill = function(x0, y0, x1, y1, colors, ratios){
            var cmd = ["beginLinearGradientFill", x0, y0, x1, y1, colors.join(":"), ratios.join(":")].join(",");
            this.flashGraphicsCommands = this.flashGraphicsCommands||[];
            this.flashGraphicsCommands.push(cmd);
            this.isDirty = true;
            return this;
        };

        Hilo.Graphics.prototype.drawSVGPath = function(pathData){
            var me = this, addAction = me._addAction,
                path = pathData.split(/,| (?=[a-zA-Z])/);
            
            me.beginPath();
            for(var i = 0, len = path.length; i < len; i++){
                var str = path[i], cmd = str.charAt(0).toUpperCase(), p = str.substring(1).split(/,| /);
                if(p[0].length == 0) p.shift();

                switch(cmd){
                    case 'M':
                        me.moveTo(p[0], p[1]);
                        break;
                    case 'L':
                        me.lineTo(p[0], p[1]);
                        break;
                    case 'C':
                        me.bezierCurveTo(p[0], p[1], p[2], p[3], p[4], p[5]);
                        break;
                    case 'Z':
                        me.closePath();
                        break;
                }
            }
            return me;
        };

        Hilo.WebSound.removeAudio = function(source){
            var src = typeof source === 'string' ? source : source.src;
            var audio = this._audios[src];
            if(audio){
                audio.stop();
                audio.off();
                audio.release();
                this._audios[src] = null;
                delete this._audios[src];
            }
        };

        Hilo.WebAudio.isSupported = true;
        Hilo.WebAudio.enabled = true;
        Hilo.WebAudio.enable = function(){};

        Hilo.WebAudio.prototype._init = function(){
            this.fid = Hilo.getUid("audio");
            flashCommands.push("audio", "create", this.fid, this.src);
            if(this.autoPlay){
                this.play();
            }
        };

        Hilo.WebAudio.prototype.load = function(){
            flashCommands.push("audio", "load", this.fid);
            return this;
        };

        Hilo.WebAudio.prototype.play = function(){
            flashCommands.push("audio", "play", this.fid, this.loop?1:0);
            return this;
        };

        Hilo.WebAudio.prototype.pause = function(){
            flashCommands.push("audio", "pause", this.fid);
            return this;
        };

        Hilo.WebAudio.prototype.resume = function(){
            flashCommands.push("audio", "resume", this.fid);
            return this;
        };

        Hilo.WebAudio.prototype.stop = function(){
            flashCommands.push("audio", "stop", this.fid);
            return this;
        };
        
        Hilo.WebAudio.prototype.setVolume = function(volume){
            flashCommands.push("audio", "setVolume", this.fid, volume);
            return this;
        };

        Hilo.WebAudio.prototype.setMute = function(muted){
            flashCommands.push("audio", "setMute", this.fid, muted?1:0);
            return this;
        };

        Hilo.WebAudio.prototype.release = function(){
            flashCommands.push("audio", "release", this.fid);
            return this;
        };
    },
    _insertSwf:function(option){
        var that = this;
        var swf;
        var src = option.url;
        var id = option.id;
        var color = option.color||null;
        var fps = option.fps;
        var container = option.container;
        var width = option.width;
        var height = option.height;
        
        this.setFps(fps);

        if(window.attachEvent){
            var hasHTML = container.innerHTML;
            container.innerHTML = 
            '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"' +
            ' codebase="' + location.protocol + '//fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0">' +
            '<param name="allowScriptAccess" value="always">' +
            '<param name="flashvars" value="id=' + id + '">' +
            '<param name="wmode" value="transparent">' +
            '<param name="bgcolor" value="' + color + '">' +
            '</object>' + hasHTML;
            var swf = container.getElementsByTagName("object")[0];
            swf["movie"] = src;
        }
        else{
            swf = document.createElement("embed");
            swf.setAttribute("src",src);
            swf.setAttribute("type","application/x-shockwave-flash");
            swf.setAttribute("allowScriptAccess","always");
            swf.setAttribute("allowFullScreen","true");
            swf.setAttribute("bgcolor",color);
            swf.setAttribute("pluginspage","http://www.adobe.com/go/getflashplayer_cn");
            swf.setAttribute("wmode", "transparent");
            swf.setAttribute("FlashVars", "debug=0");
            container.appendChild(swf);
        }
        swf.name = id;
        swf.width = width;
        swf.height = height;
        swf.id = id;
            
        this._swf = swf;
        setInterval(function(){
            that.tick();
        }, 1000/fps)
        return swf;
    },
    tick:function(){
        if(isFlashReady && flashCommands.length > 0){
            this._swf.CallFunction(
                '<invoke name="executeCommand" returntype="javascript"><arguments><string>'
                + flashCommands.join("√") + '</string></arguments></invoke>'
            );
            // console.log("executeCommand", flashCommands.join(","));
            flashCommands.length = 0;
        }
    },
    _addFlashCallback:function(){
        /*
         * 加载flash图片
         * @method loadFlashImage
        */
        Hilo.loadFlashImage = function(src, successHandler, errorHandler){
            imageCallBacks[src] = imageCallBacks[src]||[];
            imageCallBacks[src].push([successHandler, errorHandler||successHandler]);
            flashCommands.push("loadImage", src);
        };

        /*
         *flash 可以调接口时回调函数
        */
        Hilo.unlock = function(){
            isFlashReady = true;
        };

        /*
         * falsh图片加载完回调函数
         * @argument src:图片地址
         * @argument errorCode: 0:图片加载成功, 1:图片加载失败
         * @argument width:图片宽
         * argument height:图片高
        */
        Hilo.imageCallBack = function(src, errorCode, width, height){
            // console.log("imageCallBack", src, errorCode);
            var arr = imageCallBacks[src];
            if(arr && arr.length > 0){
                for(var i = 0, l = arr.length;i < l;i ++){
                    arr[i][errorCode]({
                        target:{
                            src:src, 
                            width:width, 
                            height:height,
                            isFlash:true
                        },
                        errorCode:errorCode
                    });
                }
                arr.length = 0;
            }
        }
    }
};

function fixEvent(){
    var event = window.event; 
    var e = {
        rawEvent:event,
        type:event.type,
        target:event.srcElememt,
        preventDefault:function(){
            event.returnValue = false;
        },
        stopPropagation:function(){
            event.cancelBubble = true;
        }
    };
    
    if(event.type.indexOf("mouse") != -1){
        e.clientX = event.clientX;
        e.clientY = event.clientY;
        if(event.type == "mouseover") 
            e.relatedTarget = event.fromElement; 
        else if(event.type == "mouseout") 
            e.relatedTarget = event.toElement; 
    }
    else if(event.type.indexOf("key") != -1){
        e.charCode = e.keyCode = event.keyCode; 
    }
    return e; 
}

if(selfScript.getAttribute('data-auto') === 'true') Adaptor.init();
return Hilo.FlashAdaptor = Adaptor;

})();

})();

================================================
FILE: hilo/hilo-standalone.js
================================================
/**
 * Hilo 1.1.7 for standalone
 * Copyright 2016 alibaba.com
 * Licensed under the MIT License
 */
!function(t){t.Hilo||(t.Hilo={});var e=function(){var e=navigator.userAgent,i=document,n=t,r=i.documentElement,a={iphone:/iphone/i.test(e),ipad:/ipad/i.test(e),ipod:/ipod/i.test(e),ios:/iphone|ipad|ipod/i.test(e),android:/android/i.test(e),webkit:/webkit/i.test(e),chrome:/chrome/i.test(e),safari:/safari/i.test(e),firefox:/firefox/i.test(e),ie:/msie/i.test(e),opera:/opera/i.test(e),supportTouch:"ontouchstart"in n,supportCanvas:null!=i.createElement("canvas").getContext,supportStorage:!1,supportOrientation:"orientation"in n||"orientation"in n.screen,supportDeviceMotion:"ondevicemotion"in n};try{var o="hilo";localStorage.setItem(o,o),localStorage.removeItem(o),a.supportStorage=!0}catch(s){}var l=a.jsVendor=a.webkit?"webkit":a.firefox?"webkit":a.opera?"o":a.ie?"ms":"",h=a.cssVendor="-"+l+"-",c=i.createElement("div"),u=c.style,d=void 0!=u[l+"Transform"],f=void 0!=u[l+"Perspective"];return f&&(c.id="test3d",u=i.createElement("style"),u.textContent="@media ("+h+"transform-3d){#test3d{height:3px}}",i.head.appendChild(u),r.appendChild(c),f=3==c.offsetHeight,i.head.removeChild(u),r.removeChild(c)),a.supportTransform=d,a.supportTransform3D=f,a}();t.Hilo.browser=e}(window),function(t){t.Hilo||(t.Hilo={});var e={copy:function(t,e,i){for(var n in e)i&&!t.hasOwnProperty(n)&&void 0===t[n]||(t[n]=e[n]);return t}};t.Hilo.util=e}(window),function(t){t.Hilo||(t.Hilo={});var e=t.Hilo.browser,i=t.Hilo.util,n=t,r=document,a=r.documentElement,o=0,s={},l={version:"1.1.7",getUid:function(t){var e=++o;if(t){var i=t.charCodeAt(t.length-1);return i>=48&&i<=57&&(t+="_"),t+e}return e},viewToString:function(t){for(var e,i=t;i;)e=e?i.id+"."+e:i.id,i=i.parent;return e},copy:function(t,e,n){return i.copy(t,e,n),s.copy||(s.copy=!0,console.warn("Hilo.copy has been Deprecated! Use Hilo.util.copy instead.")),t},browser:e,event:function(){var t="ontouchstart"in n;return{POINTER_START:t?"touchstart":"mousedown",POINTER_MOVE:t?"touchmove":"mousemove",POINTER_END:t?"touchend":"mouseup"}}(),align:{TOP_LEFT:"TL",TOP:"T",TOP_RIGHT:"TR",LEFT:"L",CENTER:"C",RIGHT:"R",BOTTOM_LEFT:"BL",BOTTOM:"B",BOTTOM_RIGHT:"BR"},getElementRect:function(t){var e;try{e=t.getBoundingClientRect()}catch(i){e={top:t.offsetTop,left:t.offsetLeft,right:t.offsetLeft+t.offsetWidth,bottom:t.offsetTop+t.offsetHeight}}var r=(n.pageXOffset||a.scrollLeft)-(a.clientLeft||0)||0,o=(n.pageYOffset||a.scrollTop)-(a.clientTop||0)||0,s=n.getComputedStyle?getComputedStyle(t):t.currentStyle,l=parseInt,h=l(s.paddingLeft)+l(s.borderLeftWidth)||0,c=l(s.paddingTop)+l(s.borderTopWidth)||0,u=l(s.paddingRight)+l(s.borderRightWidth)||0,d=l(s.paddingBottom)+l(s.borderBottomWidth)||0,f=e.top||0,p=e.left||0,v=e.right||0,m=e.bottom||0;return{left:p+r+h,top:f+o+c,width:v-u-p-h,height:m-d-f-c}},createElement:function(t,e){var i,n,a,o=r.createElement(t);for(i in e)if(n=e[i],"style"===i)for(a in n)o.style[a]=n[a];else o[i]=n;return o},getElement:function(t){return r.getElementById(t)},setElementStyleByView:function(t){var e=t.drawable,i=e.domElement.style,n=t._stateCache||(t._stateCache={}),r=l.browser.jsVendor,a="px",o=!1;if(this.cacheStateIfChanged(t,["visible"],n)&&(i.display=t.visible?"":"none"),this.cacheStateIfChanged(t,["alpha"],n)&&(i.opacity=t.alpha),t.visible&&!(t.alpha<=0)){this.cacheStateIfChanged(t,["width"],n)&&(i.width=t.width+a),this.cacheStateIfChanged(t,["height"],n)&&(i.height=t.height+a),this.cacheStateIfChanged(t,["depth"],n)&&(i.zIndex=t.depth+1),(o=this.cacheStateIfChanged(t,["pivotX","pivotY"],n))&&(i[r+"TransformOrigin"]=t.pivotX+a+" "+t.pivotY+a),(this.cacheStateIfChanged(t,["x","y","rotation","scaleX","scaleY"],n)||o)&&(i[r+"Transform"]=this.getTransformCSS(t)),this.cacheStateIfChanged(t,["background"],n)&&(i.backgroundColor=t.background),i.pointerEvents||(i.pointerEvents="none");var s=e.image;if(s){var h=s.src;h!==n.image&&(n.image=h,i.backgroundImage="url("+h+")");var c=e.rect;if(c){var u=c[0],d=c[1];u!==n.sx&&(n.sx=u,i.backgroundPositionX=-u+a),d!==n.sy&&(n.sy=d,i.backgroundPositionY=-d+a)}}var f=t.mask;if(f){var p=f.drawable.domElement.style.backgroundImage;p!==n.maskImage&&(n.maskImage=p,i[r+"MaskImage"]=p,i[r+"MaskRepeat"]="no-repeat");var v=f.x,m=f.y;v===n.maskX&&m===n.maskY||(n.maskX=v,n.maskY=m,i[r+"MaskPosition"]=v+a+" "+m+a)}}},cacheStateIfChanged:function(t,e,i){var n,r,a,o,s=!1;for(n=0,r=e.length;n<r;n++)a=e[n],o=t[a],o!=i[a]&&(i[a]=o,s=!0);return s},getTransformCSS:function(t){var e=this.browser.supportTransform3D,i=e?"3d":"";return"translate"+i+"("+(t.x-t.pivotX)+"px, "+(t.y-t.pivotY)+(e?"px, 0px)":"px)")+"rotate"+i+(e?"(0, 0, 1, ":"(")+t.rotation+"deg)scale"+i+"("+t.scaleX+", "+t.scaleY+(e?", 1)":")")}};for(var h in l)t.Hilo[h]=l[h]}(window),function(t){t.Hilo||(t.Hilo={});var e=function(){var t,e,i=function(t){t=t||{};var e=t.hasOwnProperty("constructor")?t.constructor:function(){};return n.call(e,t),e},n=function(t){var e,i,n={};for(e in t)i=t[e],r.hasOwnProperty(e)?r[e].call(this,i):n[e]=i;o(this.prototype,n)},r={Extends:function(t){var e=this.prototype,i=a(t.prototype);o(this,t),o(i,e),i.constructor=this,this.prototype=i,this.superclass=t.prototype},Mixes:function(t){t instanceof Array||(t=[t]);for(var e,i=this.prototype;e=t.shift();)o(i,e.prototype||e)},Statics:function(t){o(this,t)}},a=function(){if(Object.__proto__)return function(t){return{__proto__:t}};var t=function(){};return function(e){return t.prototype=e,new t}}(),o=function(t){for(var i=1,n=arguments.length;i<n;i++){var r,a=arguments[i];for(var o in a){var s=a[o];!s||"object"!=typeof s||void 0===s.value&&"function"!=typeof s.get&&"function"!=typeof s.set?t[o]=s:(r=r||{},r[o]=s)}r&&e(t,r)}return t};try{t=Object.defineProperty,e=Object.defineProperties,t({},"$",{value:0})}catch(s){"__defineGetter__"in Object&&(t=function(t,e,i){return"value"in i&&(t[e]=i.value),"get"in i&&t.__defineGetter__(e,i.get),"set"in i&&t.__defineSetter__(e,i.set),t},e=function(e,i){for(var n in i)i.hasOwnProperty(n)&&t(e,n,i[n]);return e})}return{create:i,mix:o}}();t.Hilo.Class=e}(window),function(t){t.Hilo||(t.Hilo={});var e=t.Hilo.Class,i=e.create({constructor:function(t,e,i,n,r,a){this.a=t,this.b=e,this.c=i,this.d=n,this.tx=r,this.ty=a},concat:function(t){var e,i,n,r,a,o,s=arguments,l=this.a,h=this.b,c=this.c,u=this.d,d=this.tx,f=this.ty;return s.length>=6?(e=s[0],i=s[1],n=s[2],r=s[3],a=s[4],o=s[5]):(e=t.a,i=t.b,n=t.c,r=t.d,a=t.tx,o=t.ty),this.a=l*e+h*n,this.b=l*i+h*r,this.c=c*e+u*n,this.d=c*i+u*r,this.tx=d*e+f*n+a,this.ty=d*i+f*r+o,this},rotate:function(t){var e=Math.sin(t),i=Math.cos(t),n=this.a,r=this.b,a=this.c,o=this.d,s=this.tx,l=this.ty;return this.a=n*i-r*e,this.b=n*e+r*i,this.c=a*i-o*e,this.d=a*e+o*i,this.tx=s*i-l*e,this.ty=s*e+l*i,this},scale:function(t,e){return this.a*=t,this.d*=e,this.c*=t,this.b*=e,this.tx*=t,this.ty*=e,this},translate:function(t,e){return this.tx+=t,this.ty+=e,this},identity:function(){return this.a=this.d=1,this.b=this.c=this.tx=this.ty=0,this},invert:function(){var t=this.a,e=this.b,i=this.c,n=this.d,r=this.tx,a=t*n-e*i;return this.a=n/a,this.b=-e/a,this.c=-i/a,this.d=t/a,this.tx=(i*this.ty-n*r)/a,this.ty=-(t*this.ty-e*r)/a,this},transformPoint:function(t,e,i){var n=t.x*this.a+t.y*this.c+this.tx,r=t.x*this.b+t.y*this.d+this.ty;return e&&(n=n+.5>>0,r=r+.5>>0),i?{x:n,y:r}:(t.x=n,t.y=r,t)}});t.Hilo.Matrix=i}(window),function(t){t.Hilo||(t.Hilo={});var e=t.Hilo.Class,i={_listeners:null,on:function(t,e,i){for(var n=this._listeners=this._listeners||{},r=n[t]=n[t]||[],a=0,o=r.length;a<o;a++){var s=r[a];if(s.listener===e)return}return r.push({listener:e,once:i}),this},off:function(t,e){if(0==arguments.length)return this._listeners=null,this;var i=this._listeners&&this._listeners[t];if(i){if(1==arguments.length)return delete this._listeners[t],this;for(var n=0,r=i.length;n<r;n++){var a=i[n];if(a.listener===e){i.splice(n,1),0===i.length&&delete this._listeners[t];break}}}return this},fire:function(t,e){var i,r;"string"==typeof t?r=t:(i=t,r=t.type);var a=this._listeners;if(!a)return!1;var o=a[r];if(o){var s=o.slice(0);if(i=i||new n(r,this,e),i._stopped)return!1;for(var l=0;l<s.length;l++){var h=s[l];if(h.listener.call(this,i),h.once){var c=o.indexOf(h);c>-1&&o.splice(c,1)}}return 0==o.length&&delete a[r],!0}return!1}},n=e.create({constructor:function(t,e,i){this.type=t,this.target=e,this.detail=i,this.timeStamp=+new Date},type:null,target:null,detail:null,timeStamp:0,stopImmediatePropagation:function(){this._stopped=!0}}),r=t.Event;if(r){var a=r.prototype,o=a.stopImmediatePropagation;a.stopImmediatePropagation=function(){o&&o.call(this),this._stopped=!0}}t.Hilo.EventMixin=i}(window),function(t){t.Hilo||(t.Hilo={});var e=t.Hilo.Class,i=t.Hilo.util,n=e.create({constructor:function(t){this.init(t)},image:null,rect:null,init:function(t){var e=this,r=e.image;n.isDrawable(t)?e.image=t:i.copy(e,t,!0);var a=e.image;if("string"==typeof a){if(!r||a!==r.getAttribute("src")){e.image=null;var o=new Image;return t.crossOrigin&&(o.crossOrigin=t.crossOrigin),o.onload=function(){o.onload=null,e.init(o)},void(o.src=a)}a=e.image=r}a&&!e.rect&&(e.rect=[0,0,a.width,a.height])},Statics:{isDrawable:function(t){if(!t||!t.tagName)return!1;var e=t.tagName.toLowerCase();return"img"===e||"canvas"===e||"video"===e}}});t.Hilo.Drawable=n}(window),function(t){t.Hilo||(t.Hilo={});var e=t.Hilo.Class,i=t.Hilo.util,n=e.create({constructor:function(t){t=t||{},i.copy(this,t,!0)},renderType:null,canvas:null,stage:null,blendMode:"source-over",startDraw:function(t){},draw:function(t){},endDraw:function(t){},transform:function(){},hide:function(){},remove:function(t){},clear:function(t,e,i,n){},resize:function(t,e){}});t.Hilo.Renderer=n}(window),function(t){t.Hilo||(t.Hilo={});var e=t.Hilo.Class,i=t.Hilo,n=t.Hilo.Renderer,r=e.create({Extends:n,constructor:function(t){r.superclass.constructor.call(this,t),this.context=this.canvas.getContext("2d")},renderType:"canvas",context:null,startDraw:function(t){return!!(t.visible&&t.alpha>0)&&(t===this.stage&&this.context.clearRect(0,0,t.width,t.height),t.blendMode!==this.blendMode&&(this.context.globalCompositeOperation=this.blendMode=t.blendMode),this.context.save(),!0)},draw:function(t){var e=this.context,i=t.width,n=t.height,r=t.background;r&&(e.fillStyle=r,e.fillRect(0,0,i,n));var a=t.drawable,o=a&&a.image;if(o){var s=a.rect,l=s[2],h=s[3],c=s[4],u=s[5];if(!l||!h)return;i||n||(i=t.width=l,n=t.height=h),(c||u)&&e.translate(c-.5*l,u-.5*h),e.drawImage(o,s[0],s[1],l,h,0,0,i,n)}},endDraw:function(t){this.context.restore()},transform:function(t){var e=t.drawable;if(e&&e.domElement)return void i.setElementStyleByView(t);var n=this.context,r=t.scaleX,a=t.scaleY;if(t===this.stage){var o=this.canvas.style,s=t._scaleX,l=t._scaleY,h=!1;(!s&&1!=r||s&&s!=r)&&(t._scaleX=r,o.width=r*t.width+"px",h=!0),(!l&&1!=a||l&&l!=a)&&(t._scaleY=a,o.height=a*t.height+"px",h=!0),h&&t.updateViewport()}else{var c=t.x,u=t.y,d=t.pivotX,f=t.pivotY,p=t.rotation%360,v=t.mask;v&&(v._render(this),n.clip());var m=t.align;if(m){var g=t.getAlignPosition();c=g.x,u=g.y}0==c&&0==u||n.translate(c,u),0!=p&&n.rotate(p*Math.PI/180),1==r&&1==a||n.scale(r,a),0==d&&0==f||n.translate(-d,-f)}t.alpha>0&&(n.globalAlpha*=t.alpha)},remove:function(t){var e=t.drawable,i=e&&e.domElement;if(i){var n=i.parentNode;n&&n.removeChild(i)}},clear:function(t,e,i,n){this.context.clearRect(t,e,i,n)},resize:function(t,e){var i=this.canvas,n=this.stage,r=i.style;i.width=t,i.height=e,r.width=n.width*n.scaleX+"px",r.height=n.height*n.scaleY+"px"}});t.Hilo.CanvasRenderer=r}(window),function(t){t.Hilo||(t.Hilo={});var e=t.Hilo.Class,i=t.Hilo,n=t.Hilo.Renderer,r=t.Hilo.Drawable,a=function(){function t(t,e){var n=t.tagName||"div",r=e.image,a=t.width||r&&r.width,o=t.height||r&&r.height,s=i.createElement(n),l=s.style;if(t.id&&(s.id=t.id),l.position="absolute",l.left=(t.left||0)+"px",l.top=(t.top||0)+"px",l.width=a+"px",l.height=o+"px","canvas"==n){if(s.width=a,s.height=o,r){var h=s.getContext("2d"),c=e.rect||[0,0,a,o];h.drawImage(r,c[0],c[1],c[2],c[3],t.x||0,t.y||0,t.width||c[2],t.height||c[3])}}else if(l.opacity=void 0!=t.alpha?t.alpha:1,(t===this.stage||t.clipChildren)&&(l.overflow="hidden"),r&&r.src){l.backgroundImage="url("+r.src+")";var u=t.rectX||0,d=t.rectY||0;l.backgroundPosition=-u+"px "+-d+"px"}return s}return e.create({Extends:n,constructor:function(t){a.superclass.constructor.call(this,t)},renderType:"dom",startDraw:function(e){var i=e.drawable=e.drawable||new r;return i.domElement=i.domElement||t(e,i),!0},draw:function(t){var e=t.parent,i=t.drawable.domElement,n=i.parentNode;if(e){var r=e.drawable.domElement;if(r!=n&&r.appendChild(i),!t.width&&!t.height){var a=t.drawable.rect;a&&(a[2]||a[3])&&(t.width=a[2],t.height=a[3])}}else t!==this.stage||n||(i.style.overflow="hidden",this.canvas.appendChild(i))},transform:function(t){if(i.setElementStyleByView(t),t===this.stage){var e=this.canvas.style,n=t._scaleX,r=t._scaleY,a=t.scaleX,o=t.scaleY;(!n&&1!=a||n&&n!=a)&&(t._scaleX=a,e.width=a*t.width+"px"),(!r&&1!=o||r&&r!=o)&&(t._scaleY=o,e.height=o*t.height+"px")}},remove:function(t){var e=t.drawable,i=e&&e.domElement;if(i){var n=i.parentNode;n&&n.removeChild(i)}},hide:function(t){var e=t.drawable&&t.drawable.domElement;e&&(e.style.display="none")},resize:function(t,e){var i=this.canvas.style;i.width=t+"px",i.height=e+"px","absolute"!=i.position&&(i.position="relative")}})}();t.Hilo.DOMRenderer=a}(window),function(t){t.Hilo||(t.Hilo={});var e=t.Hilo.Class,i=t.Hilo,n=t.Hilo.Renderer,r=t.Hilo.Matrix,a=Math.PI/180,o=e.create({Extends:n,Statics:{MAX_BATCH_NUM:2e3,ATTRIBUTE_NUM:5,isSupport:function(){if(void 0==this._isSupported){var t=document.createElement("canvas");t.getContext&&(t.getContext("webgl")||t.getContext("experimental-webgl"))?this._isSupported=!0:this._isSupported=!1}return this._isSupported}},renderType:"webgl",gl:null,_isContextLost:!1,_cacheTexture:{},constructor:function(t){o.superclass.constructor.call(this,t);var e=this;this.gl=this.canvas.getContext("webgl")||this.canvas.getContext("experimental-webgl"),this.maxBatchNum=o.MAX_BATCH_NUM,this.positionStride=4*o.ATTRIBUTE_NUM;var i=this.maxBatchNum*o.ATTRIBUTE_NUM*4,n=6*this.maxBatchNum;this.arrayBuffer=new ArrayBuffer(4*i),this.float32Array=new Float32Array(this.arrayBuffer),this.uint32Array=new Uint32Array(this.arrayBuffer),this.indexs=new Uint16Array(n);for(var r=0,a=0;r<n;r+=6,a+=4)this.indexs[r+0]=a+0,this.indexs[r+1]=a+1,this.indexs[r+2]=a+2,this.indexs[r+3]=a+1,this.indexs[r+4]=a+2,this.indexs[r+5]=a+3;this.batchIndex=0,this.sprites=[],this.canvas.addEventListener("webglcontextlost",function(t){e._isContextLost=!0,t.preventDefault()},!1),this.canvas.addEventListener("webglcontextrestored",function(t){e._isContextLost=!1,e.setupWebGLStateAndResource()},!1),this.setupWebGLStateAndResource()},setupWebGLStateAndResource:function(){var t=this.gl;t.blendFunc(t.ONE,t.ONE_MINUS_SRC_ALPHA),t.clearColor(0,0,0,0),t.disable(t.DEPTH_TEST),t.disable(t.CULL_FACE),t.enable(t.BLEND),this._cacheTexture={},this._initShaders(),this.defaultShader.active(),this.positionBuffer=t.createBuffer(),this.indexBuffer=t.createBuffer(),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),t.bufferData(t.ELEMENT_ARRAY_BUFFER,this.indexs,t.STATIC_DRAW),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.bufferData(t.ARRAY_BUFFER,this.arrayBuffer,t.DYNAMIC_DRAW),t.vertexAttribPointer(this.a_position,2,t.FLOAT,!1,this.positionStride,0),t.vertexAttribPointer(this.a_TexCoord,2,t.FLOAT,!1,this.positionStride,8),t.vertexAttribPointer(this.a_tint,4,t.UNSIGNED_BYTE,!0,this.positionStride,16)},context:null,startDraw:function(t){return!!(t.visible&&t.alpha>0)&&(t===this.stage&&this.clear(),!0)},draw:function(t){var e=t.width,i=t.height,n=(t.background,t.drawable),r=n&&n.image;if(r){var a=n.rect,o=a[2],s=a[3];e||i||(e=t.width=o,i=t.height=s),this.batchIndex>=this.maxBatchNum&&this._renderBatches();var l=this._createVertexs(r,a[0],a[1],o,s,0,0,e,i),h=this.batchIndex*this.positionStride,c=this.float32Array,u=this.uint32Array,d=(t.tint>>16)+(65280&t.tint)+((255&t.tint)<<16)+(255*t.__webglRenderAlpha<<24);c[h+0]=l[0],c[h+1]=l[1],c[h+2]=l[2],c[h+3]=l[3],u[h+4]=d,c[h+5]=l[4],c[h+6]=l[5],c[h+7]=l[6],c[h+8]=l[7],u[h+9]=d,c[h+10]=l[8],c[h+11]=l[9],c[h+12]=l[10],c[h+13]=l[11],u[h+14]=d,c[h+15]=l[12],c[h+16]=l[13],c[h+17]=l[14],c[h+18]=l[15],u[h+19]=d;for(var f=t.__webglWorldMatrix,p=0;p<4;p++){var v=c[h+5*p],m=c[h+5*p+1];c[h+5*p]=f.a*v+f.c*m+f.tx,c[h+5*p+1]=f.b*v+f.d*m+f.ty}t.__textureImage=r,this.sprites[this.batchIndex++]=t}},endDraw:function(t){t===this.stage&&this._renderBatches()},transform:function(t){var e=t.drawable;if(e&&e.domElement)return void i.setElementStyleByView(t);var n=t.scaleX,a=t.scaleY;if(t===this.stage){var o=this.canvas.style,s=t._scaleX,l=t._scaleY,h=!1;(!s&&1!=n||s&&s!=n)&&(t._scaleX=n,o.width=n*t.width+"px",h=!0),(!l&&1!=a||l&&l!=a)&&(t._scaleY=a,o.height=a*t.height+"px",h=!0),h&&t.updateViewport(),t.__webglWorldMatrix=t.__webglWorldMatrix||new r(1,0,0,1,0,0)}else t.paren
Download .txt
gitextract_p7xxa54u/

├── .gitignore
├── README.md
├── blog/
│   ├── ES6系列——let和const深入理解.md
│   ├── Es2016、2017新特性(上).md
│   ├── Host解析.md
│   ├── JS知识总揽.md
│   ├── JS系列(一).md
│   ├── JS系列(三).md
│   ├── JS系列(二).md
│   ├── JS系列(四).md
│   ├── JavaScript之(a==1 && a==2 && a==3)能输出ture么?.md
│   ├── JavaScript之a==1&&a==2&&a==3能输出ture么?.md
│   ├── []为false,!![]为true,[true] == 'true'为true,傻傻分不清.md
│   ├── ajax简述.md
│   ├── console命令还能这么用.md
│   ├── cookie.md
│   ├── css代码规范.md
│   ├── css层叠关系.md
│   ├── html代码规范.md
│   ├── instance_init.md
│   ├── instanceof你懂吗.md
│   ├── js代码规范.md
│   ├── js的实用小技巧.md
│   ├── keep-alive.md
│   ├── lifecycle.md
│   ├── nodejs几种文件路径及path模块.md
│   ├── node中的EventLoop.md
│   ├── require工作原理.md
│   ├── sticky你了解多少.md
│   ├── this的重新认识.md
│   ├── vdom的vnode.md
│   ├── vue生命周期详解.md
│   ├── webpack中hash和chunkhash是不是很眼熟?.md
│   ├── webpack中的entry和context.md
│   ├── 【译】node js event loop part 1.1.md
│   ├── 【译】nodeJsEventLoopPart1.1.md
│   ├── 一看就懂的JS抽象语法树.md
│   ├── 从babel讲到AST.md
│   ├── 函数.md
│   ├── 前端和后端的发展路径.md
│   ├── 响应式原理.md
│   ├── 定时器和计时器.md
│   ├── 建议使用nvm管理node.md
│   ├── 把session聊清楚.md
│   ├── 模块.md
│   ├── 汇总2017JS项目,总结我们从中学到了什么?.md
│   ├── 浏览器中的eventloop.md
│   ├── 细说Array.prototype.slice.call.md
│   ├── 编写一个分析代码依赖的工具(一).md
│   ├── 编码与解码.md
│   ├── 聊聊数据接口.md
│   └── 🚀述说Parcel:A blazing fast, zero configuration web application bundler 📦.md
├── hilo/
│   ├── flybird/
│   │   ├── index.html
│   │   └── js/
│   │       ├── asset.js
│   │       ├── bird.js
│   │       ├── game.js
│   │       ├── overScene.js
│   │       └── readyScene.js
│   ├── hilo-flash.js
│   └── hilo-standalone.js
├── package.json
└── test/
    ├── es-2016.js
    ├── setTimeout.js
    ├── vdom/
    │   └── vnode.js
    └── websocket/
        ├── CHANGES
        ├── LICENSE
        ├── README.md
        ├── count.sh
        ├── index.html
        ├── node-socket.js
        ├── web-socket.html
        └── websocketd
Download .txt
SYMBOL INDEX (23 symbols across 2 files)

FILE: hilo/hilo-flash.js
  function createFid (line 26) | function createFid(target){
  function fixEvent (line 553) | function fixEvent(){

FILE: hilo/hilo-standalone.js
  function t (line 6) | function t(t,e){var n=t.tagName||"div",r=e.image,a=t.width||r&&r.width,o...
  function t (line 6) | function t(t,e,i){for(var n,r,a,o,s=0,l=!1,h=0,c=i.length;h<c;h++){var u...
  function o (line 6) | function o(t,e){var i=s(t,e,{overlap:-(1/0),normal:{x:0,y:0}});return!!i...
  function s (line 6) | function s(t,e,i){for(var n,r,a,o,s,l,h,c,u,d=t.length,f=e.length,p={x:0...
  function t (line 7) | function t(t){var e,i,n=t.frames;if(!n)return null;var r,a=[];if(n insta...
  function i (line 7) | function i(t,e){var i,a,o=t.sprites;if(!o)return null;var s,l,h,c={};for...
  function n (line 7) | function n(t,e){var i={image:t.image,rect:t.rect};return e&&(i.name=e.na...
  function r (line 7) | function r(t){return"number"==typeof t}
  function e (line 7) | function e(){var a=r.concat(i.call(arguments));return n.apply(this insta...
  function i (line 7) | function i(t){t.stopPropagation(),a(t),l.off(e.event.POINTER_START,i),l....
  function n (line 7) | function n(t){document.removeEventListener(e.event.POINTER_END,n),s&&s.o...
  function r (line 7) | function r(t){a(t);var e=h.x+l.__dragX,i=h.y+l.__dragY;l.x=Math.max(c,Ma...
  function a (line 7) | function a(t){h.preX=h.x,h.preY=h.y,h.x=t.stageX,h.y=t.stageY}
  function o (line 7) | function o(){this._isDragStart=!1,document.removeEventListener(e.event.P...
  function t (line 7) | function t(){return+new Date}
  function t (line 7) | function t(t,e,i,n,r){return t=t||{},e&&(t.EaseIn=e),i&&(t.EaseOut=i),n&...
  function e (line 7) | function e(t){var e,i,n=/\/?[^\/]+\.(\w+)(\?\S+)?$/i;return(e=t.match(n)...
  function t (line 8) | function t(t,e,i,n,r){return{x:t,y:e*n-i*r,z:e*r+i*n}}
  function n (line 8) | function n(t,e,i,n,r){return{x:t*n-i*r,y:e,z:t*r+i*n}}
  function r (line 8) | function r(t,e,i,n,r){return{x:t*n-e*r,y:t*r+e*n,z:i}}
  function t (line 8) | function t(t,e){return e?t+2*(Math.random()-.5)*e:t}
Condensed preview — 72 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (361K chars).
[
  {
    "path": ".gitignore",
    "chars": 64,
    "preview": "image\n\nceshi.js\n\njs知识点1.gliffy\n\nnode_modules/\n\npackage-lock.json"
  },
  {
    "path": "README.md",
    "chars": 3198,
    "preview": "### 博客\n\n##### [所有文章](https://github.com/laihuamin/JS-total/issues)\n\n#### 【ES6】系列\n\n##### [【ES6】变量声明(整理篇)](https://github."
  },
  {
    "path": "blog/ES6系列——let和const深入理解.md",
    "chars": 4441,
    "preview": "### 前言\n\n在ES6中多了两个变量定义的操作符——let和const,在现在项目中,ES6已经是不可获缺,我打算在掘金上整理一套ES6的系列,会收集常用的知识点,喜欢的可以点个喜欢,关注,或者可以去[github](https://gi"
  },
  {
    "path": "blog/Es2016、2017新特性(上).md",
    "chars": 1273,
    "preview": "### 前言\n\nes2015虽然是主流,但是每年都会有新的东西更新,在这些东西中,有许多东西值得我们去学习,以及使用,本篇文章,将提供一些平常业务开发中经常会用到的方法。希望能对大家的学习有帮助。个人的[github博客](https://"
  },
  {
    "path": "blog/Host解析.md",
    "chars": 1614,
    "preview": "## host\n\n### 前言\n\n在实习过程中,这个东西用到的确实多,当初只知道我该怎么去配这个东西,预发调试的时候,这个东西用的很多,等会我会先介绍工具,然后再去了解这个东西的原理,工作中应该都能用的到,我去两家公司实习过,确实都用到了,"
  },
  {
    "path": "blog/JS知识总揽.md",
    "chars": 35,
    "preview": "### JS知识导图\n![](../image/js知识点1.png)"
  },
  {
    "path": "blog/JS系列(一).md",
    "chars": 1615,
    "preview": "> 学前端也快一年了,最近想试试大公司的面试,然后这里把所有的知识点都整理出来,然后慢慢消化。该片总字数:1494,速读3分钟,普通阅读5分钟。有兴趣的可以关注一下我的[blog](https://github.com/laihuamin/"
  },
  {
    "path": "blog/JS系列(三).md",
    "chars": 3080,
    "preview": "> 学前端也快一年了,最近想试试大公司的面试,然后这里把所有的知识点都整理出来,然后慢慢消化。该片总字数:2585,速读四分半,普通阅读7分钟。有兴趣的可以关注一下我的[blog](https://github.com/laihuamin/"
  },
  {
    "path": "blog/JS系列(二).md",
    "chars": 2017,
    "preview": "> 学前端也快一年了,最近想试试大公司的面试,然后这里把所有的知识点都整理出来,然后慢慢消化。该片总字数:1770,速读三分半,普通6分钟。有兴趣的可以关注一下我的[blog](https://github.com/laihuamin/JS"
  },
  {
    "path": "blog/JS系列(四).md",
    "chars": 2166,
    "preview": "> 学前端也快一年了,最近想试试大公司的面试,然后这里把所有的知识点都整理出来,然后慢慢消化。该片总字数:1861,速读三分半钟,普通阅读五分钟。有兴趣的可以关注一下我的[blog](https://github.com/laihuamin"
  },
  {
    "path": "blog/JavaScript之(a==1 && a==2 && a==3)能输出ture么?.md",
    "chars": 2849,
    "preview": "> 如果你能确切的答出可以,那恭喜你,你可以绕道了\n\n### 前言\n有人会说,这个问题好奇葩,放在别的语言里,这要是能输出true,估计是见鬼了,但是你别说,放在js中好真有可能。最近在一个人的推特上提了一个问题:\n\n- 问题:Can (a"
  },
  {
    "path": "blog/JavaScript之a==1&&a==2&&a==3能输出ture么?.md",
    "chars": 2849,
    "preview": "> 如果你能确切的答出可以,那恭喜你,你可以绕道了\n\n### 前言\n有人会说,这个问题好奇葩,放在别的语言里,这要是能输出true,估计是见鬼了,但是你别说,放在js中好真有可能。最近在一个人的推特上提了一个问题:\n\n- 问题:Can (a"
  },
  {
    "path": "blog/[]为false,!![]为true,[true] == 'true'为true,傻傻分不清.md",
    "chars": 3891,
    "preview": "### 先看看官方对于隐式转换的定义\n![abstract-equality.png](https://i.loli.net/2018/08/21/5b7b624412c35.png)\n\n注意图片中的第7条:If Type(y) is Bo"
  },
  {
    "path": "blog/ajax简述.md",
    "chars": 3725,
    "preview": "## 一篇关于ajax的故事\n### 前言\n我为什么要写这个呢,以前面试的时候问过这些,还有就是我个人来看,学习前端其实闭包啊,原型啊,等等的问题,被写烂了,但是关于数据交互这一块的很少,我们在业务中,数据交互用的并不占少数,整理一篇给大家"
  },
  {
    "path": "blog/console命令还能这么用.md",
    "chars": 1988,
    "preview": "### 前言\n这篇文章是网上看到的,自己实验了一遍,把过程和结果都分享给大家,网上的那片我也会注明出处,希望对大家真实过程中有用,喜欢的点个赞,或者给我的[github](https://github.com/laihuamin/JS-to"
  },
  {
    "path": "blog/cookie.md",
    "chars": 3590,
    "preview": "### 前言\n\ncookie在web开发中时常被用到,也是面试官喜欢问的一块技术,很多人或许和我以前一样,只知其一不知其二,谈起web存储,都会答localStorage、sessionStorage、还有就是cookie,然后一些区别啊什"
  },
  {
    "path": "blog/css代码规范.md",
    "chars": 3685,
    "preview": "### 前言\n\n下周是我们公司的代码规范考试,所以我把一些相关的代码规范整理一遍,然后还可以一起看几个例子。\n\n### css代码规范\n\n我司氛围建议和强制两个部分:\n\n### 建议\n\n- css文件使用无BOM的UTF-8编码。\n\n理由:"
  },
  {
    "path": "blog/css层叠关系.md",
    "chars": 121,
    "preview": "## css层叠关系\n\n### 前言\ncss的层叠关系,发现很少人去关注它,我以前看书的时候关注过,但是现在和别人讨论这一块的时候,我们之间观念发生了偏差,我去网上看了很多博文,大部分的论调都是我以前的观念,所以,我要写下这篇文章\n\n###"
  },
  {
    "path": "blog/html代码规范.md",
    "chars": 2181,
    "preview": "### 前言\n\n前两篇书写了css和js的,html其实也有一些规范,我司给出了相应的规范,在规范中,我们和前面一样,将其分为建议和强制\n\n### 建议\n- 每行不得超过 `120` 个字符。\n- `class` 必须代表相应模块或部件的内"
  },
  {
    "path": "blog/instance_init.md",
    "chars": 2143,
    "preview": "## instance中的init.js\n- 其实我们可以从一个小例子来看一下整体\n```js\nnew Vue({\n    el: '#app',\n    data: {\n        a: '1',\n        b: '2'\n   "
  },
  {
    "path": "blog/instanceof你懂吗.md",
    "chars": 2527,
    "preview": "### 简单介绍——instanceof\n> instanceof的发明是为了弥补typeof所带来的缺陷,因为typeof在检测object类型的时候,总是会返回object,所以js提供了另外一个接口来实现对对象类型的判断,那就是我们的"
  },
  {
    "path": "blog/js代码规范.md",
    "chars": 6569,
    "preview": "### 前言\n\n和前面一样,上面一篇是和样式相关的,这一篇是和js相关的,我们也一起来过一遍,然后还可以看几个例子。我们在代码规范中将分为建议和强制两个篇章\n\n### 建议\n- `JavaScript` 文件使用无 `BOM` 的 `UTF"
  },
  {
    "path": "blog/js的实用小技巧.md",
    "chars": 2596,
    "preview": "## 前言\n每一门语言都有一些奇技淫巧,JS也不例外,一直想总结这么篇文章,我包括一些新手,都会有这么一个疑问,每次面对一张空白的页面,不知从何下手,没有思路,高手有的是设计模式,但是在这里讲一些设计模式,我可能不够格,这些书籍都有可以自己"
  },
  {
    "path": "blog/keep-alive.md",
    "chars": 5539,
    "preview": "## 前言\n- vue中有一个内置组件就是keep-alive组件,这个组件自身不会被渲染成一个Dom对象,也不会监听任何事件\n## 流程图\n![](../image/keep-alive.png)\n## keep-alive的实现\n- 既"
  },
  {
    "path": "blog/lifecycle.md",
    "chars": 339,
    "preview": "## lifecycle.js源码\n### 我们先来看一张网上的图\n- [vue生命周期](https://cn.vuejs.org/images/lifecycle.png)\n### 接下来是我画的源码流程图\n\n### 各个击破\n- ac"
  },
  {
    "path": "blog/nodejs几种文件路径及path模块.md",
    "chars": 4968,
    "preview": "### 前言\n\n最近在写一篇weex的webpack配置,刚刚踩坑了,weekpack中会用到path模块,而对于这个模块,我想抽离出来看一下,因为这个用到的还是比较多的,喜欢的朋友可以点个喜欢,或者去我的[github](https://"
  },
  {
    "path": "blog/node中的EventLoop.md",
    "chars": 3705,
    "preview": "关于事件这一块在《深入浅出的nodejs》中很少讲到,书里面只是在第三章提及了4个API方法,比如两个定时器(setTimeout和setInterval),process.nextTick()和setImmediate。\n\n[浏览器中的e"
  },
  {
    "path": "blog/require工作原理.md",
    "chars": 4093,
    "preview": "在平常开发中,当我们需要用一个模块的时候,只需要require一下就行了,但是对于其内部的原理,不一定都清楚。读《深入浅出的nodejs》的时候,我们会发现,书中提到,每一个模块在编译过程中,node都会在模块外面封装一层,(functio"
  },
  {
    "path": "blog/sticky你了解多少.md",
    "chars": 3522,
    "preview": "## sticky你了解多少\n### 前言\n以前呢position只有4种属性,但是到了css3之后呢,又添了这个家伙,刚接触css的时候也不知道,最近业务中碰到的次数越来越多了,就想写一篇自己研究的,让大家参考参考,可能写的不够好,不过,"
  },
  {
    "path": "blog/this的重新认识.md",
    "chars": 5068,
    "preview": "## 原来写的this,最近看书又有了新的认识\n[原来的博客](https://github.com/laihuamin/program-blog/issues/19)\n## 对this的重新认识\n### 默认绑定的情况\n- 在严格模式下,"
  },
  {
    "path": "blog/vdom的vnode.md",
    "chars": 3651,
    "preview": "### 前言\n\n对于vdom而言,更新有3个步骤,一个是create,第二个是diff,第三个是patch。这是他的大格局,但是看这个东西之前,我们可以先来看看vnode,这是虚拟的节点。\n\n### vnode\n\nvnode是一个虚拟的节点"
  },
  {
    "path": "blog/vue生命周期详解.md",
    "chars": 5995,
    "preview": "### 前言\n\n最近在写业务的时候,总是会遇到一些和vue的生命周期相关的问题,比如:\n你用ajax请求数据,然后将数据props到子组件的时候,因为ajax是异步的,然后会发生没有数据。然后查找原因还是自己对这个东西理解不够深入。\n\n##"
  },
  {
    "path": "blog/webpack中hash和chunkhash是不是很眼熟?.md",
    "chars": 4174,
    "preview": "### 前言\n最近在研究webpack的东西,然后也陆陆续续整理了几篇文章,从里面可以学到许多有用的插件和node的一些模块,今天我要介绍的就是hash和chunkhash,这个在webpack打包的过程中经常见到,有兴趣的可以关注一下我的"
  },
  {
    "path": "blog/webpack中的entry和context.md",
    "chars": 3869,
    "preview": "### 前言\n配置webpack的环节中,entry是必不可少的一个环节,我最近做了一篇关于webpack的分享,然后也想做一个关于webpack的一个系列,讲讲我对于webpack的理解,以及我对于我们工程架构的理解。有兴趣的也可以关注一"
  },
  {
    "path": "blog/【译】node js event loop part 1.1.md",
    "chars": 2443,
    "preview": "[原文](https://jsblog.insiderattack.net/event-loop-and-the-big-picture-nodejs-event-loop-part-1-1cb67a182810)\n\n先说1.1总揽:\n- "
  },
  {
    "path": "blog/【译】nodeJsEventLoopPart1.1.md",
    "chars": 2443,
    "preview": "[原文](https://jsblog.insiderattack.net/event-loop-and-the-big-picture-nodejs-event-loop-part-1-1cb67a182810)\n\n先说1.1总揽:\n- "
  },
  {
    "path": "blog/一看就懂的JS抽象语法树.md",
    "chars": 3973,
    "preview": "## 前言\nbabel是现在几乎每个项目中必备的一个东西,但是其工作原理避不开对js的解析在生成的过程,babel有引擎babylon,早期fork了项目acron,了解这个之前我们先来看看这种引擎解析出来是什么东西。不光是babel还有w"
  },
  {
    "path": "blog/从babel讲到AST.md",
    "chars": 9515,
    "preview": "### 前言\n\n最近给团队分享了一篇babel原理,然后我把他整理成一篇blog,本篇总字数6059(含代码),速读3分钟,普通阅读5分钟,有兴趣的可以关注一下我的[github博客](https://github.com/laihuami"
  },
  {
    "path": "blog/函数.md",
    "chars": 2619,
    "preview": "## 函数\n### 函数声明与表达式\n- 函数声明\n> 语法\n```\n// MDN中有说明一个函数最多拥有255个参数\nfunction functionName(arg){\n    //函数体\n}\n```\n> 示例\n```\nfunctio"
  },
  {
    "path": "blog/前端和后端的发展路径.md",
    "chars": 337,
    "preview": "### 前言\n\n看到medium上面的一篇外文,就来转发一下,写的主要是前端和后端改如何发展,感觉路线上还是可以的,只是细化部分做的不够,需要自己学到那一块的时候满满细化。\n\n### Font-end\n\n![1_V7TMAzvhW7_cn9"
  },
  {
    "path": "blog/响应式原理.md",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "blog/定时器和计时器.md",
    "chars": 1984,
    "preview": "## setTimeout和setInterval\n- setTimeout的使用\n```\nsetTimeout(cb, time);\n```\n> setTimeout传入的是两个参数,第一个参数是cb代表的是回调函数callback,第二"
  },
  {
    "path": "blog/建议使用nvm管理node.md",
    "chars": 1011,
    "preview": "### 前言\n\n为什么题目是这个呢,因为自己最近在用npm包的时候被mac的权限恶心到了。\n\n在mac中全局安装npm包,需要用sudo,因为npm包安装的位置在/usr/local/bin中,然后自己在安装weex-toolkit的时候,"
  },
  {
    "path": "blog/把session聊清楚.md",
    "chars": 249,
    "preview": "## 把session聊清楚\n### 前言\n前一篇写了关于[cookie](https://github.com/laihuamin/JS-total/issues/12)的,想必session大家也不陌生,我对这个东西初印象是什么,coo"
  },
  {
    "path": "blog/模块.md",
    "chars": 2769,
    "preview": "# 模块\n## 模块化的好处\n- 可维护性\n> 可维护性显而易见,未模块化之前,你要改代码,可能得改好几处,模块化之后,代码复用了,改一处就可以了\n- 命名空间\n> 什么叫命名空间,简单点讲就是变量容易重名,你叫张三,他也叫张三,这就很尴尬"
  },
  {
    "path": "blog/汇总2017JS项目,总结我们从中学到了什么?.md",
    "chars": 3221,
    "preview": "![](http://laihuamin.oss-cn-beijing.aliyuncs.com/frameTotal.png)\n\n### 当红辣子鸡——vue\n\n和去年一样,vue是js项目中点赞数增加最多的,我们可以看下图:\n\n![]("
  },
  {
    "path": "blog/浏览器中的eventloop.md",
    "chars": 2017,
    "preview": "最近一直在研究event loop相关的,首先我们可以从[HTML standard](https://html.spec.whatwg.org/multipage/webappapis.html#task-queue),标准中对于even"
  },
  {
    "path": "blog/细说Array.prototype.slice.call.md",
    "chars": 1933,
    "preview": "### 前言\n\n总觉得这个方法比较眼熟,但是不知道这个方法是干什么用的,那我们今天就来剖析一下这个方法\n\n### 基本语法的讲解\n\n- Array.prototype.slice()\nArray是一个构造函数,原型中带有的slice方法就是"
  },
  {
    "path": "blog/编写一个分析代码依赖的工具(一).md",
    "chars": 4229,
    "preview": "一个源码中,理不清的依赖是最烦的,让我们继续往下看,如何实现它,__工具的[github](https://github.com/laihuamin/JS-structure)地址__,觉得可以的可以点个star,__[博客](https:"
  },
  {
    "path": "blog/编码与解码.md",
    "chars": 3284,
    "preview": "## 前言\n### 说说我为什么要写关于编码的一篇博文?有两个原因\n1.艺龙的面试面试官问到了,让我知道了你想扩展的你的基础,你不能放弃任意一个知识点\n2.就是今天做业务碰到了这个bug,肯定有人想知道,这个会产生什么bug,bug一般都是"
  },
  {
    "path": "blog/聊聊数据接口.md",
    "chars": 123,
    "preview": "## 聊聊数据接口\n其实我挺想聊聊这方面的东西的,前有ajax今有fetch、axios,其实前端发展的挺迅猛,有时候掌握原理,追本溯源是很重要的,而且,我觉得书写数据接口,其实在前端项目中的比重不可小觑,所以让我们进入接口的世界,讲讲它的"
  },
  {
    "path": "blog/🚀述说Parcel:A blazing fast, zero configuration web application bundler 📦.md",
    "chars": 2050,
    "preview": "> 这篇文章总字数:1214,普通阅读4分钟,速读2分钟,主要讲的是新的打包工具parcel的一些新特性,谢谢,有兴趣朋友可以关注一下我的[github](https://github.com/laihuamin/JS-total)上面有3"
  },
  {
    "path": "hilo/flybird/index.html",
    "chars": 1306,
    "preview": "<!doctype html>\n<head>\n<title>Flappy Bird</title>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"
  },
  {
    "path": "hilo/flybird/js/asset.js",
    "chars": 1832,
    "preview": "// 该模块主要是资源的整合和加载\n(function(game) {\n    var Asset = game.asset = Hilo.Class.create({\n        Mixes: Hilo.EventMixin,\n   "
  },
  {
    "path": "hilo/flybird/js/bird.js",
    "chars": 180,
    "preview": "(function(game) {\n    var Bird = game.Bird = {\n        Extends: Hilo.Sprite,\n        construtor: function() {\n          "
  },
  {
    "path": "hilo/flybird/js/game.js",
    "chars": 2769,
    "preview": "(function() {\n    window.onload = function() {\n        game.init();\n    }\n    var game = window.game = {\n        // 初始化数"
  },
  {
    "path": "hilo/flybird/js/overScene.js",
    "chars": 2058,
    "preview": "(function(game) {\n    var OverScene = game.OverScene = Hilo.Class.create({\n        Extends: Hilo.Container,\n        cons"
  },
  {
    "path": "hilo/flybird/js/readyScene.js",
    "chars": 892,
    "preview": "(function(game) {\n    var ReadyScene = game.ReadyScene = Hilo.Class.create({\n        Extends: Hilo.Container,\n        co"
  },
  {
    "path": "hilo/hilo-flash.js",
    "chars": 18897,
    "preview": ";(function(){\n\n/**\n * @class Flash渲染器。将可视对象以flash方式渲染出来。\n * @augments Renderer\n * @param {Object} properties 创建对象的属性参数。可"
  },
  {
    "path": "hilo/hilo-standalone.js",
    "chars": 70467,
    "preview": "/**\n * Hilo 1.1.7 for standalone\n * Copyright 2016 alibaba.com\n * Licensed under the MIT License\n */\n!function(t){t.Hilo"
  },
  {
    "path": "package.json",
    "chars": 668,
    "preview": "{\n  \"name\": \"JS-total\",\n  \"version\": \"1.0.0\",\n  \"description\": \"#### 备注: ``` 答案有错误的地方可以提交issues,喜欢我写的博文的,欢迎欢迎Star。\",\n  \""
  },
  {
    "path": "test/es-2016.js",
    "chars": 230,
    "preview": "const formatted = [0, 1, 12, 123, 1234, 12345].map(num => num.toString().padStart(10, '0'))\n\n//从前面开始用0把字符串补全到10位\n\nconsol"
  },
  {
    "path": "test/setTimeout.js",
    "chars": 369,
    "preview": "var fs = require('fs');\n\nfs.readFile(__filename, () => {\n  setTimeout(() => {\n    console.log('setTimeout');\n  }, 0);\n  "
  },
  {
    "path": "test/vdom/vnode.js",
    "chars": 495,
    "preview": "var VNode = require(\"vtree/vnode\")\nvar diff = require(\"vtree/diff\")\n\nvar createElement = require(\"vdom/create-element\")\n"
  },
  {
    "path": "test/websocket/CHANGES",
    "chars": 1462,
    "preview": "Version 0.3.0  (??, 2017)\n\n* Migration of underlying websocket server to Gorilla Websocket lib.\n* Binaries build code sw"
  },
  {
    "path": "test/websocket/LICENSE",
    "chars": 1327,
    "preview": "Copyright (c) 2014, Joe Walnes and the websocketd authors.\nAll rights reserved.\n\nRedistribution and use in source and bi"
  },
  {
    "path": "test/websocket/README.md",
    "chars": 4909,
    "preview": "websocketd\n==========\n\n`websocketd` is a small command-line tool that will wrap an existing command-line interface progr"
  },
  {
    "path": "test/websocket/count.sh",
    "chars": 84,
    "preview": "#!/bin/bash\nfor ((COUNT = 1; COUNT <= 10; COUNT++)); do\n  echo $COUNT\n  sleep 1\ndone"
  },
  {
    "path": "test/websocket/index.html",
    "chars": 602,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/websocket/node-socket.js",
    "chars": 460,
    "preview": "const WebSocketServer = require('ws').Server;\nconst wss = new WebSocketServer({port: 8080});\nwss.on('connection', () => "
  },
  {
    "path": "test/websocket/web-socket.html",
    "chars": 578,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, in"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the laihuamin/JS-total GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 72 files (250.9 KB), approximately 97.2k tokens, and a symbol index with 23 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!