-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjin-se-meng-xiang.ghost.2016-09-20.json
1 lines (1 loc) · 650 KB
/
jin-se-meng-xiang.ghost.2016-09-20.json
1
{"db":[{"meta":{"exported_on":1474330741059,"version":"004"},"data":{"posts":[{"id":1,"uuid":"7773c41c-7513-46d6-a5eb-a1256d0f73a2","title":"Welcome to Ghost","slug":"welcome-to-ghost","markdown":"You're live! Nice. We've put together a little post to introduce you to the Ghost editor and get you started. You can manage your content by signing in to the admin area at `<your blog URL>/ghost/`. When you arrive, you can select this post from a list on the left and see a preview of it on the right. Click the little pencil icon at the top of the preview to edit this post and read the next section!\n\n## Getting Started\n\nGhost uses something called Markdown for writing. Essentially, it's a shorthand way to manage your post formatting as you write!\n\nWriting in Markdown is really easy. In the left hand panel of Ghost, you simply write as you normally would. Where appropriate, you can use *shortcuts* to **style** your content. For example, a list:\n\n* Item number one\n* Item number two\n * A nested item\n* A final item\n\nor with numbers!\n\n1. Remember to buy some milk\n2. Drink the milk\n3. Tweet that I remembered to buy the milk, and drank it\n\n### Links\n\nWant to link to a source? No problem. If you paste in a URL, like http://ghost.org - it'll automatically be linked up. But if you want to customise your anchor text, you can do that too! Here's a link to [the Ghost website](http://ghost.org). Neat.\n\n### What about Images?\n\nImages work too! Already know the URL of the image you want to include in your article? Simply paste it in like this to make it show up:\n\n![The Ghost Logo](https://ghost.org/images/ghost.png)\n\nNot sure which image you want to use yet? That's ok too. Leave yourself a descriptive placeholder and keep writing. Come back later and drag and drop the image in to upload:\n\n![A bowl of bananas]\n\n\n### Quoting\n\nSometimes a link isn't enough, you want to quote someone on what they've said. Perhaps you've started using a new blogging platform and feel the sudden urge to share their slogan? A quote might be just the way to do it!\n\n> Ghost - Just a blogging platform\n\n### Working with Code\n\nGot a streak of geek? We've got you covered there, too. You can write inline `<code>` blocks really easily with back ticks. Want to show off something more comprehensive? 4 spaces of indentation gets you there.\n\n .awesome-thing {\n display: block;\n width: 100%;\n }\n\n### Ready for a Break? \n\nThrow 3 or more dashes down on any new line and you've got yourself a fancy new divider. Aw yeah.\n\n---\n\n### Advanced Usage\n\nThere's one fantastic secret about Markdown. If you want, you can write plain old HTML and it'll still work! Very flexible.\n\n<input type=\"text\" placeholder=\"I'm an input field!\" />\n\nThat should be enough to get you started. Have fun - and let us know what you think :)","html":"<p>You're live! Nice. We've put together a little post to introduce you to the Ghost editor and get you started. You can manage your content by signing in to the admin area at <code><your blog URL>/ghost/</code>. When you arrive, you can select this post from a list on the left and see a preview of it on the right. Click the little pencil icon at the top of the preview to edit this post and read the next section!</p>\n\n<h2 id=\"gettingstarted\">Getting Started</h2>\n\n<p>Ghost uses something called Markdown for writing. Essentially, it's a shorthand way to manage your post formatting as you write!</p>\n\n<p>Writing in Markdown is really easy. In the left hand panel of Ghost, you simply write as you normally would. Where appropriate, you can use <em>shortcuts</em> to <strong>style</strong> your content. For example, a list:</p>\n\n<ul>\n<li>Item number one</li>\n<li>Item number two\n<ul><li>A nested item</li></ul></li>\n<li>A final item</li>\n</ul>\n\n<p>or with numbers!</p>\n\n<ol>\n<li>Remember to buy some milk </li>\n<li>Drink the milk </li>\n<li>Tweet that I remembered to buy the milk, and drank it</li>\n</ol>\n\n<h3 id=\"links\">Links</h3>\n\n<p>Want to link to a source? No problem. If you paste in a URL, like <a href=\"http://ghost.org\">http://ghost.org</a> - it'll automatically be linked up. But if you want to customise your anchor text, you can do that too! Here's a link to <a href=\"http://ghost.org\">the Ghost website</a>. Neat.</p>\n\n<h3 id=\"whataboutimages\">What about Images?</h3>\n\n<p>Images work too! Already know the URL of the image you want to include in your article? Simply paste it in like this to make it show up:</p>\n\n<p><img src=\"https://ghost.org/images/ghost.png\" alt=\"The Ghost Logo\" /></p>\n\n<p>Not sure which image you want to use yet? That's ok too. Leave yourself a descriptive placeholder and keep writing. Come back later and drag and drop the image in to upload:</p>\n\n<h3 id=\"quoting\">Quoting</h3>\n\n<p>Sometimes a link isn't enough, you want to quote someone on what they've said. Perhaps you've started using a new blogging platform and feel the sudden urge to share their slogan? A quote might be just the way to do it!</p>\n\n<blockquote>\n <p>Ghost - Just a blogging platform</p>\n</blockquote>\n\n<h3 id=\"workingwithcode\">Working with Code</h3>\n\n<p>Got a streak of geek? We've got you covered there, too. You can write inline <code><code></code> blocks really easily with back ticks. Want to show off something more comprehensive? 4 spaces of indentation gets you there.</p>\n\n<pre><code>.awesome-thing {\n display: block;\n width: 100%;\n}\n</code></pre>\n\n<h3 id=\"readyforabreak\">Ready for a Break?</h3>\n\n<p>Throw 3 or more dashes down on any new line and you've got yourself a fancy new divider. Aw yeah.</p>\n\n<hr />\n\n<h3 id=\"advancedusage\">Advanced Usage</h3>\n\n<p>There's one fantastic secret about Markdown. If you want, you can write plain old HTML and it'll still work! Very flexible.</p>\n\n<p><input type=\"text\" placeholder=\"I'm an input field!\" /></p>\n\n<p>That should be enough to get you started. Have fun - and let us know what you think :)</p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1463567128012,"created_by":1,"updated_at":1463567128012,"updated_by":1,"published_at":1463567128044,"published_by":1},{"id":2,"uuid":"9a9d44c4-cfa6-45eb-85aa-0ad8e81d4389","title":"redux源码分析系列之compose.js","slug":"compose-js","markdown":"## compose.js\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=27566686&auto=1&height=66\"></iframe>\n\n听宝宝的话要早点睡,所以就先写最简单的一个哈😳!废话不多说先贴源码:\n\n```\nexport default function compose(...funcs) {\n if (funcs.length === 0) {\n return arg => arg\n } else {\n const last = funcs[funcs.length - 1]\n const rest = funcs.slice(0, -1)\n return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))\n }\n}\n```\n\n先来看看代码的意思,如果没有传入函数,则返回了一个接受参数并原样返回的函数;如果传入的函数数组长度大于0,则取出右边的函数为`last`,其余的为`rest`,然后返回一个函数。这个函数用`last(...args)`为高阶函数`reduceRight`迭代的初始值进行迭代,每次迭代的结果将被传入下一个函数充当参数。\n> 注:`reduceRight`为es5引入的方法,使用形式如下Array.reduceRight((prev,current,index,array) => (...), initialValue),它会为数组中的元素从右向左执行回调方法。\n\n举例来说,有三个函数`a,b,c`,参数为args,`f=compose(a,b,c)`,`f(...args)`执行的效果就和`a(b(c(...args)))`一样,目的就是将多个函数组合起来。在redux中像`middleware`、`reducer`等多处使用到了compose。不早了,今天先到这哈😀!\n\n","html":"<h2 id=\"composejs\">compose.js</h2>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=27566686&auto=1&height=66\"></iframe>\n\n<p>听宝宝的话要早点睡,所以就先写最简单的一个哈😳!废话不多说先贴源码:</p>\n\n<pre><code>export default function compose(...funcs) { \n if (funcs.length === 0) {\n return arg => arg\n } else {\n const last = funcs[funcs.length - 1]\n const rest = funcs.slice(0, -1)\n return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))\n }\n}\n</code></pre>\n\n<p>先来看看代码的意思,如果没有传入函数,则返回了一个接受参数并原样返回的函数;如果传入的函数数组长度大于0,则取出右边的函数为<code>last</code>,其余的为<code>rest</code>,然后返回一个函数。这个函数用<code>last(...args)</code>为高阶函数<code>reduceRight</code>迭代的初始值进行迭代,每次迭代的结果将被传入下一个函数充当参数。</p>\n\n<blockquote>\n <p>注:<code>reduceRight</code>为es5引入的方法,使用形式如下Array.reduceRight((prev,current,index,array) => (...), initialValue),它会为数组中的元素从右向左执行回调方法。</p>\n</blockquote>\n\n<p>举例来说,有三个函数<code>a,b,c</code>,参数为args,<code>f=compose(a,b,c)</code>,<code>f(...args)</code>执行的效果就和<code>a(b(c(...args)))</code>一样,目的就是将多个函数组合起来。在redux中像<code>middleware</code>、<code>reducer</code>等多处使用到了compose。不早了,今天先到这哈😀!</p>","image":"/content/images/2016/05/687474703a2f2f692e696d6775722e636f6d2f4a65567164514d2e706e67-2.png","featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1463567445619,"created_by":1,"updated_at":1473918309062,"updated_by":1,"published_at":1463755965662,"published_by":1},{"id":3,"uuid":"4cfddac0-e27b-478d-a53e-21edf2583e73","title":"mac木有随意行的解决方案——实践篇","slug":"macmu-you-sui-yi-xing-de-jie-jue-fang-an-shi-jian-pian","markdown":"由于把旧的电脑带回家了,宝宝再也不能够在床上玩电脑了,可恶的是还要配置mac的网络。万恶的移动没有提供mac版的随意行,因此需要自己设置vpn,可怕!宝宝慌了,还好凭借宝宝的聪明脑瓜,把网络配置好了,先拿出来分享。至于其中原理,宝宝还要研究研究,下次分享哈!\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=26590186&auto=1&height=66\"></iframe>\n\n#### 第一步 买一个端口转换器\n\n> 注:附上我的购买链接[点我点我。](https://detail.tmall.com/item.htm?_u=l22095huca16&id=524667361946)不过这个上网网速还不如实验室2M小水管,建议大家上京东买正版,可恶的奎君周不早说😒!\n\n#### 第二步 连接网线、端口转换器、usb端口\n\n> 此时会自动产生如下图选中的Apple USB Ethernet Adapter。\n\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f776b73grwj31140vadl6.jpg)\n\n#### 第三步 选择此处的创建PPPoE服务\n\n![](https://ws1.sinaimg.cn/large/006bH5BKgw1f776e04d0qj31140vatf6.jpg)\n\n#### 第四步 设置PPPoE服务相关参数,也就是宽带连接的用户名密码\n\n![](https://ws1.sinaimg.cn/large/006bH5BKgw1f776bw1r5qj31140va44m.jpg)\n\n#### 第五步 创建vpn,选择IPSec上的L2TP\n\n![](https://ws3.sinaimg.cn/large/006bH5BKgw1f776eeuu5qj31140va44u.jpg)\n\n### 第六步 设置vpn相关参数,服务器地址和账户名,密码,并把高级设置里的通过VPN发送所有流量钩上\n\n![](https://ws4.sinaimg.cn/large/006bH5BKgw1f776eulgsgj31140va0yw.jpg)\n\n### 第七步 增加 /etc/ppp/options 配置文件,内容如下图\n\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f776fek55yj30vo0p2mz7.jpg)\n\n![](https://ws3.sinaimg.cn/large/006bH5BKgw1f776fqp7x6j30vo0p275d.jpg)\n","html":"<p>由于把旧的电脑带回家了,宝宝再也不能够在床上玩电脑了,可恶的是还要配置mac的网络。万恶的移动没有提供mac版的随意行,因此需要自己设置vpn,可怕!宝宝慌了,还好凭借宝宝的聪明脑瓜,把网络配置好了,先拿出来分享。至于其中原理,宝宝还要研究研究,下次分享哈!</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=26590186&auto=1&height=66\"></iframe>\n\n<h4 id=\"\">第一步 买一个端口转换器</h4>\n\n<blockquote>\n <p>注:附上我的购买链接<a href=\"https://detail.tmall.com/item.htm?_u=l22095huca16&id=524667361946\">点我点我。</a>不过这个上网网速还不如实验室2M小水管,建议大家上京东买正版,可恶的奎君周不早说😒!</p>\n</blockquote>\n\n<h4 id=\"usb\">第二步 连接网线、端口转换器、usb端口</h4>\n\n<blockquote>\n <p>此时会自动产生如下图选中的Apple USB Ethernet Adapter。</p>\n</blockquote>\n\n<p><img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f776b73grwj31140vadl6.jpg\" alt=\"\" /></p>\n\n<h4 id=\"pppoe\">第三步 选择此处的创建PPPoE服务</h4>\n\n<p><img src=\"https://ws1.sinaimg.cn/large/006bH5BKgw1f776e04d0qj31140vatf6.jpg\" alt=\"\" /></p>\n\n<h4 id=\"pppoe\">第四步 设置PPPoE服务相关参数,也就是宽带连接的用户名密码</h4>\n\n<p><img src=\"https://ws1.sinaimg.cn/large/006bH5BKgw1f776bw1r5qj31140va44m.jpg\" alt=\"\" /></p>\n\n<h4 id=\"vpnipsecl2tp\">第五步 创建vpn,选择IPSec上的L2TP</h4>\n\n<p><img src=\"https://ws3.sinaimg.cn/large/006bH5BKgw1f776eeuu5qj31140va44u.jpg\" alt=\"\" /></p>\n\n<h3 id=\"vpnvpn\">第六步 设置vpn相关参数,服务器地址和账户名,密码,并把高级设置里的通过VPN发送所有流量钩上</h3>\n\n<p><img src=\"https://ws4.sinaimg.cn/large/006bH5BKgw1f776eulgsgj31140va0yw.jpg\" alt=\"\" /></p>\n\n<h3 id=\"etcpppoptions\">第七步 增加 /etc/ppp/options 配置文件,内容如下图</h3>\n\n<p><img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f776fek55yj30vo0p2mz7.jpg\" alt=\"\" /></p>\n\n<p><img src=\"https://ws3.sinaimg.cn/large/006bH5BKgw1f776fqp7x6j30vo0p275d.jpg\" alt=\"\" /></p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1463834942922,"created_by":1,"updated_at":1473918298141,"updated_by":1,"published_at":1463837040160,"published_by":1},{"id":4,"uuid":"52d5cb6e-e34b-46a8-a91f-adda39d7e944","title":"mac安装mongodb","slug":"macan-zhuang-mongodb","markdown":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=41665696&auto=1&height=66\"></iframe>\n\n#### 安装Homebrew\n Homebrew是Mac OSX下一个包依赖管理工具,用它来安装软件非常的方便只需要brew install 软件名这一条命令就可以将你所需要的软件安装好,不用再操心安装过程中软件的依赖问题,这些问题Homebrew统统帮你搞定。Homebrew安装方法也很简单:打开终端,然后输入命令\n \n<!--bash-->\n\truby -e \"$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)\"\n\n#### 安装mongodb\n 如果是之前就已经安装好的Homebrew,那先要更新一下库:\n \n<!--bash-->\n\tbrew update\n 然后执行下面命令安装mongodb:\n \n<!--bash-->\n\tbrew install mongodb\n --with-openssl参数表示通过ssl源安装,--devel表示安装最新开发版。此时通过mongod --config /usr/local/etc/mongodb.conf命令已经可以启动。(3.0.7版本是如此。)\n\n#### 遇到的几个问题\n1.\t我第一次安装完以后,装了Robomongo,但是在连接时发现了bug,一直Authorization skip by you的错误。然后改dbpath、logpath等等操作,都不奏效,就把mongodb玩坏了。最后google得到Robomongo还不支持mongodb3,解决方案在此[http://http://liyanjie918.blog.163.com/blog/static/2022729020156261410274/](http://http://liyanjie918.blog.163.com/blog/static/2022729020156261410274/ \"使用Robomongo 连接MongoDB 3.x 报 Authorization failed 解决办法\")\n2.\t可能还会遇到找不到/data/db路径,这是mongdb在之前版本时默认的dbpath,如果安装的是2.*的版本可能就会遇到这个问题,就需要:mkdir -p /data/db\n另外,最后还会有一个权限问题,用sudo chmod R 当前用户名 /data 命令能够解决。\n3.\t大致问题如上,出现这些问题主要还是对mac系统文件和权限等不熟,已经太久没有动mongo,所以也有点生疏了。","html":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=41665696&auto=1&height=66\"></iframe>\n\n<h4 id=\"homebrew\">安装Homebrew</h4>\n\n Homebrew是Mac OSX下一个包依赖管理工具,用它来安装软件非常的方便只需要brew install 软件名这一条命令就可以将你所需要的软件安装好,不用再操心安装过程中软件的依赖问题,这些问题Homebrew统统帮你搞定。Homebrew安装方法也很简单:打开终端,然后输入命令\n \n<!--bash--> \n\n<pre><code>ruby -e \"$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)\"\n</code></pre>\n\n<h4 id=\"mongodb\">安装mongodb</h4>\n\n 如果是之前就已经安装好的Homebrew,那先要更新一下库:\n \n<!--bash--> \n\n<pre><code>brew update\n</code></pre>\n\n 然后执行下面命令安装mongodb:\n \n<!--bash--> \n\n<pre><code>brew install mongodb\n</code></pre>\n\n<p> --with-openssl参数表示通过ssl源安装,--devel表示安装最新开发版。此时通过mongod --config /usr/local/etc/mongodb.conf命令已经可以启动。(3.0.7版本是如此。)</p>\n\n<h4 id=\"\">遇到的几个问题</h4>\n\n<ol>\n<li>我第一次安装完以后,装了Robomongo,但是在连接时发现了bug,一直Authorization skip by you的错误。然后改dbpath、logpath等等操作,都不奏效,就把mongodb玩坏了。最后google得到Robomongo还不支持mongodb3,解决方案在此<a href=\"http://http://liyanjie918.blog.163.com/blog/static/2022729020156261410274/\" title=\"使用Robomongo 连接MongoDB 3.x 报 Authorization failed 解决办法\">http://http://liyanjie918.blog.163.com/blog/static/2022729020156261410274/</a> </li>\n<li>可能还会遇到找不到/data/db路径,这是mongdb在之前版本时默认的dbpath,如果安装的是2.*的版本可能就会遇到这个问题,就需要:mkdir -p /data/db <br />\n另外,最后还会有一个权限问题,用sudo chmod R 当前用户名 /data 命令能够解决。</li>\n<li>大致问题如上,出现这些问题主要还是对mac系统文件和权限等不熟,已经太久没有动mongo,所以也有点生疏了。</li>\n</ol>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1463838003388,"created_by":1,"updated_at":1473918271679,"updated_by":1,"published_at":1463838208563,"published_by":1},{"id":5,"uuid":"2b51ebd5-cda4-4792-b84e-5874f72d42a2","title":"koa1源码分析之Application","slug":"koa1yuan-ma-fen-xi-zhi-application","markdown":"##Application\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=32507038&auto=1&height=66\"></iframe>\n\n原来写的文章,迁移过来,还是koa1的,koa2也出了,待更新。\n\n###依赖\n\n- debug:debug源码分析;\n- compose_es7:与co.wrap相似,不过能够接受 async/await 函数;\n- onFinished:捕捉finish或error事件,并根据第二个参数执行回调;\n- compose:compose_es7的简化版,只接受generator;\n- isJSON:顾名思义,判断传入参数是否为JSON;\n- statuses:将http码分为三类,redirect、empty以及retry,然后根据传入的http来判断属于哪一类;\n- accepts:根据req的内容判断(content negotiation)字段,如accept,Accept-Encoding,Accept-Language等字段对这些字段进行格式化,方便后续处理;\n- only:只返回在传入参数里所包含的字段;\n- co:co源码解析\n\n###构造器源码\n\n<!--javascript-->\n\t/**\n\t * Initialize a new `Application`.\n\t *\n\t * @api public\n\t */\n\tfunction Application() {\n\t if (!(this instanceof Application)) return new Application;\n\t this.env = process.env.NODE_ENV || 'development';\n\t this.subdomainOffset = 2;\n\t this.middleware = [];\n\t this.proxy = false;\n\t this.context = Object.create(context);\n\t this.request = Object.create(request);\n\t this.response = Object.create(response);\n\t}\n\n上面koa的Application的构造函数,可以看到在创建Application时有初始化如下属性:\n\n1. env表示是开发环境还是生产环境,通过process.env.NODE_ENV读取环境变量中的参数设置,如果没有设置默认为开发环境\n2. subdomainOffset,表示子域名的偏移,默认为2\n3. proxy如果为true,则解析 \"Host\" 的 header 域,并支持X-Forwarded-Host,默认为false。\n4. context上下文\n5. request请求对象\n6. response响应对象\n\n#### 有哪些方法\n\n#### app.listen\n\n<!--javascript-->\n\t/**\n\t * Shorthand for:\n\t *\n\t * http.createServer(app.callback()).listen(...)\n\t *\n\t * @param {Mixed} ...\n\t * @return {Server}\n\t * @api public\n\t */\n\tapp.listen = function(){\n\t debug('listen');\n\t var server = http.createServer(this.callback());\n\t return server.listen.apply(server, arguments);\n\t};\n\n 这个方法调用了node原生的http模块创建了一个server,要关注的是传入的这个callback。\n\n\n\n#### app.callback\n<!--javascript-->\n\n\t/**\t\n\t * Return a request handler callback\n\t * for node's native http server.\n\t *\n\t * @return {Function}\n\t * @api public\n\t */\n\tapp.callback = function(){\n\t var fn = this.experimental\n\t ? compose_es7(this.middleware)\n\t : co.wrap(compose(this.middleware));\n\t var self = this;\n\t if (!this.listeners('error').length) this.on('error', this.onerror);\n\t return function(req, res){\n\t res.statusCode = 404;\n\t var ctx = self.createContext(req, res);\n\t onFinished(res, ctx.onerror);\n\t fn.call(ctx).then(function () {\n\t respond.call(ctx);\n\t }).catch(ctx.onerror);\n\t }\n\t};\n\n 返回一个适合 http.createServer() 方法的回调函数用来处理请求。 您也可以使用这个回调函数将您的app挂载在 Connect/Express 应用上。一点点看下来,this.middleware是中间件的一个数组,用co.wrap或者compose_es7进行包装,co是tj大神的一个用来包装执行generator函数的库,而compose_es7应该是es7可能会引入的相似功能的特性,不同的是后者支持es7的async/await。后面一句处理错误,最后是返回一个请求处理函数。在函数中会调用createContext创建上下文,用onFinished模块捕捉finish或error事件,并根据第二个参数执行回调。然后调用co包装返回的promise对象进行中间件的调用,调用就由co来进行。调用成功后执行调用respond函数。\n\n#### 最后就来看看respond函数\n\n<!--javascript-->\n\n\t/**\t\n\t * Response helper.\n\t */\n\tfunction respond() {\n\t // allow bypassing koa\n\t if (false === this.respond) return;\n\t var res = this.res;\n\t if (res.headersSent || !this.writable) return;\n\t var body = this.body;\n\t var code = this.status;\n\t // ignore body\n\t if (statuses.empty[code]) {\n\t // strip headers\n\t this.body = null;\n\t return res.end();\n\t }\n\t if ('HEAD' == this.method) {\n\t if (isJSON(body)) this.length = Buffer.byteLength(JSON.stringify(body));\n\t return res.end();\n\t }\n\t // status body\n\t if (null == body) {\n\t this.type = 'text';\n\t body = this.message || String(code);\n\t this.length = Buffer.byteLength(body);\n\t return res.end(body);\n\t }\n\t // responses\n\t if (Buffer.isBuffer(body)) return res.end(body);\n\t if ('string' == typeof body) return res.end(body);\n\t if (body instanceof Stream) return body.pipe(res);\n\t // body: json\n\t body = JSON.stringify(body);\n\t this.length = Buffer.byteLength(body);\n\t res.end(body);\n\t}\n\n 在1.0.0版本中respond函数第一行就是yield *next,也就是一开始不执行,到所有中间件执行完后对res进行处理。但co进入4.*以后,用promise代替了thunk函数的实现,返回一个promise,因此当前1.1.2版本中的respond函数的调用写在了co返回的promise的第一个回调中。\n\n#### 其它\n\n- app.use向中间件数组中添加中间件\n- app.createContext创建请求上下文\n- app.inspect/toJSON返回配置的参数subdomainOffset、proxy和env。","html":"<h2 id=\"application\">Application</h2>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=32507038&auto=1&height=66\"></iframe>\n\n<p>原来写的文章,迁移过来,还是koa1的,koa2也出了,待更新。</p>\n\n<h3 id=\"\">依赖</h3>\n\n<ul>\n<li>debug:debug源码分析;</li>\n<li>compose_es7:与co.wrap相似,不过能够接受 async/await 函数;</li>\n<li>onFinished:捕捉finish或error事件,并根据第二个参数执行回调;</li>\n<li>compose:compose_es7的简化版,只接受generator;</li>\n<li>isJSON:顾名思义,判断传入参数是否为JSON;</li>\n<li>statuses:将http码分为三类,redirect、empty以及retry,然后根据传入的http来判断属于哪一类;</li>\n<li>accepts:根据req的内容判断(content negotiation)字段,如accept,Accept-Encoding,Accept-Language等字段对这些字段进行格式化,方便后续处理;</li>\n<li>only:只返回在传入参数里所包含的字段;</li>\n<li>co:co源码解析</li>\n</ul>\n\n<h3 id=\"\">构造器源码</h3>\n\n<!--javascript--> \n\n<pre><code>/**\n * Initialize a new `Application`.\n *\n * @api public\n */\nfunction Application() {\n if (!(this instanceof Application)) return new Application;\n this.env = process.env.NODE_ENV || 'development';\n this.subdomainOffset = 2;\n this.middleware = [];\n this.proxy = false;\n this.context = Object.create(context);\n this.request = Object.create(request);\n this.response = Object.create(response);\n}\n</code></pre>\n\n<p>上面koa的Application的构造函数,可以看到在创建Application时有初始化如下属性:</p>\n\n<ol>\n<li>env表示是开发环境还是生产环境,通过process.env.NODE_ENV读取环境变量中的参数设置,如果没有设置默认为开发环境 </li>\n<li>subdomainOffset,表示子域名的偏移,默认为2 </li>\n<li>proxy如果为true,则解析 \"Host\" 的 header 域,并支持X-Forwarded-Host,默认为false。 </li>\n<li>context上下文 </li>\n<li>request请求对象 </li>\n<li>response响应对象</li>\n</ol>\n\n<h4 id=\"\">有哪些方法</h4>\n\n<h4 id=\"applisten\">app.listen</h4>\n\n<!--javascript--> \n\n<pre><code>/**\n * Shorthand for:\n *\n * http.createServer(app.callback()).listen(...)\n *\n * @param {Mixed} ...\n * @return {Server}\n * @api public\n */\napp.listen = function(){\n debug('listen');\n var server = http.createServer(this.callback());\n return server.listen.apply(server, arguments);\n};\n</code></pre>\n\n<p> 这个方法调用了node原生的http模块创建了一个server,要关注的是传入的这个callback。</p>\n\n<h4 id=\"appcallback\">app.callback</h4>\n\n<!--javascript-->\n\n<pre><code>/** \n * Return a request handler callback\n * for node's native http server.\n *\n * @return {Function}\n * @api public\n */\napp.callback = function(){\n var fn = this.experimental\n ? compose_es7(this.middleware)\n : co.wrap(compose(this.middleware));\n var self = this;\n if (!this.listeners('error').length) this.on('error', this.onerror);\n return function(req, res){\n res.statusCode = 404;\n var ctx = self.createContext(req, res);\n onFinished(res, ctx.onerror);\n fn.call(ctx).then(function () {\n respond.call(ctx);\n }).catch(ctx.onerror);\n }\n};\n</code></pre>\n\n<p> 返回一个适合 http.createServer() 方法的回调函数用来处理请求。 您也可以使用这个回调函数将您的app挂载在 Connect/Express 应用上。一点点看下来,this.middleware是中间件的一个数组,用co.wrap或者compose<em>es7进行包装,co是tj大神的一个用来包装执行generator函数的库,而compose</em>es7应该是es7可能会引入的相似功能的特性,不同的是后者支持es7的async/await。后面一句处理错误,最后是返回一个请求处理函数。在函数中会调用createContext创建上下文,用onFinished模块捕捉finish或error事件,并根据第二个参数执行回调。然后调用co包装返回的promise对象进行中间件的调用,调用就由co来进行。调用成功后执行调用respond函数。</p>\n\n<h4 id=\"respond\">最后就来看看respond函数</h4>\n\n<!--javascript-->\n\n<pre><code>/** \n * Response helper.\n */\nfunction respond() {\n // allow bypassing koa\n if (false === this.respond) return;\n var res = this.res;\n if (res.headersSent || !this.writable) return;\n var body = this.body;\n var code = this.status;\n // ignore body\n if (statuses.empty[code]) {\n // strip headers\n this.body = null;\n return res.end();\n }\n if ('HEAD' == this.method) {\n if (isJSON(body)) this.length = Buffer.byteLength(JSON.stringify(body));\n return res.end();\n }\n // status body\n if (null == body) {\n this.type = 'text';\n body = this.message || String(code);\n this.length = Buffer.byteLength(body);\n return res.end(body);\n }\n // responses\n if (Buffer.isBuffer(body)) return res.end(body);\n if ('string' == typeof body) return res.end(body);\n if (body instanceof Stream) return body.pipe(res);\n // body: json\n body = JSON.stringify(body);\n this.length = Buffer.byteLength(body);\n res.end(body);\n}\n</code></pre>\n\n<p> 在1.0.0版本中respond函数第一行就是yield <em>next,也就是一开始不执行,到所有中间件执行完后对res进行处理。但co进入4.</em>以后,用promise代替了thunk函数的实现,返回一个promise,因此当前1.1.2版本中的respond函数的调用写在了co返回的promise的第一个回调中。</p>\n\n<h4 id=\"\">其它</h4>\n\n<ul>\n<li>app.use向中间件数组中添加中间件</li>\n<li>app.createContext创建请求上下文</li>\n<li>app.inspect/toJSON返回配置的参数subdomainOffset、proxy和env。</li>\n</ul>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1463838292023,"created_by":1,"updated_at":1473918260244,"updated_by":1,"published_at":1463838479996,"published_by":1},{"id":6,"uuid":"9a4be2b6-a671-4302-99fe-d0a54af86417","title":"Yesterday's","slug":"yesterday","markdown":"T团20周年的开场曲,晚上写作业时又拿出来循环播放,听完又是元气满满的一天呀!歌词很棒,拿出来分享分享!\n看到网易云音乐的评论里,一个考研党在2015年9月24日23点33发了一句“正在考研的我听着每一句都是泪”,到4月13日他又评论了一句“我考上了”,中间没有任何别的回复,但多了4个赞。我看到这莫名地就释然了,点了个赞,继续写作业去了,很奇妙的感受,我叫它感同身受!\n20周年那一版因为版权无法生成外链,只有下面这个版本了,不过也很不错!\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=22690729&auto=1&height=66\"></iframe>\n\n風の無い夜の公園で 動かないブランコ達は<br>\n在无风的夜晚公园里 不会摇晃的秋千们<br>\n与此刻的自己 有着可怕的相似<br>\n恐い位似てるんだよ 今の僕自身に<br>\n欲しい物は山ほどある<br>\n想要的东西如山般高<br>\n但手中却什么也没有<br>\nだけど欠片(かけら)も手にできず<br>\n無くしてきた 色んな夢を探してる<br>\n只是搜寻着 那些消逝的梦想<br>\n弱さは人の運命(さだめ)だと<br>\n命运两字是人生的无奈<br>\nなんとなく気付いてるけど<br>\n似乎我也注意到了<br>\n「支えられたい、支えてみたい」忙しく思うよ<br>\n却不停的想着「能撑得,在撑撑看」<br>\n強がる事に不器用で<br>\n就算是徒费力气<br>\n空回りしてもいいから<br>\n硬撑也无所谓<br>\n我只是想不断追寻 那变化多端的未来<br>\n追い続けたい 色んな形の未来を<br>\n觉得容易的事情却意外地<br>\n容易(たやす)く思える事が意外に結構<br>\n无法简单得手<br>\n手に入れるのが難しく思える<br>\n这难道不是跟盯着自己的脚步<br>\nそれってもしかして自分の足元を<br>\n見つめてみればきっと転がってんじゃない<br>\n就会摔跤是一样的道理吗<br>\n激しい雨が降った後にはきれいな花が咲く<br>\n在倾盆大雨过后 娇艳的花儿即将绽放<br>\nあきらめたりしない 届くはずさ想いは<br>\n千万不要放弃 因为梦想即将到达<br>\nそしていっそ昨日までの自分を捨て去ろう<br>\n然后索性忘却到昨天为止的自己吧<br>\n静けさを引き裂くように 空き缶を強く蹴り飛ばす<br>\n仿佛是要将寂静撕裂 狠狠的将罐子踢飞<br>\n胸に秘めた 色んな迷いを詰め込んで<br>\n深藏在内心的 是满满的各种迷茫<br>\n結果だけにしがみついていたよずっと<br>\n一直以来总是只紧抓住结果不放<br>\nプロセスなんか馬鹿らしく思えて<br>\n觉得过程什么的都不重要<br>\n残缺的月 带着勇敢的微笑<br>\n満ち欠ける月のけなげな微笑み<br>\n看吧 太阳一定依旧在你眼前升起<br>\n日はまた昇るきっと目の前にほら<br>\n頬を伝ったどんな涙も大きな価値がある<br>\n脸颊上滑落的 是无价眼泪带来的讯息<br>\n二度とない時を負けないように進むよ<br>\n怀着仅此一次绝不认输的心情前进<br>\n激しい雨が降った後にはきれいな花が咲く<br>\n在大雨倾盆过后 娇艳的花儿即将绽放<br>\nあきらめたりしない 届くはずさ願いは<br>\n千万不要放弃 因为梦想即将到达<br>\nそしていっそ昨日までの自分を捨て去ろう<br>\n然后索性忘却到昨天为止的自己吧<br>\n遠くない近くない捉えずらいホントにいつもやっかいもんは自分<br>\n最难琢磨的是那忽远忽近难以搞定的真实的自己<br>\nそれでも向き合って生きていかなきゃダメさ<br>\n但是不好好奋斗下去是不行的<br>\nだから「昨日までの自分を捨て去ろう」って唄おう<br>\n所以 就让我们唱着「忘却到昨天为止的自己吧」<br>\nいつかはどんな部分(こころ)も愛せる気がするよ<br>\n无论何时 无论何地都感受到爱<br>\n何が起こっても構わない荒波に打たれても<br>\n无论发生任何事 就算是再大的风浪<br>\n僕は信じている最終形の自分を<br>\n我始终相信着最真实的自己<br>\nどれ位こうしてたんだろう<br>\n到底这样过了多久<br>\n街は息を吹き返した<br>\n整个街道重新开始呼吸<br>\n不思議なんだ僕の胸に光がともってく<br>\n不可思议的是我的心也渐渐亮了起来<br>\nそよぎはじめていた風が淋しさを全部連れ去り<br>\n徐徐吹起的微风将一切寂寞带走<br>\n踊りだしたブランコ達も笑ってる<br>\n跳起舞来的秋千们也露出笑意<br>","html":"<p>T团20周年的开场曲,晚上写作业时又拿出来循环播放,听完又是元气满满的一天呀!歌词很棒,拿出来分享分享! <br />\n看到网易云音乐的评论里,一个考研党在2015年9月24日23点33发了一句“正在考研的我听着每一句都是泪”,到4月13日他又评论了一句“我考上了”,中间没有任何别的回复,但多了4个赞。我看到这莫名地就释然了,点了个赞,继续写作业去了,很奇妙的感受,我叫它感同身受!\n20周年那一版因为版权无法生成外链,只有下面这个版本了,不过也很不错!</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=22690729&auto=1&height=66\"></iframe>\n\n<p>風の無い夜の公園で 動かないブランコ達は<br>\n在无风的夜晚公园里 不会摇晃的秋千们<br>\n与此刻的自己 有着可怕的相似<br>\n恐い位似てるんだよ 今の僕自身に<br>\n欲しい物は山ほどある<br>\n想要的东西如山般高<br>\n但手中却什么也没有<br>\nだけど欠片(かけら)も手にできず<br>\n無くしてきた 色んな夢を探してる<br>\n只是搜寻着 那些消逝的梦想<br>\n弱さは人の運命(さだめ)だと<br>\n命运两字是人生的无奈<br>\nなんとなく気付いてるけど<br>\n似乎我也注意到了<br>\n「支えられたい、支えてみたい」忙しく思うよ<br>\n却不停的想着「能撑得,在撑撑看」<br>\n強がる事に不器用で<br>\n就算是徒费力气<br>\n空回りしてもいいから<br>\n硬撑也无所谓<br>\n我只是想不断追寻 那变化多端的未来<br>\n追い続けたい 色んな形の未来を<br>\n觉得容易的事情却意外地<br>\n容易(たやす)く思える事が意外に結構<br>\n无法简单得手<br>\n手に入れるのが難しく思える<br>\n这难道不是跟盯着自己的脚步<br>\nそれってもしかして自分の足元を<br>\n見つめてみればきっと転がってんじゃない<br>\n就会摔跤是一样的道理吗<br>\n激しい雨が降った後にはきれいな花が咲く<br>\n在倾盆大雨过后 娇艳的花儿即将绽放<br>\nあきらめたりしない 届くはずさ想いは<br>\n千万不要放弃 因为梦想即将到达<br>\nそしていっそ昨日までの自分を捨て去ろう<br>\n然后索性忘却到昨天为止的自己吧<br>\n静けさを引き裂くように 空き缶を強く蹴り飛ばす<br>\n仿佛是要将寂静撕裂 狠狠的将罐子踢飞<br>\n胸に秘めた 色んな迷いを詰め込んで<br>\n深藏在内心的 是满满的各种迷茫<br>\n結果だけにしがみついていたよずっと<br>\n一直以来总是只紧抓住结果不放<br>\nプロセスなんか馬鹿らしく思えて<br>\n觉得过程什么的都不重要<br>\n残缺的月 带着勇敢的微笑<br>\n満ち欠ける月のけなげな微笑み<br>\n看吧 太阳一定依旧在你眼前升起<br>\n日はまた昇るきっと目の前にほら<br>\n頬を伝ったどんな涙も大きな価値がある<br>\n脸颊上滑落的 是无价眼泪带来的讯息<br>\n二度とない時を負けないように進むよ<br>\n怀着仅此一次绝不认输的心情前进<br>\n激しい雨が降った後にはきれいな花が咲く<br>\n在大雨倾盆过后 娇艳的花儿即将绽放<br>\nあきらめたりしない 届くはずさ願いは<br>\n千万不要放弃 因为梦想即将到达<br>\nそしていっそ昨日までの自分を捨て去ろう<br>\n然后索性忘却到昨天为止的自己吧<br>\n遠くない近くない捉えずらいホントにいつもやっかいもんは自分<br>\n最难琢磨的是那忽远忽近难以搞定的真实的自己<br>\nそれでも向き合って生きていかなきゃダメさ<br>\n但是不好好奋斗下去是不行的<br>\nだから「昨日までの自分を捨て去ろう」って唄おう<br>\n所以 就让我们唱着「忘却到昨天为止的自己吧」<br>\nいつかはどんな部分(こころ)も愛せる気がするよ<br>\n无论何时 无论何地都感受到爱<br>\n何が起こっても構わない荒波に打たれても<br>\n无论发生任何事 就算是再大的风浪<br>\n僕は信じている最終形の自分を<br>\n我始终相信着最真实的自己<br>\nどれ位こうしてたんだろう<br>\n到底这样过了多久<br>\n街は息を吹き返した<br>\n整个街道重新开始呼吸<br>\n不思議なんだ僕の胸に光がともってく<br>\n不可思议的是我的心也渐渐亮了起来<br>\nそよぎはじめていた風が淋しさを全部連れ去り<br>\n徐徐吹起的微风将一切寂寞带走<br>\n踊りだしたブランコ達も笑ってる<br>\n跳起舞来的秋千们也露出笑意<br></p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1463918607786,"created_by":1,"updated_at":1473918251634,"updated_by":1,"published_at":1463919661601,"published_by":1},{"id":7,"uuid":"d4620769-971a-4d03-b3f7-dac29ab33ead","title":"Fetch API 异常处理","slug":"liu-lan-qi-xia-zai-google-playde-apk","markdown":"江边跑了几公里,有点累🙄,不过今天的风儿真喧嚣啊!发现江边有个地方和动漫里还挺像,就在那块草坪上坐了坐🤗。好了不费话了,写完还要洗澡!\n\n继之前服务端 Jersey 的异常处理之后,这篇要谈谈我在 react native 客户端 Fetch API 中进行异常处理遇到的一些问题!总所周知,Fetch 是使用 Promise 进行异步的编写的,所以这个问题其实也就是 Promise 的异常处理了!\n\n`Promise.prototype.catch` 这个方法就是用来捕获调用链上的异常的,但是在使用中还是有比较多的坑的。首先,这个方法是 `.then(null, rejection)` 的别名,或者说语法糖应该也可以吧!\n\n几个注意点:\n\n1. 如果 Promise 状态已经变成 Resolved ,再抛出错误是无效的;\n2. Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获。\n3. 跟传统的try/catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。\n4. *需要注意的是,catch 方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法。*\n\n这是 ES6 入门中,阮一峰老师提到的几个比较重要的点。然后我来讲讲我总结的 Fetch 中使用 catch 捕获异常的最佳实践,也是我目前的做法:\n\n1. 首先建议使用 catch 而不是 Promise 的第二个参数,由 catch 统一处理调用链上的异常;\n2. 第二注意 catch 返回的是一个新的 Promise,因此如果想要在封装的 Rest 类中统一 catch 处理异常是不行的,因为此处处理了异常后,后面的业务逻辑还是会执行,就可能导致一些意外的错误。即使在此处统一抛出一个新的异常,那么在后面调用时还是需要再 catch,这是得不偿失的。所以建议还是统一在每个调用后面单独处理;\n3. 当然,单独处理的逻辑可以提取公共的,减少下一次维护的成本和代码量。\n\n因为探讨的是我所在 app 的场景,而我又自己封装了 Fetch,因此可能上面的说法存在一些难懂的地方。但是原则是一致的,用 catch 不用 reject,单独处理,抽取公共逻辑。","html":"<p>江边跑了几公里,有点累🙄,不过今天的风儿真喧嚣啊!发现江边有个地方和动漫里还挺像,就在那块草坪上坐了坐🤗。好了不费话了,写完还要洗澡!</p>\n\n<p>继之前服务端 Jersey 的异常处理之后,这篇要谈谈我在 react native 客户端 Fetch API 中进行异常处理遇到的一些问题!总所周知,Fetch 是使用 Promise 进行异步的编写的,所以这个问题其实也就是 Promise 的异常处理了!</p>\n\n<p><code>Promise.prototype.catch</code> 这个方法就是用来捕获调用链上的异常的,但是在使用中还是有比较多的坑的。首先,这个方法是 <code>.then(null, rejection)</code> 的别名,或者说语法糖应该也可以吧!</p>\n\n<p>几个注意点:</p>\n\n<ol>\n<li>如果 Promise 状态已经变成 Resolved ,再抛出错误是无效的; </li>\n<li>Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获。 </li>\n<li>跟传统的try/catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。 </li>\n<li><em>需要注意的是,catch 方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法。</em></li>\n</ol>\n\n<p>这是 ES6 入门中,阮一峰老师提到的几个比较重要的点。然后我来讲讲我总结的 Fetch 中使用 catch 捕获异常的最佳实践,也是我目前的做法:</p>\n\n<ol>\n<li>首先建议使用 catch 而不是 Promise 的第二个参数,由 catch 统一处理调用链上的异常; </li>\n<li>第二注意 catch 返回的是一个新的 Promise,因此如果想要在封装的 Rest 类中统一 catch 处理异常是不行的,因为此处处理了异常后,后面的业务逻辑还是会执行,就可能导致一些意外的错误。即使在此处统一抛出一个新的异常,那么在后面调用时还是需要再 catch,这是得不偿失的。所以建议还是统一在每个调用后面单独处理; </li>\n<li>当然,单独处理的逻辑可以提取公共的,减少下一次维护的成本和代码量。</li>\n</ol>\n\n<p>因为探讨的是我所在 app 的场景,而我又自己封装了 Fetch,因此可能上面的说法存在一些难懂的地方。但是原则是一致的,用 catch 不用 reject,单独处理,抽取公共逻辑。</p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464107300210,"created_by":1,"updated_at":1471011841046,"updated_by":1,"published_at":1471011841047,"published_by":1},{"id":8,"uuid":"b8b11fe7-0536-434c-ab84-10bfa539d770","title":"转:Reactjs 的 PropTypes 使用方法","slug":"zhuan-reactjs-de-proptypes-shi-yong-fang-fa","markdown":"### Reactjs 的 PropTypes 使用方法\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=35528482&auto=1&height=66\"></iframe>\n\npropTypes 使用来规范组件Props的类型\n\n```\n var Test = React.createClass({\n propTypes: {\n // required\n requiredFunc: React.PropTypes.func.isRequired,\n requiredAny: React.PropTypes.any.isRequired,\n\n // primitives, optional by default\n bool: React.PropTypes.bool,\n func: React.PropTypes.func,\n number: React.PropTypes.number,\n string: React.PropTypes.string,\n },\n render:function(){\n return <div/>\n }\n});\n\nvar component = React.render(\n <Test requiredFunc=\"bar\" bool=\"true\" requiredAny=\"a\"/>, \n document.body\n);\n```\n\n若没有按照规范,会显示警告。\n\n```\nReact.PropTypes 的种类\nReact.PropTypes.array // 数组\nReact.PropTypes.bool.isRequired // Boolean 且必要。\nReact.PropTypes.func // 函数\nReact.PropTypes.number // 数字\nReact.PropTypes.object // 对象\nReact.PropTypes.string // 字符串\nReact.PropTypes.any // 任何类型的: numbers, strings, elements等\nReact.PropTypes.element // React 元素\nReact.PropTypes.instanceOf(XXX) // 某种XXX类型的实体\nReact.PropTypes.oneOf(['foo', 'bar']) // 其中一个字符串\nReact.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.array]) // 其中一种类型\nReact.PropTypes.arrayOf(React.PropTypes.string) // 某种类型的数组\nReact.PropTypes.objectOf(React.PropTypes.string) // 某种类型的对象\nReact.PropTypes.shape({ // 是否符合指定的格式\n color: React.PropTypes.string,\n fontSize: React.PropTypes.number\n});\nReact.PropTypes.any.isRequired // 可以是任何格式,且必要。\n\n// 自定义格式(当不符合的时候,会显示Error) \n// 不要用`console.warn` 或者 throw, 因为它在`oneOfType` 的情况下无效。\ncustomPropType: function(props, propName, componentName) {\n if (!/^[0-9]/.test(props[propName])) {\n return new Error('Validation failed!');\n }\n}\n```\n\ngetDefaultProps\n\n当父组件没有提供props的属性时,可以采用getDefaultProps,预设props属性的方式,让元件使用预设的值,确保有props带入。\n\n```\n var ComponentWithDefaultProps = React.createClass({ \n getDefaultProps : function () { \n return {\n value : 'default value' \n }; \n }, \n /* ... */ \n});\n```\n\nPosted by James Yang March 19, 2015 ReactJS","html":"<h3 id=\"reactjsproptypes\">Reactjs 的 PropTypes 使用方法</h3>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=35528482&auto=1&height=66\"></iframe>\n\n<p>propTypes 使用来规范组件Props的类型</p>\n\n<pre><code> var Test = React.createClass({\n propTypes: {\n // required\n requiredFunc: React.PropTypes.func.isRequired,\n requiredAny: React.PropTypes.any.isRequired,\n\n // primitives, optional by default\n bool: React.PropTypes.bool,\n func: React.PropTypes.func,\n number: React.PropTypes.number,\n string: React.PropTypes.string,\n },\n render:function(){\n return <div/>\n }\n});\n\nvar component = React.render( \n <Test requiredFunc=\"bar\" bool=\"true\" requiredAny=\"a\"/>, \n document.body\n);\n</code></pre>\n\n<p>若没有按照规范,会显示警告。</p>\n\n<pre><code>React.PropTypes 的种类 \nReact.PropTypes.array // 数组 \nReact.PropTypes.bool.isRequired // Boolean 且必要。 \nReact.PropTypes.func // 函数 \nReact.PropTypes.number // 数字 \nReact.PropTypes.object // 对象 \nReact.PropTypes.string // 字符串 \nReact.PropTypes.any // 任何类型的: numbers, strings, elements等 \nReact.PropTypes.element // React 元素 \nReact.PropTypes.instanceOf(XXX) // 某种XXX类型的实体 \nReact.PropTypes.oneOf(['foo', 'bar']) // 其中一个字符串 \nReact.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.array]) // 其中一种类型 \nReact.PropTypes.arrayOf(React.PropTypes.string) // 某种类型的数组 \nReact.PropTypes.objectOf(React.PropTypes.string) // 某种类型的对象 \nReact.PropTypes.shape({ // 是否符合指定的格式 \n color: React.PropTypes.string,\n fontSize: React.PropTypes.number\n});\nReact.PropTypes.any.isRequired // 可以是任何格式,且必要。\n\n// 自定义格式(当不符合的时候,会显示Error) \n// 不要用`console.warn` 或者 throw, 因为它在`oneOfType` 的情况下无效。\ncustomPropType: function(props, propName, componentName) { \n if (!/^[0-9]/.test(props[propName])) {\n return new Error('Validation failed!');\n }\n}\n</code></pre>\n\n<p>getDefaultProps</p>\n\n<p>当父组件没有提供props的属性时,可以采用getDefaultProps,预设props属性的方式,让元件使用预设的值,确保有props带入。</p>\n\n<pre><code> var ComponentWithDefaultProps = React.createClass({ \n getDefaultProps : function () { \n return {\n value : 'default value' \n }; \n }, \n /* ... */ \n});\n</code></pre>\n\n<p>Posted by James Yang March 19, 2015 ReactJS</p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464234823225,"created_by":1,"updated_at":1473918230619,"updated_by":1,"published_at":1464247836046,"published_by":1},{"id":9,"uuid":"727848e2-4e25-4943-bb82-5a6f2bba13b5","title":"什么是视差滚动","slug":"shi-yao-shi-shi-chai-gun-dong","markdown":"下午在研究facebook今年f8大会上开源的基于react native的f8app的时候看到一个ParallaxBackground组件,不明觉厉啊!于是查了一下单词Parallax,意思为视差,所以ParallaxBackground其实是一个视差的背景,最后我把目光落到了视差滚动。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=385757&auto=1&height=66\"></iframe>\n\n视差滚动是近些年国外web design中的一种潮流,它能够提高网页的立体性。它是如何运作的呢!上百度百科。\n> 视差效果,原本是一个天文学术语,当我们观察星空时,离我们远的星星移动速度较慢,离我们近的星星移动速度则较快。当我们坐在车上向车窗外 看时,也会有这样的感觉,远处的群山似乎没有在动,而近处的稻田却在飞速掠过。许多游戏中都使用视差效果来增加场景的立体感。说的简单点就是网页内的元素在滚动屏幕时发生的位置的变化,然而各个不同的元素位置变化的速度不同,导致网页内的元素有层次错落的错觉,这和我们人体的眼球效果很像。我看到多家产品商用视差滚动效果来展示产品,从不同的空间角度和用户体验,起到了非常不错的效果。 目前这种视差滚动效果被越来越多的国外网站所应用, 成为网页设计的热点趋势。 通过一个很长的网页页面,其中利用一些令人惊叹的插图和图形,并使用视差滚动(Parallax Scrolling)效果,让多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验。完美的展示了一个复杂的过程,让你犹如置身其中。\n\n百度百科看了还是木有很深刻的认识,那看看下面这个例子你一定会说“噢,来是这样”。[点我点我](http://hotdot.pro/#)\n\n在我看来视差滚动其实是通过页面多层次不同速度的滚动来达到一种视觉上的落差,使人感到页面有一种立体感!","html":"<p>下午在研究facebook今年f8大会上开源的基于react native的f8app的时候看到一个ParallaxBackground组件,不明觉厉啊!于是查了一下单词Parallax,意思为视差,所以ParallaxBackground其实是一个视差的背景,最后我把目光落到了视差滚动。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=385757&auto=1&height=66\"></iframe>\n\n<p>视差滚动是近些年国外web design中的一种潮流,它能够提高网页的立体性。它是如何运作的呢!上百度百科。</p>\n\n<blockquote>\n <p>视差效果,原本是一个天文学术语,当我们观察星空时,离我们远的星星移动速度较慢,离我们近的星星移动速度则较快。当我们坐在车上向车窗外 看时,也会有这样的感觉,远处的群山似乎没有在动,而近处的稻田却在飞速掠过。许多游戏中都使用视差效果来增加场景的立体感。说的简单点就是网页内的元素在滚动屏幕时发生的位置的变化,然而各个不同的元素位置变化的速度不同,导致网页内的元素有层次错落的错觉,这和我们人体的眼球效果很像。我看到多家产品商用视差滚动效果来展示产品,从不同的空间角度和用户体验,起到了非常不错的效果。 目前这种视差滚动效果被越来越多的国外网站所应用, 成为网页设计的热点趋势。 通过一个很长的网页页面,其中利用一些令人惊叹的插图和图形,并使用视差滚动(Parallax Scrolling)效果,让多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验。完美的展示了一个复杂的过程,让你犹如置身其中。</p>\n</blockquote>\n\n<p>百度百科看了还是木有很深刻的认识,那看看下面这个例子你一定会说“噢,来是这样”。<a href=\"http://hotdot.pro/#\">点我点我</a></p>\n\n<p>在我看来视差滚动其实是通过页面多层次不同速度的滚动来达到一种视觉上的落差,使人感到页面有一种立体感!</p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464244796501,"created_by":1,"updated_at":1473918240474,"updated_by":1,"published_at":1464245322285,"published_by":1},{"id":10,"uuid":"5995d7ae-ad04-4a41-9710-a30426873cf3","title":"一脸懵逼:git大小写不敏感","slug":"gitda-xiao-xie-bu-min-gan","markdown":"用了这么久git,今天第一次遇到文件大小写不区分的问题,因为node找依赖模块时区分大小写,项目就跑不起来了!!!\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=188647&auto=1&height=66\"></iframe>\n\n只能找办法解决:首先修改git的配置,使其大小写敏感\n```\ngit config core.ignorecase false\n```\n其次需要删除本地文件重新add和commit\n```\ngit rm -f filename\n\ngit add filename\n\ngit commit -m \"message\"\n```","html":"<p>用了这么久git,今天第一次遇到文件大小写不区分的问题,因为node找依赖模块时区分大小写,项目就跑不起来了!!!</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=188647&auto=1&height=66\"></iframe>\n\n<p>只能找办法解决:首先修改git的配置,使其大小写敏感</p>\n\n<pre><code>git config core.ignorecase false \n</code></pre>\n\n<p>其次需要删除本地文件重新add和commit</p>\n\n<pre><code>git rm -f filename\n\ngit add filename\n\ngit commit -m \"message\" \n</code></pre>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464334877736,"created_by":1,"updated_at":1473918221706,"updated_by":1,"published_at":1464335179180,"published_by":1},{"id":11,"uuid":"203b9723-2d0c-4d53-8634-98ad9d8087f4","title":"一口老血:ESLint配置文档翻译","slug":"pei-zhi-eslint-2","markdown":"## 配置ESLint\nESLint 被设计为完全可配置的,这意味着你可以关闭任意规则,仅仅运行基础的句法检查,或混合和匹配捆绑的规则和你习惯的规则来最适合你的项目。这有两种主要的方式来配置 ESLint 。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=308353&auto=1&height=66\"></iframe>\n\n1. 配置注释 - 直接把配置信息通过 JavaScript 注释的方式写入文件\n2. 配置文件 - 使用一个 JavaScript ,JSON or YAML 文件来具体说明整个文件夹和所有它的子文件夹的配置信息。它可以在一个 `.eslintrc.*` 文件或在 `package.json` 文件的 `eslintConfig` 字段,ESLint 将会自动寻找并读取它们,或者你可以通过命令行具体制定配置文件。\n\n有以下几块信息是可以被配置的:\n\n* Environments - 你的脚本被设计运行在什么环境。每个环境将会带有一组预定义的全局变量。\n* Globals - 你的脚本执行期间所需的额外的全局变量。\n* Rules - 将打开的规则,以及它的错误级别。\n\nESLint 所有这些选项使你你有了对你的代码细力度的控制力。\n\n## 制定解析器选项\nESLint 允许你去指定你想要支持的 JavaScript 语言选项。默认,ESLint 支持 ES5 的句法。你可以通过覆盖解析器选项的设置去启用 ES6 和 ES7 甚至JSX的支持。\n\n请注意支持 JSX 句法不等同于支持 React 。React 应用了 ESLint 无法识别的更具体的语法。如果你在使用 React 并想要支持 React 语法,我们推荐你使用 `eslint-plugin-react` 。\n\n解析器选项在你的 `.eslintrc.*` 文件的 `parserOptions` 属性中被指定。这个可用的选项如下:\n\n* ecmaVersion - 设置你想要使用的 ECMAScript 的具体版本,可以为3、5(默认)、6或者7。\n* sourceType - 设置 **\"script\"** (默认),如果你的代码 ECMAScript 模块当中则使用 **\"module\"** 。\n* ecmaFeatures - 一个指示你所想要使用的语言特性的对象:\n * globalReturn - 允许 return 表达式在全局作用域当中\n * impliedStrict - 开启严格模式(如果 ecmaVersion 大于等于5)\n * jsx - 启用 JSX\n * experimentalObjectRestSpread -启用对于实验性的对象剩余/展开参数的支持。(重要:这是一个实验特性,将来可能会出现显著的变化。建议你不要写依赖于这一功能的规则,除非你已经愿意去维护其改变时所引起的问题。)\n\n这有一个例子 `.eslintrc.json` 文件:\n\n```\n{\n \"parserOptions\": {\n \"ecmaVersion\": 6,\n \"sourceType\": \"module\",\n \"ecmaFeatures\": {\n \"jsx\": true\n }\n },\n \"rules\": {\n \"semi\": 2\n }\n}\n```\n\n设置解析器选项帮助 ESLint 决定什么是一个解析错误。所有语言选项默认是 false 。\n\n## 指定解析器\nESLint 默认使用 `Espree` 作为它的解析器。你可以在你的配置文件当中可选的指定一个不同的解析器,只要它满足以下的要求:\n\n1. 它必须是一个本地安装的npm模块。\n2. 它必须有一个 Esprima 兼容接口(它必须导出一个 `parse()` 方法)。\n3. 它必须产出 Esprima 兼容的抽象语法树和表意的对象。\n\n注意甚至有了这些兼容性,我们不保证一个外部的解析器将和 ESLint 正确地工作,ESLint将不会解决这些由其他解析器不兼容造成的问题。\n\n你需要使用 `.eslintrc` 文件中的 `parser` 选项来指定一个npm模块作为你的解析器。例如,下面指定使用 Esprima 来代替 Espree :\n\n```\n{\n \"parser\": \"esprima\",\n \"rules\": {\n \"semi\": \"error\"\n }\n}\n```\n\n下列解析器是兼容 ESLint 的:\n* Esprima\n* Babel-ESLint - 一个 Babel 解析器的包装,使其兼容 ESLint 。\n\n注意当你使用一个习惯的解析器时, `parserOptions` 配置属性仍然是 ESLint 在非默认的ES5环境工作所必须的。解析器通过了所有的 `parserOptions` ,可以也可以不使用它们去决定启用的特性。\n\n## 指定环境\n一个环境预定义了全局变量。可用的环境如下:\n\n* browser - 浏览器全局变量。\n* node - Node.js 全局变量和 Node.js 作用域。\n* commonjs - CommonJS 全局变量和 CommonJS 作用域(只有在使用 Browserify或WebPack产生的浏览器代码中使用)。\n* shared-node-browser - Node and Browser公共的全局变量。\n* es6 - 启用所有的 ES6 module 所需要的特性。\n* worker - web workers 全局变量.\n* amd - defines require() and define() as global variables as per the amd spec.\n* mocha - 加入 Mocha 测试的所有全局变量。\n* jasmine - 加入 Jasmine 测试的所有全局变量,针对1.3和2.0版本。\n* jest - Jest 全局变量。\n* phantomjs - PhantomJS 全局变量。\n* protractor - Protractor 全局变量。\n* qunit - QUnit 全局变量。\n* jquery - jQuery 全局变量。\n* prototypejs - Prototype.js 全局变量。\n* shelljs - ShellJS 全局变量。\n* meteor - Meteor 全局变量。\n* mongo - MongoDB 全局变量。\n* applescript - AppleScript 全局变量。\n* nashorn - Java 8 Nashorn 引擎全局变量。\n* serviceworker - Service Worker 全局变量。\n* atomtest - Atom 测试 helper 变量。\n* embertest - Ember 测试 helper 变量。\n* webextensions - WebExtensions 变量。\n* greasemonkey - GreaseMonkey 变量。\n\n这些环境不相互影响,所以你可以同时定义超过一个环境。\n\nEnvironments 能够在文件当中,在配置文件中或使用命令行的 `--env` 参数指定。\n\n在你的 JavaScript 中使用注释指定环境,使用下面的格式:\n\n```\n/*eslint-env node, mocha */\n```\n\n他启用了 Node.js 和 Mocha 的环境。\n\n在配置文件当中指定环境,使用 `env` 键并把你需要启用的环境的属性设为 `true` 。例如,下面例子启用了 brower 和 Node.js 环境:\n\n```\n{\n \"env\": {\n \"browser\": true,\n \"node\": true\n }\n}\n```\n\n或者在 `package.json` 文件\n\n```\n{\n \"name\": \"mypackage\",\n \"version\": \"0.0.1\",\n \"eslintConfig\": {\n \"env\": {\n \"browser\": true,\n \"node\": true\n }\n }\n}\n```\n\n在 YAML 文件中:\n\n```\n---\n env:\n browser: true\n node: true\n```\n\n如果你想去使用一个来自插件的环境,确保指定了插件名在 `plugins` 数组,然后使用不带前缀的插件名,其次是斜杠,再其次是环境名。举例来说:\n\n```\n{\n \"plugins\": [\"example\"],\n \"env\": {\n \"example/custom\": true\n }\n}\n```\n\n在一个 `package.json` 文件中\n\n```\n{\n \"name\": \"mypackage\",\n \"version\": \"0.0.1\",\n \"eslintConfig\": {\n \"plugins\": [\"example\"],\n \"env\": {\n \"example/custom\": true\n }\n }\n}\n```\n\nYAML 文件:\n\n```\n---\n plugins:\n - example\n env:\n example/custom: true\n```\n\n## 指定全局变量\nno-undef 规则将会警告将要访问的为定义的变量。如果你使用全局变量在一个文件中,它将是值得的去定义 globals 以至于 ESLint 将不会警告它们的使用。你可以定义全局变量使用文件中的注释或者配置文件。\n\n在你的 JavaScript 文件中指定全局变量,使用下面的格式:\n\n```\n/* global var1, var2 */\n```\n\n定义两个全局变量, `var1` 和 `var2` 。如果你想要去可选的制定这些全局变量不应当被修改(只读),你可以设置每个全局变量一个 false 标记:\n\n```\n/* global var1:false, var2:false */\n```\n\n在配置文件中配置全局变量,使用 `globals` 键并指出你想要使用的全局变量。设置全局变量为 `true` 来使其可以被覆盖或制定 `false` 使其只读。举例:\n\n```\n{\n \"globals\": {\n \"var1\": true,\n \"var2\": false\n }\n}\n```\n\nYAML中:\n\n```\n---\n globals:\n var1: true\n var2: false\n```\n\n这个例子允许 `var1` 可以在代码中覆盖,但 `var2` 不允许。\n\n## 配置插件\nESLint 支持使用第三方的插件。在使用插件前,需要用 npm 将它安装。\n\n在配置文件中配置插件,使用 `plugins` 键,它包含一个插件名的列表。 `eslint-plugin-` 的前缀可以从插件名中省略。\n\n```\n{\n \"plugins\": [\n \"plugin1\",\n \"eslint-plugin-plugin2\"\n ]\n}\n```\n\nYAML中:\n```\n---\n plugins:\n - plugin1\n - eslint-plugin-plugin2\n```\n\n注意:一个全局安装的 ESLint 只能改使用全局安装的 ESLint 插件。本地安装的 ESLint 能够使用本地和全局的 ESLint 插件。\n\n## 配置规则\nESlint 自带了大量的规则。你能够通过注释和配置文件的方式修改你项目中的规则。修改一个规则的设置,你必须设置 rule ID 为下列中的一个值:\n\n* `\"off\"` 或 `0` - 关闭规则\n* `\"warn\"` 或 `1` - 打开规则起提醒作用(不退出代码执行)\n* `\"error\"` 或 `2` - 打开规则起报错作用(将触发退出代码)\n\n在文件中使用注释配置,使用下面的格式:\n\n```\n/* eslint eqeqeq: \"off\", curly: \"error\" */\n```\n\n在这个例子中 `eqeqeq` 被关闭, `curly` 被打开 error 级别。你也可以使用等价的数字表示规则的级别:\n\n```\n/* eslint eqeqeq: 0, curly: 2 */\n```\n\n这个例子和上一个例子相同,只是它使用数字编号代替了字符串值。`eqeqeq` 被关闭, `curly` 被打开 error 级别。\n\n如果一个规则有可选的选项,你可以使用数组字面量设置它们,例如:\n\n```\n/* eslint quotes: [\"error\", \"double\"], curly: 2 */\n```\n\n这个注释为 `quotes` 规则指定了两个选项。数组的第一项总是严重的规则(数字或字符串)。\n\n在配置文件配置规则,使用 `rules` 键跟着一个 error 级别和任何选项你想要使用的。例如:\n\n```\n{\n \"rules\": {\n \"eqeqeq\": \"off\",\n \"curly\": \"error\",\n \"quotes\": [\"error\", \"double\"]\n }\n}\n```\n\nYAML 文件中:\n\n```\n---\nrules:\n eqeqeq: off\n curly: error\n quotes:\n - error\n - double\n```\n\n配置一个在插件中定义的规则,你必须为 rule ID 加上插件名作为前缀,以及 `/`。举例来说:\n\n```\n{\n \"plugins\": [\n \"plugin1\"\n ],\n \"rules\": {\n \"eqeqeq\": \"off\",\n \"curly\": \"error\",\n \"quotes\": [\"error\", \"double\"],\n \"plugin1/rule1\": \"error\"\n }\n}\n```\n\nYAML 文件中:\n\n```\n---\nplugins:\n - plugin1\nrules:\n eqeqeq: 0\n curly: error\n quotes:\n - error\n - \"double\"\n plugin1/rule1: error\n```\n\n在这个配置文件中,规则 `plugin1/rule1` 来自插件 `plugin1` 。你也能够使用如下格式配置注释的方式,例如:\n\n```\n/* eslint \"plugin1/rule1\": \"error\" */\n```\n\n注意:当指定来自插件的规则,确保省略 `eslint-plugin-` 。ESLint 只使用无前缀的名字来定位内部规则。\n\n## 使用行内注释来禁用规则\n使用如下的格式来临时地禁用文件中规则的警告:\n\n```\n/* eslint-disable */\n\n// Disables all rules between comments\nalert('foo');\n\n/* eslint-enable */\n```\n\n你也可以禁用或启用指定的规则:\n\n```\n/* eslint-disable no-alert, no-console */\n\n// Disables no-alert and no-console warnings between comments\nalert('foo');\nconsole.log('bar');\n\n/* eslint-enable no-alert, no-console */\n```\n\n在整个文件中禁用规则警告,把 `/* eslint-disable */` 放到文件顶部:\n\n```\n/* eslint-disable */\n\n// Disables all rules for the rest of the file\nalert('foo');\n```\n\n你也可以在整个文件禁用指定的规则:\n\n```\n/* eslint-disable no-alert */\n\n// Disables no-alert for the rest of the file\nalert('foo');\n```\n\n在指定行禁用所有规则:\n\n```\nalert('foo'); // eslint-disable-line\n\n// eslint-disable-next-line\nalert('foo');\n```\n\n在指定行禁用指定的规则:\n\n```\nalert('foo'); // eslint-disable-line no-alert\n\n// eslint-disable-next-line no-alert\nalert('foo');\n```\n\n指定行禁用多个规则:\n\n```\nalert('foo'); // eslint-disable-line no-alert, quotes, semi\n\n// eslint-disable-next-line no-alert, quotes, semi\nalert('foo');\n```\n\n注意:注释仅仅是禁用了一部分警告,告诉 ESlint 不要报告这部分代码违规。 ESlint 解析整个文件,所以禁用的代码仍然需要符合合法的 JavaScript 语法。\n\n## 添加共享的设置\nESlint 支持向配置文件中添加共享的配置。你能够添加 `settings` 对象到 ESLint 配置文件中,它将会提供给每一个将要执行的规则。这也许是有用的如果你添加了习惯的规则并想要它们能够访问相同的信息并轻松配置。\n\n在 JSON 中:\n\n```\n{\n \"settings\": {\n \"sharedData\": \"Hello\"\n }\n}\n```\n\n在 YAML 文件中:\n\n```\n---\n settings:\n sharedData: \"Hello\"\n```\n\n## 使用配置文件\n这有两种方式去使用配置文件。第一种是保存文件到你喜欢的地方,然后传递它的地址到 CLI 使用 `-c` 选项,例如:\n\n```\neslint -c myconfig.json myfiletotest.js\n```\n\n第二种方式是去使用配置文件通过 `.eslintrc.*` 或 `package.json` 文件。ESLint 将会自动在目录中寻找它们,从连续的父目录一路找到文件系统根目录。这个选项时有用的当你想要对项目的不同部分使用不同的配置,或者当你想要别的直接去使用 ESLint 而不需要记住它在配置文件中传递。\n\n两种方式,配置文件的设置都将会覆盖默认的配置。\n\n## 配置文件格式\nESLint 支持各种格式的配置文件:\n\n* **JavaScript** - 使用 `.eslintrc.js` 并导出一个包含你配置信息的对象。\n* **YAML** - 使用 `.eslintrc.yaml` 或 `.eslintrc.yml` 来定义配置结构。\n* **JSON** - 使用 `.eslintrc.json` 来定义配置结构。 ESLint's JSON 文件也支持 JavaScript 风格的注释。\n* **Deprecated** - 使用 `eslintrc`,过时的,可以是 JSON 或 YAML。\n* **package.json** - 创建一个 `eslintConfig` 属性在你的 `package.json`文件并在这里定义你的配置。\n\n如果有多种配置文件在同一个目录,ESLint将会使用一个。它们的优先级如下:\n\n1. `.eslintrc.js`\n2. `.eslintrc.yaml`\n3. `.eslintrc.yml`\n4. `.eslintrc.json`\n5. `.eslintrc`\n6. `package.json`\n\n## 配置及联和层次结构\n当使用 `.eslintrc.*` 和 `package.json` 文件来配置,你可以利用配置级联的优势。举例来说,假设你有下列的目录结构:\n\n```\nyour-project\n├── .eslintrc\n├── lib\n│ └── source.js\n└─┬ tests\n ├── .eslintrc\n └── test.js\n```\n\n配置级联将会使用最近的 `.eslintrc` 文件作为最高优先级,然后是任何父级目录的配置文件等。当你在项目中运行 ESLint ,所有 `lib/` 目录下的文件将会使用根目录下的 `.eslintrc` 文件作为它们的配置。如果 ESLint 进入 `test/` 目录,它将会使用 `your-project/tests/.eslintrc` 而不是 `your-project/.eslintrc` 。所以 `your-project/tests/test.js` 的检测是基于目录层次结构中的两个 `.eslintrc` 文件的组合的,近的拥有更高的优先级。这样的方式,你可以拥有项目级别的 ESLint 设置,并可以用目录层次的配置来覆盖它。\n\n相同的方式,在 `package.json` 文件中也可行。根目录中 `package.json` 的 `eslintConfig` 字段将在其所有子目录生效,但是在 tests 目录的 `.eslintrc` 文件将会覆盖所有冲突的配置。\n\n```\nyour-project\n├── package.json\n├── lib\n│ └── source.js\n└─┬ tests\n ├── .eslintrc\n └── test.js\n```\n\n如果在一个目录中,同时找到了 `.eslintrc` 和 `package.json` 文件,`eslintrc` 文件将有更高的优先级, `package.json` 文件将不被使用。\n\n注意:如果你又一个个人的配置文件在你的用户目录( `~/.eslintrc` ),它将会仅仅在没有别的配置文件没发现的情况下使用。它将会对你所有用户目录下的文件生效,包括第三方的代码,因此在运行 ESLint 很可能会产生问题。\n\n默认地, ESLint 将会在所有父目录直到根目录寻找配置文件。这将是有用的,如果你想要所有你的项目去跟随某个约定,但是有时候会导致意想不到的结果。限制 ESLint 在一个指定的项目,把 `\"root\": true` 加到 `package.json` 的 `eslintConfig` 字段或者 `.eslintrc.*` 文件,在你的根目录。 ESLint 将会停止寻找父目录当它找到了 `\"root\": true` 的配置。\n\n```\n{\n \"root\": true\n}\n```\nYAML 文件中:\n\n```\n---\n root: true\n```\n\n举例来说, 这个例子当中 `main.js` 将会使用 `lib/` 中的配置,而不会使用 `productA` 目录下的 `.eslintrc` 文件的配置,因为在 `lib/` 中的 `.eslintrc` 有 `\"root\": true` 的设置。\n\n```\nhome\n└── user\n ├── .eslintrc <- Always skipped if other configs present\n └── projectA\n ├── .eslintrc <- Not used\n └── lib\n ├── .eslintrc <- { \"root\": true }\n └── main.js\n```\n\n完整的配置层次,从高到低如下:\n\n1. 行内配置\n 1. `/*eslint-disable*/` and `/*eslint-enable*/`\n 2. `/*global*/`\n 3. `/*eslint*/`\n 4. `/*eslint-env*/`\n2. 命令行配置\n 1. `--global`\n 2. `--rule`\n 3. `--env`\n 4. `-c, --config`\n3. 项目级别配置\n 1. `.eslintrc.*` 或 `package.json` 文件\n 2. 祖先目录中寻找,除非有 `\"root\": true` 将不会向上寻找\n 3. 个人某人的配置在 `~/.eslintrc`\n\n## 扩展配置文件\n如果你想要扩展一个指定的配置文件,你可以使用 `extends` 属性,并制定路径。可以是相对的或是绝对的路径。\n\n配置能够被如下文件扩展:\n\n1. YAML 文件\n2. JSON 文件\n3. JS 文件\n4. 共享的配置包\n\n扩展配置提供了基础的规则,并可以覆盖。举例:\n\n```\n{\n \"extends\": \"./node_modules/coding-standard/.eslintrc\",\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": 1\n }\n}\n```\n\n配置也许也可以以数组的方式提供,后面的会覆盖前面的相同的规则的配置。举例:\n\n```\n{\n \"extends\": [\n \"./node_modules/coding-standard/eslintDefaults.js\",\n // Override eslintDefaults.js\n \"./node_modules/coding-standard/.eslintrc-es6\",\n // Override .eslintrc-es6\n \"./node_modules/coding-standard/.eslintrc-jsx\",\n ],\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": \"warn\"\n }\n}\n```\n\n扩展配置能够包涵它们自己的 `extends` ,导致循环的引用。\n\n你也可以使用共享的配置包。你需要使用 npm 去安装它们,例如:\n\n```\n{\n \"extends\": \"eslint-config-myrules\",\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": \"warn\"\n }\n}\n```\n\n在这个例子, `eslint-config-myrules` 包将会加载为一个对象并作为这个配置的父配置。\n\n注意:你可以省略 `eslint-config-` 前缀, ESLint 将会自动为你添加,和插件工作相似。\n\nESlint 也支持插件提供的扩展配置:\n\n```\n{\n \"extends\": \"plugin:eslint-plugin-myplugin/myConfig\",\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": \"warn\"\n }\n}\n```\n\n在这个例子, `eslint-plugin-myplugin` 包包含了名为 `default` 的配置。\n\n**十分重要**:使用插件中的配置时,必须要加上 `plugin:` 前缀来指明哈。但你可以可选地省略 `eslint-plugin` 前缀。\n\n注意:对于根目录或者其它父目录, `extends` 处理路径会用当前工作目录,而不是文件自身。\n\n## 配置文件中的注释\nJSON 和 YAML 配置文件都支持注释, `package.json` 不能包括注释。你可以使用 JAVAScript 风格的注释或 YAML 风格的注释, ESLint 将安全地忽略它们。这可以允许你的配置文件跟友好。举例:\n\n```\n{\n \"env\": {\n \"browser\": true\n },\n \"rules\": {\n // Override our default settings just for this directory\n \"eqeqeq\": \"warn\",\n \"strict\": \"off\"\n }\n}\n```\n\n## 指定需要检测的文件后缀\n当前指定后缀需要在命令行选项 `--ext` 后,添加用空格分开的后缀名列表。\n\n## 忽略文件或目录\n\n\n你可以告诉 ESLint 去忽略指定的文件或目录,通过一个 `.eslintignore` 文件在你的根目录。 `.eslintignore` 文件是一个普通文本文件,每一行定义了要被忽略的文件或目录。举例:下列表达式将会省略所有 JavaScript 文件:\n\n```\n**/*.js\n```\n\n当 ESLint 运行的时候,它会寻找当前工作目录的 `.eslintignore` 文件在它决定监测之前。如果找到了文件,这里面的配置将会生效。 `.eslintignore` 只会被使用一次,因此目录中其他的 `.eslintignore` 将不会被使用。\n\n* #好开头将会被认为是注释\n* 相对于 `.eslintignore` 文件路径或当前工作目录的路径\n* 忽略规则参照 `.gitignore` 规范\n* !用来取消前面的忽略的匹配\n\n`/node_modules/*` 和 `/bower_components/*` 将默认被忽略。\n\n举例,把下面的 `.eslintignore` 文件放倒工作目录下将会忽略`/node_modules/*` 和 `/bower_components/*` 目录,任何扩展名为 `.ts.js` 或 `.coffee.js` 可能被转换,任何在 `build/*` 目录除了 `build/index.js` 将被忽略:\n\n```\n# /node_modules/* and /bower_components/* ignored by default\n\n# Ignore built files except build/index.js\nbuild/*\n!build/index.js\n```\n\n## 使用替换的文件\n如果你喜欢去使用别的文件而不是 `.eslintignore` 来工作,你在命令行中可以指定 `--ignore-path` 选项。举例来说,你可以使用 `.jshintignore` ,因为两者的配置相同:\n\n```\neslint --ignore-path .jshintignore file.js\n```\n\n你也可以使用 `.gitignore` 文件:\n\n```\neslint --ignore-path .gitignore file.js\n```\n\n任何标准的 ignore 文件可以被使用。\n\n## 忽略文件警告\n当你 eslint 一个被忽略的文件或目录时,会发出警告,你可以使用 `--no-ignore` 来进行省略。\n","html":"<h2 id=\"eslint\">配置ESLint</h2>\n\n<p>ESLint 被设计为完全可配置的,这意味着你可以关闭任意规则,仅仅运行基础的句法检查,或混合和匹配捆绑的规则和你习惯的规则来最适合你的项目。这有两种主要的方式来配置 ESLint 。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=308353&auto=1&height=66\"></iframe>\n\n<ol>\n<li>配置注释 - 直接把配置信息通过 JavaScript 注释的方式写入文件 </li>\n<li>配置文件 - 使用一个 JavaScript ,JSON or YAML 文件来具体说明整个文件夹和所有它的子文件夹的配置信息。它可以在一个 <code>.eslintrc.*</code> 文件或在 <code>package.json</code> 文件的 <code>eslintConfig</code> 字段,ESLint 将会自动寻找并读取它们,或者你可以通过命令行具体制定配置文件。</li>\n</ol>\n\n<p>有以下几块信息是可以被配置的:</p>\n\n<ul>\n<li>Environments - 你的脚本被设计运行在什么环境。每个环境将会带有一组预定义的全局变量。</li>\n<li>Globals - 你的脚本执行期间所需的额外的全局变量。</li>\n<li>Rules - 将打开的规则,以及它的错误级别。</li>\n</ul>\n\n<p>ESLint 所有这些选项使你你有了对你的代码细力度的控制力。</p>\n\n<h2 id=\"\">制定解析器选项</h2>\n\n<p>ESLint 允许你去指定你想要支持的 JavaScript 语言选项。默认,ESLint 支持 ES5 的句法。你可以通过覆盖解析器选项的设置去启用 ES6 和 ES7 甚至JSX的支持。</p>\n\n<p>请注意支持 JSX 句法不等同于支持 React 。React 应用了 ESLint 无法识别的更具体的语法。如果你在使用 React 并想要支持 React 语法,我们推荐你使用 <code>eslint-plugin-react</code> 。</p>\n\n<p>解析器选项在你的 <code>.eslintrc.*</code> 文件的 <code>parserOptions</code> 属性中被指定。这个可用的选项如下:</p>\n\n<ul>\n<li>ecmaVersion - 设置你想要使用的 ECMAScript 的具体版本,可以为3、5(默认)、6或者7。</li>\n<li>sourceType - 设置 <strong>\"script\"</strong> (默认),如果你的代码 ECMAScript 模块当中则使用 <strong>\"module\"</strong> 。</li>\n<li>ecmaFeatures - 一个指示你所想要使用的语言特性的对象:\n<ul><li>globalReturn - 允许 return 表达式在全局作用域当中</li>\n<li>impliedStrict - 开启严格模式(如果 ecmaVersion 大于等于5)</li>\n<li>jsx - 启用 JSX</li>\n<li>experimentalObjectRestSpread -启用对于实验性的对象剩余/展开参数的支持。(重要:这是一个实验特性,将来可能会出现显著的变化。建议你不要写依赖于这一功能的规则,除非你已经愿意去维护其改变时所引起的问题。)</li></ul></li>\n</ul>\n\n<p>这有一个例子 <code>.eslintrc.json</code> 文件:</p>\n\n<pre><code>{\n \"parserOptions\": {\n \"ecmaVersion\": 6,\n \"sourceType\": \"module\",\n \"ecmaFeatures\": {\n \"jsx\": true\n }\n },\n \"rules\": {\n \"semi\": 2\n }\n}\n</code></pre>\n\n<p>设置解析器选项帮助 ESLint 决定什么是一个解析错误。所有语言选项默认是 false 。</p>\n\n<h2 id=\"\">指定解析器</h2>\n\n<p>ESLint 默认使用 <code>Espree</code> 作为它的解析器。你可以在你的配置文件当中可选的指定一个不同的解析器,只要它满足以下的要求:</p>\n\n<ol>\n<li>它必须是一个本地安装的npm模块。 </li>\n<li>它必须有一个 Esprima 兼容接口(它必须导出一个 <code>parse()</code> 方法)。 </li>\n<li>它必须产出 Esprima 兼容的抽象语法树和表意的对象。</li>\n</ol>\n\n<p>注意甚至有了这些兼容性,我们不保证一个外部的解析器将和 ESLint 正确地工作,ESLint将不会解决这些由其他解析器不兼容造成的问题。</p>\n\n<p>你需要使用 <code>.eslintrc</code> 文件中的 <code>parser</code> 选项来指定一个npm模块作为你的解析器。例如,下面指定使用 Esprima 来代替 Espree :</p>\n\n<pre><code>{\n \"parser\": \"esprima\",\n \"rules\": {\n \"semi\": \"error\"\n }\n}\n</code></pre>\n\n<p>下列解析器是兼容 ESLint 的:\n* Esprima\n* Babel-ESLint - 一个 Babel 解析器的包装,使其兼容 ESLint 。</p>\n\n<p>注意当你使用一个习惯的解析器时, <code>parserOptions</code> 配置属性仍然是 ESLint 在非默认的ES5环境工作所必须的。解析器通过了所有的 <code>parserOptions</code> ,可以也可以不使用它们去决定启用的特性。</p>\n\n<h2 id=\"\">指定环境</h2>\n\n<p>一个环境预定义了全局变量。可用的环境如下:</p>\n\n<ul>\n<li>browser - 浏览器全局变量。</li>\n<li>node - Node.js 全局变量和 Node.js 作用域。</li>\n<li>commonjs - CommonJS 全局变量和 CommonJS 作用域(只有在使用 Browserify或WebPack产生的浏览器代码中使用)。</li>\n<li>shared-node-browser - Node and Browser公共的全局变量。</li>\n<li>es6 - 启用所有的 ES6 module 所需要的特性。</li>\n<li>worker - web workers 全局变量.</li>\n<li>amd - defines require() and define() as global variables as per the amd spec.</li>\n<li>mocha - 加入 Mocha 测试的所有全局变量。</li>\n<li>jasmine - 加入 Jasmine 测试的所有全局变量,针对1.3和2.0版本。</li>\n<li>jest - Jest 全局变量。</li>\n<li>phantomjs - PhantomJS 全局变量。</li>\n<li>protractor - Protractor 全局变量。</li>\n<li>qunit - QUnit 全局变量。</li>\n<li>jquery - jQuery 全局变量。</li>\n<li>prototypejs - Prototype.js 全局变量。</li>\n<li>shelljs - ShellJS 全局变量。</li>\n<li>meteor - Meteor 全局变量。</li>\n<li>mongo - MongoDB 全局变量。</li>\n<li>applescript - AppleScript 全局变量。</li>\n<li>nashorn - Java 8 Nashorn 引擎全局变量。</li>\n<li>serviceworker - Service Worker 全局变量。</li>\n<li>atomtest - Atom 测试 helper 变量。</li>\n<li>embertest - Ember 测试 helper 变量。</li>\n<li>webextensions - WebExtensions 变量。</li>\n<li>greasemonkey - GreaseMonkey 变量。</li>\n</ul>\n\n<p>这些环境不相互影响,所以你可以同时定义超过一个环境。</p>\n\n<p>Environments 能够在文件当中,在配置文件中或使用命令行的 <code>--env</code> 参数指定。</p>\n\n<p>在你的 JavaScript 中使用注释指定环境,使用下面的格式:</p>\n\n<pre><code>/*eslint-env node, mocha */\n</code></pre>\n\n<p>他启用了 Node.js 和 Mocha 的环境。</p>\n\n<p>在配置文件当中指定环境,使用 <code>env</code> 键并把你需要启用的环境的属性设为 <code>true</code> 。例如,下面例子启用了 brower 和 Node.js 环境:</p>\n\n<pre><code>{\n \"env\": {\n \"browser\": true,\n \"node\": true\n }\n}\n</code></pre>\n\n<p>或者在 <code>package.json</code> 文件</p>\n\n<pre><code>{\n \"name\": \"mypackage\",\n \"version\": \"0.0.1\",\n \"eslintConfig\": {\n \"env\": {\n \"browser\": true,\n \"node\": true\n }\n }\n}\n</code></pre>\n\n<p>在 YAML 文件中:</p>\n\n<pre><code>---\n env:\n browser: true\n node: true\n</code></pre>\n\n<p>如果你想去使用一个来自插件的环境,确保指定了插件名在 <code>plugins</code> 数组,然后使用不带前缀的插件名,其次是斜杠,再其次是环境名。举例来说:</p>\n\n<pre><code>{\n \"plugins\": [\"example\"],\n \"env\": {\n \"example/custom\": true\n }\n}\n</code></pre>\n\n<p>在一个 <code>package.json</code> 文件中</p>\n\n<pre><code>{\n \"name\": \"mypackage\",\n \"version\": \"0.0.1\",\n \"eslintConfig\": {\n \"plugins\": [\"example\"],\n \"env\": {\n \"example/custom\": true\n }\n }\n}\n</code></pre>\n\n<p>YAML 文件:</p>\n\n<pre><code>---\n plugins:\n - example\n env:\n example/custom: true\n</code></pre>\n\n<h2 id=\"\">指定全局变量</h2>\n\n<p>no-undef 规则将会警告将要访问的为定义的变量。如果你使用全局变量在一个文件中,它将是值得的去定义 globals 以至于 ESLint 将不会警告它们的使用。你可以定义全局变量使用文件中的注释或者配置文件。</p>\n\n<p>在你的 JavaScript 文件中指定全局变量,使用下面的格式:</p>\n\n<pre><code>/* global var1, var2 */\n</code></pre>\n\n<p>定义两个全局变量, <code>var1</code> 和 <code>var2</code> 。如果你想要去可选的制定这些全局变量不应当被修改(只读),你可以设置每个全局变量一个 false 标记:</p>\n\n<pre><code>/* global var1:false, var2:false */\n</code></pre>\n\n<p>在配置文件中配置全局变量,使用 <code>globals</code> 键并指出你想要使用的全局变量。设置全局变量为 <code>true</code> 来使其可以被覆盖或制定 <code>false</code> 使其只读。举例:</p>\n\n<pre><code>{\n \"globals\": {\n \"var1\": true,\n \"var2\": false\n }\n}\n</code></pre>\n\n<p>YAML中:</p>\n\n<pre><code>---\n globals:\n var1: true\n var2: false\n</code></pre>\n\n<p>这个例子允许 <code>var1</code> 可以在代码中覆盖,但 <code>var2</code> 不允许。</p>\n\n<h2 id=\"\">配置插件</h2>\n\n<p>ESLint 支持使用第三方的插件。在使用插件前,需要用 npm 将它安装。</p>\n\n<p>在配置文件中配置插件,使用 <code>plugins</code> 键,它包含一个插件名的列表。 <code>eslint-plugin-</code> 的前缀可以从插件名中省略。</p>\n\n<pre><code>{\n \"plugins\": [\n \"plugin1\",\n \"eslint-plugin-plugin2\"\n ]\n}\n</code></pre>\n\n<p>YAML中: </p>\n\n<pre><code>---\n plugins:\n - plugin1\n - eslint-plugin-plugin2\n</code></pre>\n\n<p>注意:一个全局安装的 ESLint 只能改使用全局安装的 ESLint 插件。本地安装的 ESLint 能够使用本地和全局的 ESLint 插件。</p>\n\n<h2 id=\"\">配置规则</h2>\n\n<p>ESlint 自带了大量的规则。你能够通过注释和配置文件的方式修改你项目中的规则。修改一个规则的设置,你必须设置 rule ID 为下列中的一个值:</p>\n\n<ul>\n<li><code>\"off\"</code> 或 <code>0</code> - 关闭规则</li>\n<li><code>\"warn\"</code> 或 <code>1</code> - 打开规则起提醒作用(不退出代码执行)</li>\n<li><code>\"error\"</code> 或 <code>2</code> - 打开规则起报错作用(将触发退出代码)</li>\n</ul>\n\n<p>在文件中使用注释配置,使用下面的格式:</p>\n\n<pre><code>/* eslint eqeqeq: \"off\", curly: \"error\" */\n</code></pre>\n\n<p>在这个例子中 <code>eqeqeq</code> 被关闭, <code>curly</code> 被打开 error 级别。你也可以使用等价的数字表示规则的级别:</p>\n\n<pre><code>/* eslint eqeqeq: 0, curly: 2 */\n</code></pre>\n\n<p>这个例子和上一个例子相同,只是它使用数字编号代替了字符串值。<code>eqeqeq</code> 被关闭, <code>curly</code> 被打开 error 级别。</p>\n\n<p>如果一个规则有可选的选项,你可以使用数组字面量设置它们,例如:</p>\n\n<pre><code>/* eslint quotes: [\"error\", \"double\"], curly: 2 */\n</code></pre>\n\n<p>这个注释为 <code>quotes</code> 规则指定了两个选项。数组的第一项总是严重的规则(数字或字符串)。</p>\n\n<p>在配置文件配置规则,使用 <code>rules</code> 键跟着一个 error 级别和任何选项你想要使用的。例如:</p>\n\n<pre><code>{\n \"rules\": {\n \"eqeqeq\": \"off\",\n \"curly\": \"error\",\n \"quotes\": [\"error\", \"double\"]\n }\n}\n</code></pre>\n\n<p>YAML 文件中:</p>\n\n<pre><code>---\nrules: \n eqeqeq: off\n curly: error\n quotes:\n - error\n - double\n</code></pre>\n\n<p>配置一个在插件中定义的规则,你必须为 rule ID 加上插件名作为前缀,以及 <code>/</code>。举例来说:</p>\n\n<pre><code>{\n \"plugins\": [\n \"plugin1\"\n ],\n \"rules\": {\n \"eqeqeq\": \"off\",\n \"curly\": \"error\",\n \"quotes\": [\"error\", \"double\"],\n \"plugin1/rule1\": \"error\"\n }\n}\n</code></pre>\n\n<p>YAML 文件中:</p>\n\n<pre><code>---\nplugins: \n - plugin1\nrules: \n eqeqeq: 0\n curly: error\n quotes:\n - error\n - \"double\"\n plugin1/rule1: error\n</code></pre>\n\n<p>在这个配置文件中,规则 <code>plugin1/rule1</code> 来自插件 <code>plugin1</code> 。你也能够使用如下格式配置注释的方式,例如:</p>\n\n<pre><code>/* eslint \"plugin1/rule1\": \"error\" */\n</code></pre>\n\n<p>注意:当指定来自插件的规则,确保省略 <code>eslint-plugin-</code> 。ESLint 只使用无前缀的名字来定位内部规则。</p>\n\n<h2 id=\"\">使用行内注释来禁用规则</h2>\n\n<p>使用如下的格式来临时地禁用文件中规则的警告:</p>\n\n<pre><code>/* eslint-disable */\n\n// Disables all rules between comments\nalert('foo');\n\n/* eslint-enable */\n</code></pre>\n\n<p>你也可以禁用或启用指定的规则:</p>\n\n<pre><code>/* eslint-disable no-alert, no-console */\n\n// Disables no-alert and no-console warnings between comments\nalert('foo'); \nconsole.log('bar');\n\n/* eslint-enable no-alert, no-console */\n</code></pre>\n\n<p>在整个文件中禁用规则警告,把 <code>/* eslint-disable */</code> 放到文件顶部:</p>\n\n<pre><code>/* eslint-disable */\n\n// Disables all rules for the rest of the file\nalert('foo'); \n</code></pre>\n\n<p>你也可以在整个文件禁用指定的规则:</p>\n\n<pre><code>/* eslint-disable no-alert */\n\n// Disables no-alert for the rest of the file\nalert('foo'); \n</code></pre>\n\n<p>在指定行禁用所有规则:</p>\n\n<pre><code>alert('foo'); // eslint-disable-line\n\n// eslint-disable-next-line\nalert('foo'); \n</code></pre>\n\n<p>在指定行禁用指定的规则:</p>\n\n<pre><code>alert('foo'); // eslint-disable-line no-alert\n\n// eslint-disable-next-line no-alert\nalert('foo'); \n</code></pre>\n\n<p>指定行禁用多个规则:</p>\n\n<pre><code>alert('foo'); // eslint-disable-line no-alert, quotes, semi\n\n// eslint-disable-next-line no-alert, quotes, semi\nalert('foo'); \n</code></pre>\n\n<p>注意:注释仅仅是禁用了一部分警告,告诉 ESlint 不要报告这部分代码违规。 ESlint 解析整个文件,所以禁用的代码仍然需要符合合法的 JavaScript 语法。</p>\n\n<h2 id=\"\">添加共享的设置</h2>\n\n<p>ESlint 支持向配置文件中添加共享的配置。你能够添加 <code>settings</code> 对象到 ESLint 配置文件中,它将会提供给每一个将要执行的规则。这也许是有用的如果你添加了习惯的规则并想要它们能够访问相同的信息并轻松配置。</p>\n\n<p>在 JSON 中:</p>\n\n<pre><code>{\n \"settings\": {\n \"sharedData\": \"Hello\"\n }\n}\n</code></pre>\n\n<p>在 YAML 文件中:</p>\n\n<pre><code>---\n settings:\n sharedData: \"Hello\"\n</code></pre>\n\n<h2 id=\"\">使用配置文件</h2>\n\n<p>这有两种方式去使用配置文件。第一种是保存文件到你喜欢的地方,然后传递它的地址到 CLI 使用 <code>-c</code> 选项,例如:</p>\n\n<pre><code>eslint -c myconfig.json myfiletotest.js \n</code></pre>\n\n<p>第二种方式是去使用配置文件通过 <code>.eslintrc.*</code> 或 <code>package.json</code> 文件。ESLint 将会自动在目录中寻找它们,从连续的父目录一路找到文件系统根目录。这个选项时有用的当你想要对项目的不同部分使用不同的配置,或者当你想要别的直接去使用 ESLint 而不需要记住它在配置文件中传递。</p>\n\n<p>两种方式,配置文件的设置都将会覆盖默认的配置。</p>\n\n<h2 id=\"\">配置文件格式</h2>\n\n<p>ESLint 支持各种格式的配置文件:</p>\n\n<ul>\n<li><strong>JavaScript</strong> - 使用 <code>.eslintrc.js</code> 并导出一个包含你配置信息的对象。</li>\n<li><strong>YAML</strong> - 使用 <code>.eslintrc.yaml</code> 或 <code>.eslintrc.yml</code> 来定义配置结构。</li>\n<li><strong>JSON</strong> - 使用 <code>.eslintrc.json</code> 来定义配置结构。 ESLint's JSON 文件也支持 JavaScript 风格的注释。</li>\n<li><strong>Deprecated</strong> - 使用 <code>eslintrc</code>,过时的,可以是 JSON 或 YAML。</li>\n<li><strong>package.json</strong> - 创建一个 <code>eslintConfig</code> 属性在你的 <code>package.json</code>文件并在这里定义你的配置。</li>\n</ul>\n\n<p>如果有多种配置文件在同一个目录,ESLint将会使用一个。它们的优先级如下:</p>\n\n<ol>\n<li><code>.eslintrc.js</code> </li>\n<li><code>.eslintrc.yaml</code> </li>\n<li><code>.eslintrc.yml</code> </li>\n<li><code>.eslintrc.json</code> </li>\n<li><code>.eslintrc</code> </li>\n<li><code>package.json</code></li>\n</ol>\n\n<h2 id=\"\">配置及联和层次结构</h2>\n\n<p>当使用 <code>.eslintrc.*</code> 和 <code>package.json</code> 文件来配置,你可以利用配置级联的优势。举例来说,假设你有下列的目录结构:</p>\n\n<pre><code>your-project \n├── .eslintrc\n├── lib\n│ └── source.js\n└─┬ tests\n ├── .eslintrc\n └── test.js\n</code></pre>\n\n<p>配置级联将会使用最近的 <code>.eslintrc</code> 文件作为最高优先级,然后是任何父级目录的配置文件等。当你在项目中运行 ESLint ,所有 <code>lib/</code> 目录下的文件将会使用根目录下的 <code>.eslintrc</code> 文件作为它们的配置。如果 ESLint 进入 <code>test/</code> 目录,它将会使用 <code>your-project/tests/.eslintrc</code> 而不是 <code>your-project/.eslintrc</code> 。所以 <code>your-project/tests/test.js</code> 的检测是基于目录层次结构中的两个 <code>.eslintrc</code> 文件的组合的,近的拥有更高的优先级。这样的方式,你可以拥有项目级别的 ESLint 设置,并可以用目录层次的配置来覆盖它。</p>\n\n<p>相同的方式,在 <code>package.json</code> 文件中也可行。根目录中 <code>package.json</code> 的 <code>eslintConfig</code> 字段将在其所有子目录生效,但是在 tests 目录的 <code>.eslintrc</code> 文件将会覆盖所有冲突的配置。</p>\n\n<pre><code>your-project \n├── package.json\n├── lib\n│ └── source.js\n└─┬ tests\n ├── .eslintrc\n └── test.js\n</code></pre>\n\n<p>如果在一个目录中,同时找到了 <code>.eslintrc</code> 和 <code>package.json</code> 文件,<code>eslintrc</code> 文件将有更高的优先级, <code>package.json</code> 文件将不被使用。</p>\n\n<p>注意:如果你又一个个人的配置文件在你的用户目录( <code>~/.eslintrc</code> ),它将会仅仅在没有别的配置文件没发现的情况下使用。它将会对你所有用户目录下的文件生效,包括第三方的代码,因此在运行 ESLint 很可能会产生问题。</p>\n\n<p>默认地, ESLint 将会在所有父目录直到根目录寻找配置文件。这将是有用的,如果你想要所有你的项目去跟随某个约定,但是有时候会导致意想不到的结果。限制 ESLint 在一个指定的项目,把 <code>\"root\": true</code> 加到 <code>package.json</code> 的 <code>eslintConfig</code> 字段或者 <code>.eslintrc.*</code> 文件,在你的根目录。 ESLint 将会停止寻找父目录当它找到了 <code>\"root\": true</code> 的配置。</p>\n\n<pre><code>{\n \"root\": true\n}\n</code></pre>\n\n<p>YAML 文件中:</p>\n\n<pre><code>---\n root: true\n</code></pre>\n\n<p>举例来说, 这个例子当中 <code>main.js</code> 将会使用 <code>lib/</code> 中的配置,而不会使用 <code>productA</code> 目录下的 <code>.eslintrc</code> 文件的配置,因为在 <code>lib/</code> 中的 <code>.eslintrc</code> 有 <code>\"root\": true</code> 的设置。</p>\n\n<pre><code>home \n└── user\n ├── .eslintrc <- Always skipped if other configs present\n └── projectA\n ├── .eslintrc <- Not used\n └── lib\n ├── .eslintrc <- { \"root\": true }\n └── main.js\n</code></pre>\n\n<p>完整的配置层次,从高到低如下:</p>\n\n<ol>\n<li>行内配置 <br />\n<ol><li><code>/*eslint-disable*/</code> and <code>/*eslint-enable*/</code></li>\n<li><code>/*global*/</code></li>\n<li><code>/*eslint*/</code></li>\n<li><code>/*eslint-env*/</code></li></ol></li>\n<li>命令行配置 <br />\n<ol><li><code>--global</code></li>\n<li><code>--rule</code></li>\n<li><code>--env</code></li>\n<li><code>-c, --config</code></li></ol></li>\n<li>项目级别配置 <br />\n<ol><li><code>.eslintrc.*</code> 或 <code>package.json</code> 文件</li>\n<li>祖先目录中寻找,除非有 <code>\"root\": true</code> 将不会向上寻找</li>\n<li>个人某人的配置在 <code>~/.eslintrc</code></li></ol></li>\n</ol>\n\n<h2 id=\"\">扩展配置文件</h2>\n\n<p>如果你想要扩展一个指定的配置文件,你可以使用 <code>extends</code> 属性,并制定路径。可以是相对的或是绝对的路径。</p>\n\n<p>配置能够被如下文件扩展:</p>\n\n<ol>\n<li>YAML 文件 </li>\n<li>JSON 文件 </li>\n<li>JS 文件 </li>\n<li>共享的配置包</li>\n</ol>\n\n<p>扩展配置提供了基础的规则,并可以覆盖。举例:</p>\n\n<pre><code>{\n \"extends\": \"./node_modules/coding-standard/.eslintrc\",\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": 1\n }\n}\n</code></pre>\n\n<p>配置也许也可以以数组的方式提供,后面的会覆盖前面的相同的规则的配置。举例:</p>\n\n<pre><code>{\n \"extends\": [\n \"./node_modules/coding-standard/eslintDefaults.js\",\n // Override eslintDefaults.js\n \"./node_modules/coding-standard/.eslintrc-es6\",\n // Override .eslintrc-es6\n \"./node_modules/coding-standard/.eslintrc-jsx\",\n ],\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": \"warn\"\n }\n}\n</code></pre>\n\n<p>扩展配置能够包涵它们自己的 <code>extends</code> ,导致循环的引用。</p>\n\n<p>你也可以使用共享的配置包。你需要使用 npm 去安装它们,例如:</p>\n\n<pre><code>{\n \"extends\": \"eslint-config-myrules\",\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": \"warn\"\n }\n}\n</code></pre>\n\n<p>在这个例子, <code>eslint-config-myrules</code> 包将会加载为一个对象并作为这个配置的父配置。</p>\n\n<p>注意:你可以省略 <code>eslint-config-</code> 前缀, ESLint 将会自动为你添加,和插件工作相似。</p>\n\n<p>ESlint 也支持插件提供的扩展配置:</p>\n\n<pre><code>{\n \"extends\": \"plugin:eslint-plugin-myplugin/myConfig\",\n\n \"rules\": {\n // Override any settings from the \"parent\" configuration\n \"eqeqeq\": \"warn\"\n }\n}\n</code></pre>\n\n<p>在这个例子, <code>eslint-plugin-myplugin</code> 包包含了名为 <code>default</code> 的配置。</p>\n\n<p><strong>十分重要</strong>:使用插件中的配置时,必须要加上 <code>plugin:</code> 前缀来指明哈。但你可以可选地省略 <code>eslint-plugin</code> 前缀。</p>\n\n<p>注意:对于根目录或者其它父目录, <code>extends</code> 处理路径会用当前工作目录,而不是文件自身。</p>\n\n<h2 id=\"\">配置文件中的注释</h2>\n\n<p>JSON 和 YAML 配置文件都支持注释, <code>package.json</code> 不能包括注释。你可以使用 JAVAScript 风格的注释或 YAML 风格的注释, ESLint 将安全地忽略它们。这可以允许你的配置文件跟友好。举例:</p>\n\n<pre><code>{\n \"env\": {\n \"browser\": true\n },\n \"rules\": {\n // Override our default settings just for this directory\n \"eqeqeq\": \"warn\",\n \"strict\": \"off\"\n }\n}\n</code></pre>\n\n<h2 id=\"\">指定需要检测的文件后缀</h2>\n\n<p>当前指定后缀需要在命令行选项 <code>--ext</code> 后,添加用空格分开的后缀名列表。</p>\n\n<h2 id=\"\">忽略文件或目录</h2>\n\n<p>你可以告诉 ESLint 去忽略指定的文件或目录,通过一个 <code>.eslintignore</code> 文件在你的根目录。 <code>.eslintignore</code> 文件是一个普通文本文件,每一行定义了要被忽略的文件或目录。举例:下列表达式将会省略所有 JavaScript 文件:</p>\n\n<pre><code>**/*.js\n</code></pre>\n\n<p>当 ESLint 运行的时候,它会寻找当前工作目录的 <code>.eslintignore</code> 文件在它决定监测之前。如果找到了文件,这里面的配置将会生效。 <code>.eslintignore</code> 只会被使用一次,因此目录中其他的 <code>.eslintignore</code> 将不会被使用。</p>\n\n<ul>\n<li>#好开头将会被认为是注释</li>\n<li>相对于 <code>.eslintignore</code> 文件路径或当前工作目录的路径</li>\n<li>忽略规则参照 <code>.gitignore</code> 规范</li>\n<li>!用来取消前面的忽略的匹配</li>\n</ul>\n\n<p><code>/node_modules/*</code> 和 <code>/bower_components/*</code> 将默认被忽略。</p>\n\n<p>举例,把下面的 <code>.eslintignore</code> 文件放倒工作目录下将会忽略<code>/node_modules/*</code> 和 <code>/bower_components/*</code> 目录,任何扩展名为 <code>.ts.js</code> 或 <code>.coffee.js</code> 可能被转换,任何在 <code>build/*</code> 目录除了 <code>build/index.js</code> 将被忽略:</p>\n\n<pre><code># /node_modules/* and /bower_components/* ignored by default\n\n# Ignore built files except build/index.js\nbuild/* \n!build/index.js\n</code></pre>\n\n<h2 id=\"\">使用替换的文件</h2>\n\n<p>如果你喜欢去使用别的文件而不是 <code>.eslintignore</code> 来工作,你在命令行中可以指定 <code>--ignore-path</code> 选项。举例来说,你可以使用 <code>.jshintignore</code> ,因为两者的配置相同:</p>\n\n<pre><code>eslint --ignore-path .jshintignore file.js \n</code></pre>\n\n<p>你也可以使用 <code>.gitignore</code> 文件:</p>\n\n<pre><code>eslint --ignore-path .gitignore file.js \n</code></pre>\n\n<p>任何标准的 ignore 文件可以被使用。</p>\n\n<h2 id=\"\">忽略文件警告</h2>\n\n<p>当你 eslint 一个被忽略的文件或目录时,会发出警告,你可以使用 <code>--no-ignore</code> 来进行省略。</p>","image":null,"featured":0,"page":0,"status":"published","language":"en_US","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464345663531,"created_by":1,"updated_at":1473918211589,"updated_by":1,"published_at":1464348992506,"published_by":1},{"id":12,"uuid":"d0ae47ae-bcbc-4333-93af-004ae853c2a2","title":"react 的虫洞 context(官方文档翻译)","slug":"react-de-chong-dong-context","markdown":"> 中文社区还停留在 0.14.7,文档上有不少落后了,比如 shadowEqual、context 等的知识,所以只能自己来了。\n\n#### Context\n\nReact 一个强大之处就是很容易去跟踪数据在组件中的流动。当你观察一个组件时,你能够容易地确切地看出什么 props 被传递,这使得你的 apps 能够容易地推导。\n\n偶尔,你想要去传递数据贯穿整个组件树,不想手动地一级级传递下去。React 的 \"context\" 特性帮助你做到这一点。(译外音:形象地描述就是像虫洞那样达到穿越效果,props 能够在自己共享,有点像全局变量)。\n\n> 注意: Context 是一个先进的、实验性的特性。这个 API 可能会在未来的发行版有所改变。\n\n> 大多数应用将不需要去使用 context。尤其如果你仅仅是初学 React ,你应当尽量不用 context。使用 context 将会试你的代码难以去理解,因为它使得数据的流动不清晰。它就像全局变量传递变量到整个应用。\n\n> 如果你不得不使用 context,请谨慎地使用。无论你是在在构建一个应用还是库,尝试去隔离 context 到一个小的区域和避免直接地使用 context API,这样可以在 API 改变时,方便地升级。\n\n#### 在组件树中自动地传递信息\n\n假定你有如下的结构:\n\n```\nclass Button extends React.Component {\n render() {\n return (\n <button style={{background: this.props.color}}>\n {this.props.children}\n </button>\n );\n }\n}\n\nclass Message extends React.Component {\n render() {\n return (\n <div>\n {this.props.text} <Button color={this.props.color}>Delete</Button>\n </div>\n );\n }\n}\n\nclass MessageList extends React.Component {\n render() {\n const color = \"purple\";\n const children = this.props.messages.map((message) =>\n <Message text={message.text} color={color} />\n );\n return <div>{children}</div>;\n }\n}\n```\n\n在这个例子里,我们为了合适地设置 Button 和 Message 的样式,手动传递一个 color prop 。主题是一个好的例子,当你想要让整个子级可以访问一些信息片段(如:一个颜色)。 使用 context ,你可以传递这些自动地。\n\n```\nclass Button extends React.Component {\n render() {\n return (\n <button style={{background: this.context.color}}>\n {this.props.children}\n </button>\n );\n }\n}\n\nButton.contextTypes = {\n color: React.PropTypes.string\n};\n\nclass Message extends React.Component {\n render() {\n return (\n <div>\n {this.props.text} <Button>Delete</Button>\n </div>\n );\n }\n}\n\nclass MessageList extends React.Component {\n getChildContext() {\n return {color: \"purple\"};\n }\n\n render() {\n const children = this.props.messages.map((message) =>\n <Message text={message.text} />\n );\n return <div>{children}</div>;\n }\n}\n\nMessageList.childContextTypes = {\n color: React.PropTypes.string\n};\n```\n\n通过在 MessageList(context的提供者) 添加 ` childContextTypes ` 和 ` getChildContext `, React 将会自动地传递信息下去,在所有的子组件中通过定义 ` contextTypes ` 就可以访问。\n\n如果 ` contextTypes ` 没有定义,context 会是一个空的对象。\n\n#### 父子耦合(这一段优点不明觉厉)\nContext 也能够让你创建一个 API 如下:\n\n```\n<Menu>\n <MenuItem>aubergine</MenuItem>\n <MenuItem>butternut squash</MenuItem>\n <MenuItem>clementine</MenuItem>\n</Menu>\n```\n\n通过在 Menu 组件传递相关的信息,每一个 MenuItem 能够传回 Menu 容器。\n\n在你使用这个 API 构建组件之前,考虑是否有更干净的替代方案。我们盲目的传递 items 作为一个数组,如下:\n\n```\n<Menu items={['aubergine', 'butternut squash', 'clementine']} />\n```\n\n再次提醒,你能够在 props 中传递整个 React 组件如果你喜欢。\n\n#### 在生命周期方法引用 context \n\n如果 ` contextTypes ` 被定义在一个组件中,下列生命周期方法将会收到额外的参数:\n\n```\nvoid componentWillReceiveProps(\n object nextProps, object nextContext\n)\n\nboolean shouldComponentUpdate(\n object nextProps, object nextState, object nextContext\n)\n\nvoid componentWillUpdate(\n object nextProps, object nextState, object nextContext\n)\n\nvoid componentDidUpdate(\n object prevProps, object prevState, object prevContext\n)\n```\n\n#### 在无状态函数组件引用 context \n\n无状态函数组件也能够引用 context ,如果 ` contextTypes ` 被定义为函数的参数。下列代码显示了基于无状态函数的 Button 组件。\n\n```\nconst Button = ({children}, context) =>\n <button style={{background: context.color}}>\n {children}\n </button>;\n\nButton.contextTypes = {color: React.PropTypes.string};\n```\n\n#### 更新 context\n当 state 或 props 改变, ` getChildContext ` 函数将被调用。为了更新 context 中的数据, 用 ` this.setState ` 触发一个本地的 state 的更新。这将会触发一个新的 context 然后子组件将会收到改变的 context。\n\n```\nclass MediaQuery extends React.Component {\n constructor(props) {\n super(props);\n this.state = {type:'desktop'};\n }\n\n getChildContext() {\n return {type: this.state.type};\n }\n\n componentDidMount() {\n const checkMediaQuery = () => {\n const type = window.matchMedia(\"(min-width: 1025px)\").matches ? 'desktop' : 'mobile';\n if (type !== this.state.type) {\n this.setState({type});\n }\n };\n\n window.addEventListener('resize', checkMediaQuery);\n checkMediaQuery();\n }\n\n render() {\n return this.props.children;\n }\n}\n\nMediaQuery.childContextTypes = {\n type: React.PropTypes.string\n};\n```\n\n#### 什么时候不该使用 context \n\n在写干净的代码时,全局变量是最好要避免的,你应当避免使用 context 在那多数情况下。尤其是在使用它去节省代码和代替明确的传递 props 时,要思考再三。\n\n最好的方式对于 context 是绝对地向下传递 登录用户、当前语言或主题信息等。所有这些除了全局变量,context 推荐你把它们规定在一个单一的 React 组件树内。\n\n在组件中不要使用 context 去传递你的模型数据。在整个树上明确地传递你的数据是更加容易去理解的。使用 context 使你的组件更多的耦合和更少的复用性,因为它们的渲染因所处的环境而不同。\n\n#### 已知的限制\n\n如果一个 context 的值被组件的改变提供,当中间组件使用了` shouldComponentUpdate ` 并返回了 `false\n ` 那么后代的值将不会改变。详见 github issue [#2517](https://github.com/facebook/react/issues/2517)\n\n","html":"<blockquote>\n <p>中文社区还停留在 0.14.7,文档上有不少落后了,比如 shadowEqual、context 等的知识,所以只能自己来了。</p>\n</blockquote>\n\n<h4 id=\"context\">Context</h4>\n\n<p>React 一个强大之处就是很容易去跟踪数据在组件中的流动。当你观察一个组件时,你能够容易地确切地看出什么 props 被传递,这使得你的 apps 能够容易地推导。</p>\n\n<p>偶尔,你想要去传递数据贯穿整个组件树,不想手动地一级级传递下去。React 的 \"context\" 特性帮助你做到这一点。(译外音:形象地描述就是像虫洞那样达到穿越效果,props 能够在自己共享,有点像全局变量)。</p>\n\n<blockquote>\n <p>注意: Context 是一个先进的、实验性的特性。这个 API 可能会在未来的发行版有所改变。</p>\n \n <p>大多数应用将不需要去使用 context。尤其如果你仅仅是初学 React ,你应当尽量不用 context。使用 context 将会试你的代码难以去理解,因为它使得数据的流动不清晰。它就像全局变量传递变量到整个应用。</p>\n \n <p>如果你不得不使用 context,请谨慎地使用。无论你是在在构建一个应用还是库,尝试去隔离 context 到一个小的区域和避免直接地使用 context API,这样可以在 API 改变时,方便地升级。</p>\n</blockquote>\n\n<h4 id=\"\">在组件树中自动地传递信息</h4>\n\n<p>假定你有如下的结构:</p>\n\n<pre><code>class Button extends React.Component { \n render() {\n return (\n <button style={{background: this.props.color}}>\n {this.props.children}\n </button>\n );\n }\n}\n\nclass Message extends React.Component { \n render() {\n return (\n <div>\n {this.props.text} <Button color={this.props.color}>Delete</Button>\n </div>\n );\n }\n}\n\nclass MessageList extends React.Component { \n render() {\n const color = \"purple\";\n const children = this.props.messages.map((message) =>\n <Message text={message.text} color={color} />\n );\n return <div>{children}</div>;\n }\n}\n</code></pre>\n\n<p>在这个例子里,我们为了合适地设置 Button 和 Message 的样式,手动传递一个 color prop 。主题是一个好的例子,当你想要让整个子级可以访问一些信息片段(如:一个颜色)。 使用 context ,你可以传递这些自动地。</p>\n\n<pre><code>class Button extends React.Component { \n render() {\n return (\n <button style={{background: this.context.color}}>\n {this.props.children}\n </button>\n );\n }\n}\n\nButton.contextTypes = { \n color: React.PropTypes.string\n};\n\nclass Message extends React.Component { \n render() {\n return (\n <div>\n {this.props.text} <Button>Delete</Button>\n </div>\n );\n }\n}\n\nclass MessageList extends React.Component { \n getChildContext() {\n return {color: \"purple\"};\n }\n\n render() {\n const children = this.props.messages.map((message) =>\n <Message text={message.text} />\n );\n return <div>{children}</div>;\n }\n}\n\nMessageList.childContextTypes = { \n color: React.PropTypes.string\n};\n</code></pre>\n\n<p>通过在 MessageList(context的提供者) 添加 <code>childContextTypes</code> 和 <code>getChildContext</code>, React 将会自动地传递信息下去,在所有的子组件中通过定义 <code>contextTypes</code> 就可以访问。</p>\n\n<p>如果 <code>contextTypes</code> 没有定义,context 会是一个空的对象。</p>\n\n<h4 id=\"\">父子耦合(这一段优点不明觉厉)</h4>\n\n<p>Context 也能够让你创建一个 API 如下:</p>\n\n<pre><code><Menu> \n <MenuItem>aubergine</MenuItem>\n <MenuItem>butternut squash</MenuItem>\n <MenuItem>clementine</MenuItem>\n</Menu> \n</code></pre>\n\n<p>通过在 Menu 组件传递相关的信息,每一个 MenuItem 能够传回 Menu 容器。</p>\n\n<p>在你使用这个 API 构建组件之前,考虑是否有更干净的替代方案。我们盲目的传递 items 作为一个数组,如下:</p>\n\n<pre><code><Menu items={['aubergine', 'butternut squash', 'clementine']} /> \n</code></pre>\n\n<p>再次提醒,你能够在 props 中传递整个 React 组件如果你喜欢。</p>\n\n<h4 id=\"context\">在生命周期方法引用 context</h4>\n\n<p>如果 <code>contextTypes</code> 被定义在一个组件中,下列生命周期方法将会收到额外的参数:</p>\n\n<pre><code>void componentWillReceiveProps( \n object nextProps, object nextContext\n)\n\nboolean shouldComponentUpdate( \n object nextProps, object nextState, object nextContext\n)\n\nvoid componentWillUpdate( \n object nextProps, object nextState, object nextContext\n)\n\nvoid componentDidUpdate( \n object prevProps, object prevState, object prevContext\n)\n</code></pre>\n\n<h4 id=\"context\">在无状态函数组件引用 context</h4>\n\n<p>无状态函数组件也能够引用 context ,如果 <code>contextTypes</code> 被定义为函数的参数。下列代码显示了基于无状态函数的 Button 组件。</p>\n\n<pre><code>const Button = ({children}, context) => \n <button style={{background: context.color}}>\n {children}\n </button>;\n\nButton.contextTypes = {color: React.PropTypes.string}; \n</code></pre>\n\n<h4 id=\"context\">更新 context</h4>\n\n<p>当 state 或 props 改变, <code>getChildContext</code> 函数将被调用。为了更新 context 中的数据, 用 <code>this.setState</code> 触发一个本地的 state 的更新。这将会触发一个新的 context 然后子组件将会收到改变的 context。</p>\n\n<pre><code>class MediaQuery extends React.Component { \n constructor(props) {\n super(props);\n this.state = {type:'desktop'};\n }\n\n getChildContext() {\n return {type: this.state.type};\n }\n\n componentDidMount() {\n const checkMediaQuery = () => {\n const type = window.matchMedia(\"(min-width: 1025px)\").matches ? 'desktop' : 'mobile';\n if (type !== this.state.type) {\n this.setState({type});\n }\n };\n\n window.addEventListener('resize', checkMediaQuery);\n checkMediaQuery();\n }\n\n render() {\n return this.props.children;\n }\n}\n\nMediaQuery.childContextTypes = { \n type: React.PropTypes.string\n};\n</code></pre>\n\n<h4 id=\"context\">什么时候不该使用 context</h4>\n\n<p>在写干净的代码时,全局变量是最好要避免的,你应当避免使用 context 在那多数情况下。尤其是在使用它去节省代码和代替明确的传递 props 时,要思考再三。</p>\n\n<p>最好的方式对于 context 是绝对地向下传递 登录用户、当前语言或主题信息等。所有这些除了全局变量,context 推荐你把它们规定在一个单一的 React 组件树内。</p>\n\n<p>在组件中不要使用 context 去传递你的模型数据。在整个树上明确地传递你的数据是更加容易去理解的。使用 context 使你的组件更多的耦合和更少的复用性,因为它们的渲染因所处的环境而不同。</p>\n\n<h4 id=\"\">已知的限制</h4>\n\n<p>如果一个 context 的值被组件的改变提供,当中间组件使用了<code>shouldComponentUpdate</code> 并返回了 <code>false\n</code> 那么后代的值将不会改变。详见 github issue <a href=\"https://github.com/facebook/react/issues/2517\">#2517</a></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464684672461,"created_by":1,"updated_at":1467696306538,"updated_by":1,"published_at":1467685948549,"published_by":1},{"id":13,"uuid":"e38601e3-c8d0-4448-9fae-9f792e249b1a","title":"react native 踩坑实录","slug":"react-native-keng","markdown":"### 持续更新。。。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=26402069&auto=1&height=66\"></iframe>\n\n1、 [Android] StatusBar Translucent not working in Android 4.x.x after upgrading to react 0.23 [#6876](https://github.com/facebook/react-native/issues/6876)\n\n> solution: I changed the implementation of translucent and it only works on API 21+ now. Can you just not add top padding on Android 4.4- using Platform.Version < 21?\n\n2、 在使用pure-render相关的组件时,尽量不要使用箭头函数或者是行内的bind,因为这两种方式都是会返回一个新的实例,从而导致re-render。\n\n> solution: bind function in the constructor.\n\n3、 定时器在rn中使用还是比较广泛的,但是 timeMixin 暂时不支持在 es6 class 中的使用,于是去github看了下issue,官网也推荐使用 reactMixin 将 mixin 绑定到 es6 class ,并亲自测试了一下,通过。中文网也给出了手动 clear 的方案,相比之下复用性低,其实就是不用 mixin 的方案,但是依赖比较少,各有优势吧!\n\n```\nimport React, { Component } from 'react-native';\nimport TimerMixin from 'react-timer-mixin';\nimport reactMixin from 'react-mixin';\nclass MyClass extends Component {\n componentDidMount() {\n this.setTimeout(\n () => { console.log('tick'); },\n 5000\n );\n }\n}\nreactMixin(MyClass.prototype, TimerMixin);\nmodule.exports = MyClass;\n```\n\n4、 这两天集成 redux 时,发现rn的 ListView 经常不更新,于是深入到了源码内一看了究竟。看看是什么时候 ListView 会更新row。[有点多,新开了一篇](http://m2mbob.cn:1314/listview-row-update/)\n\n5、 不知道哪个版本 ListView 引入了 ` enableEmptySections `属性,这个属性只能有一个值true,为true时,会渲染没有 ` row ` 的 ` section ` 。下面这段代码可以看到,这个属性将在之后的版本不被推荐使用,官方建议在传参数时把空的 ` section ` 过滤掉。不过在实践过程中发现空数组实际上也会构造一个 ` section ` ,导致 warning ,强迫症想把它去掉啊!😂 \n```\nif (rowIDs.length === 0) {\n if (this.props.enableEmptySections === undefined) {\n var warning = require('fbjs/lib/warning');\n warning(false, 'In next release empty section headers will be rendered.'\n + ' In this release you can use \\'enableEmptySections\\' flag to render empty section headers.');\n continue;\n } else {\n var invariant = require('fbjs/lib/invariant');\n invariant(\n this.props.enableEmptySections,\n 'In next release \\'enableEmptySections\\' flag will be deprecated, empty section headers will always be rendered.'\n + ' If empty section headers are not desirable their indices should be excluded from sectionIDs object.'\n + ' In this release \\'enableEmptySections\\' may only have value \\'true\\' to allow empty section headers rendering.');\n }\n }\n```\n\n6、 对于 TouchableHighlight 组件,会出现没有效果的情况,解决方案是在外面包一层背景。\n\n7、 ios 模拟器点击无效的解决方案,在模拟器的Debug菜单中,关掉Slow Animations就好了。关掉Slow Animations之后,模拟器响应的都快了,非常好用,建议模拟器这一项不要开着。\n\n8、 这个不是 react native 本身的问题,而是在使用 react-native-vector-icons 的 ToolBarAndroid 组件过程中遇到的问题。首先 actions 的 title字段是必须的,其次是在 react-native-vector-icons 当中不是使用 icon 而是使用 iconName, iconColor, iconSize 这三个参数!! 😒\n\n9、 今天拿出自己的安卓机测流程时发现,文本在安卓上的 padding 不见了,然后去 github ,果然有道友提出了相同的问题[#7848](https://github.com/facebook/react-native/issues/7848#issuecomment-233517201)。不过 margin 是工作良好的,所以暂时先用 margin 替换了原来的 padding。\n\n10、 TouchableNativeFeedback 组件只支持 个人 View 作为子元素,其他元素将无法显示,多个元素则会报错。\n\n11、 WebSocket connection to 'ws://localhost:8081/debugger-proxy?role=debugger&name=Chrome' failed: Invalid frame header[6627](https://github.com/facebook/react-native/issues/6627) node 6.3 下面 rn 调试会出问题,建议先滚回 6.2,不过 rn 0.30 貌似解决了这个问题,但是暂时不升级 rn ,所以先记一下。","html":"<h3 id=\"\">持续更新。。。</h3>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=26402069&auto=1&height=66\"></iframe>\n\n<p>1、 [Android] StatusBar Translucent not working in Android 4.x.x after upgrading to react 0.23 <a href=\"https://github.com/facebook/react-native/issues/6876\">#6876</a></p>\n\n<blockquote>\n <p>solution: I changed the implementation of translucent and it only works on API 21+ now. Can you just not add top padding on Android 4.4- using Platform.Version < 21?</p>\n</blockquote>\n\n<p>2、 在使用pure-render相关的组件时,尽量不要使用箭头函数或者是行内的bind,因为这两种方式都是会返回一个新的实例,从而导致re-render。</p>\n\n<blockquote>\n <p>solution: bind function in the constructor.</p>\n</blockquote>\n\n<p>3、 定时器在rn中使用还是比较广泛的,但是 timeMixin 暂时不支持在 es6 class 中的使用,于是去github看了下issue,官网也推荐使用 reactMixin 将 mixin 绑定到 es6 class ,并亲自测试了一下,通过。中文网也给出了手动 clear 的方案,相比之下复用性低,其实就是不用 mixin 的方案,但是依赖比较少,各有优势吧!</p>\n\n<pre><code>import React, { Component } from 'react-native'; \nimport TimerMixin from 'react-timer-mixin'; \nimport reactMixin from 'react-mixin'; \nclass MyClass extends Component { \n componentDidMount() {\n this.setTimeout(\n () => { console.log('tick'); },\n 5000\n );\n }\n}\nreactMixin(MyClass.prototype, TimerMixin); \nmodule.exports = MyClass; \n</code></pre>\n\n<p>4、 这两天集成 redux 时,发现rn的 ListView 经常不更新,于是深入到了源码内一看了究竟。看看是什么时候 ListView 会更新row。<a href=\"http://m2mbob.cn:1314/listview-row-update/\">有点多,新开了一篇</a></p>\n\n<p>5、 不知道哪个版本 ListView 引入了 <code>enableEmptySections</code>属性,这个属性只能有一个值true,为true时,会渲染没有 <code>row</code> 的 <code>section</code> 。下面这段代码可以看到,这个属性将在之后的版本不被推荐使用,官方建议在传参数时把空的 <code>section</code> 过滤掉。不过在实践过程中发现空数组实际上也会构造一个 <code>section</code> ,导致 warning ,强迫症想把它去掉啊!😂 </p>\n\n<pre><code>if (rowIDs.length === 0) { \n if (this.props.enableEmptySections === undefined) {\n var warning = require('fbjs/lib/warning');\n warning(false, 'In next release empty section headers will be rendered.'\n + ' In this release you can use \\'enableEmptySections\\' flag to render empty section headers.');\n continue;\n } else {\n var invariant = require('fbjs/lib/invariant');\n invariant(\n this.props.enableEmptySections,\n 'In next release \\'enableEmptySections\\' flag will be deprecated, empty section headers will always be rendered.'\n + ' If empty section headers are not desirable their indices should be excluded from sectionIDs object.'\n + ' In this release \\'enableEmptySections\\' may only have value \\'true\\' to allow empty section headers rendering.');\n }\n }\n</code></pre>\n\n<p>6、 对于 TouchableHighlight 组件,会出现没有效果的情况,解决方案是在外面包一层背景。</p>\n\n<p>7、 ios 模拟器点击无效的解决方案,在模拟器的Debug菜单中,关掉Slow Animations就好了。关掉Slow Animations之后,模拟器响应的都快了,非常好用,建议模拟器这一项不要开着。</p>\n\n<p>8、 这个不是 react native 本身的问题,而是在使用 react-native-vector-icons 的 ToolBarAndroid 组件过程中遇到的问题。首先 actions 的 title字段是必须的,其次是在 react-native-vector-icons 当中不是使用 icon 而是使用 iconName, iconColor, iconSize 这三个参数!! 😒</p>\n\n<p>9、 今天拿出自己的安卓机测流程时发现,文本在安卓上的 padding 不见了,然后去 github ,果然有道友提出了相同的问题<a href=\"https://github.com/facebook/react-native/issues/7848#issuecomment-233517201\">#7848</a>。不过 margin 是工作良好的,所以暂时先用 margin 替换了原来的 padding。</p>\n\n<p>10、 TouchableNativeFeedback 组件只支持 个人 View 作为子元素,其他元素将无法显示,多个元素则会报错。</p>\n\n<p>11、 WebSocket connection to 'ws://localhost:8081/debugger-proxy?role=debugger&name=Chrome' failed: Invalid frame header<a href=\"https://github.com/facebook/react-native/issues/6627\">6627</a> node 6.3 下面 rn 调试会出问题,建议先滚回 6.2,不过 rn 0.30 貌似解决了这个问题,但是暂时不升级 rn ,所以先记一下。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1464770450231,"created_by":1,"updated_at":1474249886869,"updated_by":1,"published_at":1466558940000,"published_by":1},{"id":14,"uuid":"5b126970-3765-4b5e-9e06-920981bfd050","title":"Immutable.js 文档翻译(API部分)","slug":"immutable-js-wen-dang-fan-yi-apibu-fen","markdown":"不可变数据鼓励纯函数(相同的数据输入得到相同的输出),适用于更加简单的应用开发,并支持延迟计算等函数式编程的技术。\n\n当设计把这些强大的功能性概念带给 ` JavaScript ` 时,它提出了一个 ` JavaScript ` 工程师熟悉的面向对象的 API并十分接近地反映了 ` Array ` , ` Map\n ` 和 ` Set ` 等数据结构。它能够很容易并高效地与普通 ` JavaScript ` 基本类型转换。 \n\n注意:所有的例子用 [ES6](https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla)。为了在所有浏览器中运行,它们需要呗专为 ES3 。例如:\n\n```\n// ES6\nfoo.map(x => x * x);\n// ES3\nfoo.map(function (x) { return x * x; });\n```\n\n#### API\n\n[fromJS()](www.baidu.com)\n\n把普通的 JS 对象和数组深度转换为不可变的 Maps 和 Lists。\n\n[is()](www.baidu.com)\n\n比较直是否相等,类似于 ES6 新引入的 [Object.is](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) ,但是它把不可变的 [Iterable](www.baidu.com) 当作值,如果第二个 [Iterable](www.baidu.com) 包含相等的值就相等。\n\n[List](www.baidu.com)\n\nLists 是排序索引密集的集合,与 ` JavaScript ` 的 ` Array ` 十分相似。 \n\n[Map](www.baidu.com)\n\n不可变的 Map ,获取和设置的复杂度为 ` O(log32 N) ` 。 \n\n[OrderedMap](www.baidu.com)\n\n一种保证了内容顺序与 set() 顺序一致的 Map。 \n\n[Set](www.baidu.com)\n\n唯一值集合,添加和判断存在复杂度为 ` O(log32 N) ` 。 \n\n[OrderedSet](www.baidu.com)\n\n一种保证了内容顺序与 add() 顺序一致的 Set。\n\n[Stack](www.baidu.com)\n\n支持高效的添加和和删除的索引集合,栈。\n\n[Range()](www.baidu.com)","html":"<p>不可变数据鼓励纯函数(相同的数据输入得到相同的输出),适用于更加简单的应用开发,并支持延迟计算等函数式编程的技术。</p>\n\n<p>当设计把这些强大的功能性概念带给 <code>JavaScript</code> 时,它提出了一个 <code>JavaScript</code> 工程师熟悉的面向对象的 API并十分接近地反映了 <code>Array</code> , <code>Map\n</code> 和 <code>Set</code> 等数据结构。它能够很容易并高效地与普通 <code>JavaScript</code> 基本类型转换。 </p>\n\n<p>注意:所有的例子用 <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla\">ES6</a>。为了在所有浏览器中运行,它们需要呗专为 ES3 。例如:</p>\n\n<pre><code>// ES6\nfoo.map(x => x * x); \n// ES3\nfoo.map(function (x) { return x * x; }); \n</code></pre>\n\n<h4 id=\"api\">API</h4>\n\n<p><a href=\"www.baidu.com\">fromJS()</a></p>\n\n<p>把普通的 JS 对象和数组深度转换为不可变的 Maps 和 Lists。</p>\n\n<p><a href=\"www.baidu.com\">is()</a></p>\n\n<p>比较直是否相等,类似于 ES6 新引入的 <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is\">Object.is</a> ,但是它把不可变的 <a href=\"www.baidu.com\">Iterable</a> 当作值,如果第二个 <a href=\"www.baidu.com\">Iterable</a> 包含相等的值就相等。</p>\n\n<p><a href=\"www.baidu.com\">List</a></p>\n\n<p>Lists 是排序索引密集的集合,与 <code>JavaScript</code> 的 <code>Array</code> 十分相似。 </p>\n\n<p><a href=\"www.baidu.com\">Map</a></p>\n\n<p>不可变的 Map ,获取和设置的复杂度为 <code>O(log32 N)</code> 。 </p>\n\n<p><a href=\"www.baidu.com\">OrderedMap</a></p>\n\n<p>一种保证了内容顺序与 set() 顺序一致的 Map。 </p>\n\n<p><a href=\"www.baidu.com\">Set</a></p>\n\n<p>唯一值集合,添加和判断存在复杂度为 <code>O(log32 N)</code> 。 </p>\n\n<p><a href=\"www.baidu.com\">OrderedSet</a></p>\n\n<p>一种保证了内容顺序与 add() 顺序一致的 Set。</p>\n\n<p><a href=\"www.baidu.com\">Stack</a></p>\n\n<p>支持高效的添加和和删除的索引集合,栈。</p>\n\n<p><a href=\"www.baidu.com\">Range()</a></p>","image":null,"featured":0,"page":0,"status":"draft","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1466583064760,"created_by":1,"updated_at":1466588205800,"updated_by":1,"published_at":null,"published_by":null},{"id":15,"uuid":"d84b75b7-f57b-4d44-abee-7ba5a9c92cc3","title":"深入RN ListView源码,了解 row 更新机制","slug":"listview-row-update","markdown":"> version 0.25.1\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=31445772&auto=1&height=66\"></iframe>\n\n打开`/node_modules/react-native/Libraries/CustomComponents/ListView` 文件夹,下面有两个js,分别为 ` ListView ` 和 ` ListViewDataSource `。首先看 ` ListView ` 321 行:\n```\n componentWillReceiveProps: function(nextProps) {\n // 只有传入了新的dataSource或者initialListSize改变时才会重新渲染整个列表\n if (this.props.dataSource !== nextProps.dataSource ||\n this.props.initialListSize !== nextProps.initialListSize) {\n this.setState((state, props) => {\n // 同一个dataSource上一次以渲染的行数置为0\n this._prevRenderedRowsCount = 0;\n return {\n // 第一次要渲染的行数\n curRenderedRowsCount: Math.min(\n Math.max(\n state.curRenderedRowsCount,\n props.initialListSize\n ),\n props.enableEmptySections ? props.dataSource.getRowAndSectionCount() : props.dataSource.getRowCount()\n ),\n };\n }, () => this._renderMoreRowsIfNeeded());\n }\n },\n```\n其次是 `render` 函数:\n```\nfor (var rowIdx = 0; rowIdx < rowIDs.length; rowIdx++) {\n var rowID = rowIDs[rowIdx];\n var comboID = sectionID + '_' + rowID;\n // 当前行数大于已经渲染的行数且dataSource.rowShouldUpdate为true才进行新的一行的渲染。\n var shouldUpdateRow = rowCount >= this._prevRenderedRowsCount &&\n dataSource.rowShouldUpdate(sectionIdx, rowIdx);\n var row =\n <StaticRenderer\n key={'r_' + comboID}\n shouldUpdate={!!shouldUpdateRow}\n render={this.props.renderRow.bind(\n null,\n dataSource.getRowData(sectionIdx, rowIdx),\n sectionID,\n rowID,\n this._onRowHighlighted\n )}\n />;\n bodyComponents.push(row);\n totalIndex++;\n```\n最后就要看看 ` dataSource.rowShouldUpdate ` 发生了什么,打开 ` ListViewDataSource ` 236行:\n```\nrowShouldUpdate(sectionIndex: number, rowIndex: number): bool {\n // 尼玛_dirtyRows是啥\n var needsUpdate = this._dirtyRows[sectionIndex][rowIndex];\n warning(needsUpdate !== undefined,\n 'missing dirtyBit for section, row: ' + sectionIndex + ', ' + rowIndex);\n return needsUpdate;\n }\n```\n再到376行吧:\n```\n this._dirtyRows[sIndex] = [];\n for (var rIndex = 0; rIndex < this.rowIdentities[sIndex].length; rIndex++) {\n var rowID = this.rowIdentities[sIndex][rIndex];\n // 如果sectionID、rowID是新的或者调用我们定义的rowHasChanged返回是true的话就是脏的,就需要更新。\n dirty =\n !prevSectionsHash[sectionID] ||\n !prevRowsHash[sectionID][rowID] ||\n this._rowHasChanged(\n this._getRowData(prevDataBlob, sectionID, rowID),\n this._getRowData(this._dataBlob, sectionID, rowID)\n );\n this._dirtyRows[sIndex].push(!!dirty);\n }\n```\n到这里我终于发现自己的 row 为何不更新了,我们平时写的 ` rowHasChanged `方法都是直接 `===` 比较的,如果内容变了引用没变比较返回的就是 ` true ` 。这就要追溯到我在 reselect 中的操作了,我只改变了 列表数据的引用,而没有改变其中每一项数据的引用,所以在 ` rowHasChanged ` 比较时出了问题。不过改变引用之后,新的问题又来了, ` rowHasChanged `无论如何返回的都是true,然后每次都会更新,这是无法接受的。临时解决方案是,使用 fbjs 中的 ` shadowEqual ` 进行比较,而不是简单的 ` === `。 `shadowEqual` 只会比较第一层的属性,还不是最好的方案,最好的方案我认为是引入 `immutablejs`。","html":"<blockquote>\n <p>version 0.25.1</p>\n</blockquote>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=31445772&auto=1&height=66\"></iframe>\n\n<p>打开<code>/node_modules/react-native/Libraries/CustomComponents/ListView</code> 文件夹,下面有两个js,分别为 <code>ListView</code> 和 <code>ListViewDataSource</code>。首先看 <code>ListView</code> 321 行:</p>\n\n<pre><code> componentWillReceiveProps: function(nextProps) {\n // 只有传入了新的dataSource或者initialListSize改变时才会重新渲染整个列表\n if (this.props.dataSource !== nextProps.dataSource ||\n this.props.initialListSize !== nextProps.initialListSize) {\n this.setState((state, props) => {\n // 同一个dataSource上一次以渲染的行数置为0\n this._prevRenderedRowsCount = 0;\n return {\n // 第一次要渲染的行数\n curRenderedRowsCount: Math.min(\n Math.max(\n state.curRenderedRowsCount,\n props.initialListSize\n ),\n props.enableEmptySections ? props.dataSource.getRowAndSectionCount() : props.dataSource.getRowCount()\n ),\n };\n }, () => this._renderMoreRowsIfNeeded());\n }\n },\n</code></pre>\n\n<p>其次是 <code>render</code> 函数:</p>\n\n<pre><code>for (var rowIdx = 0; rowIdx < rowIDs.length; rowIdx++) { \n var rowID = rowIDs[rowIdx];\n var comboID = sectionID + '_' + rowID;\n // 当前行数大于已经渲染的行数且dataSource.rowShouldUpdate为true才进行新的一行的渲染。\n var shouldUpdateRow = rowCount >= this._prevRenderedRowsCount &&\n dataSource.rowShouldUpdate(sectionIdx, rowIdx);\n var row =\n <StaticRenderer\n key={'r_' + comboID}\n shouldUpdate={!!shouldUpdateRow}\n render={this.props.renderRow.bind(\n null,\n dataSource.getRowData(sectionIdx, rowIdx),\n sectionID,\n rowID,\n this._onRowHighlighted\n )}\n />;\n bodyComponents.push(row);\n totalIndex++;\n</code></pre>\n\n<p>最后就要看看 <code>dataSource.rowShouldUpdate</code> 发生了什么,打开 <code>ListViewDataSource</code> 236行:</p>\n\n<pre><code>rowShouldUpdate(sectionIndex: number, rowIndex: number): bool { \n // 尼玛_dirtyRows是啥\n var needsUpdate = this._dirtyRows[sectionIndex][rowIndex];\n warning(needsUpdate !== undefined,\n 'missing dirtyBit for section, row: ' + sectionIndex + ', ' + rowIndex);\n return needsUpdate;\n }\n</code></pre>\n\n<p>再到376行吧:</p>\n\n<pre><code> this._dirtyRows[sIndex] = [];\n for (var rIndex = 0; rIndex < this.rowIdentities[sIndex].length; rIndex++) {\n var rowID = this.rowIdentities[sIndex][rIndex];\n // 如果sectionID、rowID是新的或者调用我们定义的rowHasChanged返回是true的话就是脏的,就需要更新。\n dirty =\n !prevSectionsHash[sectionID] ||\n !prevRowsHash[sectionID][rowID] ||\n this._rowHasChanged(\n this._getRowData(prevDataBlob, sectionID, rowID),\n this._getRowData(this._dataBlob, sectionID, rowID)\n );\n this._dirtyRows[sIndex].push(!!dirty);\n }\n</code></pre>\n\n<p>到这里我终于发现自己的 row 为何不更新了,我们平时写的 <code>rowHasChanged</code>方法都是直接 <code>===</code> 比较的,如果内容变了引用没变比较返回的就是 <code>true</code> 。这就要追溯到我在 reselect 中的操作了,我只改变了 列表数据的引用,而没有改变其中每一项数据的引用,所以在 <code>rowHasChanged</code> 比较时出了问题。不过改变引用之后,新的问题又来了, <code>rowHasChanged</code>无论如何返回的都是true,然后每次都会更新,这是无法接受的。临时解决方案是,使用 fbjs 中的 <code>shadowEqual</code> 进行比较,而不是简单的 <code>===</code>。 <code>shadowEqual</code> 只会比较第一层的属性,还不是最好的方案,最好的方案我认为是引入 <code>immutablejs</code>。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1466662979518,"created_by":1,"updated_at":1473918178729,"updated_by":1,"published_at":1466843081339,"published_by":1},{"id":16,"uuid":"00f8ea34-3b29-434c-a49c-c4f0fab84839","title":"关于 ES 草案","slug":"guan-yu-es-cao-an","markdown":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=414611126&auto=1&height=66\"></iframe>\n\nbabel6 模块化之后,使用 Presets 来引入 babel 转译时需要支持的特性,此时我们能够看到几个 stage-x 的 persets,一脸懵逼,于是就去了解了一下这是啥。\n\n这一切要从 ES 草案的制定说起。ECMA的第39号技术专家委员会(Technical Committee 39,简称TC39)负责制订ECMAScript标准,成员包括Microsoft、Mozilla、Google等大公司。\n\n任何人都可以向TC39提案,从提案到变成正式标准,需要经历五个阶段。每个阶段的变动都需要由TC39委员会批准。\n\n- Stage 0 - Strawman(展示阶段)\n- Stage 1 - Proposal(征求意见阶段)\n- Stage 2 - Draft(草案阶段)\n- Stage 3 - Candidate(候选人阶段)\n- Stage 4 - Finished(定案阶段) \n\n我们可以在 github 这个仓库查看各个阶段有哪些提案,[各阶段提案](https://github.com/tc39/proposals)。我们可以看到 ` Async Functions ` 已经进入 Stage 3 了呢,😬,这意味着离它进入 ES 规范的日子不远了,不过今年已经的新特性已经发布了哈哈,明年见。今年的规范更新了 ` Array.includes ` 和 指数两个特性。至于为什么今年相较于去年只更新了两个特性,可以看知乎这个回答,相当详细啊[如何评价 ECMAScript 2016(ES7)只新增2个特性?](http://www.zhihu.com/question/39993685)","html":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=414611126&auto=1&height=66\"></iframe>\n\n<p>babel6 模块化之后,使用 Presets 来引入 babel 转译时需要支持的特性,此时我们能够看到几个 stage-x 的 persets,一脸懵逼,于是就去了解了一下这是啥。</p>\n\n<p>这一切要从 ES 草案的制定说起。ECMA的第39号技术专家委员会(Technical Committee 39,简称TC39)负责制订ECMAScript标准,成员包括Microsoft、Mozilla、Google等大公司。</p>\n\n<p>任何人都可以向TC39提案,从提案到变成正式标准,需要经历五个阶段。每个阶段的变动都需要由TC39委员会批准。</p>\n\n<ul>\n<li>Stage 0 - Strawman(展示阶段)</li>\n<li>Stage 1 - Proposal(征求意见阶段)</li>\n<li>Stage 2 - Draft(草案阶段)</li>\n<li>Stage 3 - Candidate(候选人阶段)</li>\n<li>Stage 4 - Finished(定案阶段) </li>\n</ul>\n\n<p>我们可以在 github 这个仓库查看各个阶段有哪些提案,<a href=\"https://github.com/tc39/proposals\">各阶段提案</a>。我们可以看到 <code>Async Functions</code> 已经进入 Stage 3 了呢,😬,这意味着离它进入 ES 规范的日子不远了,不过今年已经的新特性已经发布了哈哈,明年见。今年的规范更新了 <code>Array.includes</code> 和 指数两个特性。至于为什么今年相较于去年只更新了两个特性,可以看知乎这个回答,相当详细啊<a href=\"http://www.zhihu.com/question/39993685\">如何评价 ECMAScript 2016(ES7)只新增2个特性?</a></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1466844324720,"created_by":1,"updated_at":1473918164670,"updated_by":1,"published_at":1466845477628,"published_by":1},{"id":17,"uuid":"cf9bc202-eb88-46f0-ad4c-a31097b78801","title":"最后一次了我的宝贝","slug":"zui-hou-yi-ci-liao-wo-de-bao-bei-2","markdown":"疯疯癫癫了一晚,四瓶酒下去,本以为能让自己睡个好觉,但是不到十二点酒醒来,却再也睡不着了,一遍遍循环着张悬翻唱的 you'll see。想写的很多,还是留在心底吧,花几个不眠之夜慢慢地消化。\n\n谢谢你,我的宝贝,这是最后一次这么叫了。今天的你还是那么地率真、勇敢,我也还是爱你的,但是却没有理由挽留你了,我知道你已经不那么爱我了,我是那么的无奈,像个小丑说这些无聊地话。\n\n不过还是谢谢了,谢谢你给了我这么好的时光,在你身上我学到了很多,祝你幸福!为什么把你删了,是我怕自己控制不值,我还是爱你的啊!还有这首 you'll see 送给你也送给我自己。晚安!最后一次了!应该听不到了吧!\n\n还有,应该说对不起的是我,没有能够兑现我的承诺,没有给你想要的爱情,对不起!\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=26590183&auto=1&height=66\"></iframe>","html":"<p>疯疯癫癫了一晚,四瓶酒下去,本以为能让自己睡个好觉,但是不到十二点酒醒来,却再也睡不着了,一遍遍循环着张悬翻唱的 you'll see。想写的很多,还是留在心底吧,花几个不眠之夜慢慢地消化。</p>\n\n<p>谢谢你,我的宝贝,这是最后一次这么叫了。今天的你还是那么地率真、勇敢,我也还是爱你的,但是却没有理由挽留你了,我知道你已经不那么爱我了,我是那么的无奈,像个小丑说这些无聊地话。</p>\n\n<p>不过还是谢谢了,谢谢你给了我这么好的时光,在你身上我学到了很多,祝你幸福!为什么把你删了,是我怕自己控制不值,我还是爱你的啊!还有这首 you'll see 送给你也送给我自己。晚安!最后一次了!应该听不到了吧!</p>\n\n<p>还有,应该说对不起的是我,没有能够兑现我的承诺,没有给你想要的爱情,对不起!</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=26590183&auto=1&height=66\"></iframe>","image":null,"featured":0,"page":0,"status":"draft","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467047961295,"created_by":1,"updated_at":1467050137342,"updated_by":1,"published_at":1467048907781,"published_by":1},{"id":19,"uuid":"3cdea6e0-1225-42e2-bf91-42bd5bf05cb1","title":"脑洞和感动","slug":"nao-dong-he-gan-dong","markdown":"<embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=3138291&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed>","html":"<p><embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=3138291&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467110745929,"created_by":1,"updated_at":1467111347667,"updated_by":1,"published_at":1467111153989,"published_by":1},{"id":20,"uuid":"217d0fe2-7ee8-44bc-9e26-e5688017cd83","title":"无题","slug":"wu-ti","markdown":"养不了你了,祝你幸福,找到那个‘尹天仇’!\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=27876900&auto=1&height=66\"></iframe>","html":"<p>养不了你了,祝你幸福,找到那个‘尹天仇’!</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"http://music.163.com/outchain/player?type=2&id=27876900&auto=1&height=66\"></iframe>","image":null,"featured":0,"page":0,"status":"draft","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467111739636,"created_by":1,"updated_at":1467120521732,"updated_by":1,"published_at":1467111989657,"published_by":1},{"id":21,"uuid":"e2fa0f4a-0bcf-49de-9520-ee74d98f21f5","title":"关于色彩搭配(基础知识篇)","slug":"guan-yu-se-cai-da-pei","markdown":"App写得差不多了,感觉其中的色彩的运用比较乱,没有统一起来,于是花了一个下午,学习了一下色彩搭配相关的一些知识,准备统一一下App的配色。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=29431066&auto=1&height=66\"></iframe>\n\n###### 一个色环\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f7768iqi36j305k05kweo.jpg)\n伊顿12色环是由近代著名的瑞士色彩学大叔😳约翰内斯•伊顿先生设计的,如上图。他把红、黄蓝作为三原色,而有两种原色不同比例混合混合所得到的为二次色,也叫间色。而由两个间色或是三个三原色混合而得到的颜色为三次色,或者称为复色,包括了除了原色和间色以外的所有颜色。\n\n###### 两种混色\n![](https://ws1.sinaimg.cn/large/006bH5BKgw1f7768wt9q6j30dw05fq3b.jpg)\n色光三原色(加法混色)和色料三原色(减法混色),这两个让我不明觉历啊🤔。水好深啊!\n\n加法混合是指色光的混合,两种以上的光混合在一起,光亮度会提高,混合色的光的总亮度等于相混各色光亮度之和。色光混合中,三原色是朱红、翠绿、蓝紫。这三色光是不能用其它别的色光相混而产生的。\n\n白色光线透过有色滤光片之后,一部分光线被反射而吸收其余的光线,减少掉一部分辐射功率,最后透过的光是两次减光的结果,这样的色彩混合称为减法混合。一般说来,透明性强的染料,混合后具有明显的减光作用???减法混合的三原色是加法混合的三原色的补色,即:翠绿的补色红(品红)、蓝紫的补色黄(淡黄)、朱红的补色蓝(天蓝)。\n\n###### 三种要素\n![](https://ws1.sinaimg.cn/large/006bH5BKgw1f77699dmzhj30go0a5dgk.jpg)\n色相😍😍,即各类色彩的相貌称谓,如大红、普蓝、柠檬黄等。色相是色彩的首要特征,是区别各种不同色彩的最准确的标准。事实上任何黑白灰以外的颜色都有色相的属性,而色相也就是由原色、间色和复色来构成的。\n\n饱和度(纯度),饱和度是指色彩的鲜艳程度,也称色彩的纯度。饱和度取决于该色中含色成分和消色成分(灰色)的比例。含色成分越大,饱和度越大;消色成分越大,饱和度越小。\n\n明度可以简单理解为颜色的亮度,不同的颜色具有不同的明度,例如黄色就比蓝色的明度高。任何色彩都存在明暗变化。其中黄色明度最高,紫色明度最低,绿、红、蓝、橙的明度相近,为中间明度。另外在同一色相的明度中还存在深浅的变化。如绿色中由浅到深有粉绿、淡绿、翠绿等明度变化。 \n\n###### 六种关系\n![](https://ws3.sinaimg.cn/large/006bH5BKgw1f7769n991mj30go0apta8.jpg)\n接下来这个就比较重要啦,也就是各种颜色的关系,从图上我们可以看到,在伊顿12色环上,间隔180度或者两两相视的就是补色,间隔120度的是对比色,间隔90度的是中度色,间隔60度是类似色,间隔30度是相近色,0度就是同色啦,同色好像木有意义吧。\n\n未完待续。。。\n\n\n\n","html":"<p>App写得差不多了,感觉其中的色彩的运用比较乱,没有统一起来,于是花了一个下午,学习了一下色彩搭配相关的一些知识,准备统一一下App的配色。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=29431066&auto=1&height=66\"></iframe>\n\n<h6 id=\"\">一个色环</h6>\n\n<p><img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f7768iqi36j305k05kweo.jpg\" alt=\"\" />\n伊顿12色环是由近代著名的瑞士色彩学大叔😳约翰内斯•伊顿先生设计的,如上图。他把红、黄蓝作为三原色,而有两种原色不同比例混合混合所得到的为二次色,也叫间色。而由两个间色或是三个三原色混合而得到的颜色为三次色,或者称为复色,包括了除了原色和间色以外的所有颜色。</p>\n\n<h6 id=\"\">两种混色</h6>\n\n<p><img src=\"https://ws1.sinaimg.cn/large/006bH5BKgw1f7768wt9q6j30dw05fq3b.jpg\" alt=\"\" />\n色光三原色(加法混色)和色料三原色(减法混色),这两个让我不明觉历啊🤔。水好深啊!</p>\n\n<p>加法混合是指色光的混合,两种以上的光混合在一起,光亮度会提高,混合色的光的总亮度等于相混各色光亮度之和。色光混合中,三原色是朱红、翠绿、蓝紫。这三色光是不能用其它别的色光相混而产生的。</p>\n\n<p>白色光线透过有色滤光片之后,一部分光线被反射而吸收其余的光线,减少掉一部分辐射功率,最后透过的光是两次减光的结果,这样的色彩混合称为减法混合。一般说来,透明性强的染料,混合后具有明显的减光作用???减法混合的三原色是加法混合的三原色的补色,即:翠绿的补色红(品红)、蓝紫的补色黄(淡黄)、朱红的补色蓝(天蓝)。</p>\n\n<h6 id=\"\">三种要素</h6>\n\n<p><img src=\"https://ws1.sinaimg.cn/large/006bH5BKgw1f77699dmzhj30go0a5dgk.jpg\" alt=\"\" />\n色相😍😍,即各类色彩的相貌称谓,如大红、普蓝、柠檬黄等。色相是色彩的首要特征,是区别各种不同色彩的最准确的标准。事实上任何黑白灰以外的颜色都有色相的属性,而色相也就是由原色、间色和复色来构成的。</p>\n\n<p>饱和度(纯度),饱和度是指色彩的鲜艳程度,也称色彩的纯度。饱和度取决于该色中含色成分和消色成分(灰色)的比例。含色成分越大,饱和度越大;消色成分越大,饱和度越小。</p>\n\n<p>明度可以简单理解为颜色的亮度,不同的颜色具有不同的明度,例如黄色就比蓝色的明度高。任何色彩都存在明暗变化。其中黄色明度最高,紫色明度最低,绿、红、蓝、橙的明度相近,为中间明度。另外在同一色相的明度中还存在深浅的变化。如绿色中由浅到深有粉绿、淡绿、翠绿等明度变化。 </p>\n\n<h6 id=\"\">六种关系</h6>\n\n<p><img src=\"https://ws3.sinaimg.cn/large/006bH5BKgw1f7769n991mj30go0apta8.jpg\" alt=\"\" />\n接下来这个就比较重要啦,也就是各种颜色的关系,从图上我们可以看到,在伊顿12色环上,间隔180度或者两两相视的就是补色,间隔120度的是对比色,间隔90度的是中度色,间隔60度是类似色,间隔30度是相近色,0度就是同色啦,同色好像木有意义吧。</p>\n\n<p>未完待续。。。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467268894725,"created_by":1,"updated_at":1473918152221,"updated_by":1,"published_at":1467269256812,"published_by":1},{"id":22,"uuid":"ed8fba41-eab5-471c-b545-371e350cd4a9","title":"关于色彩搭配(实践篇)","slug":"guan-yu-se-cai-da-pei-shi-jian-pian","markdown":"先来简单介绍一下我要重构的 app 的色彩,主要包括以下几个方面,首先是主色调,例如滴滴的主色调是橙色;其次是背景色、字体颜色、图标颜色、边框颜色。相较于原来,本次需要做一个统一。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=247169&auto=1&height=66\"></iframe>\n\n主色调,主色调负责给整个应用一个整体的印象,而这个印象与每种色调所要表达的意思相关,各种颜色所能够表达的意思如下:\n\n- 红色:激情、爱情、愤怒。\n- 橙色:活力、快乐、生气。\n- 黄色:快乐、希望、虚伪。\n- 绿色:新生、富饶、自然\n- 蓝色:冷静、责任、忧伤。\n- 紫色:创造力、高贵、财富。\n- 黑色:神秘、优雅、邪恶。\n- 灰色:忧郁、保守、严谨。\n- 白色:纯洁、干净、贞洁。\n- 褐色:自然、健康、可靠。\n- 米黄色或茶色:保守、虔诚、无趣。\n- 奶油色或乳白色:平静、优雅、纯粹。 \n\n其次是背景色,背景色主要以白和灰色为主,虽然在传统的单色配色方案等中也有不同的选择,但是在 app 中,白色和灰色还更多的选择,在不同场景下也可能有别的选择,需要更多的实践。\n\n字体色,我选择了三种字体色,第一种是较深的,接近黑色,但又不那么浓黑;第二种是灰色;第三种是白色,白色字体主要使用在背景不是灰色或白色的场景,这样字能够比较突出;另外如果要增强色彩感,可以对于一些触发动作的字,使用主色调的颜色。\n\n图标颜色,暂时有三类情况,第一是两个寓意相反的图标情况,对比色能够比较好地表达;对于如尖头等图标,在一个没有色彩要求等场景中时,可以采用与字的颜色相近的色彩;第三种场景就是主色调的图标。\n\n边框:默认边框以灰色为主,而激活的边框则以主色调一致。\n\n以上是个人在简要学习了一些色彩搭配的知识后,对 app 的整体色彩进行了重构的内容。但是对于正规的项目,还是需要设计人员给出设计稿的,这样就省了开发很多的时间呢!不过作为一个有理想的开发,学习一下色彩搭配也是很有用的,比如美化一下自己的博客!\n\n未完待续。。。","html":"<p>先来简单介绍一下我要重构的 app 的色彩,主要包括以下几个方面,首先是主色调,例如滴滴的主色调是橙色;其次是背景色、字体颜色、图标颜色、边框颜色。相较于原来,本次需要做一个统一。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=247169&auto=1&height=66\"></iframe>\n\n<p>主色调,主色调负责给整个应用一个整体的印象,而这个印象与每种色调所要表达的意思相关,各种颜色所能够表达的意思如下:</p>\n\n<ul>\n<li>红色:激情、爱情、愤怒。</li>\n<li>橙色:活力、快乐、生气。</li>\n<li>黄色:快乐、希望、虚伪。</li>\n<li>绿色:新生、富饶、自然</li>\n<li>蓝色:冷静、责任、忧伤。</li>\n<li>紫色:创造力、高贵、财富。</li>\n<li>黑色:神秘、优雅、邪恶。</li>\n<li>灰色:忧郁、保守、严谨。</li>\n<li>白色:纯洁、干净、贞洁。</li>\n<li>褐色:自然、健康、可靠。</li>\n<li>米黄色或茶色:保守、虔诚、无趣。</li>\n<li>奶油色或乳白色:平静、优雅、纯粹。 </li>\n</ul>\n\n<p>其次是背景色,背景色主要以白和灰色为主,虽然在传统的单色配色方案等中也有不同的选择,但是在 app 中,白色和灰色还更多的选择,在不同场景下也可能有别的选择,需要更多的实践。</p>\n\n<p>字体色,我选择了三种字体色,第一种是较深的,接近黑色,但又不那么浓黑;第二种是灰色;第三种是白色,白色字体主要使用在背景不是灰色或白色的场景,这样字能够比较突出;另外如果要增强色彩感,可以对于一些触发动作的字,使用主色调的颜色。</p>\n\n<p>图标颜色,暂时有三类情况,第一是两个寓意相反的图标情况,对比色能够比较好地表达;对于如尖头等图标,在一个没有色彩要求等场景中时,可以采用与字的颜色相近的色彩;第三种场景就是主色调的图标。</p>\n\n<p>边框:默认边框以灰色为主,而激活的边框则以主色调一致。</p>\n\n<p>以上是个人在简要学习了一些色彩搭配的知识后,对 app 的整体色彩进行了重构的内容。但是对于正规的项目,还是需要设计人员给出设计稿的,这样就省了开发很多的时间呢!不过作为一个有理想的开发,学习一下色彩搭配也是很有用的,比如美化一下自己的博客!</p>\n\n<p>未完待续。。。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467618067380,"created_by":1,"updated_at":1473918141096,"updated_by":1,"published_at":1467619563580,"published_by":1},{"id":23,"uuid":"f5668ae7-96d1-48ff-9a50-2a811539d22e","title":"react native 布局","slug":"react-native-bu-ju","markdown":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=27514453&auto=1&height=66\"></iframe>\n\n###### 尺寸单位\n\nreact native 使用的单位是 dp 。 dp 是设备独立像素,与 px 不同,他在像素密度不同的设备上能够拥有一致的表现。\n\n对于图片来说,我们获取图片时需要利用像素密度进行一下转换,如下:\n\n```\n var image = getImage({\n width: 200 * PixelRatio.get(),\n height: 100 * PixelRatio.get()\n });\n <Image source={image} style={{width: 200, height: 100}} />\n```\n\n这样基本就不用做太多屏幕适配的工作了。\n\n###### flex的布局\n\nreact native flex 布局默认的方向为竖直方向,使用 justifyContent 指定主轴方向的对其方式,alignItems 指定侧轴的对齐方式,因此垂直居中通过设置这两个参数就 ok 了。根节点下的view 宽度默认为 100% ,可设置宽度。\n\n###### 网格布局\n\n等分的网格\n\n```\n<View style={styles.flexContainer}>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n cell1\n </Text>\n </View>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n cell2\n </Text>\n </View>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n cell3\n </Text>\n </View>\n </View>\n\n styles = {\n flexContainer: {\n // 容器需要添加direction才能变成让子元素flex\n flexDirection: 'row'\n },\n cell: {\n flex: 1,\n height: 50,\n backgroundColor: '#aaaaaa'\n },\n welcome: {\n fontSize: 20,\n textAlign: 'center',\n margin: 10\n },\n }\n```\n\n左边固定, 右边固定,中间flex的布局\n\n```\n <View style={styles.flexContainer}>\n <View style={styles.cellfixed}>\n <Text style={styles.welcome}>\n fixed\n </Text>\n </View>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n flex\n </Text>\n </View>\n <View style={styles.cellfixed}>\n <Text style={styles.welcome}>\n fixed\n </Text>\n </View>\n </View>\n\n styles = {\n flexContainer: {\n // 容器需要添加direction才能变成让子元素flex\n flexDirection: 'row'\n },\n cell: {\n flex: 1,\n height: 50,\n backgroundColor: '#aaaaaa'\n },\n welcome: {\n fontSize: 20,\n textAlign: 'center',\n margin: 10\n },\n cellfixed: {\n height: 50,\n width: 80,\n backgroundColor: '#fefefe'\n } \n }\n```\n\n###### 绝对定位和相对定位\n\n和css的标准不同的是, 元素容器不用` position:'absolute|relative' ` 。另外测试下,不支持垂直居中的写法,只支持水平居中的写法。\n\n###### padding和margin\n\n在View上设置padding很顺利,没有任何问题, 但是如果在inline元素上设置padding, 发现会出现上面的错误, paddingTop和paddingBottom都被挤成marginBottom了。 按理说,不应该对Text做padding处理, 但是确实有这样的问题存在,所以可以将这个问题mark一下。\n\n我们知道,对于inline元素,设置margin-left和margin-right有效,top和bottom按理是不会生效的, 但是上图的结果可以看到,实际是生效了的。所以现在给我的感觉是Text元素更应该理解为一个不能设置padding的block。\n\n算了不要猜了, 我们看看官方文档怎么说Text,https://facebook.github.io/react-native/docs/text.html\n\n```\n <Text>\n <Text>First part and </Text>\n <Text>second part</Text>\n </Text>\n // Text container: all the text flows as if it was one\n // |First part |\n // |and second |\n // |part |\n\n <View>\n <Text>First part and </Text>\n <Text>second part</Text>\n </View>\n // View container: each text is its own block\n // |First part |\n // |and |\n // |second part|\n```\n\n也就是如果Text元素在Text里边,可以考虑为inline, 如果单独在View里边,那就是Block。 \n下面会专门研究一下文本相关的布局\n\n###### 文本元素\n\n在实践中,我主要定义三种大小的字体,12或13的小号字体,行高20;15-17的中号字体,行高23;24号左右的大号字体,行高27。\n\n字体选择默认的,如果自己引入字体那另当别论。\n\n样式继承最近的父元素的样式。\n\n支持的属性如下:\n\n```\n Attributes.style = {\n color string\n containerBackgroundColor string\n fontFamily string\n fontSize number\n fontStyle enum('normal', 'italic')\n fontWeight enum(\"normal\", 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900')\n lineHeight number\n textAlign enum(\"auto\", 'left', 'right', 'center')\n writingDirection enum(\"auto\", 'ltr', 'rtl')\n }\n```\n\n参考: https://segmentfault.com/a/1190000002658374","html":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=27514453&auto=1&height=66\"></iframe>\n\n<h6 id=\"\">尺寸单位</h6>\n\n<p>react native 使用的单位是 dp 。 dp 是设备独立像素,与 px 不同,他在像素密度不同的设备上能够拥有一致的表现。</p>\n\n<p>对于图片来说,我们获取图片时需要利用像素密度进行一下转换,如下:</p>\n\n<pre><code> var image = getImage({\n width: 200 * PixelRatio.get(),\n height: 100 * PixelRatio.get()\n });\n <Image source={image} style={{width: 200, height: 100}} />\n</code></pre>\n\n<p>这样基本就不用做太多屏幕适配的工作了。</p>\n\n<h6 id=\"flex\">flex的布局</h6>\n\n<p>react native flex 布局默认的方向为竖直方向,使用 justifyContent 指定主轴方向的对其方式,alignItems 指定侧轴的对齐方式,因此垂直居中通过设置这两个参数就 ok 了。根节点下的view 宽度默认为 100% ,可设置宽度。</p>\n\n<h6 id=\"\">网格布局</h6>\n\n<p>等分的网格</p>\n\n<pre><code><View style={styles.flexContainer}> \n <View style={styles.cell}>\n <Text style={styles.welcome}>\n cell1\n </Text>\n </View>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n cell2\n </Text>\n </View>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n cell3\n </Text>\n </View>\n </View>\n\n styles = {\n flexContainer: {\n // 容器需要添加direction才能变成让子元素flex\n flexDirection: 'row'\n },\n cell: {\n flex: 1,\n height: 50,\n backgroundColor: '#aaaaaa'\n },\n welcome: {\n fontSize: 20,\n textAlign: 'center',\n margin: 10\n },\n }\n</code></pre>\n\n<p>左边固定, 右边固定,中间flex的布局</p>\n\n<pre><code> <View style={styles.flexContainer}>\n <View style={styles.cellfixed}>\n <Text style={styles.welcome}>\n fixed\n </Text>\n </View>\n <View style={styles.cell}>\n <Text style={styles.welcome}>\n flex\n </Text>\n </View>\n <View style={styles.cellfixed}>\n <Text style={styles.welcome}>\n fixed\n </Text>\n </View>\n </View>\n\n styles = {\n flexContainer: {\n // 容器需要添加direction才能变成让子元素flex\n flexDirection: 'row'\n },\n cell: {\n flex: 1,\n height: 50,\n backgroundColor: '#aaaaaa'\n },\n welcome: {\n fontSize: 20,\n textAlign: 'center',\n margin: 10\n },\n cellfixed: {\n height: 50,\n width: 80,\n backgroundColor: '#fefefe'\n } \n }\n</code></pre>\n\n<h6 id=\"\">绝对定位和相对定位</h6>\n\n<p>和css的标准不同的是, 元素容器不用<code>position:'absolute|relative'</code> 。另外测试下,不支持垂直居中的写法,只支持水平居中的写法。</p>\n\n<h6 id=\"paddingmargin\">padding和margin</h6>\n\n<p>在View上设置padding很顺利,没有任何问题, 但是如果在inline元素上设置padding, 发现会出现上面的错误, paddingTop和paddingBottom都被挤成marginBottom了。 按理说,不应该对Text做padding处理, 但是确实有这样的问题存在,所以可以将这个问题mark一下。</p>\n\n<p>我们知道,对于inline元素,设置margin-left和margin-right有效,top和bottom按理是不会生效的, 但是上图的结果可以看到,实际是生效了的。所以现在给我的感觉是Text元素更应该理解为一个不能设置padding的block。</p>\n\n<p>算了不要猜了, 我们看看官方文档怎么说Text,<a href=\"https://facebook.github.io/react-native/docs/text.html\">https://facebook.github.io/react-native/docs/text.html</a></p>\n\n<pre><code> <Text>\n <Text>First part and </Text>\n <Text>second part</Text>\n </Text>\n // Text container: all the text flows as if it was one\n // |First part |\n // |and second |\n // |part |\n\n <View>\n <Text>First part and </Text>\n <Text>second part</Text>\n </View>\n // View container: each text is its own block\n // |First part |\n // |and |\n // |second part|\n</code></pre>\n\n<p>也就是如果Text元素在Text里边,可以考虑为inline, 如果单独在View里边,那就是Block。 \n下面会专门研究一下文本相关的布局</p>\n\n<h6 id=\"\">文本元素</h6>\n\n<p>在实践中,我主要定义三种大小的字体,12或13的小号字体,行高20;15-17的中号字体,行高23;24号左右的大号字体,行高27。</p>\n\n<p>字体选择默认的,如果自己引入字体那另当别论。</p>\n\n<p>样式继承最近的父元素的样式。</p>\n\n<p>支持的属性如下:</p>\n\n<pre><code> Attributes.style = {\n color string\n containerBackgroundColor string\n fontFamily string\n fontSize number\n fontStyle enum('normal', 'italic')\n fontWeight enum(\"normal\", 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900')\n lineHeight number\n textAlign enum(\"auto\", 'left', 'right', 'center')\n writingDirection enum(\"auto\", 'ltr', 'rtl')\n }\n</code></pre>\n\n<p>参考: <a href=\"https://segmentfault.com/a/1190000002658374\">https://segmentfault.com/a/1190000002658374</a></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467619667519,"created_by":1,"updated_at":1473918127435,"updated_by":1,"published_at":1467621393993,"published_by":1},{"id":24,"uuid":"e92a7d4f-bd2a-436e-ae77-9db3d4ccac97","title":"react Stateless Functions(官方文档翻译)","slug":"react-stateless-functions-guan-fang-wen-dang-fan-yi","markdown":"> 中文社区还停留在 0.14.7,文档上有不少落后了,比如 shadowEqual、context 等的知识,所以只能自己来了。\n\n你也可以用一个普通的 JavaScript 函数定义你的 React classes 。例如使用 stateless function 语法:\n\n```\nfunction HelloMessage(props) {\n return <div>Hello {props.name}</div>;\n}\nReactDOM.render(<HelloMessage name=\"Sebastian\" />, mountNode);\n```\n\n或者使用 ES6 的 箭头函数语法:\n\n```\nconst HelloMessage = (props) => <div>Hello {props.name}</div>;\nReactDOM.render(<HelloMessage name=\"Sebastian\" />, mountNode);\n```\n\n这个简化的组件 API 设计为纯函数几它们的参数。这些组件必须不能保留内部的状态,没有支持的实例并且没有生命周期方法。 它们是转换起输入的纯函数,没有副作用。然而,你仍然可以具体说明 ` .propTypes ` 和 ` .defaultProps ` 通过设置它们喂函数的参数,就像在 ES6 class上一样设置。\n\n> 注意:\n因为 stateless functions 没有一个支持的实例,你不能使用 ref 来得到一个stateless function 组件。通常这不是一个 issue,因为 stateless functions 不提供一个必须服从的 AP I。没有一个必须服从的 API,也就没有必须要对实例要求做的事。然而,如果一个用户想要找到 stateless function 组件的 DOM 节点 ,它们必须包裹在一个有状态的组件内,然后用 ref 去访问有状态的包裹组件。\n注意:\n在 React v0.14, stateless functional 组件将不被允许去返回 ` null ` 或 ` false `(变通的方法是返回 ` <noscript /> ` 代替)。这个问题在 React v15 当中被修复了, stateless functional 组件现在被允许返回 ` null ` 。\n\n在理想的情况下,你的大部分组件都将是 stateless functions,因为在将来我们也能够给这些组件带来性能优化的规范,通过避免不需要的检查和内存分配。如果可能的话,这是理想的模式。","html":"<blockquote>\n <p>中文社区还停留在 0.14.7,文档上有不少落后了,比如 shadowEqual、context 等的知识,所以只能自己来了。</p>\n</blockquote>\n\n<p>你也可以用一个普通的 JavaScript 函数定义你的 React classes 。例如使用 stateless function 语法:</p>\n\n<pre><code>function HelloMessage(props) { \n return <div>Hello {props.name}</div>;\n}\nReactDOM.render(<HelloMessage name=\"Sebastian\" />, mountNode); \n</code></pre>\n\n<p>或者使用 ES6 的 箭头函数语法:</p>\n\n<pre><code>const HelloMessage = (props) => <div>Hello {props.name}</div>; \nReactDOM.render(<HelloMessage name=\"Sebastian\" />, mountNode); \n</code></pre>\n\n<p>这个简化的组件 API 设计为纯函数几它们的参数。这些组件必须不能保留内部的状态,没有支持的实例并且没有生命周期方法。 它们是转换起输入的纯函数,没有副作用。然而,你仍然可以具体说明 <code>.propTypes</code> 和 <code>.defaultProps</code> 通过设置它们喂函数的参数,就像在 ES6 class上一样设置。</p>\n\n<blockquote>\n <p>注意:\n 因为 stateless functions 没有一个支持的实例,你不能使用 ref 来得到一个stateless function 组件。通常这不是一个 issue,因为 stateless functions 不提供一个必须服从的 AP I。没有一个必须服从的 API,也就没有必须要对实例要求做的事。然而,如果一个用户想要找到 stateless function 组件的 DOM 节点 ,它们必须包裹在一个有状态的组件内,然后用 ref 去访问有状态的包裹组件。\n 注意:\n 在 React v0.14, stateless functional 组件将不被允许去返回 <code>null</code> 或 <code>false</code>(变通的方法是返回 <code><noscript /></code> 代替)。这个问题在 React v15 当中被修复了, stateless functional 组件现在被允许返回 <code>null</code> 。</p>\n</blockquote>\n\n<p>在理想的情况下,你的大部分组件都将是 stateless functions,因为在将来我们也能够给这些组件带来性能优化的规范,通过避免不需要的检查和内存分配。如果可能的话,这是理想的模式。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467695307621,"created_by":1,"updated_at":1467697494691,"updated_by":1,"published_at":1467696282266,"published_by":1},{"id":25,"uuid":"9b062008-ea10-4824-b493-1b795e1eae9b","title":"小葵唱的《ソラニン》","slug":"xiao-kui-chang-de-soranin","markdown":"小葵的声音好好听啊,单曲循环,元气满满!😊就是木有找到能下载的mp3。\n\n<embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=180951&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed>\n\n思い违いは空のかなた\nさよならだけの人生か\nほんの少しの未来は见えたのに\nさよならなんだ\n\n昔住んでた小さな部屋は今は谁かが住んでんだ\n君に言われたひどい言叶も\n无駄な気がした毎日も\n\nあの日こうしていれば\nあの日に戻れれば\nあの顷の仆にはもう戻れないよ\n\nたとえばゆるい幸せがだらっと続いたとする\nきっと悪い种が芽を出して\nもうさよならんだ\n\n寒い冬の冷えた缶コーヒー 虹色の长いマフラー\n小走りで路地裏を抜けて思い出してみる\n\nたとえばゆるい幸せがだらっと続いたとする\nきっと悪い种が芽を出して\nもうさよならなんだ\n\nさよなら それもいいさ\nどこかで元気でやれよ\nさよなら 仆もどーにかやるさ\nさよなら そうするよ\n\n---\n\n误会在天空的彼端\n难道人生只充满了再见吗\n只差那麼一点就可以看见未来\n却只能说再见\n\n以前住过的小房间现在又是谁住在那里\n无论是你对我说过的过分话语\n还是感觉白费力气的每一天\n\n若说那天我这麼做\n若说能回到那一天\n我已无法再回到那一天\n\n如果说弛缓的幸福缓慢地延续\n这样下去坏种子一定会发芽\n还是只能说再见\n\n寒冷冬天中冷掉的罐装咖啡 七彩的长围巾\n小跑步穿过小巷中 我试著回忆起\n\n如果说弛缓的幸福缓慢地延续\n这样下去坏种子一定会发芽\n还是只能说再见\n\n说再见 这样也好\n无论在哪都要好好地过\n再见了 我也一定有办法的\n说再见 就这样吧\n\n---\n\no mo i chi ga i wa so ra no ka na ta\nsa yo na ra da ke no jin sei ka\nho n no su ko shi no mi rai wa mi e ta no ni\nsa yo na ra na n da\n\nmu ka shi su n de ta chii sa na he ya wa i ma wa da re ka ga su n de n da\nki mi ni i wa re ta hi do i ko ta ba mo\nmu da na ki ga shi ta mai ni chi mo\n\na no hi ko u shi te i re ba\na no hi ni mo do re re ba\na no ko ro no bo ku ni wa mo u mo do re nai yo\n\nta to e ba yu ru i shi a wa se ga da ra tto tsu du i ta to su ru\nki tto wa ru i ta ne ga me o da shi te\nmo u sa yo na ra na n da\n\nsa mu i fu yu no hi e ta kan ko- hi- ni ji i ro no na ga i ma fu ra-\nko ba shi ri de ro ji u ra o nu ke te o mo i da shi te mi ru\n\nta to e ba yu ru i shi a wa se ga da ra tto tsu du i ta to su ru\nki tto wa ru i ta ne ga me o da shi te\nmo u sa yo na ra na n da\n\nsa ya na ra so re mo i i sa\ndo ko ka de gen ki de ya re yo\nsa yo na ra bo ku mo do- ni ka ya ru sa\nsa yo na ra so u su ru yo","html":"<p>小葵的声音好好听啊,单曲循环,元气满满!😊就是木有找到能下载的mp3。</p>\n\n<p><embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=180951&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed></p>\n\n<p>思い违いは空のかなた\nさよならだけの人生か\nほんの少しの未来は见えたのに\nさよならなんだ</p>\n\n<p>昔住んでた小さな部屋は今は谁かが住んでんだ\n君に言われたひどい言叶も\n无駄な気がした毎日も</p>\n\n<p>あの日こうしていれば\nあの日に戻れれば\nあの顷の仆にはもう戻れないよ</p>\n\n<p>たとえばゆるい幸せがだらっと続いたとする\nきっと悪い种が芽を出して\nもうさよならんだ</p>\n\n<p>寒い冬の冷えた缶コーヒー 虹色の长いマフラー\n小走りで路地裏を抜けて思い出してみる</p>\n\n<p>たとえばゆるい幸せがだらっと続いたとする\nきっと悪い种が芽を出して\nもうさよならなんだ</p>\n\n<p>さよなら それもいいさ\nどこかで元気でやれよ\nさよなら 仆もどーにかやるさ\nさよなら そうするよ</p>\n\n<p>---</p>\n\n<p>误会在天空的彼端\n难道人生只充满了再见吗\n只差那麼一点就可以看见未来\n却只能说再见</p>\n\n<p>以前住过的小房间现在又是谁住在那里\n无论是你对我说过的过分话语\n还是感觉白费力气的每一天</p>\n\n<p>若说那天我这麼做\n若说能回到那一天\n我已无法再回到那一天</p>\n\n<p>如果说弛缓的幸福缓慢地延续\n这样下去坏种子一定会发芽\n还是只能说再见</p>\n\n<p>寒冷冬天中冷掉的罐装咖啡 七彩的长围巾\n小跑步穿过小巷中 我试著回忆起</p>\n\n<p>如果说弛缓的幸福缓慢地延续\n这样下去坏种子一定会发芽\n还是只能说再见</p>\n\n<p>说再见 这样也好\n无论在哪都要好好地过\n再见了 我也一定有办法的\n说再见 就这样吧</p>\n\n<p>---</p>\n\n<p>o mo i chi ga i wa so ra no ka na ta <br />\nsa yo na ra da ke no jin sei ka <br />\nho n no su ko shi no mi rai wa mi e ta no ni <br />\nsa yo na ra na n da</p>\n\n<p>mu ka shi su n de ta chii sa na he ya wa i ma wa da re ka ga su n de n da <br />\nki mi ni i wa re ta hi do i ko ta ba mo <br />\nmu da na ki ga shi ta mai ni chi mo</p>\n\n<p>a no hi ko u shi te i re ba <br />\na no hi ni mo do re re ba <br />\na no ko ro no bo ku ni wa mo u mo do re nai yo</p>\n\n<p>ta to e ba yu ru i shi a wa se ga da ra tto tsu du i ta to su ru <br />\nki tto wa ru i ta ne ga me o da shi te <br />\nmo u sa yo na ra na n da</p>\n\n<p>sa mu i fu yu no hi e ta kan ko- hi- ni ji i ro no na ga i ma fu ra- <br />\nko ba shi ri de ro ji u ra o nu ke te o mo i da shi te mi ru</p>\n\n<p>ta to e ba yu ru i shi a wa se ga da ra tto tsu du i ta to su ru <br />\nki tto wa ru i ta ne ga me o da shi te <br />\nmo u sa yo na ra na n da</p>\n\n<p>sa ya na ra so re mo i i sa <br />\ndo ko ka de gen ki de ya re yo <br />\nsa yo na ra bo ku mo do- ni ka ya ru sa <br />\nsa yo na ra so u su ru yo</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1467696313896,"created_by":1,"updated_at":1467762751870,"updated_by":1,"published_at":1467696583407,"published_by":1},{"id":26,"uuid":"6c84f077-565f-4041-b904-5f22ce2b3bf4","title":"Jersey 异常处理","slug":"jerseyyi-chang-chu-li","markdown":"今天的两篇文章都是关于异常处理的,一篇是服务端 Jersey 的异常处理,另一篇是 Fetch API Promise 的异常处理。废话不多说,直接开始。\n\n根据 Jersey 官方文档的说法,Jersey 异常处理有两种方式:\n\n第一是继承 WebApplicationException,不继承默认的状态码为 500,继承后可以修改成对应的状态码,下面是官网的一个例子:\n\n```\npublic class CustomNotFoundException extends WebApplicationException {\n \n /**\n * Create a HTTP 404 (Not Found) exception.\n */\n public CustomNotFoundException() {\n super(Responses.notFound().build());\n }\n \n /**\n * Create a HTTP 404 (Not Found) exception.\n * @param message the String that is the entity of the 404 response.\n */\n public CustomNotFoundException(String message) {\n super(Response.status(Responses.NOT_FOUND).\n entity(message).type(\"text/plain\").build());\n }\n}\n```\n\n第二是针对一些现有的异常,官网建议使用实现 javax.ws.rs.ext.ExceptionMapper<T> 的方式,T为所要抛出的异常类型。当 Jersey 捕获到T异常,就返回实现类的响应。\n\n当抛出 RuntimeException 异常,就会返回404异常。需要注意的是:实现类要加 @Provider 注解,而且要放在 Jersey 资源所在的包路径,以便jersey扫描到。\n\n下面是官网的例子:\n\n```\n@Provider\npublic class EntityNotFoundMapper implements ExceptionMapper<javax.persistence.EntityNotFoundException> {\n public Response toResponse(javax.persistence.EntityNotFoundException ex) {\n return Response.status(404).\n entity(ex.getMessage()).\n type(\"text/plain\").\n build();\n }\n}\n```\n\n了解了这些以后,需要针对接口调用的异常进行一个较为统一的处理,使得客户端能够等到统一格式的错误信息,而不会出现 JSON Parse 的错误。目前我的做法是加一个判断,针对 MediaType 为 application/json 的异常(包括 WebApplicationException 抛出的异常)进行 Response 的统一包装,来达到给客户端统一返回结果的目的。如果说要更加规范地解决这个问题,那应当是创建各个情况的异常类继承不同场景的基类,然后用 ExceptionMapper 统一处理该基类的异常,同时也能够区分 rest 接口的异常和网页端抛出的异常。\n\n关于异常更复杂,偏原理的部分请看参考资料,不在本文讨论范围,因为那可以写很长一篇文章了,我这里只关注这个项目中 Jersey 端的实现。\n\n参考资料:\n\n1、 [文档传送门](https://jersey.java.net/documentation/latest/representations.html#d0e6754)\n2、[Jersey Rest 异常统一处理机制](http://blog.csdn.net/niityzu/article/details/51112878)\n3、[Jersey框架的统一异常处理机制](http://redhacker.iteye.com/blog/1924071)\n4、[Checked 和 UnChecked 异常 的使用场合](http://www.tuicool.com/articles/ramuyu)","html":"<p>今天的两篇文章都是关于异常处理的,一篇是服务端 Jersey 的异常处理,另一篇是 Fetch API Promise 的异常处理。废话不多说,直接开始。</p>\n\n<p>根据 Jersey 官方文档的说法,Jersey 异常处理有两种方式:</p>\n\n<p>第一是继承 WebApplicationException,不继承默认的状态码为 500,继承后可以修改成对应的状态码,下面是官网的一个例子:</p>\n\n<pre><code>public class CustomNotFoundException extends WebApplicationException {\n\n /**\n * Create a HTTP 404 (Not Found) exception.\n */\n public CustomNotFoundException() {\n super(Responses.notFound().build());\n }\n\n /**\n * Create a HTTP 404 (Not Found) exception.\n * @param message the String that is the entity of the 404 response.\n */\n public CustomNotFoundException(String message) {\n super(Response.status(Responses.NOT_FOUND).\n entity(message).type(\"text/plain\").build());\n }\n}\n</code></pre>\n\n<p>第二是针对一些现有的异常,官网建议使用实现 javax.ws.rs.ext.ExceptionMapper<T> 的方式,T为所要抛出的异常类型。当 Jersey 捕获到T异常,就返回实现类的响应。</p>\n\n<p>当抛出 RuntimeException 异常,就会返回404异常。需要注意的是:实现类要加 @Provider 注解,而且要放在 Jersey 资源所在的包路径,以便jersey扫描到。</p>\n\n<p>下面是官网的例子:</p>\n\n<pre><code>@Provider\npublic class EntityNotFoundMapper implements ExceptionMapper<javax.persistence.EntityNotFoundException> { \n public Response toResponse(javax.persistence.EntityNotFoundException ex) {\n return Response.status(404).\n entity(ex.getMessage()).\n type(\"text/plain\").\n build();\n }\n}\n</code></pre>\n\n<p>了解了这些以后,需要针对接口调用的异常进行一个较为统一的处理,使得客户端能够等到统一格式的错误信息,而不会出现 JSON Parse 的错误。目前我的做法是加一个判断,针对 MediaType 为 application/json 的异常(包括 WebApplicationException 抛出的异常)进行 Response 的统一包装,来达到给客户端统一返回结果的目的。如果说要更加规范地解决这个问题,那应当是创建各个情况的异常类继承不同场景的基类,然后用 ExceptionMapper 统一处理该基类的异常,同时也能够区分 rest 接口的异常和网页端抛出的异常。</p>\n\n<p>关于异常更复杂,偏原理的部分请看参考资料,不在本文讨论范围,因为那可以写很长一篇文章了,我这里只关注这个项目中 Jersey 端的实现。</p>\n\n<p>参考资料:</p>\n\n<p>1、 <a href=\"https://jersey.java.net/documentation/latest/representations.html#d0e6754\">文档传送门</a> <br />\n2、<a href=\"http://blog.csdn.net/niityzu/article/details/51112878\">Jersey Rest 异常统一处理机制</a> <br />\n3、<a href=\"http://redhacker.iteye.com/blog/1924071\">Jersey框架的统一异常处理机制</a> <br />\n4、<a href=\"http://www.tuicool.com/articles/ramuyu\">Checked 和 UnChecked 异常 的使用场合</a></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1468372912162,"created_by":1,"updated_at":1471010424392,"updated_by":1,"published_at":1470999361119,"published_by":1},{"id":27,"uuid":"7107ba84-ee23-401d-90a0-6f72c9919ec9","title":"无题","slug":"dyht","markdown":"最近忙屎了,没时间写东西🙄。马上要开工,随便写点。最近大鱼可以说是在舆论的风口浪尖,虽然没去电影院看,但是通过b站的一个个影评,已是略知一二了。\n\n作为一部花了12年撸出的一个东西,确实在剧情上如大家所说有点不如人意,但是画面、配乐都是在向国外看齐的。正如一个up主说,大鱼给他的感觉绝不是让人失望的那种,只是有点微妙。这种微妙感,来源于12年的大鱼承载了太多,来源于作者不那么会讲故事。不过对于国漫来说,这不能说是划时代,却可以称得上是一个新的开始。大雨让我看到仍有那么一批人在认真地做着动画,这是最让我欣慰的。\n\n下面是昨天在b站看到的一组和我一样大的学生做的动画,瞬间对国漫燃起了信心🤗。有这么多有才的少年在追逐着梦想,希望10年、20年后能够看到每一个短片能够出线在大荧幕之上。干活去了!😭\n\n<embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=5278719&page=4\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed>","html":"<p>最近忙屎了,没时间写东西🙄。马上要开工,随便写点。最近大鱼可以说是在舆论的风口浪尖,虽然没去电影院看,但是通过b站的一个个影评,已是略知一二了。</p>\n\n<p>作为一部花了12年撸出的一个东西,确实在剧情上如大家所说有点不如人意,但是画面、配乐都是在向国外看齐的。正如一个up主说,大鱼给他的感觉绝不是让人失望的那种,只是有点微妙。这种微妙感,来源于12年的大鱼承载了太多,来源于作者不那么会讲故事。不过对于国漫来说,这不能说是划时代,却可以称得上是一个新的开始。大雨让我看到仍有那么一批人在认真地做着动画,这是最让我欣慰的。</p>\n\n<p>下面是昨天在b站看到的一组和我一样大的学生做的动画,瞬间对国漫燃起了信心🤗。有这么多有才的少年在追逐着梦想,希望10年、20年后能够看到每一个短片能够出线在大荧幕之上。干活去了!😭</p>\n\n<p><embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=5278719&page=4\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1468459601710,"created_by":1,"updated_at":1468460846200,"updated_by":1,"published_at":1468460846201,"published_by":1},{"id":28,"uuid":"8aa4ff0b-b33b-4f2c-8741-d86ac04536c5","title":"转:CET,UTC,GMT,CST几种常见时间概述","slug":"zhuan-cet-utc-gmt-cstji-chong-chang-jian-shi-jian-gai-shu","markdown":"###### CET(欧洲中部时间)\n\n欧洲中部时间(英語:Central European Time,CET)是比世界标准时间(UTC)早一个小时的时区名称之一。它被大部分欧洲国家和部分北非国家采用。\n\n冬季时间为UTC+1,夏季欧洲夏令时为UTC+2。\n\n###### WET(欧洲西部时间)\n\n欧洲西部时间(Western European Time,缩写WET)和世界标准时间(UTC)相同。\n\n###### EET(欧洲东部时间)\n\n欧洲东部时间(Eastern European Time,缩写EET)是比世界标准时间(UTC)早二个小时的时区名称之一。它被部分欧洲国家、北非国家和中东国家采用。\n\n###### UTC(世界标准时间)\n\n协调世界时,又称世界标准时间或世界協調時間,简称UTC(从英文「Coordinated Universal Time」/法文「Temps Universel Cordonné」而来),是最主要的世界時間標準,其以原子时秒长为基础,在时刻上尽量接近于格林尼治平時。\n\n###### GMT(格林尼治平时)\n\n格林尼治平时(又称格林尼治平均时间或格林尼治标准时间,旧译格林威治标准时间;英语:Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义在通过那里的经线。\n\n自1924年2月5日开始,格林尼治天文台每隔一小时会向全世界发放调时信息。\n\n理论上来说,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能与实际的太阳时有误差,最大误差达16分钟。\n\n由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治时间已经不再被作为标准时间使用。现在的标准时间,是由原子钟报时的协调世界时(UTC)。\n\n###### CST(北京时间)\n\n北京时间,China Standard Time,中国标准时间。在时区划分上,属东八区,比协调世界时早8小时,记为UTC+8。\n\n不过这个CST这个缩写比较纠结的是它可以同时代表四个不同的时间:\n\nCentral Standard Time (USA) UT-6:00\nCentral Standard Time (Australia) UT+9:30\nChina Standard Time UT+8:00\nCuba Standard Time UT-4:00\n因此你平时编写程序过程中也有可能遇到javascript客户端时间和服务端时间不一致的问题,编程的时候还是要多注意。\n\n###### ISO 日期和时间的表示方法\n\n国际标准化组织的国际标准ISO 8601是日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。目前是第三版ISO8601:2004以替代第一版ISO8601:1988與第二版ISO8601:2000。\n\n这是仅仅就只是一个日期时间的表示方法,我们用这样的方法表示一个UTC时间。","html":"<h6 id=\"cet\">CET(欧洲中部时间)</h6>\n\n<p>欧洲中部时间(英語:Central European Time,CET)是比世界标准时间(UTC)早一个小时的时区名称之一。它被大部分欧洲国家和部分北非国家采用。</p>\n\n<p>冬季时间为UTC+1,夏季欧洲夏令时为UTC+2。</p>\n\n<h6 id=\"wet\">WET(欧洲西部时间)</h6>\n\n<p>欧洲西部时间(Western European Time,缩写WET)和世界标准时间(UTC)相同。</p>\n\n<h6 id=\"eet\">EET(欧洲东部时间)</h6>\n\n<p>欧洲东部时间(Eastern European Time,缩写EET)是比世界标准时间(UTC)早二个小时的时区名称之一。它被部分欧洲国家、北非国家和中东国家采用。</p>\n\n<h6 id=\"utc\">UTC(世界标准时间)</h6>\n\n<p>协调世界时,又称世界标准时间或世界協調時間,简称UTC(从英文「Coordinated Universal Time」/法文「Temps Universel Cordonné」而来),是最主要的世界時間標準,其以原子时秒长为基础,在时刻上尽量接近于格林尼治平時。</p>\n\n<h6 id=\"gmt\">GMT(格林尼治平时)</h6>\n\n<p>格林尼治平时(又称格林尼治平均时间或格林尼治标准时间,旧译格林威治标准时间;英语:Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义在通过那里的经线。</p>\n\n<p>自1924年2月5日开始,格林尼治天文台每隔一小时会向全世界发放调时信息。</p>\n\n<p>理论上来说,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能与实际的太阳时有误差,最大误差达16分钟。</p>\n\n<p>由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治时间已经不再被作为标准时间使用。现在的标准时间,是由原子钟报时的协调世界时(UTC)。</p>\n\n<h6 id=\"cst\">CST(北京时间)</h6>\n\n<p>北京时间,China Standard Time,中国标准时间。在时区划分上,属东八区,比协调世界时早8小时,记为UTC+8。</p>\n\n<p>不过这个CST这个缩写比较纠结的是它可以同时代表四个不同的时间:</p>\n\n<p>Central Standard Time (USA) UT-6:00 <br />\nCentral Standard Time (Australia) UT+9:30 <br />\nChina Standard Time UT+8:00 <br />\nCuba Standard Time UT-4:00 <br />\n因此你平时编写程序过程中也有可能遇到javascript客户端时间和服务端时间不一致的问题,编程的时候还是要多注意。</p>\n\n<h6 id=\"iso\">ISO 日期和时间的表示方法</h6>\n\n<p>国际标准化组织的国际标准ISO 8601是日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。目前是第三版ISO8601:2004以替代第一版ISO8601:1988與第二版ISO8601:2000。</p>\n\n<p>这是仅仅就只是一个日期时间的表示方法,我们用这样的方法表示一个UTC时间。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1468468310903,"created_by":1,"updated_at":1468469618333,"updated_by":1,"published_at":1468469618334,"published_by":1},{"id":29,"uuid":"026a8240-4811-4fa0-af76-7301460de880","title":"生存罪","slug":"react-native-xing-neng-zui-xin-guan-fang-wen-dang-fan-yi","markdown":"表白 up 主,剪得太棒了,又多了好多剧追!bgm 雨——清竜人,1P 2P 一起服用效果更佳,剩下的不多说了,慢慢感受吧!\n\n<embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=4753273&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed>","html":"<p>表白 up 主,剪得太棒了,又多了好多剧追!bgm 雨——清竜人,1P 2P 一起服用效果更佳,剩下的不多说了,慢慢感受吧!</p>\n\n<p><embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=4753273&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1468483412197,"created_by":1,"updated_at":1471064171790,"updated_by":1,"published_at":1471064171791,"published_by":1},{"id":30,"uuid":"e043b29e-50be-470f-9a35-e7c8d04a7bc2","title":"js Date的国际化、格式化的探究","slug":"js","markdown":"这一篇是继续上一篇而写的,大致背景就是我在 react native 中需要实现格式化时间。我首先在 MDN 上找到的是下面几个方法:\n\n```\nDate.prototype.toLocaleDateString()\nDate.prototype.toLocaleFormat()\nDate.prototype.toLocaleString()\nDate.prototype.toLocaleTimeString()\n```\n\n` Date.prototype.toLocaleFormat() ` 方法不是 ES 标准当中,所以首先就舍弃了。专向剩下的三个方法,这三个方法都可以传入两个参数 ` locates ` 和 ` options ` 。下面简单介绍下这两个参数:\n\n` locales ` 是个字符串,指定单个[语言标签](https://www.w3.org/International/articles/language-tags/),或者包含多个语言标签的类数组对象。语言标签如下面的字符串:en(普通英语),de-AT(奥地利德语),zh-Hant-TW(台湾使用的繁体中文)。语言标签可以包含一个“Unicode扩展”,形式为-u-key1-value1-key2-value2..., 其中每个key是“扩展key”。不同的构造函数对此进行具体解释。\n\n` opions ` 是个对象,其属性(如果不存在,就赋值为undefined)决定格式化器(formatter)和整理器(collator)的行为。精确的解释由构造函数决定。\n\n给定区域信息和选项,实现会尝试生成近似理想行为的最接近行为。Firefox 支持用于整理(collation)的400+区域,用于date/time和数字格式化的600+区域,所以很可能(但不保证)你想要的区域是被支持的。\n\n这两个实际上是 js 的国际化 api 里的内容,js 国际化 api 包括了 日期、数字等的格式化,不过不是本文的重点,想了解可以参考[此文](http://www.open-open.com/lib/view/open1418779460683.html#articleHeader8),虽然两年前的但是讲的很细。\n\n于是我开始使用 ` locales ` 和简单的正则来格式化我的时间,如下:\n\n```\n// 用到的地方不多,所以没用写成通用的format\nfunction DateFormat(now) {\n return now.toLocaleDateString('zh-CN').substring(5).replace(/\\//g, '月').concat('日 ', now.toLocaleTimeString().slice(0, -3));\n}\n```\n\n这段代码在 ios 下感觉良好,但是今天测试流程用了我自己的安卓机,世界就崩塌了🙄,时间是乱的。然后试了下,发现无论传入什么 ` locates ` 在安卓下都没有效果。擦擦擦,那肯定是兼容性的问题了,再次来到 MDN 翻到兼容性的地方,我方了:\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f7767bp42kj311c0asdi0.jpg)\n\n然后只能去找别的方式了,最后用的是正则表达式,亲测可用,如下:\n\n```\n// 对Date的扩展,将 Date 转化为指定格式的String \n// 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, \n// 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) \n// 例子: \n// (new Date()).Format(\"yyyy-MM-dd hh:mm:ss.S\") ==> 2006-07-02 08:09:04.423 \n// (new Date()).Format(\"yyyy-M-d h:m:s.S\") ==> 2006-7-2 8:9:4.18 \nDate.prototype.Format = function(fmt) \n{ //author: meizz \n var o = { \n \"M+\" : this.getMonth()+1, //月份 \n \"d+\" : this.getDate(), //日 \n \"h+\" : this.getHours(), //小时 \n \"m+\" : this.getMinutes(), //分 \n \"s+\" : this.getSeconds(), //秒 \n \"q+\" : Math.floor((this.getMonth()+3)/3), //季度 \n \"S\" : this.getMilliseconds() //毫秒 \n }; \n if(/(y+)/.test(fmt)) \n fmt=fmt.replace(RegExp.$1, (this.getFullYear()+\"\").substr(4 - RegExp.$1.length)); \n for(var k in o) \n if(new RegExp(\"(\"+ k +\")\").test(fmt)) \n fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : ((\"00\"+ o[k]).substr((\"\"+ o[k]).length))); \n return fmt; \n} \n```\n\n虽然最后完成了,但是对于 js 国际化和正则的部分内容,需要做更深入的研究!开饭了!😬\n\n另外如果不想折腾的话,[moment](https://github.com/moment/moment)这个库可能是一个不错的选择。😜","html":"<p>这一篇是继续上一篇而写的,大致背景就是我在 react native 中需要实现格式化时间。我首先在 MDN 上找到的是下面几个方法:</p>\n\n<pre><code>Date.prototype.toLocaleDateString() \nDate.prototype.toLocaleFormat() \nDate.prototype.toLocaleString() \nDate.prototype.toLocaleTimeString() \n</code></pre>\n\n<p><code>Date.prototype.toLocaleFormat()</code> 方法不是 ES 标准当中,所以首先就舍弃了。专向剩下的三个方法,这三个方法都可以传入两个参数 <code>locates</code> 和 <code>options</code> 。下面简单介绍下这两个参数:</p>\n\n<p><code>locales</code> 是个字符串,指定单个<a href=\"https://www.w3.org/International/articles/language-tags/\">语言标签</a>,或者包含多个语言标签的类数组对象。语言标签如下面的字符串:en(普通英语),de-AT(奥地利德语),zh-Hant-TW(台湾使用的繁体中文)。语言标签可以包含一个“Unicode扩展”,形式为-u-key1-value1-key2-value2..., 其中每个key是“扩展key”。不同的构造函数对此进行具体解释。</p>\n\n<p><code>opions</code> 是个对象,其属性(如果不存在,就赋值为undefined)决定格式化器(formatter)和整理器(collator)的行为。精确的解释由构造函数决定。</p>\n\n<p>给定区域信息和选项,实现会尝试生成近似理想行为的最接近行为。Firefox 支持用于整理(collation)的400+区域,用于date/time和数字格式化的600+区域,所以很可能(但不保证)你想要的区域是被支持的。</p>\n\n<p>这两个实际上是 js 的国际化 api 里的内容,js 国际化 api 包括了 日期、数字等的格式化,不过不是本文的重点,想了解可以参考<a href=\"http://www.open-open.com/lib/view/open1418779460683.html#articleHeader8\">此文</a>,虽然两年前的但是讲的很细。</p>\n\n<p>于是我开始使用 <code>locales</code> 和简单的正则来格式化我的时间,如下:</p>\n\n<pre><code>// 用到的地方不多,所以没用写成通用的format\nfunction DateFormat(now) { \n return now.toLocaleDateString('zh-CN').substring(5).replace(/\\//g, '月').concat('日 ', now.toLocaleTimeString().slice(0, -3));\n}\n</code></pre>\n\n<p>这段代码在 ios 下感觉良好,但是今天测试流程用了我自己的安卓机,世界就崩塌了🙄,时间是乱的。然后试了下,发现无论传入什么 <code>locates</code> 在安卓下都没有效果。擦擦擦,那肯定是兼容性的问题了,再次来到 MDN 翻到兼容性的地方,我方了:\n<img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f7767bp42kj311c0asdi0.jpg\" alt=\"\" /></p>\n\n<p>然后只能去找别的方式了,最后用的是正则表达式,亲测可用,如下:</p>\n\n<pre><code>// 对Date的扩展,将 Date 转化为指定格式的String \n// 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, \n// 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) \n// 例子: \n// (new Date()).Format(\"yyyy-MM-dd hh:mm:ss.S\") ==> 2006-07-02 08:09:04.423 \n// (new Date()).Format(\"yyyy-M-d h:m:s.S\") ==> 2006-7-2 8:9:4.18 \nDate.prototype.Format = function(fmt) \n{ //author: meizz \n var o = { \n \"M+\" : this.getMonth()+1, //月份 \n \"d+\" : this.getDate(), //日 \n \"h+\" : this.getHours(), //小时 \n \"m+\" : this.getMinutes(), //分 \n \"s+\" : this.getSeconds(), //秒 \n \"q+\" : Math.floor((this.getMonth()+3)/3), //季度 \n \"S\" : this.getMilliseconds() //毫秒 \n }; \n if(/(y+)/.test(fmt)) \n fmt=fmt.replace(RegExp.$1, (this.getFullYear()+\"\").substr(4 - RegExp.$1.length)); \n for(var k in o) \n if(new RegExp(\"(\"+ k +\")\").test(fmt)) \n fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : ((\"00\"+ o[k]).substr((\"\"+ o[k]).length))); \n return fmt; \n} \n</code></pre>\n\n<p>虽然最后完成了,但是对于 js 国际化和正则的部分内容,需要做更深入的研究!开饭了!😬</p>\n\n<p>另外如果不想折腾的话,<a href=\"https://github.com/moment/moment\">moment</a>这个库可能是一个不错的选择。😜</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1468860542709,"created_by":1,"updated_at":1472195793038,"updated_by":1,"published_at":1468899840142,"published_by":1},{"id":31,"uuid":"40e9f305-a20a-4f5c-b7d3-2fe928b04664","title":"自己撸表单验证插件——简陋版","slug":"zi-ji-lu-biao-dan-yan-zheng-cha-jian-jian-lou-ban","markdown":"写之前先吐槽一下:昨晚半夜起来好像把眼镜踩坏了😭,下午只好去了趟眼镜店,顶着杭城下午两点的太阳,也是很拼。没想到传来了更大的噩耗🙄,测散光时说我原来的眼镜散光做反了,没得救了😒。最后只好勉强得整了一下,但愿新镜片能够慢慢调整好😂。\n\n然后是正片,今天鱼头说把司机招募的验证加上去,刚好写完后台消息的AOP没什么事,所以我就寻思写的一劳永逸的验证模块,岂不是很好很nice。不过作为懒人还是先去 github 上着了一番,不过没有比较适合现在的项目的。主要原因还是页面已经完成了,改用其它组件的工程量太大。所以最后决定自己整一个不侵入现有组件的,只关注验证逻辑,可扩展的验证模块。\n\n先来看看引入插件之前的的表单验证:\n\n```\nhandleSubmit() {\n if(!this.state.credit_card){\n Alert.alert('必须填写银行卡号!');\n return;\n }\n if(!this.state.rest || this.state.rest < 0){\n Alert.alert('请填写正确的提现金额!');\n return;\n }\n if(this.state.rest > this.props.driver.driver_state.rest){\n Alert.alert('提现金额不能大于余额!');\n return;\n }\n rest.post...\n }\n```\n\n可以看到是通过 if 进行判断,这种做法,代码十分丑陋,而且复用性十分低。有什么办法能够改进上面的代码呢!进一步思考,其实表单验证的逻辑无非是对某个表单的输入,进行相关逻辑的判断。进行的逻辑判断是不变的,而判断的方式是可以改变的。是不是有点感觉了,我们可以把不变的逻辑固定下来,而可变的策略抽离出来。说到底就是策略模式了,策略模式就是通过把可变的判断策略提取出来,帮助我们省去那些 ` if ` 。废话不多说,先来看看在 web 上我们是如何使用的:[出处](http://brizer.github.io/2016/05/04/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5/)\n\n```\n//定义验证策略\nvar strategies = {\n inNonEmpty:function(value,errorMsg){//不能为空\n if(value === ''){\n return errorMsg;\n }\n },\n minLength:function(value,length,errorMsg){//最小长度\n if(value.length < length){\n return errorMsg;\n }\n },\n isReg:function(value,reg,errorMsg){//正则匹配\n if(!reg.test(value)){\n return errorMsg;\n }\n }\n};\n//定义验证类\nvar Validator = function(){\n this.cache = [];//保存效验规则\n};\n//添加验证规则\nValidator.prototype.add = function(dom,rules){\n var that = this;\n //多条规则分别对应\n for(var i = 0 ,rule; rule = rules[i++];){\n (function(rule){\n var stratgyAry = rule.strategy.split(':');\n var errorMsg = rule.errorMsg;\n\t\t\t//将数据按照策略的格式塞入参数数组\n that.cache.push(function(){\n var strategy = strategyAry.shift();\n strategyAry.unshift(dom.value);\n strategyAry.push(errorMsg);\n return strategies[strategy].apply(dom,strategyAry);\n });\n })(rule)\n }\n};\nValidator.prototype.start = function(){\n //依次验证\n for(var i = 0,validatorFunc;validatorFunc = this.cache[i++];){\n var errorMsg = validatorFunc();\n if(errorMsg){\n return errorMsg;\n }\n }\n};\n\n//调用代码\nvar validataFunc = function(){\n var validator = new Validator();\n //多个条件判断\n validator.add(form.name,[{\n strategy:'isNonEmpty',\n errorMsg:'用户不能为空'\n },{\n strategy:'minLength:10',\n errorMsg:'用户长度不能小于10位'\n }]);\n validator.add(form.password,[{\n strategy:'minLength:6',\n errorMsg:'密码长度不能低于6位'\n }]);\n var errorMsg = validator.start();\n return errorMsg;\n}\nform.onsubmit = function(){\n var errorMsg = validateFunc();\n if(errorMsg){\n alert(errorMsg);\n return false;\n }\n //验证通过,提交表单\n}\n```\n\n上面的代码简单来说,就是定义了一个验证器,在验证前把验证的 dom 和 对应的策略添加到验证器中,然后调用 start 函数,一次运行验证函数,如有验证错误则返回错误信息。这样我们就可以复用验证策略,只需要关心每个输入具体要符合什么规则并传入就可以了,代码也很清爽。\n\n但是这还不能直接运用到 rn 当中,需要稍微改造下,因为我没有 dom ,我有的是 state。[tcomb-form-native](https://github.com/gcanti/tcomb-form-native)这个库给了我一些灵感。\n\n```\n// here we are: define your domain model\nvar Person = t.struct({\n name: t.String, // a required string\n surname: t.maybe(t.String), // an optional string\n age: t.Number, // a required number\n rememberMe: t.Boolean // a boolean\n});\n```\n\n它是这么定义验证的信息的,那其实我的 state 当中的需要验证的字段和验证规则之间也可以建立一种类似的规则。于是就有了我最后的简陋版的验证插件,先看代码,然后再谈谈不足:\n\n```\n/**\n * Created by m2mbob on 16/7/20.\n */\nconst strategies = {\n isRequired:function(value,errorMsg) {\n if(value === ''){\n return errorMsg;\n }\n },\n maxLength:function(value,length,errorMsg) {//最小长度\n if(value.length > length){\n return errorMsg;\n }\n },\n minLength:function(value,length,errorMsg) {//最小长度\n if(value.length < length){\n return errorMsg;\n }\n },\n isPhone:function(value,errorMsg) {\n return this.isReg(value, /^1[3|4|5|7|8]\\d{9}$/g, errorMsg);\n },\n isEmail:function(value, errorMsg) {\n return this.isReg(value, /^(\\w-*\\.*)+@(\\w-?)+(\\.\\w{2,})+$/g, errorMsg);\n },\n isIdCard:function(value, errorMsg) {\n return this.isReg(value, /(^\\d{15}$)|(^\\d{17}([0-9]|X)$)/g, errorMsg);\n },\n isCar:function(value, errorMsg) {\n return this.isReg(value, /^[\\u4e00-\\u9fa5]{1}[A-Z]{1}[A-Z_0-9]{5}$/g, errorMsg);\n },\n isReg:function(value,reg,errorMsg) {//正则匹配\n if(!reg.test(value)){\n return errorMsg;\n }\n }\n};\n\nexport default class Validator {\n constructor() {\n this.errMsg = [];\n }\n\n validate(value, rules) {\n let flag = true;\n for(let i = 0 ,rule; rule = rules[i++];){\n let strategyAry = rule.strategy.split(':');\n let errorMsg = rule.errorMsg;\n let strategy = strategyAry.shift();\n strategyAry.unshift(value);\n strategyAry.push(errorMsg);\n let err = strategies[strategy].apply(strategies, strategyAry);\n if(err){\n flag = false;\n this.errMsg.push(err);\n }\n }\n return flag;\n }\n\n validates(source, rules) {\n for (let key of Object.keys(rules)){\n if(source[key]){\n let result = this.validate(source[key], rules[key]);\n if(!result){\n const firstError = this.errMsg.shift();\n this.errMsg = [];\n return firstError;\n }\n }else{\n return rules[key][0].errorMsg;\n }\n }\n\n }\n\n}\n\n// 调用\nconst firstError = new Validator().validates(this.state.driver, {\n ...\n car_model: [{\n strategy:'isRequired',\n errorMsg:'车牌号不能为空'\n },{\n strategy:'isCar',\n errorMsg:'车牌号格式错误'\n }]\n ...\n });\n if (firstError) {\n Alert.alert(firstError);\n return;\n }\n```\n\n主要改动在于 validates 函数,这个函数接受两个参数 ` source ` 和 ` rules ` , ` source ` 是一个包含了所有待验证输入字段的源对象,大部分情况下就是 state 或 state 中的对象; ` rules ` 是一个对象,它的 ` Object.keys ` 求值结果是 ` source ` 求值结果的子集,也就是说每一个 ` rules ` 的键都能够在 ` source ` 中找到,所以我遍历了 ` rules ` 的 ` key ` 然后向 ` validate ` 方法传入 ` source[key] ` 和 ` rules[key] ` 进行校验, ` validate ` 方法改动不大。\n\n最后来说说不足的地方,首先是没能够将错误反映到实际的表单当中,而是 alert 的方式,体验稍差,这部分是需要改进的。其次,实际使用当中,当字段很多时,有不少重复的规则,是否可以合并?也就是换个角度来做这个问题!不过在这个项目中已经够用了。\n\n不早了,先写到这,后面在研究下,撸一个更好,更通用的版本!","html":"<p>写之前先吐槽一下:昨晚半夜起来好像把眼镜踩坏了😭,下午只好去了趟眼镜店,顶着杭城下午两点的太阳,也是很拼。没想到传来了更大的噩耗🙄,测散光时说我原来的眼镜散光做反了,没得救了😒。最后只好勉强得整了一下,但愿新镜片能够慢慢调整好😂。</p>\n\n<p>然后是正片,今天鱼头说把司机招募的验证加上去,刚好写完后台消息的AOP没什么事,所以我就寻思写的一劳永逸的验证模块,岂不是很好很nice。不过作为懒人还是先去 github 上着了一番,不过没有比较适合现在的项目的。主要原因还是页面已经完成了,改用其它组件的工程量太大。所以最后决定自己整一个不侵入现有组件的,只关注验证逻辑,可扩展的验证模块。</p>\n\n<p>先来看看引入插件之前的的表单验证:</p>\n\n<pre><code>handleSubmit() { \n if(!this.state.credit_card){\n Alert.alert('必须填写银行卡号!');\n return;\n }\n if(!this.state.rest || this.state.rest < 0){\n Alert.alert('请填写正确的提现金额!');\n return;\n }\n if(this.state.rest > this.props.driver.driver_state.rest){\n Alert.alert('提现金额不能大于余额!');\n return;\n }\n rest.post...\n }\n</code></pre>\n\n<p>可以看到是通过 if 进行判断,这种做法,代码十分丑陋,而且复用性十分低。有什么办法能够改进上面的代码呢!进一步思考,其实表单验证的逻辑无非是对某个表单的输入,进行相关逻辑的判断。进行的逻辑判断是不变的,而判断的方式是可以改变的。是不是有点感觉了,我们可以把不变的逻辑固定下来,而可变的策略抽离出来。说到底就是策略模式了,策略模式就是通过把可变的判断策略提取出来,帮助我们省去那些 <code>if</code> 。废话不多说,先来看看在 web 上我们是如何使用的:<a href=\"http://brizer.github.io/2016/05/04/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5/\">出处</a></p>\n\n<pre><code>//定义验证策略\nvar strategies = { \n inNonEmpty:function(value,errorMsg){//不能为空\n if(value === ''){\n return errorMsg;\n }\n },\n minLength:function(value,length,errorMsg){//最小长度\n if(value.length < length){\n return errorMsg;\n }\n },\n isReg:function(value,reg,errorMsg){//正则匹配\n if(!reg.test(value)){\n return errorMsg;\n }\n }\n};\n//定义验证类\nvar Validator = function(){ \n this.cache = [];//保存效验规则\n};\n//添加验证规则\nValidator.prototype.add = function(dom,rules){ \n var that = this;\n //多条规则分别对应\n for(var i = 0 ,rule; rule = rules[i++];){\n (function(rule){\n var stratgyAry = rule.strategy.split(':');\n var errorMsg = rule.errorMsg;\n //将数据按照策略的格式塞入参数数组\n that.cache.push(function(){\n var strategy = strategyAry.shift();\n strategyAry.unshift(dom.value);\n strategyAry.push(errorMsg);\n return strategies[strategy].apply(dom,strategyAry);\n });\n })(rule)\n }\n};\nValidator.prototype.start = function(){ \n //依次验证\n for(var i = 0,validatorFunc;validatorFunc = this.cache[i++];){\n var errorMsg = validatorFunc();\n if(errorMsg){\n return errorMsg;\n }\n }\n};\n\n//调用代码\nvar validataFunc = function(){ \n var validator = new Validator();\n //多个条件判断\n validator.add(form.name,[{\n strategy:'isNonEmpty',\n errorMsg:'用户不能为空'\n },{\n strategy:'minLength:10',\n errorMsg:'用户长度不能小于10位'\n }]);\n validator.add(form.password,[{\n strategy:'minLength:6',\n errorMsg:'密码长度不能低于6位'\n }]);\n var errorMsg = validator.start();\n return errorMsg;\n}\nform.onsubmit = function(){ \n var errorMsg = validateFunc();\n if(errorMsg){\n alert(errorMsg);\n return false;\n }\n //验证通过,提交表单\n}\n</code></pre>\n\n<p>上面的代码简单来说,就是定义了一个验证器,在验证前把验证的 dom 和 对应的策略添加到验证器中,然后调用 start 函数,一次运行验证函数,如有验证错误则返回错误信息。这样我们就可以复用验证策略,只需要关心每个输入具体要符合什么规则并传入就可以了,代码也很清爽。</p>\n\n<p>但是这还不能直接运用到 rn 当中,需要稍微改造下,因为我没有 dom ,我有的是 state。<a href=\"https://github.com/gcanti/tcomb-form-native\">tcomb-form-native</a>这个库给了我一些灵感。</p>\n\n<pre><code>// here we are: define your domain model\nvar Person = t.struct({ \n name: t.String, // a required string\n surname: t.maybe(t.String), // an optional string\n age: t.Number, // a required number\n rememberMe: t.Boolean // a boolean\n});\n</code></pre>\n\n<p>它是这么定义验证的信息的,那其实我的 state 当中的需要验证的字段和验证规则之间也可以建立一种类似的规则。于是就有了我最后的简陋版的验证插件,先看代码,然后再谈谈不足:</p>\n\n<pre><code>/**\n * Created by m2mbob on 16/7/20.\n */\nconst strategies = { \n isRequired:function(value,errorMsg) {\n if(value === ''){\n return errorMsg;\n }\n },\n maxLength:function(value,length,errorMsg) {//最小长度\n if(value.length > length){\n return errorMsg;\n }\n },\n minLength:function(value,length,errorMsg) {//最小长度\n if(value.length < length){\n return errorMsg;\n }\n },\n isPhone:function(value,errorMsg) {\n return this.isReg(value, /^1[3|4|5|7|8]\\d{9}$/g, errorMsg);\n },\n isEmail:function(value, errorMsg) {\n return this.isReg(value, /^(\\w-*\\.*)+@(\\w-?)+(\\.\\w{2,})+$/g, errorMsg);\n },\n isIdCard:function(value, errorMsg) {\n return this.isReg(value, /(^\\d{15}$)|(^\\d{17}([0-9]|X)$)/g, errorMsg);\n },\n isCar:function(value, errorMsg) {\n return this.isReg(value, /^[\\u4e00-\\u9fa5]{1}[A-Z]{1}[A-Z_0-9]{5}$/g, errorMsg);\n },\n isReg:function(value,reg,errorMsg) {//正则匹配\n if(!reg.test(value)){\n return errorMsg;\n }\n }\n};\n\nexport default class Validator { \n constructor() {\n this.errMsg = [];\n }\n\n validate(value, rules) {\n let flag = true;\n for(let i = 0 ,rule; rule = rules[i++];){\n let strategyAry = rule.strategy.split(':');\n let errorMsg = rule.errorMsg;\n let strategy = strategyAry.shift();\n strategyAry.unshift(value);\n strategyAry.push(errorMsg);\n let err = strategies[strategy].apply(strategies, strategyAry);\n if(err){\n flag = false;\n this.errMsg.push(err);\n }\n }\n return flag;\n }\n\n validates(source, rules) {\n for (let key of Object.keys(rules)){\n if(source[key]){\n let result = this.validate(source[key], rules[key]);\n if(!result){\n const firstError = this.errMsg.shift();\n this.errMsg = [];\n return firstError;\n }\n }else{\n return rules[key][0].errorMsg;\n }\n }\n\n }\n\n}\n\n// 调用\nconst firstError = new Validator().validates(this.state.driver, { \n ...\n car_model: [{\n strategy:'isRequired',\n errorMsg:'车牌号不能为空'\n },{\n strategy:'isCar',\n errorMsg:'车牌号格式错误'\n }]\n ...\n });\n if (firstError) {\n Alert.alert(firstError);\n return;\n }\n</code></pre>\n\n<p>主要改动在于 validates 函数,这个函数接受两个参数 <code>source</code> 和 <code>rules</code> , <code>source</code> 是一个包含了所有待验证输入字段的源对象,大部分情况下就是 state 或 state 中的对象; <code>rules</code> 是一个对象,它的 <code>Object.keys</code> 求值结果是 <code>source</code> 求值结果的子集,也就是说每一个 <code>rules</code> 的键都能够在 <code>source</code> 中找到,所以我遍历了 <code>rules</code> 的 <code>key</code> 然后向 <code>validate</code> 方法传入 <code>source[key]</code> 和 <code>rules[key]</code> 进行校验, <code>validate</code> 方法改动不大。</p>\n\n<p>最后来说说不足的地方,首先是没能够将错误反映到实际的表单当中,而是 alert 的方式,体验稍差,这部分是需要改进的。其次,实际使用当中,当字段很多时,有不少重复的规则,是否可以合并?也就是换个角度来做这个问题!不过在这个项目中已经够用了。</p>\n\n<p>不早了,先写到这,后面在研究下,撸一个更好,更通用的版本!</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469026705376,"created_by":1,"updated_at":1469029771903,"updated_by":1,"published_at":1469029771905,"published_by":1},{"id":32,"uuid":"bc1e84b8-b50e-41b3-ad8d-acea8fa0d04c","title":"有趣、有感、有情","slug":"you-qu-you-gan-you-qing","markdown":"<embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=2684667&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed>","html":"<p><embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=2684667&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469065163248,"created_by":1,"updated_at":1470055042869,"updated_by":1,"published_at":1470055042869,"published_by":1},{"id":33,"uuid":"b0c15dab-e1f8-41fc-8bd1-d6de3326ca2b","title":"react native —— 安卓知识与踩坑篇(一)","slug":"react-native-an-zhuo-zhi-shi-bu-chong-pian","markdown":"这个系列文章是在编写[react native 高德后台定位插件](https://github.com/yptech/react-native-yunpeng-amplocation)的安卓部分时,使用到的一些原生安卓知识的记录。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=297728&auto=1&height=66\"></iframe>\n\n##### android studio 真机运行的时候gradle 报错\n\nFAILURE: Build failed with an exception.\n\n* What went wrong:\n\nExecution failed for task ':app:preDexDebug'.\n\n> com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'C:\\Program Files\\Java\\jdk1.7.0_71\\bin\\java.exe'' finished with non-zero exit value 1\n\n* Try:\n\nRun with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.\n\n* solution\n\n>1. 在project structure中去掉重复的jar包,\n2. 如果你的jar包是放到了libs目录中,且在gradle中发现了如下代码:\ncompile fileTree(include: ['*.jar'], dir: 'libs')\n则注释掉即可。\n\n##### Android中Parcelable序列化总结\n\nParcelable 接口与 JDK 当中的 Serializable 接口类似,是为了做对象序列化的。在我本次编写插件过程中,需要通过 intent 传递配置的对象,而这个对象要传递就需要实现 Parcelable 接口。\n\n###### Serializable与Parcelable的区别\n\n1. Serializable是JDK提供的接口,而Parcelable是Android SDK提供的。\n2. Serializable序列化是基于磁盘的,而Parcelable是基于内存的。在内存中读写肯定效率要高于磁盘,所以Android中跨进程传递对象都是使用Parcelable。\n\n最后来看看如何使用,我们需要实现 Parcelable 接口的如下方法:\n\n 1. describeContents方法。内容接口描述,默认返回0就可以;\n 2. writeToParcel 方法。该方法将类的数据写入外部提供的Parcel中.即打包需要传递的数据到Parcel容器保存,以便从parcel容器获取数据,该方法声明如下:`writeToParcel (Parcel dest, int flags)`\n 3. 静态的Parcelable.Creator接口,本接口有两个方法:`createFromParcel(Parcel in)` 从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑层。`newArray(int size)` 创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。方法是供外部类反序列化本类数组使用。\n\n下面的例子包括了大部分的使用情况:\n\n```\npackage com.suning.mobile.paysdk.pay;\n \nimport java.util.ArrayList;\n \nimport android.os.Parcel;\nimport android.os.Parcelable;\n \nimport com.yaya.test.OrderInfoBean;\n \n/**\n * \n * 〈一句话功能简述〉<br>\n * 〈功能详细描述〉 数据类型序列化\n */\npublic class ParcelableType implements Parcelable {\n /** int 类型 */\n int age;\n /** String 类型 */\n String name;\n /** boolean 注意该boolean的get和set方法 **/\n boolean isGood;\n /** boolean 类型 **/\n boolean complete;\n /** 数组 **/\n private String[] ids;\n /** 对象 [内部已经序列化] **/\n private OrderInfoBean bean;\n /** list **/\n private ArrayList<orderinfobean> listBeans;\n \n /**\n * 默认构造方法\n */\n public ParcelableType() {\n // TODO Auto-generated constructor stub\n }\n \n public ParcelableType(Parcel in) {\n readFromParcel(in);\n }\n \n /***\n * 默认实现\n */\n @Override\n public int describeContents() {\n // TODO Auto-generated method stub\n return 0;\n }\n \n @Override\n public void writeToParcel(Parcel dest, int flags) {\n /** int 写入 **/\n dest.writeInt(age);\n /** string 写入 **/\n dest.writeString(name);\n /** boolean 写入 **/\n dest.writeInt(isGood ? 1 : 0);\n /** boolean 写入 **/\n dest.writeInt(complete ? 1 : 0);\n /** 数组 写入 **/\n if (ids != null) {\n dest.writeInt(ids.length);\n } else {\n dest.writeInt(0);\n }\n dest.writeStringArray(ids);\n /** 对象 写入 **/\n dest.writeParcelable(bean, flags);\n /** list 写入 **/\n dest.writeList(listBeans);\n \n }\n \n @SuppressWarnings(\"unchecked\")\n private void readFromParcel(Parcel in) {\n \n /** int 读出 */\n age = in.readInt();\n /** stirng 读出 */\n name = in.readString();\n /** boolean 读出 */\n isGood = (in.readInt() == 1) ? true : false;\n /** boolean 读出 */\n complete = (in.readInt() == 1) ? true : false;\n /** 数组 读出 */\n int length = in.readInt();\n ids = new String[length];\n in.readStringArray(ids);\n /** 对象 读出 */\n bean = in.readParcelable(OrderInfoBean.class.getClassLoader());\n /** list 读出 */\n listBeans = in.readArrayList(OrderInfoBean.class.getClassLoader());\n \n }\n \n public static final Parcelable.Creator<parcelabletype> CREATOR = new Parcelable.Creator<parcelabletype>() {\n public ParcelableType createFromParcel(Parcel in) {\n return new ParcelableType(in);\n }\n \n public ParcelableType[] newArray(int size) {\n return new ParcelableType[size];\n }\n };\n \n public int getAge() {\n return age;\n }\n \n public void setAge(int age) {\n this.age = age;\n }\n \n public String getName() {\n return name;\n }\n \n public void setName(String name) {\n this.name = name;\n }\n \n /**\n * \n * 功能描述: <br>\n * 〈功能详细描述〉 fastJson解析时需要格式\n */\n public boolean isIsGood() {\n return isGood;\n }\n \n public void setIsGood(boolean isGood) {\n this.isGood = isGood;\n }\n \n public boolean isComplete() {\n return complete;\n }\n \n public void setComplete(boolean complete) {\n this.complete = complete;\n }\n \n public String[] getIds() {\n return ids;\n }\n \n public void setIds(String[] ids) {\n this.ids = ids;\n }\n \n public OrderInfoBean getBean() {\n return bean;\n }\n \n public void setBean(OrderInfoBean bean) {\n this.bean = bean;\n }\n \n public ArrayList<orderinfobean> getListBeans() {\n return listBeans;\n }\n \n public void setListBeans(ArrayList<orderinfobean> listBeans) {\n this.listBeans = listBeans;\n }\n \n}\n```\n\n\n\n","html":"<p>这个系列文章是在编写<a href=\"https://github.com/yptech/react-native-yunpeng-amplocation\">react native 高德后台定位插件</a>的安卓部分时,使用到的一些原生安卓知识的记录。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=297728&auto=1&height=66\"></iframe>\n\n<h5 id=\"androidstudiogradle\">android studio 真机运行的时候gradle 报错</h5>\n\n<p>FAILURE: Build failed with an exception.</p>\n\n<ul>\n<li>What went wrong:</li>\n</ul>\n\n<p>Execution failed for task ':app:preDexDebug'.</p>\n\n<blockquote>\n <p>com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'C:\\Program Files\\Java\\jdk1.7.0_71\\bin\\java.exe'' finished with non-zero exit value 1</p>\n</blockquote>\n\n<ul>\n<li>Try:</li>\n</ul>\n\n<p>Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.</p>\n\n<ul>\n<li>solution</li>\n</ul>\n\n<blockquote>\n <ol>\n <li>在project structure中去掉重复的jar包,</li>\n <li>如果你的jar包是放到了libs目录中,且在gradle中发现了如下代码: <br />\n compile fileTree(include: ['*.jar'], dir: 'libs') <br />\n 则注释掉即可。</li>\n </ol>\n</blockquote>\n\n<h5 id=\"androidparcelable\">Android中Parcelable序列化总结</h5>\n\n<p>Parcelable 接口与 JDK 当中的 Serializable 接口类似,是为了做对象序列化的。在我本次编写插件过程中,需要通过 intent 传递配置的对象,而这个对象要传递就需要实现 Parcelable 接口。</p>\n\n<h6 id=\"serializableparcelable\">Serializable与Parcelable的区别</h6>\n\n<ol>\n<li>Serializable是JDK提供的接口,而Parcelable是Android SDK提供的。 </li>\n<li>Serializable序列化是基于磁盘的,而Parcelable是基于内存的。在内存中读写肯定效率要高于磁盘,所以Android中跨进程传递对象都是使用Parcelable。</li>\n</ol>\n\n<p>最后来看看如何使用,我们需要实现 Parcelable 接口的如下方法:</p>\n\n<ol>\n<li>describeContents方法。内容接口描述,默认返回0就可以;</li>\n<li>writeToParcel 方法。该方法将类的数据写入外部提供的Parcel中.即打包需要传递的数据到Parcel容器保存,以便从parcel容器获取数据,该方法声明如下:<code>writeToParcel (Parcel dest, int flags)</code></li>\n<li>静态的Parcelable.Creator接口,本接口有两个方法:<code>createFromParcel(Parcel in)</code> 从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑层。<code>newArray(int size)</code> 创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。方法是供外部类反序列化本类数组使用。</li>\n</ol>\n\n<p>下面的例子包括了大部分的使用情况:</p>\n\n<pre><code>package com.suning.mobile.paysdk.pay;\n\nimport java.util.ArrayList;\n\nimport android.os.Parcel; \nimport android.os.Parcelable;\n\nimport com.yaya.test.OrderInfoBean;\n\n/**\n * \n * 〈一句话功能简述〉<br>\n * 〈功能详细描述〉 数据类型序列化\n */\npublic class ParcelableType implements Parcelable { \n /** int 类型 */\n int age;\n /** String 类型 */\n String name;\n /** boolean 注意该boolean的get和set方法 **/\n boolean isGood;\n /** boolean 类型 **/\n boolean complete;\n /** 数组 **/\n private String[] ids;\n /** 对象 [内部已经序列化] **/\n private OrderInfoBean bean;\n /** list **/\n private ArrayList<orderinfobean> listBeans;\n\n /**\n * 默认构造方法\n */\n public ParcelableType() {\n // TODO Auto-generated constructor stub\n }\n\n public ParcelableType(Parcel in) {\n readFromParcel(in);\n }\n\n /***\n * 默认实现\n */\n @Override\n public int describeContents() {\n // TODO Auto-generated method stub\n return 0;\n }\n\n @Override\n public void writeToParcel(Parcel dest, int flags) {\n /** int 写入 **/\n dest.writeInt(age);\n /** string 写入 **/\n dest.writeString(name);\n /** boolean 写入 **/\n dest.writeInt(isGood ? 1 : 0);\n /** boolean 写入 **/\n dest.writeInt(complete ? 1 : 0);\n /** 数组 写入 **/\n if (ids != null) {\n dest.writeInt(ids.length);\n } else {\n dest.writeInt(0);\n }\n dest.writeStringArray(ids);\n /** 对象 写入 **/\n dest.writeParcelable(bean, flags);\n /** list 写入 **/\n dest.writeList(listBeans);\n\n }\n\n @SuppressWarnings(\"unchecked\")\n private void readFromParcel(Parcel in) {\n\n /** int 读出 */\n age = in.readInt();\n /** stirng 读出 */\n name = in.readString();\n /** boolean 读出 */\n isGood = (in.readInt() == 1) ? true : false;\n /** boolean 读出 */\n complete = (in.readInt() == 1) ? true : false;\n /** 数组 读出 */\n int length = in.readInt();\n ids = new String[length];\n in.readStringArray(ids);\n /** 对象 读出 */\n bean = in.readParcelable(OrderInfoBean.class.getClassLoader());\n /** list 读出 */\n listBeans = in.readArrayList(OrderInfoBean.class.getClassLoader());\n\n }\n\n public static final Parcelable.Creator<parcelabletype> CREATOR = new Parcelable.Creator<parcelabletype>() {\n public ParcelableType createFromParcel(Parcel in) {\n return new ParcelableType(in);\n }\n\n public ParcelableType[] newArray(int size) {\n return new ParcelableType[size];\n }\n };\n\n public int getAge() {\n return age;\n }\n\n public void setAge(int age) {\n this.age = age;\n }\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n /**\n * \n * 功能描述: <br>\n * 〈功能详细描述〉 fastJson解析时需要格式\n */\n public boolean isIsGood() {\n return isGood;\n }\n\n public void setIsGood(boolean isGood) {\n this.isGood = isGood;\n }\n\n public boolean isComplete() {\n return complete;\n }\n\n public void setComplete(boolean complete) {\n this.complete = complete;\n }\n\n public String[] getIds() {\n return ids;\n }\n\n public void setIds(String[] ids) {\n this.ids = ids;\n }\n\n public OrderInfoBean getBean() {\n return bean;\n }\n\n public void setBean(OrderInfoBean bean) {\n this.bean = bean;\n }\n\n public ArrayList<orderinfobean> getListBeans() {\n return listBeans;\n }\n\n public void setListBeans(ArrayList<orderinfobean> listBeans) {\n this.listBeans = listBeans;\n }\n\n}\n</code></pre>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469240531228,"created_by":1,"updated_at":1473918099995,"updated_by":1,"published_at":1469535290970,"published_by":1},{"id":34,"uuid":"44877e28-bbec-4496-a6f0-b21cc68f2c79","title":"Android Studio 中 .gitignore 的编写","slug":"zhuan-android-studio-zhong-gitignore-de-bian-xie","markdown":"<embed src=\"https://www.xiami.com/widget/49883737_1770692731/singlePlayer.swf\" type=\"application/x-shockwave-flash\" width=\"257\" height=\"33\" wmode=\"transparent\"></embed>\n\n最近花了比较大的力气,写了一个 react native 版高德地图定位及后台持续监听定位的插件,主要写的是安卓的原生代码,所以后面会有不少关于本次编写当中原生知识相关的博文!这算是第一篇😪。还有好久木有放歌了,懒得回去加了,不过这首确实很好听,如果弹过吉他更会有不同的体会,T121 3121!😊。\n\n因为之前的项目的 .gitignore 文件都是 node 或者 react 相关的模板,或者稍微定制一下,用在这个插件当中,发现传上去很多不必要的文件,所以找了一个比较通用的放在这,以后肯定用得到。\n\n```\n# built application files\n*.apk\n*.ap_\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/\ngen/\nout/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Windows thumbnail db\nThumbs.db\n\n# OSX files\n.DS_Store\n\n# Eclipse project files\n.classpath\n.project\n\n# Android Studio\n*.iml\n.idea\n\n# Local IDEA workspace\n.idea/workspace.xml\n\n# Gradle cache\n.gradle\n\n#NDK\nobj/\n```","html":"<p><embed src=\"https://www.xiami.com/widget/49883737_1770692731/singlePlayer.swf\" type=\"application/x-shockwave-flash\" width=\"257\" height=\"33\" wmode=\"transparent\"></embed></p>\n\n<p>最近花了比较大的力气,写了一个 react native 版高德地图定位及后台持续监听定位的插件,主要写的是安卓的原生代码,所以后面会有不少关于本次编写当中原生知识相关的博文!这算是第一篇😪。还有好久木有放歌了,懒得回去加了,不过这首确实很好听,如果弹过吉他更会有不同的体会,T121 3121!😊。</p>\n\n<p>因为之前的项目的 .gitignore 文件都是 node 或者 react 相关的模板,或者稍微定制一下,用在这个插件当中,发现传上去很多不必要的文件,所以找了一个比较通用的放在这,以后肯定用得到。</p>\n\n<pre><code># built application files\n*.apk\n*.ap_\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/ \ngen/ \nout/ \nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Windows thumbnail db\nThumbs.db\n\n# OSX files\n.DS_Store\n\n# Eclipse project files\n.classpath\n.project\n\n# Android Studio\n*.iml\n.idea\n\n# Local IDEA workspace\n.idea/workspace.xml\n\n# Gradle cache\n.gradle\n\n#NDK\nobj/ \n</code></pre>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469436400951,"created_by":1,"updated_at":1473918812718,"updated_by":1,"published_at":1469436760761,"published_by":1},{"id":35,"uuid":"1a7afcb5-1488-4fac-891d-5ea2b4df142a","title":"react native —— 安卓知识与踩坑篇(二)","slug":"react-native-an-zhuo-zhi-shi-yu-cai-keng-pian-er","markdown":"这个系列文章是在编写[react native 高德后台定位插件](https://github.com/yptech/react-native-yunpeng-amplocation)的安卓部分时,使用到的一些原生安卓知识的记录。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=668472&auto=1&height=66\"></iframe>\n\n##### Android Service \n\nService 是我实现后台持续监听定位的主要工具,他是 android 的四大组件之一,它主要用于在后台处理一些耗时的逻辑,或者去执行某些需要长期运行的任务。必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态。Service 的内容很多可以看下面这几篇文章。\n\n在这里主要要讲的是如何让 Service 不被杀死,因为 app 进入后台后,当内存比较紧张时,后台 Service 就有可能被杀死,在下面的文章一中对三种杀死的情况做了讨论。\n\n1. 系统根据资源分配情况杀死服务\n2. 用户通过settings->Apps->Running->Stop方式杀死服务\n3. 用户通过settings->Apps->Downloaded->Force Stop方式杀死服务\n\n###### 第一种情况:\n\n用户不干预,完全靠系统来控制,办法有很多。比如 ` onStartCommand() ` 方法的返回值设为 ` START_STICKY ` ,服务就会在资源紧张的时候被杀掉,然后在资源足够的时候再恢复。当然也可设置为前台服务,使其有高的优先级,在资源紧张的时候也不会被杀掉。\n\n###### 第二种情况:\n 用户干预,主动杀掉运行中的服务。这个过程杀死服务会通过服务的生命周期,也就是会调用 ` onDestory() ` 方法,这时候一个方案就是在` onDestory() ` 中发送广播开启自己。这样杀死服务后会立即启动。如下:\n\n```\n@Override\npublic void onCreate() {\n // TODO Auto-generated method stub\n super.onCreate();\n\n mBR = new BroadcastReceiver() {\n @Override\n public void onReceive(Context context, Intent intent) {\n // TODO Auto-generated method stub\n Intent a = new Intent(ServiceA.this, ServiceA.class);\n startService(a);\n }\n };\n mIF = new IntentFilter();\n mIF.addAction(\"listener\");\n registerReceiver(mBR, mIF);\n}\n\n@Override\npublic void onDestroy() {\n // TODO Auto-generated method stub\n super.onDestroy();\n\n Intent intent = new Intent();\n intent.setAction(\"listener\");\n sendBroadcast(intent);\n\n unregisterReceiver(mBR);\n}\n```\n当然,从理论上来讲这个方案是可行的,实验一下也可以。但有些情况下,发送的广播在消息队列中排的靠后,就有可能服务还没接收到广播就销毁了(这是我对实验结果的猜想,具体执行步骤暂时还不了解)。所以为了能让这个机制完美运行,可以开启两个服务,相互监听,相互启动。服务A监听B的广播来启动B,服务B监听A的广播来启动A。经过实验,这个方案可行,并且用360杀掉后几秒后服务也还是能自启的。到这里再说一句,如果不是某些功能需要的服务,不建议这么做,会降低用户体验。\n\n###### 第三种情况:\n 强制关闭就没有办法。这个好像是从包的level去关的,并不走完整的生命周期。所以在服务里加代码是无法被调用的。处理这个情况的唯一方法是屏蔽掉force stop和uninstall按钮,让其不可用。方法自己去找吧。当然有些手机自带的清理功能就是从这个地方清理的,比如华为的清理。所以第三种情况我也没有什么更好的办法了。\n\n 最后再说一句,别在这上面太折腾,弄成流氓软件就不好了。我就是讨厌一些软件乱发通知,起服务才转而用iPhone的。不过下一代Android好像可以支持用户选择是否开启软件设置的权限了,倒是可以期待一下。\n\n我最后使用的是设置前台服务的方式,基本够用了。不过据说高德本身已经实现了后台定位的功能,据说还和我写的差不多😂。不过通过写这个插件,基本熟悉了 Service 用法的各个细节了。\n\n参考资料:\n\n1. http://www.cnblogs.com/rossoneri/p/4530216.html\n2. http://blog.csdn.net/guolin_blog/article/details/11952435\n3. http://blog.csdn.net/guolin_blog/article/details/9797169\n4. http://www.cnblogs.com/mengdd/archive/2013/03/24/2979944.html\n\n##### Android Intent启动flag\n\n网上一查这个 flag 有 20 多种,而且内容很多,所以先只记录下我在插件中使用的 FLAG_FROM_BACKGROUND ,这个 flag 表示这个Intent来自一个后台操作,而不是用户交互。用到了再继续记录。","html":"<p>这个系列文章是在编写<a href=\"https://github.com/yptech/react-native-yunpeng-amplocation\">react native 高德后台定位插件</a>的安卓部分时,使用到的一些原生安卓知识的记录。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=668472&auto=1&height=66\"></iframe>\n\n<h5 id=\"androidservice\">Android Service</h5>\n\n<p>Service 是我实现后台持续监听定位的主要工具,他是 android 的四大组件之一,它主要用于在后台处理一些耗时的逻辑,或者去执行某些需要长期运行的任务。必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态。Service 的内容很多可以看下面这几篇文章。</p>\n\n<p>在这里主要要讲的是如何让 Service 不被杀死,因为 app 进入后台后,当内存比较紧张时,后台 Service 就有可能被杀死,在下面的文章一中对三种杀死的情况做了讨论。</p>\n\n<ol>\n<li>系统根据资源分配情况杀死服务 </li>\n<li>用户通过settings->Apps->Running->Stop方式杀死服务 </li>\n<li>用户通过settings->Apps->Downloaded->Force Stop方式杀死服务</li>\n</ol>\n\n<h6 id=\"\">第一种情况:</h6>\n\n<p>用户不干预,完全靠系统来控制,办法有很多。比如 <code>onStartCommand()</code> 方法的返回值设为 <code>START_STICKY</code> ,服务就会在资源紧张的时候被杀掉,然后在资源足够的时候再恢复。当然也可设置为前台服务,使其有高的优先级,在资源紧张的时候也不会被杀掉。</p>\n\n<h6 id=\"\">第二种情况:</h6>\n\n<p> 用户干预,主动杀掉运行中的服务。这个过程杀死服务会通过服务的生命周期,也就是会调用 <code>onDestory()</code> 方法,这时候一个方案就是在<code>onDestory()</code> 中发送广播开启自己。这样杀死服务后会立即启动。如下:</p>\n\n<pre><code>@Override\npublic void onCreate() { \n // TODO Auto-generated method stub\n super.onCreate();\n\n mBR = new BroadcastReceiver() {\n @Override\n public void onReceive(Context context, Intent intent) {\n // TODO Auto-generated method stub\n Intent a = new Intent(ServiceA.this, ServiceA.class);\n startService(a);\n }\n };\n mIF = new IntentFilter();\n mIF.addAction(\"listener\");\n registerReceiver(mBR, mIF);\n}\n\n@Override\npublic void onDestroy() { \n // TODO Auto-generated method stub\n super.onDestroy();\n\n Intent intent = new Intent();\n intent.setAction(\"listener\");\n sendBroadcast(intent);\n\n unregisterReceiver(mBR);\n}\n</code></pre>\n\n<p>当然,从理论上来讲这个方案是可行的,实验一下也可以。但有些情况下,发送的广播在消息队列中排的靠后,就有可能服务还没接收到广播就销毁了(这是我对实验结果的猜想,具体执行步骤暂时还不了解)。所以为了能让这个机制完美运行,可以开启两个服务,相互监听,相互启动。服务A监听B的广播来启动B,服务B监听A的广播来启动A。经过实验,这个方案可行,并且用360杀掉后几秒后服务也还是能自启的。到这里再说一句,如果不是某些功能需要的服务,不建议这么做,会降低用户体验。</p>\n\n<h6 id=\"\">第三种情况:</h6>\n\n<p> 强制关闭就没有办法。这个好像是从包的level去关的,并不走完整的生命周期。所以在服务里加代码是无法被调用的。处理这个情况的唯一方法是屏蔽掉force stop和uninstall按钮,让其不可用。方法自己去找吧。当然有些手机自带的清理功能就是从这个地方清理的,比如华为的清理。所以第三种情况我也没有什么更好的办法了。</p>\n\n<p> 最后再说一句,别在这上面太折腾,弄成流氓软件就不好了。我就是讨厌一些软件乱发通知,起服务才转而用iPhone的。不过下一代Android好像可以支持用户选择是否开启软件设置的权限了,倒是可以期待一下。</p>\n\n<p>我最后使用的是设置前台服务的方式,基本够用了。不过据说高德本身已经实现了后台定位的功能,据说还和我写的差不多😂。不过通过写这个插件,基本熟悉了 Service 用法的各个细节了。</p>\n\n<p>参考资料:</p>\n\n<ol>\n<li><a href=\"http://www.cnblogs.com/rossoneri/p/4530216.html\">http://www.cnblogs.com/rossoneri/p/4530216.html</a> </li>\n<li><a href=\"http://blog.csdn.net/guolin_blog/article/details/11952435\">http://blog.csdn.net/guolin_blog/article/details/11952435</a> </li>\n<li><a href=\"http://blog.csdn.net/guolin_blog/article/details/9797169\">http://blog.csdn.net/guolin_blog/article/details/9797169</a> </li>\n<li><a href=\"http://www.cnblogs.com/mengdd/archive/2013/03/24/2979944.html\">http://www.cnblogs.com/mengdd/archive/2013/03/24/2979944.html</a></li>\n</ol>\n\n<h5 id=\"androidintentflag\">Android Intent启动flag</h5>\n\n<p>网上一查这个 flag 有 20 多种,而且内容很多,所以先只记录下我在插件中使用的 FLAG<em>FROM</em>BACKGROUND ,这个 flag 表示这个Intent来自一个后台操作,而不是用户交互。用到了再继续记录。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469538467105,"created_by":1,"updated_at":1473918078103,"updated_by":1,"published_at":1469540968037,"published_by":1},{"id":36,"uuid":"5bd403a4-4255-4ca8-9c0f-344e89f98391","title":"关于电商网站订单号","slug":"build-gradle-pei-zhi-wen-jian-xiang-jie","markdown":"在我自主编写的支付系统生成的签名串当中采用的订单号就是自增的账单 id ,好处是在支付宝的异步回调当中操作方便。但是在发布正式版测试时出现了订单号重复的问题,于是我决定修改传递给支付宝的订单号的生成策略。\n\n关于生成电商订单号,我觉得需要考虑:\n\n1. 唯一性(主要是分布式环境中,这部分我不必须满足)\n2. 不暴露信息(自增订单号会暴露网站的经营状况)\n3. 长度适中(32位guid略长,控制在15位左右)\n\n我的方案参考淘宝:\n\n> 时间 + 随机数或业务要素ID\n\n建议结合实际情况,充分利用时间、随机数、商家ID、会员ID、自增ID这些来组合,根据自身运营特点来制定。如果真的对并发要求特别高,在 guid 上做文章也是不错的方案。\n\n更多的业务场景,可以看参考资料三。\n\n参考资料:\n\n1. https://segmentfault.com/q/1010000004104517/a-1020000004119558\n2. http://blog.sina.com.cn/s/blog_47039e010100ho7v.html\n3. http://www.php1.cn/article/8506.html","html":"<p>在我自主编写的支付系统生成的签名串当中采用的订单号就是自增的账单 id ,好处是在支付宝的异步回调当中操作方便。但是在发布正式版测试时出现了订单号重复的问题,于是我决定修改传递给支付宝的订单号的生成策略。</p>\n\n<p>关于生成电商订单号,我觉得需要考虑:</p>\n\n<ol>\n<li>唯一性(主要是分布式环境中,这部分我不必须满足) </li>\n<li>不暴露信息(自增订单号会暴露网站的经营状况) </li>\n<li>长度适中(32位guid略长,控制在15位左右)</li>\n</ol>\n\n<p>我的方案参考淘宝:</p>\n\n<blockquote>\n <p>时间 + 随机数或业务要素ID</p>\n</blockquote>\n\n<p>建议结合实际情况,充分利用时间、随机数、商家ID、会员ID、自增ID这些来组合,根据自身运营特点来制定。如果真的对并发要求特别高,在 guid 上做文章也是不错的方案。</p>\n\n<p>更多的业务场景,可以看参考资料三。</p>\n\n<p>参考资料:</p>\n\n<ol>\n<li><a href=\"https://segmentfault.com/q/1010000004104517/a-1020000004119558\">https://segmentfault.com/q/1010000004104517/a-1020000004119558</a> </li>\n<li><a href=\"http://blog.sina.com.cn/s/blog_47039e010100ho7v.html\">http://blog.sina.com.cn/s/blog_47039e010100ho7v.html</a> </li>\n<li><a href=\"http://www.php1.cn/article/8506.html\">http://www.php1.cn/article/8506.html</a></li>\n</ol>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469542163724,"created_by":1,"updated_at":1470842942480,"updated_by":1,"published_at":1470842891895,"published_by":1},{"id":37,"uuid":"822a333b-0ec6-4596-b8b3-952731a62b35","title":"react native —— 安卓知识与踩坑篇(三)","slug":"react-native-an-zhuo-zhi-shi-yu-cai-keng-pian-er-2","markdown":"这个系列文章是在编写[react native 高德后台定位插件](https://github.com/yptech/react-native-yunpeng-amplocation)的安卓部分时,使用到的一些原生安卓知识的记录。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=2742998&auto=1&height=66\"></iframe>\n\n下午没事,思考和总结了下 flux 、 redux 相关的东西,爆炸了!还没有想得特别的清楚,先放松一下,把这个系列最后的一些部分写一下!\n\n##### Gradle 简介\n\n一个构建工具,写过 javaWeb 的同学肯定知道另一个构建工具 Apache Maven,两者做的工作基本差不多,就是依赖管理、编译、打包等等工作,当然还有古老的 ant。不过这篇文章不会对这几者进行比较,这里的重点是 Android 和 Gradle。\n\n##### 安装Gradle\n\n在 Android Studio 中新建项目成功后会下载Gradle,貌似这个过程不翻墙也是可以下载,但是访问特别慢,建议翻墙下载。\n\n* Mac上会默认下载到 /Users/<用户名>/.gradle/wrapper/dists 目录\n* Win平台会默认下载到 C:\\Documents and Settings<用户名>.gradle\\wrapper\\dists 目录\n\n你会看到这个目录下有个 gradle-x.xx-all 的文件夹, 如果下载实在太慢,但是又不想翻墙的话,可以自己手动到Gradle官网下载对应的版本,然后将下载的.zip文件(也可以解压)复制到上述的gradle-x.xx-all 文件夹下,不过还是建议让它直接下载的好。\n\n##### 项目下的 gradle 文件\n\n![](https://ws1.sinaimg.cn/large/006bH5BKgw1f7763a9c6rj30aq0nhwhm.jpg)\n\n首先简单介绍下那个, yunpeng_cms_app_scx 是我的 app,其余是这个 app 所依赖的第三方模块。\n\n文件一: yunpeng_cms_app_scx/app/build.gradle\n\n这个文件是app文件夹下这个Module的gradle配置文件,也可以算是整个项目最主要的gradle配置文件。也是我们在编写 react native 应用时会修改的一个文件,当然有 rnpm 很多都是自动的。这个文件一般以 `apply plugin: \"com.android.application\"` 开头,而下面会提到的模块则是一般以 `apply plugin: \"com.android.library\"` 开头,原因也显而易见。\n\n这个文件其他的配置还很多,如 task,java版本信息,android sdk版本信息,签名等 ,但平时用到不多,常用的配置主要还是依赖的配置:\n\n```\ndependencies {\n// compile project(':react-native-yunpeng-amaplocation')\n compile project(':react-native-umeng-push')\n compile project(':react-native-yunpeng-datetime')\n compile project(':react-native-image-picker')\n compile project(':react-native-share')\n compile project(':react-native-linear-gradient')\n compile project(':react-native-amaplocating')\n compile project(':react-native-vector-icons')\n compile project(':react-native-yunpeng-alipay')\n compile project(':react-native-yyamap')\n compile fileTree(dir: \"libs\", include: [\"*.jar\"])\n compile \"com.android.support:appcompat-v7:23.0.1\"\n compile \"com.facebook.react:react-native:+\" // From node_modules\n}\n``` \n\n文件二:react-native-yunpeng-datetime/build.gradle\n\n这个就是上面提到的模块的 gradle 配置文件。每一个Module都需要有一个gradle配置文件,语法都是一样,唯一不同的是开头声明的是 apply plugin: 'com.android.library'。\n\n文件三:yunpeng_cms_app_scx/gradle/\n\n这个目录下有个 wrapper 文件夹,里面可以看到有两个文件,我们主要看下 gradle-wrapper.properties 这个文件的内容:\n\n```\n#Thu Dec 18 16:02:24 CST 2014\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.2.1-all.zip\n```\n\n可以看到里面声明了gradle的目录与下载路径以及当前项目使用的gradle版本,这些默认的路径我们一般不会更改的,这个文件里指明的gradle版本不对也是很多导包不成功的原因之一。\n\n文件四:yunpeng_cms_app_scx/build.gradle\n\n这个文件是整个项目的gradle基础配置文件,我们来看看这里面的内容\n\n```\n// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n repositories {\n jcenter()\n }\n dependencies {\n classpath 'com.android.tools.build:gradle:1.0.0'\n }\n}\n\nallprojects {\n repositories {\n jcenter()\n }\n}\n```\n\n内容主要包含了两个方面:一个是声明仓库的源,这里可以看到是指明的jcenter(), 之前版本则是mavenCentral(), jcenter可以理解成是一个新的中央远程仓库,兼容maven中心仓库,而且性能更优。另一个是声明了android gradle plugin的版本,android studio 1.0正式版必须要求支持gradle plugin 1.0的版本。不过实际使用中jenter很慢啊,mavenCentral相对来说快一点。\n\n另外就是这里的buildscript和allprojects都定义了repositories,他们的区别是,前者定义的gradle自身需要使用的资源,而后者是项目所需要的资源。\n\n文件五:yunpeng_cms_app_scx/setting.gradle\n\n这是在 react native 编写过程当中另一个需要修改的文件,这个文件是全局的项目配置文件,里面主要声明一些需要加入gradle的module,因此我们往往在添加插件时会要修改这个文件。不过在编写原生模块时要注意,要把模块代码放在android文件夹下,否则rnpm的自动链接是无效的。\n\n##### sqlite\n本来还有一篇 sqlite 的,但是这个网上资料很多,就把写模块过程中的文章记在这供以后回来回顾啦。后面就要回到rn的编写啦。\n\n资料:\n\n1. http://blog.sina.com.cn/s/blog_8191005601019so8.html\n2. http://blog.csdn.net/showdy/article/details/51578138\n3. http://blog.csdn.net/codeeer/article/details/30237597\n4. http://www.cnblogs.com/youxilua/archive/2011/09/18/2180654.html\n\n\n\n\n\n\n\n","html":"<p>这个系列文章是在编写<a href=\"https://github.com/yptech/react-native-yunpeng-amplocation\">react native 高德后台定位插件</a>的安卓部分时,使用到的一些原生安卓知识的记录。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=2742998&auto=1&height=66\"></iframe>\n\n<p>下午没事,思考和总结了下 flux 、 redux 相关的东西,爆炸了!还没有想得特别的清楚,先放松一下,把这个系列最后的一些部分写一下!</p>\n\n<h5 id=\"gradle\">Gradle 简介</h5>\n\n<p>一个构建工具,写过 javaWeb 的同学肯定知道另一个构建工具 Apache Maven,两者做的工作基本差不多,就是依赖管理、编译、打包等等工作,当然还有古老的 ant。不过这篇文章不会对这几者进行比较,这里的重点是 Android 和 Gradle。</p>\n\n<h5 id=\"gradle\">安装Gradle</h5>\n\n<p>在 Android Studio 中新建项目成功后会下载Gradle,貌似这个过程不翻墙也是可以下载,但是访问特别慢,建议翻墙下载。</p>\n\n<ul>\n<li>Mac上会默认下载到 /Users/<用户名>/.gradle/wrapper/dists 目录</li>\n<li>Win平台会默认下载到 C:\\Documents and Settings<用户名>.gradle\\wrapper\\dists 目录</li>\n</ul>\n\n<p>你会看到这个目录下有个 gradle-x.xx-all 的文件夹, 如果下载实在太慢,但是又不想翻墙的话,可以自己手动到Gradle官网下载对应的版本,然后将下载的.zip文件(也可以解压)复制到上述的gradle-x.xx-all 文件夹下,不过还是建议让它直接下载的好。</p>\n\n<h5 id=\"gradle\">项目下的 gradle 文件</h5>\n\n<p><img src=\"https://ws1.sinaimg.cn/large/006bH5BKgw1f7763a9c6rj30aq0nhwhm.jpg\" alt=\"\" /></p>\n\n<p>首先简单介绍下那个, yunpeng<em>cms</em>app_scx 是我的 app,其余是这个 app 所依赖的第三方模块。</p>\n\n<p>文件一: yunpeng<em>cms</em>app_scx/app/build.gradle</p>\n\n<p>这个文件是app文件夹下这个Module的gradle配置文件,也可以算是整个项目最主要的gradle配置文件。也是我们在编写 react native 应用时会修改的一个文件,当然有 rnpm 很多都是自动的。这个文件一般以 <code>apply plugin: \"com.android.application\"</code> 开头,而下面会提到的模块则是一般以 <code>apply plugin: \"com.android.library\"</code> 开头,原因也显而易见。</p>\n\n<p>这个文件其他的配置还很多,如 task,java版本信息,android sdk版本信息,签名等 ,但平时用到不多,常用的配置主要还是依赖的配置:</p>\n\n<pre><code>dependencies { \n// compile project(':react-native-yunpeng-amaplocation')\n compile project(':react-native-umeng-push')\n compile project(':react-native-yunpeng-datetime')\n compile project(':react-native-image-picker')\n compile project(':react-native-share')\n compile project(':react-native-linear-gradient')\n compile project(':react-native-amaplocating')\n compile project(':react-native-vector-icons')\n compile project(':react-native-yunpeng-alipay')\n compile project(':react-native-yyamap')\n compile fileTree(dir: \"libs\", include: [\"*.jar\"])\n compile \"com.android.support:appcompat-v7:23.0.1\"\n compile \"com.facebook.react:react-native:+\" // From node_modules\n}\n</code></pre>\n\n<p>文件二:react-native-yunpeng-datetime/build.gradle</p>\n\n<p>这个就是上面提到的模块的 gradle 配置文件。每一个Module都需要有一个gradle配置文件,语法都是一样,唯一不同的是开头声明的是 apply plugin: 'com.android.library'。</p>\n\n<p>文件三:yunpeng<em>cms</em>app_scx/gradle/</p>\n\n<p>这个目录下有个 wrapper 文件夹,里面可以看到有两个文件,我们主要看下 gradle-wrapper.properties 这个文件的内容:</p>\n\n<pre><code>#Thu Dec 18 16:02:24 CST 2014\ndistributionBase=GRADLE_USER_HOME \ndistributionPath=wrapper/dists \nzipStoreBase=GRADLE_USER_HOME \nzipStorePath=wrapper/dists \ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.2.1-all.zip \n</code></pre>\n\n<p>可以看到里面声明了gradle的目录与下载路径以及当前项目使用的gradle版本,这些默认的路径我们一般不会更改的,这个文件里指明的gradle版本不对也是很多导包不成功的原因之一。</p>\n\n<p>文件四:yunpeng<em>cms</em>app_scx/build.gradle</p>\n\n<p>这个文件是整个项目的gradle基础配置文件,我们来看看这里面的内容</p>\n\n<pre><code>// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript { \n repositories {\n jcenter()\n }\n dependencies {\n classpath 'com.android.tools.build:gradle:1.0.0'\n }\n}\n\nallprojects { \n repositories {\n jcenter()\n }\n}\n</code></pre>\n\n<p>内容主要包含了两个方面:一个是声明仓库的源,这里可以看到是指明的jcenter(), 之前版本则是mavenCentral(), jcenter可以理解成是一个新的中央远程仓库,兼容maven中心仓库,而且性能更优。另一个是声明了android gradle plugin的版本,android studio 1.0正式版必须要求支持gradle plugin 1.0的版本。不过实际使用中jenter很慢啊,mavenCentral相对来说快一点。</p>\n\n<p>另外就是这里的buildscript和allprojects都定义了repositories,他们的区别是,前者定义的gradle自身需要使用的资源,而后者是项目所需要的资源。</p>\n\n<p>文件五:yunpeng<em>cms</em>app_scx/setting.gradle</p>\n\n<p>这是在 react native 编写过程当中另一个需要修改的文件,这个文件是全局的项目配置文件,里面主要声明一些需要加入gradle的module,因此我们往往在添加插件时会要修改这个文件。不过在编写原生模块时要注意,要把模块代码放在android文件夹下,否则rnpm的自动链接是无效的。</p>\n\n<h5 id=\"sqlite\">sqlite</h5>\n\n<p>本来还有一篇 sqlite 的,但是这个网上资料很多,就把写模块过程中的文章记在这供以后回来回顾啦。后面就要回到rn的编写啦。</p>\n\n<p>资料:</p>\n\n<ol>\n<li><a href=\"http://blog.sina.com.cn/s/blog_8191005601019so8.html\">http://blog.sina.com.cn/s/blog_8191005601019so8.html</a> </li>\n<li><a href=\"http://blog.csdn.net/showdy/article/details/51578138\">http://blog.csdn.net/showdy/article/details/51578138</a> </li>\n<li><a href=\"http://blog.csdn.net/codeeer/article/details/30237597\">http://blog.csdn.net/codeeer/article/details/30237597</a> </li>\n<li><a href=\"http://www.cnblogs.com/youxilua/archive/2011/09/18/2180654.html\">http://www.cnblogs.com/youxilua/archive/2011/09/18/2180654.html</a></li>\n</ol>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469611788552,"created_by":1,"updated_at":1473918065899,"updated_by":1,"published_at":1469625294015,"published_by":1},{"id":38,"uuid":"0df4ce60-891a-40bb-a8b3-ac2cfa62da34","title":"flux与设计模式","slug":"yi-ju-hua-she-ji-mo-shi","markdown":"##### 外观模式\n\n包装子系统接口,实现一个可统一调用的高层接口,举例来说:ie只支持attachEvent,而FF和Chrome只支持addEventListener,那么为了兼容性考虑,我们就可以封装一个高层的接口来屏蔽底层接口兼容性的问题。\n\n##### 观察者模式\n\n主要是一主题对多观察者的通信,多个观察者订阅一个主题,然后主题发生某种变化时,调用订阅的回调方法,修改观察者的状态,举例来说,多个求职者订阅了某个猎头,当有新的职位出现时,猎头会调用所有的订阅函数通知订阅的求职者。\n\n##### 中介者\n\n客户之间通信的问题。注意是客户间的通信,这是和观察者模式本质上的区别。\n\n##### flux\nreact 组件本身是一个状态机,state和props决定了组件最终的渲染,也就是中介者模式中的客户,一个组件状态的改变很可能影响到另一个组件,flux 所做的就是首先在改变的组件上触发一个 action ,这个 action 会被 Dispatcher 分发,并得到 store 的响应并触发一个 change 事件,然后所有订阅了这个事件的组件能够根据事件修改自身的状态,不过这里更像是观察者模式。","html":"<h5 id=\"\">外观模式</h5>\n\n<p>包装子系统接口,实现一个可统一调用的高层接口,举例来说:ie只支持attachEvent,而FF和Chrome只支持addEventListener,那么为了兼容性考虑,我们就可以封装一个高层的接口来屏蔽底层接口兼容性的问题。</p>\n\n<h5 id=\"\">观察者模式</h5>\n\n<p>主要是一主题对多观察者的通信,多个观察者订阅一个主题,然后主题发生某种变化时,调用订阅的回调方法,修改观察者的状态,举例来说,多个求职者订阅了某个猎头,当有新的职位出现时,猎头会调用所有的订阅函数通知订阅的求职者。</p>\n\n<h5 id=\"\">中介者</h5>\n\n<p>客户之间通信的问题。注意是客户间的通信,这是和观察者模式本质上的区别。</p>\n\n<h5 id=\"flux\">flux</h5>\n\n<p>react 组件本身是一个状态机,state和props决定了组件最终的渲染,也就是中介者模式中的客户,一个组件状态的改变很可能影响到另一个组件,flux 所做的就是首先在改变的组件上触发一个 action ,这个 action 会被 Dispatcher 分发,并得到 store 的响应并触发一个 change 事件,然后所有订阅了这个事件的组件能够根据事件修改自身的状态,不过这里更像是观察者模式。</p>","image":null,"featured":0,"page":0,"status":"draft","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469671882667,"created_by":1,"updated_at":1474093289689,"updated_by":1,"published_at":null,"published_by":null},{"id":39,"uuid":"3932a515-76f5-4594-aff1-ff63492da9d3","title":"随笔","slug":"sui-bi","markdown":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=30635613&auto=1&height=66\"></iframe>\n\n打完球、吃了点东西、看了部东野圭吾小说改编的电影《麒麟之翼》、洗了个澡还木有弹吉他就已经10点了。\n\n没投几个球,手就酸了,还是要多运动运动啊!不过现在每周能够保持打球,还是有进步的哈!\n\n三天晚饭都是蛋黄粽加酸奶,有点腻了啊,明天老弟来我这玩,可以改善改善伙食了。\n\n东野圭吾小说改编的剧还是一如往常,不到最后不知所以,越到后面则越是让人感慨,到最后更是让人泪目。另外这一部的阵容意外的强大啊,宽叔、gakki、黑木明纱。。。尤其是宽叔,还是一如龙樱中的帅,和《嫌疑人X》中的福山雅治两种风格,都很让人喜欢。🤗\n\n虽然懒得洗衣服,还是洗掉了,因为衣架空出来了,好牵强的理由🤔。\n\n好了,扯到这,大半夜也不弹琴了,滚去看看能不能睡着😪。","html":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=30635613&auto=1&height=66\"></iframe>\n\n<p>打完球、吃了点东西、看了部东野圭吾小说改编的电影《麒麟之翼》、洗了个澡还木有弹吉他就已经10点了。</p>\n\n<p>没投几个球,手就酸了,还是要多运动运动啊!不过现在每周能够保持打球,还是有进步的哈!</p>\n\n<p>三天晚饭都是蛋黄粽加酸奶,有点腻了啊,明天老弟来我这玩,可以改善改善伙食了。</p>\n\n<p>东野圭吾小说改编的剧还是一如往常,不到最后不知所以,越到后面则越是让人感慨,到最后更是让人泪目。另外这一部的阵容意外的强大啊,宽叔、gakki、黑木明纱。。。尤其是宽叔,还是一如龙樱中的帅,和《嫌疑人X》中的福山雅治两种风格,都很让人喜欢。🤗</p>\n\n<p>虽然懒得洗衣服,还是洗掉了,因为衣架空出来了,好牵强的理由🤔。</p>\n\n<p>好了,扯到这,大半夜也不弹琴了,滚去看看能不能睡着😪。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1469714938923,"created_by":1,"updated_at":1473918044544,"updated_by":1,"published_at":1469715620612,"published_by":1},{"id":40,"uuid":"489ce5b2-00a0-4fd0-a9b8-0028deaa2bf7","title":"致鱼头","slug":"zhi-yutou","markdown":"马上就要开学了,是时候给鱼头一个回复了。事实上这不光是给鱼头的回复,也是给自己大四生活的一个回复,所以这几天思考这个问题都没怎么睡好!\n\n废话不多说,直接上我的决定,然后再解释吧!我的决定包括两部分,首先是我决定接下来的一段时间闭关,不接任何项目不找任何实习;其次是关于闭关结束以后是否留在公司,我把这个决定交给公司还有我两个兄弟,和钱无关,也就是我不会要那5%的股份,如果公司能够在我闭关的这一两个月里让他们能够慢慢产生要留下来的想法,那么毕业前我之后会继续留在公司。毕业后的事我还不能够确定。\n\n接下来是我做出这个决定的原因:第一个部分是个人原因,我的大学三年有两年是在项目中度过的,前端、后端、移动端都有所涉及,但是始终没有一个主要的方向,也没有对于一个方向以专家为目标进行深层次的努力,这是我目前想要改善的东西,也是这次闭关所要做的事。第二部分是公司的原因,作为在公司呆了一年多的老人,看着原来的伙伴一个个离开,确实也有过离开的想法。所以第二个决定是我希望这段时间鱼头你能够想办法解决掉留不住人这个公司目前致命的问题,这个问题绝不是仅是钱的问题,而是从管理、个人发展等各个方面来处理的。不解决这个问题,我即使留下来,公司也不会有比较长远的发展。\n\n以上大致是我的回答!","html":"<p>马上就要开学了,是时候给鱼头一个回复了。事实上这不光是给鱼头的回复,也是给自己大四生活的一个回复,所以这几天思考这个问题都没怎么睡好!</p>\n\n<p>废话不多说,直接上我的决定,然后再解释吧!我的决定包括两部分,首先是我决定接下来的一段时间闭关,不接任何项目不找任何实习;其次是关于闭关结束以后是否留在公司,我把这个决定交给公司还有我两个兄弟,和钱无关,也就是我不会要那5%的股份,如果公司能够在我闭关的这一两个月里让他们能够慢慢产生要留下来的想法,那么毕业前我之后会继续留在公司。毕业后的事我还不能够确定。</p>\n\n<p>接下来是我做出这个决定的原因:第一个部分是个人原因,我的大学三年有两年是在项目中度过的,前端、后端、移动端都有所涉及,但是始终没有一个主要的方向,也没有对于一个方向以专家为目标进行深层次的努力,这是我目前想要改善的东西,也是这次闭关所要做的事。第二部分是公司的原因,作为在公司呆了一年多的老人,看着原来的伙伴一个个离开,确实也有过离开的想法。所以第二个决定是我希望这段时间鱼头你能够想办法解决掉留不住人这个公司目前致命的问题,这个问题绝不是仅是钱的问题,而是从管理、个人发展等各个方面来处理的。不解决这个问题,我即使留下来,公司也不会有比较长远的发展。</p>\n\n<p>以上大致是我的回答!</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1470044991675,"created_by":1,"updated_at":1473249380874,"updated_by":1,"published_at":1473246900301,"published_by":1},{"id":41,"uuid":"4df8c76c-4bf5-40d6-85ab-fcabeb3319f4","title":"有生之年系列——卡卡西露脸","slug":"you-sheng-zhi-nian-xi-lie-qia-qia-xi-lu-lian","markdown":"哈哈哈,打球回来看了最新一期的火影,卡卡西终于露脸了,此生无憾啊,必须发一波!顺便预告下,正翻译 MobX.js 中,可能会断更几天。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=531925&auto=1&height=66\"></iframe>\n\n![](https://ws4.sinaimg.cn/large/006bH5BKgw1f7761harpyj31kw0zkwjl.jpg)\n\n![](https://ws3.sinaimg.cn/large/006bH5BKgw1f7761xl5icj31kw0zktng.jpg)\n\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f7762a3re3j31kw0zktpq.jpg)","html":"<p>哈哈哈,打球回来看了最新一期的火影,卡卡西终于露脸了,此生无憾啊,必须发一波!顺便预告下,正翻译 MobX.js 中,可能会断更几天。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=531925&auto=1&height=66\"></iframe>\n\n<p><img src=\"https://ws4.sinaimg.cn/large/006bH5BKgw1f7761harpyj31kw0zkwjl.jpg\" alt=\"\" /></p>\n\n<p><img src=\"https://ws3.sinaimg.cn/large/006bH5BKgw1f7761xl5icj31kw0zktng.jpg\" alt=\"\" /></p>\n\n<p><img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f7762a3re3j31kw0zktpq.jpg\" alt=\"\" /></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1470222294757,"created_by":1,"updated_at":1473918031970,"updated_by":1,"published_at":1470222651618,"published_by":1},{"id":42,"uuid":"08b55319-bdd4-4f5e-a337-ce03dcf9c01e","title":"十分钟介绍MobX和React(译)","slug":"shi-fen-zhong-jie-shao-mobxhe-react-yi","markdown":"<style>\nhr {\n border: none;\n border-top: 1px dotted #999;\n width: 30%;\n margin: 10pt auto;\n}\n\n#project_title {\n font-family: 'Pacifico', cursive;\n font-size: 60pt;\n padding-top: 20pt;\n}\n#project_tagline {\n clear: left;\n font-family: 'arial';\n font-size: 18pt;\n text-align: center;\n padding-top: 16pt;\n}\n</style>\n<header>\n<a href=\"index.html\" style=\"float:left\">\n<img style=\"width: 120px; padding-right: 20px;\" src=\"https://ws4.sinaimg.cn/large/006bH5BKgw1f775zli3msj305k05k3yh.jpg\" id=\"logo\"></a>\n<h1 id=\"project_title\">MobX</h1>\n<h2 id=\"project_tagline\" style=\"font-size: 18pt\">Ten minute introduction to MobX and React</h2>\n<hr>\n</header>\n\nMobX 是一个简单的、可扩展的、经过实战检验的状态管理解决方案。这篇教程将会在十分钟内把 MobX 所有重要的概念教给你。MobX 是一个独立的库,但是大多数人正把他和 React 一起使用,因此本篇教程将关注两者如何一起工作。\n\n##### 核心思想\n\n状态是每个应用的心脏,产生不一致的状态或者状态与持久的本地变量不同步往往是导致应用漏洞多、难以管理最快的影响因素。因此产生了大量状态管理解决方案,这些方案限制你修改状态的方式,例如使状态不可变。但是这引入了新的问题;数据需要被规范化,参照完整性不再能够保证;它也使得像原型这样强大的概念不能够使用。\n\nMobX 通过解决核心问题,使状态管理再次变得简单:它使得应用不可能去产生一个不一致的状态。实现这一策略是简单的:*保证所有可以从应用状态派生的东西,都自动地被派生。*\n\n从概念上讲,MobX 对待你的应用就像对待一张电子表格。\n\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f7760acznkj30mi0geq4d.jpg)\n\n1. 首先,这里有应用状态。对象、数组、基础类型、引用类型的组合组成了你的应用模型。这些值就是你的应用的“数据单元”。\n2. 其次,这还有 *derivations*(推导)。从根本上来讲,任何值能够从你的应用状态上自动地被计算出来。这些被推导出来或者计算出来的值,可以是没有完成的 todos 的数量这类简单的值;也可以是代表你的 todos 的一段可渲染的 HTML 这类复杂的东西。用电子表格程序的术语,它就是你的应用的公式和图表。\n3. *Reactions*(响应)与 *derivations*(推导)类似。主要不同是响应的函数不产生新的值。相反,它会自动地运行来完成一些任务。通常是一些 I/O 相关的任务。它保证 DOM 更新和网络请求的自动触发都在正确的时间进行。\n4. 最后是 *actions*(动作)。 *actions* 是修改状态的唯一途径。MobX 将会确保所有应用状态的变动由 *actions* 产生,并自动地进行推导和响应。同步且没有干扰地。\n\n##### 一个简单的 todo store \n\n有了充足的理论,让我们用一个实际的例子来更全面地解释上面的东西。为了独创性,让我们从一个非常简单的 ToDo store 开始。下面是一个十分直白的 `TodoStore` ,它包邮一个 todos 的集合,还不涉及 MobX 。可以在[JSFiddle](https://jsfiddle.net/mweststrate/wv3yopo0/)或者[原文处](https://mobxjs.github.io/mobx/getting-started.html)运行代码修改代码。\n\n```\nclass TodoStore {\n\ttodos = [];\n\n\tget completedTodosCount() {\n \treturn this.todos.filter(\n\t\t\ttodo => todo.completed === true\n\t\t).length;\n }\n\n\treport() {\n\t\tif (this.todos.length === 0)\n\t\t\treturn \"<none>\";\n\t\treturn `Next todo: \"${this.todos[0].task}\". ` + \n\t\t\t`Progress: ${this.completedTodosCount}/${this.todos.length}`; \n\t}\n\n addTodo(task) {\n\t\tthis.todos.push({ \n\t\t\ttask: task,\n\t\t\tcompleted: false,\n assignee: null\n\t\t});\n\t}\n}\n\nconst todoStore = new TodoStore();\n```\n\n我们只是创建了一个带有 todo 集合的 `todoStore` 。是时候给 `todoStore` 加入一些对象了。为了确保能够看到改变的效果,我们在每个改变后调用 `todoStore.report` 方法,打印它。注意,这个打印过程总是有意地只输出第一条任务。它使得这个例子有点假,但是正如你下面将要看到的,它很漂亮地展示了 MobX 的追踪的动态性。\n\n```\ntodoStore.addTodo(\"read MobX tutorial\");\nconsole.log(todoStore.report());\n// Next todo: \"read MobX tutorial\". Progress: 0/1\n\ntodoStore.addTodo(\"try MobX\");\nconsole.log(todoStore.report());\n// Next todo: \"read MobX tutorial\". Progress: 0/2\n\ntodoStore.todos[0].completed = true;\nconsole.log(todoStore.report());\n// Next todo: \"read MobX tutorial\". Progress: 1/2\n\ntodoStore.todos[1].task = \"try MobX in own project\";\nconsole.log(todoStore.report());\n// Next todo: \"read MobX tutorial\". Progress: 1/2\n\ntodoStore.todos[0].task = \"grok MobX tutorial\";\nconsole.log(todoStore.report());\n// Next todo: \"grok MobX tutorial\". Progress: 1/2\n```\n\n##### 变成响应式\n\n到目前为止,这些代码还没有什么特殊的。但是如果我们不需要显式地去调用 `report` 方法,而只要声明什么需要在状态改变的时候被调用。这能够将我们从在每个可能会触发报告的地方调用 `report` 方法的则责任中解放出来。我们想要确保最新的报告被打印了,但是我们不想要被组织相关代码而干扰。\n\n幸运的是,这正是 MobX 能够为你做的。完全依据状态自动地执行代码。所以我们的 `report` 函数更新自动地,就像电子表格中的一张图表。为了达到这个目的,`TodoStore` 必须变得可观察,以至于 MobX 能够跟踪所有改变。让我们修改这个类,使它能够做到这一点。\n\n同时,`completedTodosCount` 属性能够被自动地从 todo 列表派生。通过使用 `@observable` 和 `@computed` 装饰器,我们能够在一个对象上引入可观察的属性。\n\n```\nclass ObservableTodoStore {\n\t@observable todos = [];\n @observable pendingRequests = 0;\n\n constructor() {\n mobx.autorun(() => console.log(this.report));\n }\n\n\t@computed get completedTodosCount() {\n \treturn this.todos.filter(\n\t\t\ttodo => todo.completed === true\n\t\t).length;\n }\n\n\t@computed get report() {\n\t\tif (this.todos.length === 0)\n\t\t\treturn \"<none>\";\n\t\treturn `Next todo: \"${this.todos[0].task}\". ` + \n\t\t\t`Progress: ${this.completedTodosCount}/${this.todos.length}`; \n\t}\n\n\taddTodo(task) {\n\t\tthis.todos.push({ \n\t\t\ttask: task,\n\t\t\tcompleted: false,\n\t\t\tassignee: null\n\t\t});\n\t}\n}\n\n\nconst observableTodoStore = new ObservableTodoStore();\n```\n\n就是这样!我们用 `@observable` 标记一些属性,使得 MobX能够知道这些值能够随着时间改变。计算方法用 `@computed` 装饰,来辨认那些可以从状态派生的值。\n\n`pendingRequests` 和 `assignee` 属性目前还未用到,将会在本教程后面部分使用。为了使所有的例子代码简洁,本教程使用 ES6、JSX 和 装饰器。但是不用担心过,所有的装饰器在 MobX中都有对应的 [ES5](https://mobxjs.github.io/mobx/best/syntax.html) 版本。\n\n在构造函数中,我们创建了一个小函数来打印 `report` 函数的返回值,并把它包裹在 `autorun` 函数当中。`autorun` 函数创建了一个只运行一次的响应,在这之后每当在该函数中使用的任何被观察数据改变时,这个响应都会被自动地触发。因为 `report` 函数使用了被观察的 `todos` 属性,他将会在适当的时候打印报告。这在下面的例子中展示,可以在[JSFiddle][原文处](https://mobxjs.github.io/mobx/getting-started.html)运行代码。\n\n```\nobservableTodoStore.addTodo(\"read MobX tutorial\");\n// Next todo: \"read MobX tutorial\". Progress: 0/1\nobservableTodoStore.addTodo(\"try MobX\");\n// Next todo: \"read MobX tutorial\". Progress: 0/2\nobservableTodoStore.todos[0].completed = true;\n// Next todo: \"read MobX tutorial\". Progress: 1/2\nobservableTodoStore.todos[1].task = \"try MobX in own project\";\nobservableTodoStore.todos[0].task = \"grok MobX tutorial\";\n// Next todo: \"grok MobX tutorial\". Progress: 1/2\n```\n\n`report` 函数返回值确实被自动地、同步地打印出来了,并且没有泄露中间值。如果你看日志比较仔细,你肯定看到了第四行没有触发报告的输出。因为 `report` 函数返回值被没有因为重命名的行为而真正的改变,虽然背后的数据变了。在另一方面,改变第一条 todo 的名字,更新了 `report` 函数返回值,因为这个名字在 `report` 函数中被使用。这很漂亮地演示了不仅仅是 `todos` 数组被 `autorun` 函数观察,todo 列表项中的私有属性也可以被观察。\n\n##### 使 React 响应式\n\n好了,到目前为止,我们做了一个无聊的报告响应例子。是时候用十分相似的 store 去构建一个响应式的用户界面了。\n\n##### 未完待续。。。\n","html":"<style> \nhr { \n border: none;\n border-top: 1px dotted #999;\n width: 30%;\n margin: 10pt auto;\n}\n\n#project_title {\n font-family: 'Pacifico', cursive;\n font-size: 60pt;\n padding-top: 20pt;\n}\n#project_tagline {\n clear: left;\n font-family: 'arial';\n font-size: 18pt;\n text-align: center;\n padding-top: 16pt;\n}\n</style> \n\n<header> \n<a href=\"index.html\" style=\"float:left\"> \n<img style=\"width: 120px; padding-right: 20px;\" src=\"https://ws4.sinaimg.cn/large/006bH5BKgw1f775zli3msj305k05k3yh.jpg\" id=\"logo\"></a> \n<h1 id=\"project_title\">MobX</h1> \n<h2 id=\"project_tagline\" style=\"font-size: 18pt\">Ten minute introduction to MobX and React</h2> \n<hr> \n</header>\n\n<p>MobX 是一个简单的、可扩展的、经过实战检验的状态管理解决方案。这篇教程将会在十分钟内把 MobX 所有重要的概念教给你。MobX 是一个独立的库,但是大多数人正把他和 React 一起使用,因此本篇教程将关注两者如何一起工作。</p>\n\n<h5 id=\"\">核心思想</h5>\n\n<p>状态是每个应用的心脏,产生不一致的状态或者状态与持久的本地变量不同步往往是导致应用漏洞多、难以管理最快的影响因素。因此产生了大量状态管理解决方案,这些方案限制你修改状态的方式,例如使状态不可变。但是这引入了新的问题;数据需要被规范化,参照完整性不再能够保证;它也使得像原型这样强大的概念不能够使用。</p>\n\n<p>MobX 通过解决核心问题,使状态管理再次变得简单:它使得应用不可能去产生一个不一致的状态。实现这一策略是简单的:<em>保证所有可以从应用状态派生的东西,都自动地被派生。</em></p>\n\n<p>从概念上讲,MobX 对待你的应用就像对待一张电子表格。</p>\n\n<p><img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f7760acznkj30mi0geq4d.jpg\" alt=\"\" /></p>\n\n<ol>\n<li>首先,这里有应用状态。对象、数组、基础类型、引用类型的组合组成了你的应用模型。这些值就是你的应用的“数据单元”。 </li>\n<li>其次,这还有 <em>derivations</em>(推导)。从根本上来讲,任何值能够从你的应用状态上自动地被计算出来。这些被推导出来或者计算出来的值,可以是没有完成的 todos 的数量这类简单的值;也可以是代表你的 todos 的一段可渲染的 HTML 这类复杂的东西。用电子表格程序的术语,它就是你的应用的公式和图表。 </li>\n<li><em>Reactions</em>(响应)与 <em>derivations</em>(推导)类似。主要不同是响应的函数不产生新的值。相反,它会自动地运行来完成一些任务。通常是一些 I/O 相关的任务。它保证 DOM 更新和网络请求的自动触发都在正确的时间进行。 </li>\n<li>最后是 <em>actions</em>(动作)。 <em>actions</em> 是修改状态的唯一途径。MobX 将会确保所有应用状态的变动由 <em>actions</em> 产生,并自动地进行推导和响应。同步且没有干扰地。</li>\n</ol>\n\n<h5 id=\"todostore\">一个简单的 todo store</h5>\n\n<p>有了充足的理论,让我们用一个实际的例子来更全面地解释上面的东西。为了独创性,让我们从一个非常简单的 ToDo store 开始。下面是一个十分直白的 <code>TodoStore</code> ,它包邮一个 todos 的集合,还不涉及 MobX 。可以在<a href=\"https://jsfiddle.net/mweststrate/wv3yopo0/\">JSFiddle</a>或者<a href=\"https://mobxjs.github.io/mobx/getting-started.html\">原文处</a>运行代码修改代码。</p>\n\n<pre><code>class TodoStore { \n todos = [];\n\n get completedTodosCount() {\n return this.todos.filter(\n todo => todo.completed === true\n ).length;\n }\n\n report() {\n if (this.todos.length === 0)\n return \"<none>\";\n return `Next todo: \"${this.todos[0].task}\". ` + \n `Progress: ${this.completedTodosCount}/${this.todos.length}`; \n }\n\n addTodo(task) {\n this.todos.push({ \n task: task,\n completed: false,\n assignee: null\n });\n }\n}\n\nconst todoStore = new TodoStore(); \n</code></pre>\n\n<p>我们只是创建了一个带有 todo 集合的 <code>todoStore</code> 。是时候给 <code>todoStore</code> 加入一些对象了。为了确保能够看到改变的效果,我们在每个改变后调用 <code>todoStore.report</code> 方法,打印它。注意,这个打印过程总是有意地只输出第一条任务。它使得这个例子有点假,但是正如你下面将要看到的,它很漂亮地展示了 MobX 的追踪的动态性。</p>\n\n<pre><code>todoStore.addTodo(\"read MobX tutorial\"); \nconsole.log(todoStore.report()); \n// Next todo: \"read MobX tutorial\". Progress: 0/1\n\ntodoStore.addTodo(\"try MobX\"); \nconsole.log(todoStore.report()); \n// Next todo: \"read MobX tutorial\". Progress: 0/2\n\ntodoStore.todos[0].completed = true; \nconsole.log(todoStore.report()); \n// Next todo: \"read MobX tutorial\". Progress: 1/2\n\ntodoStore.todos[1].task = \"try MobX in own project\"; \nconsole.log(todoStore.report()); \n// Next todo: \"read MobX tutorial\". Progress: 1/2\n\ntodoStore.todos[0].task = \"grok MobX tutorial\"; \nconsole.log(todoStore.report()); \n// Next todo: \"grok MobX tutorial\". Progress: 1/2\n</code></pre>\n\n<h5 id=\"\">变成响应式</h5>\n\n<p>到目前为止,这些代码还没有什么特殊的。但是如果我们不需要显式地去调用 <code>report</code> 方法,而只要声明什么需要在状态改变的时候被调用。这能够将我们从在每个可能会触发报告的地方调用 <code>report</code> 方法的则责任中解放出来。我们想要确保最新的报告被打印了,但是我们不想要被组织相关代码而干扰。</p>\n\n<p>幸运的是,这正是 MobX 能够为你做的。完全依据状态自动地执行代码。所以我们的 <code>report</code> 函数更新自动地,就像电子表格中的一张图表。为了达到这个目的,<code>TodoStore</code> 必须变得可观察,以至于 MobX 能够跟踪所有改变。让我们修改这个类,使它能够做到这一点。</p>\n\n<p>同时,<code>completedTodosCount</code> 属性能够被自动地从 todo 列表派生。通过使用 <code>@observable</code> 和 <code>@computed</code> 装饰器,我们能够在一个对象上引入可观察的属性。</p>\n\n<pre><code>class ObservableTodoStore { \n @observable todos = [];\n @observable pendingRequests = 0;\n\n constructor() {\n mobx.autorun(() => console.log(this.report));\n }\n\n @computed get completedTodosCount() {\n return this.todos.filter(\n todo => todo.completed === true\n ).length;\n }\n\n @computed get report() {\n if (this.todos.length === 0)\n return \"<none>\";\n return `Next todo: \"${this.todos[0].task}\". ` + \n `Progress: ${this.completedTodosCount}/${this.todos.length}`; \n }\n\n addTodo(task) {\n this.todos.push({ \n task: task,\n completed: false,\n assignee: null\n });\n }\n}\n\n\nconst observableTodoStore = new ObservableTodoStore(); \n</code></pre>\n\n<p>就是这样!我们用 <code>@observable</code> 标记一些属性,使得 MobX能够知道这些值能够随着时间改变。计算方法用 <code>@computed</code> 装饰,来辨认那些可以从状态派生的值。</p>\n\n<p><code>pendingRequests</code> 和 <code>assignee</code> 属性目前还未用到,将会在本教程后面部分使用。为了使所有的例子代码简洁,本教程使用 ES6、JSX 和 装饰器。但是不用担心过,所有的装饰器在 MobX中都有对应的 <a href=\"https://mobxjs.github.io/mobx/best/syntax.html\">ES5</a> 版本。</p>\n\n<p>在构造函数中,我们创建了一个小函数来打印 <code>report</code> 函数的返回值,并把它包裹在 <code>autorun</code> 函数当中。<code>autorun</code> 函数创建了一个只运行一次的响应,在这之后每当在该函数中使用的任何被观察数据改变时,这个响应都会被自动地触发。因为 <code>report</code> 函数使用了被观察的 <code>todos</code> 属性,他将会在适当的时候打印报告。这在下面的例子中展示,可以在[JSFiddle]<a href=\"https://mobxjs.github.io/mobx/getting-started.html\">原文处</a>运行代码。</p>\n\n<pre><code>observableTodoStore.addTodo(\"read MobX tutorial\"); \n// Next todo: \"read MobX tutorial\". Progress: 0/1\nobservableTodoStore.addTodo(\"try MobX\"); \n// Next todo: \"read MobX tutorial\". Progress: 0/2\nobservableTodoStore.todos[0].completed = true; \n// Next todo: \"read MobX tutorial\". Progress: 1/2\nobservableTodoStore.todos[1].task = \"try MobX in own project\"; \nobservableTodoStore.todos[0].task = \"grok MobX tutorial\"; \n// Next todo: \"grok MobX tutorial\". Progress: 1/2\n</code></pre>\n\n<p><code>report</code> 函数返回值确实被自动地、同步地打印出来了,并且没有泄露中间值。如果你看日志比较仔细,你肯定看到了第四行没有触发报告的输出。因为 <code>report</code> 函数返回值被没有因为重命名的行为而真正的改变,虽然背后的数据变了。在另一方面,改变第一条 todo 的名字,更新了 <code>report</code> 函数返回值,因为这个名字在 <code>report</code> 函数中被使用。这很漂亮地演示了不仅仅是 <code>todos</code> 数组被 <code>autorun</code> 函数观察,todo 列表项中的私有属性也可以被观察。</p>\n\n<h5 id=\"react\">使 React 响应式</h5>\n\n<p>好了,到目前为止,我们做了一个无聊的报告响应例子。是时候用十分相似的 store 去构建一个响应式的用户界面了。</p>\n\n<h5 id=\"\">未完待续。。。</h5>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1470231539886,"created_by":1,"updated_at":1472195389840,"updated_by":1,"published_at":1470231894955,"published_by":1},{"id":43,"uuid":"3b2adc01-8ea0-4bc1-85da-adfaaa286f01","title":"网站备案中","slug":"wang-zhan-bei-an-zhong","markdown":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=30814948&auto=1&height=66\"></iframe>\n\n刚刚收到阿里云的提醒,为了保证网站能够一直正常的访问,需要备下案,虽然麻烦但是已经提交了备案申请,期间可能会出现无法访问的情况😂,见谅。不过备案成功了就不用带着端口号访问了😀!","html":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=30814948&auto=1&height=66\"></iframe>\n\n<p>刚刚收到阿里云的提醒,为了保证网站能够一直正常的访问,需要备下案,虽然麻烦但是已经提交了备案申请,期间可能会出现无法访问的情况😂,见谅。不过备案成功了就不用带着端口号访问了😀!</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1470294695518,"created_by":1,"updated_at":1473918018881,"updated_by":1,"published_at":1470294781906,"published_by":1},{"id":44,"uuid":"f50fdad1-3b8c-4f77-8269-68a96d35b7d3","title":"有生之年系列——生日","slug":"you-sheng-zhi-nian-xi-lie-sheng-ri","markdown":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=30814948&auto=1&height=66\"></iframe>\n\n虽然生日在周日,但是因为明天要回家,星爷也马上要回家了,所以今天请星爷、兵哥哥还有鸣鸣吃了饭,聚了下,也算是提前过了生日。\n\n很久没有这么过生日了,很高兴,谢谢星爷的蛋糕、军哥哥的GitHub T恤,鸣鸣的全程直播,还有傻奶奶的红包!感谢有你们陪我度过这个生日!便便也不要生气,下次会补回来的,但是你要戒网瘾哇!🤗\n\n马上大四了,将要迎来又一次的离别,但是人生本就是如此一次次的别离而构成的!哈哈,要开心,不要伤感!😅\n\n许愿的时候没多想,就是希望大家都幸福!说出来会不会不灵了🤔!睡了,晚安,世界!\n\n","html":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=30814948&auto=1&height=66\"></iframe>\n\n<p>虽然生日在周日,但是因为明天要回家,星爷也马上要回家了,所以今天请星爷、兵哥哥还有鸣鸣吃了饭,聚了下,也算是提前过了生日。</p>\n\n<p>很久没有这么过生日了,很高兴,谢谢星爷的蛋糕、军哥哥的GitHub T恤,鸣鸣的全程直播,还有傻奶奶的红包!感谢有你们陪我度过这个生日!便便也不要生气,下次会补回来的,但是你要戒网瘾哇!🤗</p>\n\n<p>马上大四了,将要迎来又一次的离别,但是人生本就是如此一次次的别离而构成的!哈哈,要开心,不要伤感!😅</p>\n\n<p>许愿的时候没多想,就是希望大家都幸福!说出来会不会不灵了🤔!睡了,晚安,世界!</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1470404187519,"created_by":1,"updated_at":1473918003425,"updated_by":1,"published_at":1470404914579,"published_by":1},{"id":45,"uuid":"8fa7990c-f296-4073-ad83-577e01c3eaf1","title":"有生之年系列——面试","slug":"you-sheng-zhi-nian-xi-lie-zhi-mian-shi","markdown":"今天参加了人生的第一场面试,没什么经验,没有做什么准备,想展示最真实的自身状态,是伯乐自然会相中。🤗\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=28786397&auto=1&height=66\"></iframe>\n\n首先是一个黑衣的大叔,有点资历的,先是和他简单探讨了一些现在的情况,这一部分还是比较顺利的。然后就开始了做题,第一题是 cookie 和 storage 的区别,这就尴尬了,写了那么多应用,没有 cookie 也活的好好的我自然是答不太好,然后是一些 http 及 dom 的知识,基本也是死伤惨重,因为大部分前端的经验来自于 supersonic 或是 react native。我讲 react native 的场景,他讲前端场景,两个人都有点 get 不到对方的点,所以感觉有些不妙。大叔也意识到这点,然后开始问了一些我比较擅长的部分,js 异步、redux 等,虽然语言组织的一般,但是还是能够讲得比较全面的。\n\n然后换了一个白衣小哥,先让我自我介绍,于是照着简历上的目录介绍了一番。可是这小哥便抓住了期望职位和发展方向问了不少问题,这可能是我多写了点,导致他多想了吧,后面还是把这玩意拿掉吧。然后是我觉得本次面试收获最大的一点,我到底想写什么,面试当场的我有些犹豫,但回去路上包括现在,我认真思考过后,觉得自己会以 react native 、 weex 这类的移动开发为暂时的主要方向,把网收小,专注一点。选择这个方向主要是我看好基于 virtual dom 的这种跨平台的应用开发方式,它必将在之后几年改变整个移动端开发的格局,另外就是自己在这个方面经验和理论都比较丰富,如果今天面的是这方面的职位那结果应该不会是这样了吧。\n\n今天面试的主要失败在于对自己定位不清,选择了一个自己不擅长的职位,说了一些不该说的话,以至于把自己的缺点暴露的太明显,而优势则没有体现出来。\n\n总结一下收获:第一、找准方向,继续努力;第二、面试技巧上要提高,要诱导面试官往自己优势的地方看;第三、对问题的研究要更加深入\n\n小牢骚:🙄最后就是要吐槽这家公司面试太敷衍,没有看过我的博客,没有看过我的开源项目,没有看过一行我写的代码,即使给了我下次的机会,我想我也不会去了吧!另外我并不觉得自己只是学了一些高大上表面的东西,我能够把他们用得很好,处理复杂的业务场景,甚至能在一定基础上创造一些东西,在体验和性能上也绝不会比面试公司的人做出来的东西差。\n","html":"<p>今天参加了人生的第一场面试,没什么经验,没有做什么准备,想展示最真实的自身状态,是伯乐自然会相中。🤗</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=28786397&auto=1&height=66\"></iframe>\n\n<p>首先是一个黑衣的大叔,有点资历的,先是和他简单探讨了一些现在的情况,这一部分还是比较顺利的。然后就开始了做题,第一题是 cookie 和 storage 的区别,这就尴尬了,写了那么多应用,没有 cookie 也活的好好的我自然是答不太好,然后是一些 http 及 dom 的知识,基本也是死伤惨重,因为大部分前端的经验来自于 supersonic 或是 react native。我讲 react native 的场景,他讲前端场景,两个人都有点 get 不到对方的点,所以感觉有些不妙。大叔也意识到这点,然后开始问了一些我比较擅长的部分,js 异步、redux 等,虽然语言组织的一般,但是还是能够讲得比较全面的。</p>\n\n<p>然后换了一个白衣小哥,先让我自我介绍,于是照着简历上的目录介绍了一番。可是这小哥便抓住了期望职位和发展方向问了不少问题,这可能是我多写了点,导致他多想了吧,后面还是把这玩意拿掉吧。然后是我觉得本次面试收获最大的一点,我到底想写什么,面试当场的我有些犹豫,但回去路上包括现在,我认真思考过后,觉得自己会以 react native 、 weex 这类的移动开发为暂时的主要方向,把网收小,专注一点。选择这个方向主要是我看好基于 virtual dom 的这种跨平台的应用开发方式,它必将在之后几年改变整个移动端开发的格局,另外就是自己在这个方面经验和理论都比较丰富,如果今天面的是这方面的职位那结果应该不会是这样了吧。</p>\n\n<p>今天面试的主要失败在于对自己定位不清,选择了一个自己不擅长的职位,说了一些不该说的话,以至于把自己的缺点暴露的太明显,而优势则没有体现出来。</p>\n\n<p>总结一下收获:第一、找准方向,继续努力;第二、面试技巧上要提高,要诱导面试官往自己优势的地方看;第三、对问题的研究要更加深入</p>\n\n<p>小牢骚:🙄最后就是要吐槽这家公司面试太敷衍,没有看过我的博客,没有看过我的开源项目,没有看过一行我写的代码,即使给了我下次的机会,我想我也不会去了吧!另外我并不觉得自己只是学了一些高大上表面的东西,我能够把他们用得很好,处理复杂的业务场景,甚至能在一定基础上创造一些东西,在体验和性能上也绝不会比面试公司的人做出来的东西差。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1470838177557,"created_by":1,"updated_at":1473917993143,"updated_by":1,"published_at":1470840846607,"published_by":1},{"id":46,"uuid":"009b885b-f44e-4942-a915-1757cf8cb480","title":"react native 踩坑实录续集","slug":"react-native-cai-keng-shi-lu-er","markdown":"前面的踩坑实录已经记录比较多了,而且翻过去也比较麻烦,所以新开一篇续集,更加精彩哦!😜\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=442031&auto=1&height=66\"></iframe>\n\n1、 [Android] FormData fails to send data in multipart/form-data(简单来说就是 android 下用 fetch 上传图片时存在问题)\n\n这个问题困扰了我很久,今天在 github 找到了相对满意的答案,[5308](https://github.com/facebook/react-native/issues/5308)。简单来讲就是在使用 FormData 时不需要再设置 header 中的 Content-Type 为 multipart/form-data,因为这是 FormData 会为我们做的事。另外本人在测试中还发现的事去掉 Content-Type 后, base64 格式图片还是无法正确上传的,而直接上传图片是可以的。这部分的原因需要更加深入的而研究!\n\n2、 Android 和 IOS 的 WebView 在处理没有 DOCTYPE 以及其他的 html、body 标签的情况,而只拥有 h1 这类标签时,有着不一样的规则 其中 Android 能够正常解析,IOS 解析异常,建议手动加上 html 文件必要的这些标签,来解决这个问题。\n","html":"<p>前面的踩坑实录已经记录比较多了,而且翻过去也比较麻烦,所以新开一篇续集,更加精彩哦!😜</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=442031&auto=1&height=66\"></iframe>\n\n<p>1、 [Android] FormData fails to send data in multipart/form-data(简单来说就是 android 下用 fetch 上传图片时存在问题)</p>\n\n<p>这个问题困扰了我很久,今天在 github 找到了相对满意的答案,<a href=\"https://github.com/facebook/react-native/issues/5308\">5308</a>。简单来讲就是在使用 FormData 时不需要再设置 header 中的 Content-Type 为 multipart/form-data,因为这是 FormData 会为我们做的事。另外本人在测试中还发现的事去掉 Content-Type 后, base64 格式图片还是无法正确上传的,而直接上传图片是可以的。这部分的原因需要更加深入的而研究!</p>\n\n<p>2、 Android 和 IOS 的 WebView 在处理没有 DOCTYPE 以及其他的 html、body 标签的情况,而只拥有 h1 这类标签时,有着不一样的规则 其中 Android 能够正常解析,IOS 解析异常,建议手动加上 html 文件必要的这些标签,来解决这个问题。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":"","author_id":1,"created_at":1470925501002,"created_by":1,"updated_at":1473917964755,"updated_by":1,"published_at":1470925890042,"published_by":1},{"id":47,"uuid":"5791cb6f-5f21-477f-998d-0bde0859e898","title":"转:Cookie与Session的区别-总结很好的文章","slug":"zhuan-cookieyu-sessionde-qu-bie-zong-jie-hen-hao-de-wen-zhang","markdown":"[猛戳原文](http://mp.weixin.qq.com/s?__biz=MzA4MjA0MTc4NQ==&mid=504090000&idx=3&sn=f57d4f194c902daadd80296d5b8ed001#rd)\n\n虽然写了这么多项目,因为应用的场景有限、应用的并发有限,所以基本使用的都是 Session,今天在百川的公众号上看到这篇文章,觉得总结地很好,转过来给自己给大家补补知识!\n\n##### cookie机制\n\nCookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器。IETF RFC 2965 HTTP State Management Mechanism 是通用cookie规范。网络服务器用HTTP头向客户端发送cookies,在客户终端,浏览器解析这些cookies并将它们保存为一个本地文件,它会自动将同一服务器的任何请求缚上这些cookies 。\n\n具体来说cookie机制采用的是在客户端保持状态的方案。它是在用户端的会话状态的存贮机制,他需要用户打开客户端的cookie支持。cookie的作用就是为了解决HTTP协议无状态的缺陷所作的努力。\n\n正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript也可以生成cookie。而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。\n\ncookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。\n\n而session机制采用的是一种在服务器端保持状态的解决方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的。而session提供了方便管理全局变量的方式 。\n\nsession是针对每一个用户的,变量的值保存在服务器上,用一个sessionID来区分是哪个用户session变量,这个值是通过用户的浏览器在访问的时候返回给服务器,当客户禁用cookie时,这个值也可能设置为由get来返回给服务器。\n\n就安全性来说:当你访问一个使用session 的站点,同时在自己机子上建立一个cookie,建议在服务器端的session机制更安全些,因为它不会任意读取客户存储的信息。\n\n##### session机制\n\nsession机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。\n\n当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。\n\n保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID。但cookie可以被人为的禁止,则必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。\n\n经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面。还有一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。\n\nCookie与Session都能够进行会话跟踪,但是完成的原理不太一样。普通状况下二者均能够满足需求,但有时分不能够运用Cookie,有时分不能够运用Session。下面经过比拟阐明二者的特性以及适用的场所。\n\n1、存取方式的不同\n\nCookie中只能保管ASCII字符串,假如需求存取Unicode字符或者二进制数据,需求先进行编码。Cookie中也不能直接存取Java对象。若要存储略微复杂的信息,运用Cookie是比拟艰难的。\n\n而Session中能够存取任何类型的数据,包括而不限于String、Integer、List、Map等。Session中也能够直接保管Java Bean乃至任何Java类,对象等,运用起来十分便当。能够把Session看做是一个Java容器类。\n\n2、隐私策略的不同\n\nCookie存储在客户端阅读器中,对客户端是可见的,客户端的一些程序可能会窥探、复制以至修正Cookie中的内容。而Session存储在服务器上,对客户端是透明的,不存在敏感信息泄露的风险。\n\n假如选用Cookie,比较好的方法是,敏感的信息如账号密码等尽量不要写到Cookie中。最好是像Google、Baidu那样将Cookie信息加密,提交到服务器后再进行解密,保证Cookie中的信息只要本人能读得懂。而假如选择Session就省事多了,反正是放在服务器上,Session里任何隐私都能够有效的保护。\n\n3、有效期上的不同\n\n使用过Google的人都晓得,假如登录过Google,则Google的登录信息长期有效。用户不用每次访问都重新登录,Google会持久地记载该用户的登录信息。要到达这种效果,运用Cookie会是比较好的选择。只需要设置Cookie的过期时间属性为一个很大很大的数字。\n\n由于Session依赖于名为JSESSIONID的Cookie,而Cookie JSESSIONID的过期时间默许为–1,只需关闭了阅读器该Session就会失效,因而Session不能完成信息永世有效的效果。运用URL地址重写也不能完成。而且假如设置Session的超时时间过长,服务器累计的Session就会越多,越容易招致内存溢出。\n\n4、服务器压力的不同\n\nSession是保管在服务器端的,每个用户都会产生一个Session。假如并发访问的用户十分多,会产生十分多的Session,耗费大量的内存。因而像Google、Baidu、Sina这样并发访问量极高的网站,是不太可能运用Session来追踪客户会话的。\n\n而Cookie保管在客户端,不占用服务器资源。假如并发阅读的用户十分多,Cookie是很好的选择。关于Google、Baidu、Sina来说,Cookie或许是唯一的选择。\n\n5、浏览器支持的不同\n\nCookie是需要客户端浏览器支持的。假如客户端禁用了Cookie,或者不支持Cookie,则会话跟踪会失效。关于WAP上的应用,常规的Cookie就派不上用场了。\n\n假如客户端浏览器不支持Cookie,需要运用Session以及URL地址重写。需要注意的是一切的用到Session程序的URL都要进行URL地址重写,否则Session会话跟踪还会失效。关于WAP应用来说,Session+URL地址重写或许是它唯一的选择。\n\n假如客户端支持Cookie,则Cookie既能够设为本浏览器窗口以及子窗口内有效(把过期时间设为–1),也能够设为一切阅读器窗口内有效(把过期时间设为某个大于0的整数)。但Session只能在本阅读器窗口以及其子窗口内有效。假如两个浏览器窗口互不相干,它们将运用两个不同的Session。(IE8下不同窗口Session相干)\n\n6、跨域支持上的不同\n\nCookie支持跨域名访问,例如将domain属性设置为“.biaodianfu.com”,则以“.biaodianfu.com”为后缀的一切域名均能够访问该Cookie。跨域名Cookie如今被普遍用在网络中,例如Google、Baidu、Sina等。而Session则不会支持跨域名访问。Session仅在他所在的域名内有效。\n\n仅运用Cookie或者仅运用Session可能完成不了理想的效果。这时应该尝试一下同时运用Cookie与Session。Cookie与Session的搭配运用在实践项目中会完成很多意想不到的效果。","html":"<p><a href=\"http://mp.weixin.qq.com/s?__biz=MzA4MjA0MTc4NQ==&mid=504090000&idx=3&sn=f57d4f194c902daadd80296d5b8ed001#rd\">猛戳原文</a></p>\n\n<p>虽然写了这么多项目,因为应用的场景有限、应用的并发有限,所以基本使用的都是 Session,今天在百川的公众号上看到这篇文章,觉得总结地很好,转过来给自己给大家补补知识!</p>\n\n<h5 id=\"cookie\">cookie机制</h5>\n\n<p>Cookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器。IETF RFC 2965 HTTP State Management Mechanism 是通用cookie规范。网络服务器用HTTP头向客户端发送cookies,在客户终端,浏览器解析这些cookies并将它们保存为一个本地文件,它会自动将同一服务器的任何请求缚上这些cookies 。</p>\n\n<p>具体来说cookie机制采用的是在客户端保持状态的方案。它是在用户端的会话状态的存贮机制,他需要用户打开客户端的cookie支持。cookie的作用就是为了解决HTTP协议无状态的缺陷所作的努力。</p>\n\n<p>正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript也可以生成cookie。而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。</p>\n\n<p>cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。</p>\n\n<p>而session机制采用的是一种在服务器端保持状态的解决方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的。而session提供了方便管理全局变量的方式 。\n\nsession是针对每一个用户的,变量的值保存在服务器上,用一个sessionID来区分是哪个用户session变量,这个值是通过用户的浏览器在访问的时候返回给服务器,当客户禁用cookie时,这个值也可能设置为由get来返回给服务器。</p>\n\n<p>就安全性来说:当你访问一个使用session 的站点,同时在自己机子上建立一个cookie,建议在服务器端的session机制更安全些,因为它不会任意读取客户存储的信息。</p>\n\n<h5 id=\"session\">session机制</h5>\n\n<p>session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。</p>\n\n<p>当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。</p>\n\n<p>保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID。但cookie可以被人为的禁止,则必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。</p>\n\n<p>经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面。还有一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。</p>\n\n<p>Cookie与Session都能够进行会话跟踪,但是完成的原理不太一样。普通状况下二者均能够满足需求,但有时分不能够运用Cookie,有时分不能够运用Session。下面经过比拟阐明二者的特性以及适用的场所。</p>\n\n<p>1、存取方式的不同</p>\n\n<p>Cookie中只能保管ASCII字符串,假如需求存取Unicode字符或者二进制数据,需求先进行编码。Cookie中也不能直接存取Java对象。若要存储略微复杂的信息,运用Cookie是比拟艰难的。</p>\n\n<p>而Session中能够存取任何类型的数据,包括而不限于String、Integer、List、Map等。Session中也能够直接保管Java Bean乃至任何Java类,对象等,运用起来十分便当。能够把Session看做是一个Java容器类。</p>\n\n<p>2、隐私策略的不同</p>\n\n<p>Cookie存储在客户端阅读器中,对客户端是可见的,客户端的一些程序可能会窥探、复制以至修正Cookie中的内容。而Session存储在服务器上,对客户端是透明的,不存在敏感信息泄露的风险。</p>\n\n<p>假如选用Cookie,比较好的方法是,敏感的信息如账号密码等尽量不要写到Cookie中。最好是像Google、Baidu那样将Cookie信息加密,提交到服务器后再进行解密,保证Cookie中的信息只要本人能读得懂。而假如选择Session就省事多了,反正是放在服务器上,Session里任何隐私都能够有效的保护。</p>\n\n<p>3、有效期上的不同</p>\n\n<p>使用过Google的人都晓得,假如登录过Google,则Google的登录信息长期有效。用户不用每次访问都重新登录,Google会持久地记载该用户的登录信息。要到达这种效果,运用Cookie会是比较好的选择。只需要设置Cookie的过期时间属性为一个很大很大的数字。</p>\n\n<p>由于Session依赖于名为JSESSIONID的Cookie,而Cookie JSESSIONID的过期时间默许为–1,只需关闭了阅读器该Session就会失效,因而Session不能完成信息永世有效的效果。运用URL地址重写也不能完成。而且假如设置Session的超时时间过长,服务器累计的Session就会越多,越容易招致内存溢出。</p>\n\n<p>4、服务器压力的不同</p>\n\n<p>Session是保管在服务器端的,每个用户都会产生一个Session。假如并发访问的用户十分多,会产生十分多的Session,耗费大量的内存。因而像Google、Baidu、Sina这样并发访问量极高的网站,是不太可能运用Session来追踪客户会话的。</p>\n\n<p>而Cookie保管在客户端,不占用服务器资源。假如并发阅读的用户十分多,Cookie是很好的选择。关于Google、Baidu、Sina来说,Cookie或许是唯一的选择。</p>\n\n<p>5、浏览器支持的不同</p>\n\n<p>Cookie是需要客户端浏览器支持的。假如客户端禁用了Cookie,或者不支持Cookie,则会话跟踪会失效。关于WAP上的应用,常规的Cookie就派不上用场了。</p>\n\n<p>假如客户端浏览器不支持Cookie,需要运用Session以及URL地址重写。需要注意的是一切的用到Session程序的URL都要进行URL地址重写,否则Session会话跟踪还会失效。关于WAP应用来说,Session+URL地址重写或许是它唯一的选择。</p>\n\n<p>假如客户端支持Cookie,则Cookie既能够设为本浏览器窗口以及子窗口内有效(把过期时间设为–1),也能够设为一切阅读器窗口内有效(把过期时间设为某个大于0的整数)。但Session只能在本阅读器窗口以及其子窗口内有效。假如两个浏览器窗口互不相干,它们将运用两个不同的Session。(IE8下不同窗口Session相干)</p>\n\n<p>6、跨域支持上的不同</p>\n\n<p>Cookie支持跨域名访问,例如将domain属性设置为“.biaodianfu.com”,则以“.biaodianfu.com”为后缀的一切域名均能够访问该Cookie。跨域名Cookie如今被普遍用在网络中,例如Google、Baidu、Sina等。而Session则不会支持跨域名访问。Session仅在他所在的域名内有效。</p>\n\n<p>仅运用Cookie或者仅运用Session可能完成不了理想的效果。这时应该尝试一下同时运用Cookie与Session。Cookie与Session的搭配运用在实践项目中会完成很多意想不到的效果。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1471071021724,"created_by":1,"updated_at":1471071706555,"updated_by":1,"published_at":1471071283539,"published_by":1},{"id":48,"uuid":"69cc2b15-2e63-45ce-b878-3c7bdfdd9a55","title":"有生之年系列——护国神翼","slug":"you-sheng-zhi-nian-xi-lie-zhi-hu-guo-shen-yi","markdown":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=4209157&auto=1&height=66\"></iframe>\n\n作为一个多年的老 Dotaer,虽然不玩 Dota2 而且也很久没有玩 Dota 这款游戏了,但是还是十分关注本届的 Ti 。看了一早上,我们的护国神翼终于翻盘 3:1 拿下了本届 Ti 的冠军。虽然护国神翼这个名字有些又爱又恨的味道,但是这一刻,他们值得这么叫。\n\n![](https://ws4.sinaimg.cn/large/006bH5BKgw1f775vnmgbqj30dw08pwgf.jpg)\n\n这是一只年轻的队伍,和去年的 CDEC 很像,但是但又很不同,因为我在这支队伍上看到了不凡的创造力,在国内这样的竞技环境下,他们能够保持这样一种创造力实在让人惊讶。于创造力之外,他们又显得十分的稳重,在先失一局的情况下,连下三局,拿下冠军。完全不像是几个比我可能还小的男孩所做的事,但是看到这些真的很高兴,我们的创造力、实力是完全不输给欧美的。\n\n![](https://ws4.sinaimg.cn/large/006bH5BKgw1f775vnlqolj30dw08pq6m.jpg)\n\n当然车长老和 DC 老师以及弹幕们的毒奶也是功不可没,O(∩_∩)O哈哈~尤其是当最后一句,Wings 在拿下一场团战胜利,扭转胜负的时候,DC 老师果断阻止了车长老的毒奶,真是笑死了!那一刻的弹幕就是冷静啊,车长老😂。\n\n再来谈谈 DC,之前看了他和 EHome 的比赛,从 BP 上就能看出这是一只同样具备灵性及韧性的队伍,能够干倒 EG 冲回来确实也令人刮目相看!\n\n最后希望拿到冠军的几个小伙子能够保持这样的一种创造力和团队,在明年给我们带来新的惊喜!\n\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f775vnlt8kj30dw08p0ww.jpg)","html":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=4209157&auto=1&height=66\"></iframe>\n\n<p>作为一个多年的老 Dotaer,虽然不玩 Dota2 而且也很久没有玩 Dota 这款游戏了,但是还是十分关注本届的 Ti 。看了一早上,我们的护国神翼终于翻盘 3:1 拿下了本届 Ti 的冠军。虽然护国神翼这个名字有些又爱又恨的味道,但是这一刻,他们值得这么叫。</p>\n\n<p><img src=\"https://ws4.sinaimg.cn/large/006bH5BKgw1f775vnmgbqj30dw08pwgf.jpg\" alt=\"\" /></p>\n\n<p>这是一只年轻的队伍,和去年的 CDEC 很像,但是但又很不同,因为我在这支队伍上看到了不凡的创造力,在国内这样的竞技环境下,他们能够保持这样一种创造力实在让人惊讶。于创造力之外,他们又显得十分的稳重,在先失一局的情况下,连下三局,拿下冠军。完全不像是几个比我可能还小的男孩所做的事,但是看到这些真的很高兴,我们的创造力、实力是完全不输给欧美的。</p>\n\n<p><img src=\"https://ws4.sinaimg.cn/large/006bH5BKgw1f775vnlqolj30dw08pq6m.jpg\" alt=\"\" /></p>\n\n<p>当然车长老和 DC 老师以及弹幕们的毒奶也是功不可没,O(∩_∩)O哈哈~尤其是当最后一句,Wings 在拿下一场团战胜利,扭转胜负的时候,DC 老师果断阻止了车长老的毒奶,真是笑死了!那一刻的弹幕就是冷静啊,车长老😂。</p>\n\n<p>再来谈谈 DC,之前看了他和 EHome 的比赛,从 BP 上就能看出这是一只同样具备灵性及韧性的队伍,能够干倒 EG 冲回来确实也令人刮目相看!</p>\n\n<p>最后希望拿到冠军的几个小伙子能够保持这样的一种创造力和团队,在明年给我们带来新的惊喜!</p>\n\n<p><img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f775vnlt8kj30dw08p0ww.jpg\" alt=\"\" /></p>","image":"/content/images/2016/08/17185891_1200x1000_0-1.jpg","featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1471146558750,"created_by":1,"updated_at":1473917908578,"updated_by":1,"published_at":1471147979226,"published_by":1},{"id":49,"uuid":"e0b8e7ae-089d-4506-9d08-696fba564fd0","title":"http 缓存","slug":"http-huan-cun","markdown":"怎么说呢,这块知识说重要但是往往不是我们直接需要做的,因此很容易在实际的项目中所忽略。毕竟像 Last-Modified 以及 Etag 这些缓存的策略大部分的服务器已经帮我们实现了,拿比较熟悉的 Tomcat 来说,Tomcat 自带了 DefaultServlet,用来处理静态资源的缓存问题。但是了解了 http 缓存我们还是能够做更多的事的,比如启用 Cache-Control ,这样静态资源得到的状态码将是 200(from cache)而不是 304,可以减少 http 请求。\n\n讲了了一些废话,那就开始慢慢介绍 http 缓存的一些基础知识!\n![](https://ws4.sinaimg.cn/large/006bH5BKgw1f775tm5vg3j30fe0eojsk.jpg)\n\n##### Pragma 和 Expires\n\n这是 http 1.0 时代,关于缓存的两个用来控制缓存的字段。\n\nPragma 的启用是通过添加如下的信息到 http 文件头部,来禁用客户端缓存该资源,主要是页面资源。\n\n```\n<meta http-equiv=\"Pragma\" content=\"no-cache\">\n```\n\n但是使用时存在一些注意点,首先只有 IE 支持这个 meta 属性,其次是往往需要放到 body 后面[点我点我](https://support.microsoft.com/zh-cn/kb/222064)。总的来说这种客户端定义Pragma的形式基本没起到多少作用。而在响应头部中加入这个字段,反而能够禁用缓存生效。\n\n有了 Pragma 来禁用缓存,自然也需要有个东西来启用缓存和定义缓存时间,对http 1.0 而言,Expires 就是做这件事的首部字段。\n\nExpires 的值对应一个GMT(格林尼治时间),比如“Mon, 22 Jul 2002 11:12:01 GMT”来告诉浏览器资源缓存过期时间,如果还没过该时间点则不发请求。\n\n在客户端我们同样可以使用meta标签来知会IE(也仅有IE能识别)页面(同样也只对页面有效,对页面上的资源无效)缓存时间:\n\n```\n<meta http-equiv=\"expires\" content=\"mon, 18 apr 2016 14:30:00 GMT\">\n```\n\n如果希望在IE下页面不走缓存,希望每次刷新页面都能发新请求,那么可以把“content”里的值写为“-1”或“0”。\n\n注意的是该方式仅仅作为知会IE缓存时间的标记,你并不能在请求或响应报文中找到Expires字段。如果是在服务端报头返回Expires字段,则在任何浏览器中都能正确设置资源缓存的时间。\n\n另外在优先级上 Pragma 要高于 Expires,并且 Expires 存在的劣势也很明显,就是客户端和服务器时间的不一致可能导致问题。\n\n实践:为了向下兼容 http 1.0 的标准,还是有很多网站会使用这两个字段的,正确的使用姿势也是在服务端向 response header 中设置对应的参数。\n\n##### Cache-Control\n\n针对上述的“Expires时间是相对服务器而言,无法保证和客户端时间统一”的问题,http1.1新增了 Cache-Control 来定义缓存过期时间,若报文中同时出现了 Pragma、Expires 和 Cache-Control,会以 Cache-Control 为准。\n\nCache-Control 是一个通用首部,有很多取值,这里主要介绍几个常用的值,其余的可以查阅 RFC 2616 文档。\n\n>值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age\n>\n各个消息中的指令含义如下:\n>\nPublic指示响应可被任何缓存区缓存。\nPrivate指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。\nno-cache指示请求或响应消息不能缓存,该选项并不是说可以设置”不缓存“,容易望文生义~\nno-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存,完全不存下來。\nmax-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。\nmin-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。\nmax-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。\n\n实践,针对页面资源,主要还是要根据页面的更新速度,如果经常变化则可以不用缓存或者设置一个较短的过期时间。腾讯首页选择的是设置 max-age=60,较短时间的缓存,而百度则是使用 private(简要介绍一下private,首先这是默认值,在地址栏回车或后退键是不会重新请求的,刷新或者第一次访问时才会请求)。而针对静态资源,一般可以设置一个较长的缓存时间,百度设置的是 30 天,部分资源会达到一年,不过在工程化的前端当中静态资源的更新是一个比较常见的场景,这会在之后的文章中有所涉及。\n\n##### Last-Modified 和 Etag\n\n服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一起返回给客户端。\n\n客户端会为资源标记上该信息,下次再次请求时,会把该信息附带在请求报文中一并带给服务器去做检查,若传递的时间值与服务器上该资源最终修改时间是一致的,则说明该资源没有被修改过,直接返回304状态码即可。\n\n至于传递标记起来的最终修改时间的请求报文首部字段一共有两个:\n\n1、 If-Modified-Since: Last-Modified-value\n\n示例为 If-Modified-Since: Thu, 31 Mar 2016 07:07:52 GMT\n该请求首部告诉服务器如果客户端传来的最后修改时间与服务器上的一致,则直接回送304 和响应报头即可。当前各浏览器均是使用的该请求首部来向服务器传递保存的 Last-Modified 值。\n\n2、 If-Unmodified-Since: Last-Modified-value\n\n告诉服务器,若Last-Modified没有匹配上(资源在服务端的最后更新时间改变了),则应当返回412(Precondition Failed) 状态码给客户端。\n\n当遇到下面情况时,If-Unmodified-Since 字段会被忽略:\n\n1. Last-Modified值对上了(资源在服务端没有新的修改);\n2. 服务端需返回2XX和412之外的状态码;\n3. 传来的指定日期不合法\n\nLast-Modified 说好却也不是特别好,因为如果在服务器上,一个资源被修改了,但其实际内容根本没发生改变,会因为Last-Modified时间匹配不上而返回了整个实体给客户端(即使客户端缓存里有个一模一样的资源)。\n\n为了解决上面的这个问题,http 1.1 又提出了Etag。服务器会通过某种算法,给资源计算得出一个唯一标志符(比如md5标志),在把资源响应给客户端的时候,会在实体首部加上“ETag: 唯一标识符”一起返回给客户端。\n\n客户端会保留该 ETag 字段,并在下一次请求时将其一并带过去给服务器。服务器只需要比较客户端传来的ETag跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。\n\n如果服务器发现ETag匹配不上,那么直接以常规GET 200回包形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag是一致的,则直接返回304知会客户端直接使用本地缓存即可。\n\n那么客户端是如何把标记在资源上的 ETag 传去给服务器的呢?请求报文中有两个首部字段可以带上 ETag 值:\n\n1、If-None-Match: ETag-value\n\n示例为 If-None-Match: \"56fcccc8-1699\"\n告诉服务端如果 ETag 没匹配上需要重发资源数据,否则直接回送304 和响应报头即可。\n\n当前各浏览器均是使用的该请求首部来向服务器传递保存的 ETag 值。\n\n2、If-Match: ETag-value\n\n告诉服务器如果没有匹配到ETag,或者收到了“*”值而当前并没有该资源实体,则应当返回412(Precondition Failed) 状态码给客户端。否则服务器直接忽略该字段。\n\nIf-Match 的一个应用场景是,客户端走PUT方法向服务端请求上传/更替资源,这时候可以通过 If-Match 传递资源的ETag。\n\n需要注意的是,如果资源是走分布式服务器(比如CDN)存储的情况,需要这些服务器上计算ETag唯一值的算法保持一致,才不会导致明明同一个文件,在服务器A和服务器B上生成的ETag却不一样。\n\nLast-Modified 和 Etag 很好,但是配合上面提到的三个东西一起使用,会有更好的效果,因为对于一些资源,完全可以使用 200(from cache),而不是重新去请求,通过 304 在做,这样可以减少大量的不必要的 http 请求。\n\n##### 不同用户行为的影响\n\n<table>\n<thead>\n<tr>\n<td>用户操作</td>\n<td>Expires/Cache-Control</td>\n<td>Last-Modified/Etag</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>地址栏回车</td>\n<td>有效</td>\n<td>有效</td>\n</tr>\n<tr>\n<td>页面链接跳转</td>\n<td>有效</td>\n<td>有效</td>\n</tr>\n<tr>\n<td>新开窗口</td>\n<td>有效</td>\n<td>有效</td>\n</tr>\n<tr>\n<td>前进、后退</td>\n<td>有效</td>\n<td>有效</td>\n</tr>\n<tr>\n<td>F5/按钮刷新</td>\n<td>无效(BR重置max-age=0)</td>\n<td>有效</td>\n</tr>\n<tr>\n<td>Ctrl+F5刷新</td>\n<td>无效(重置CC=no-cache)</td>\n<td>无效(请求头丢弃该选项)</td>\n</tr>\n</tbody>\n</table>\n\n##### 总结\n\n在服务器为我们实现了 Last-Modified 和 Etag 的基础上,我们需要对自己的静态资源以及页面资源进行缓存的设置。对于静态资源可以设置一个较长时间的缓存,而对于页面的缓存可以根据页面的更新频次进行精确地控制,具体的策略我也需要更多的实践来进行试验。当然写了这么多很多东西还是没有涉及的,有兴趣可以再回去啃啃 http 1.1 的 RFC 文档,虽然看起来一点都不好啃。最后不得不吐槽下 ghost 博客的 markdown 支持太差了!🙄\n\n参考资料:\n\n1、 [浅谈浏览器http的缓存机制](http://www.cnblogs.com/vajoy/p/5341664.html)\n","html":"<p>怎么说呢,这块知识说重要但是往往不是我们直接需要做的,因此很容易在实际的项目中所忽略。毕竟像 Last-Modified 以及 Etag 这些缓存的策略大部分的服务器已经帮我们实现了,拿比较熟悉的 Tomcat 来说,Tomcat 自带了 DefaultServlet,用来处理静态资源的缓存问题。但是了解了 http 缓存我们还是能够做更多的事的,比如启用 Cache-Control ,这样静态资源得到的状态码将是 200(from cache)而不是 304,可以减少 http 请求。</p>\n\n<p>讲了了一些废话,那就开始慢慢介绍 http 缓存的一些基础知识!\n<img src=\"https://ws4.sinaimg.cn/large/006bH5BKgw1f775tm5vg3j30fe0eojsk.jpg\" alt=\"\" /></p>\n\n<h5 id=\"pragmaexpires\">Pragma 和 Expires</h5>\n\n<p>这是 http 1.0 时代,关于缓存的两个用来控制缓存的字段。</p>\n\n<p>Pragma 的启用是通过添加如下的信息到 http 文件头部,来禁用客户端缓存该资源,主要是页面资源。</p>\n\n<pre><code><meta http-equiv=\"Pragma\" content=\"no-cache\"> \n</code></pre>\n\n<p>但是使用时存在一些注意点,首先只有 IE 支持这个 meta 属性,其次是往往需要放到 body 后面<a href=\"https://support.microsoft.com/zh-cn/kb/222064\">点我点我</a>。总的来说这种客户端定义Pragma的形式基本没起到多少作用。而在响应头部中加入这个字段,反而能够禁用缓存生效。</p>\n\n<p>有了 Pragma 来禁用缓存,自然也需要有个东西来启用缓存和定义缓存时间,对http 1.0 而言,Expires 就是做这件事的首部字段。</p>\n\n<p>Expires 的值对应一个GMT(格林尼治时间),比如“Mon, 22 Jul 2002 11:12:01 GMT”来告诉浏览器资源缓存过期时间,如果还没过该时间点则不发请求。</p>\n\n<p>在客户端我们同样可以使用meta标签来知会IE(也仅有IE能识别)页面(同样也只对页面有效,对页面上的资源无效)缓存时间:</p>\n\n<pre><code><meta http-equiv=\"expires\" content=\"mon, 18 apr 2016 14:30:00 GMT\"> \n</code></pre>\n\n<p>如果希望在IE下页面不走缓存,希望每次刷新页面都能发新请求,那么可以把“content”里的值写为“-1”或“0”。</p>\n\n<p>注意的是该方式仅仅作为知会IE缓存时间的标记,你并不能在请求或响应报文中找到Expires字段。如果是在服务端报头返回Expires字段,则在任何浏览器中都能正确设置资源缓存的时间。</p>\n\n<p>另外在优先级上 Pragma 要高于 Expires,并且 Expires 存在的劣势也很明显,就是客户端和服务器时间的不一致可能导致问题。</p>\n\n<p>实践:为了向下兼容 http 1.0 的标准,还是有很多网站会使用这两个字段的,正确的使用姿势也是在服务端向 response header 中设置对应的参数。</p>\n\n<h5 id=\"cachecontrol\">Cache-Control</h5>\n\n<p>针对上述的“Expires时间是相对服务器而言,无法保证和客户端时间统一”的问题,http1.1新增了 Cache-Control 来定义缓存过期时间,若报文中同时出现了 Pragma、Expires 和 Cache-Control,会以 Cache-Control 为准。</p>\n\n<p>Cache-Control 是一个通用首部,有很多取值,这里主要介绍几个常用的值,其余的可以查阅 RFC 2616 文档。</p>\n\n<blockquote>\n <p>值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age</p>\n \n <p>各个消息中的指令含义如下:</p>\n \n <p>Public指示响应可被任何缓存区缓存。 <br />\n Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。 <br />\n no-cache指示请求或响应消息不能缓存,该选项并不是说可以设置”不缓存“,容易望文生义~ <br />\n no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存,完全不存下來。 <br />\n max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。 <br />\n min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。 <br />\n max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。</p>\n</blockquote>\n\n<p>实践,针对页面资源,主要还是要根据页面的更新速度,如果经常变化则可以不用缓存或者设置一个较短的过期时间。腾讯首页选择的是设置 max-age=60,较短时间的缓存,而百度则是使用 private(简要介绍一下private,首先这是默认值,在地址栏回车或后退键是不会重新请求的,刷新或者第一次访问时才会请求)。而针对静态资源,一般可以设置一个较长的缓存时间,百度设置的是 30 天,部分资源会达到一年,不过在工程化的前端当中静态资源的更新是一个比较常见的场景,这会在之后的文章中有所涉及。</p>\n\n<h5 id=\"lastmodifiedetag\">Last-Modified 和 Etag</h5>\n\n<p>服务器将资源传递给客户端时,会将资源最后更改的时间以“Last-Modified: GMT”的形式加在实体首部上一起返回给客户端。</p>\n\n<p>客户端会为资源标记上该信息,下次再次请求时,会把该信息附带在请求报文中一并带给服务器去做检查,若传递的时间值与服务器上该资源最终修改时间是一致的,则说明该资源没有被修改过,直接返回304状态码即可。</p>\n\n<p>至于传递标记起来的最终修改时间的请求报文首部字段一共有两个:</p>\n\n<p>1、 If-Modified-Since: Last-Modified-value</p>\n\n<p>示例为 If-Modified-Since: Thu, 31 Mar 2016 07:07:52 GMT\n该请求首部告诉服务器如果客户端传来的最后修改时间与服务器上的一致,则直接回送304 和响应报头即可。当前各浏览器均是使用的该请求首部来向服务器传递保存的 Last-Modified 值。</p>\n\n<p>2、 If-Unmodified-Since: Last-Modified-value</p>\n\n<p>告诉服务器,若Last-Modified没有匹配上(资源在服务端的最后更新时间改变了),则应当返回412(Precondition Failed) 状态码给客户端。</p>\n\n<p>当遇到下面情况时,If-Unmodified-Since 字段会被忽略:</p>\n\n<ol>\n<li>Last-Modified值对上了(资源在服务端没有新的修改); </li>\n<li>服务端需返回2XX和412之外的状态码; </li>\n<li>传来的指定日期不合法</li>\n</ol>\n\n<p>Last-Modified 说好却也不是特别好,因为如果在服务器上,一个资源被修改了,但其实际内容根本没发生改变,会因为Last-Modified时间匹配不上而返回了整个实体给客户端(即使客户端缓存里有个一模一样的资源)。</p>\n\n<p>为了解决上面的这个问题,http 1.1 又提出了Etag。服务器会通过某种算法,给资源计算得出一个唯一标志符(比如md5标志),在把资源响应给客户端的时候,会在实体首部加上“ETag: 唯一标识符”一起返回给客户端。</p>\n\n<p>客户端会保留该 ETag 字段,并在下一次请求时将其一并带过去给服务器。服务器只需要比较客户端传来的ETag跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。</p>\n\n<p>如果服务器发现ETag匹配不上,那么直接以常规GET 200回包形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag是一致的,则直接返回304知会客户端直接使用本地缓存即可。</p>\n\n<p>那么客户端是如何把标记在资源上的 ETag 传去给服务器的呢?请求报文中有两个首部字段可以带上 ETag 值:</p>\n\n<p>1、If-None-Match: ETag-value</p>\n\n<p>示例为 If-None-Match: \"56fcccc8-1699\"\n告诉服务端如果 ETag 没匹配上需要重发资源数据,否则直接回送304 和响应报头即可。</p>\n\n<p>当前各浏览器均是使用的该请求首部来向服务器传递保存的 ETag 值。</p>\n\n<p>2、If-Match: ETag-value</p>\n\n<p>告诉服务器如果没有匹配到ETag,或者收到了“*”值而当前并没有该资源实体,则应当返回412(Precondition Failed) 状态码给客户端。否则服务器直接忽略该字段。</p>\n\n<p>If-Match 的一个应用场景是,客户端走PUT方法向服务端请求上传/更替资源,这时候可以通过 If-Match 传递资源的ETag。</p>\n\n<p>需要注意的是,如果资源是走分布式服务器(比如CDN)存储的情况,需要这些服务器上计算ETag唯一值的算法保持一致,才不会导致明明同一个文件,在服务器A和服务器B上生成的ETag却不一样。</p>\n\n<p>Last-Modified 和 Etag 很好,但是配合上面提到的三个东西一起使用,会有更好的效果,因为对于一些资源,完全可以使用 200(from cache),而不是重新去请求,通过 304 在做,这样可以减少大量的不必要的 http 请求。</p>\n\n<h5 id=\"\">不同用户行为的影响</h5>\n\n<table> \n<thead> \n<tr> \n<td>用户操作</td> \n<td>Expires/Cache-Control</td> \n<td>Last-Modified/Etag</td> \n</tr> \n</thead> \n<tbody> \n<tr> \n<td>地址栏回车</td> \n<td>有效</td> \n<td>有效</td> \n</tr> \n<tr> \n<td>页面链接跳转</td> \n<td>有效</td> \n<td>有效</td> \n</tr> \n<tr> \n<td>新开窗口</td> \n<td>有效</td> \n<td>有效</td> \n</tr> \n<tr> \n<td>前进、后退</td> \n<td>有效</td> \n<td>有效</td> \n</tr> \n<tr> \n<td>F5/按钮刷新</td> \n<td>无效(BR重置max-age=0)</td> \n<td>有效</td> \n</tr> \n<tr> \n<td>Ctrl+F5刷新</td> \n<td>无效(重置CC=no-cache)</td> \n<td>无效(请求头丢弃该选项)</td> \n</tr> \n</tbody> \n</table>\n\n<h5 id=\"\">总结</h5>\n\n<p>在服务器为我们实现了 Last-Modified 和 Etag 的基础上,我们需要对自己的静态资源以及页面资源进行缓存的设置。对于静态资源可以设置一个较长时间的缓存,而对于页面的缓存可以根据页面的更新频次进行精确地控制,具体的策略我也需要更多的实践来进行试验。当然写了这么多很多东西还是没有涉及的,有兴趣可以再回去啃啃 http 1.1 的 RFC 文档,虽然看起来一点都不好啃。最后不得不吐槽下 ghost 博客的 markdown 支持太差了!🙄</p>\n\n<p>参考资料:</p>\n\n<p>1、 <a href=\"http://www.cnblogs.com/vajoy/p/5341664.html\">浅谈浏览器http的缓存机制</a></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1471156822874,"created_by":1,"updated_at":1472195008893,"updated_by":1,"published_at":1471164888546,"published_by":1},{"id":50,"uuid":"7b3a5b8f-2231-4013-bed4-c073406dec87","title":"随笔","slug":"shenlun","markdown":"晚上做完客回到家已是 11 点,洗完澡看了这周的火影已经是 12 点,本来想分享点关于今天看的 《css揭秘》一书中的 css 历史及草案制定的内容的,但是感觉还需要看一遍,再找点资料,才能写的好点,所以放到明天。但还是要写点什么的,养成习惯了,毕竟这不仅仅是一个单纯的技术博客,所以扯点别的也无所谓,金色梦乡嘛!!\n\n晚上饭后,爸妈和叔叔阿姨讨论着那些未来的事,大致也就是考研啊、工作啊什么的!听了很多遍了,也烦了,就管自己看新歌声周董队的 pk 了!\n\n对于已经放弃保研的我来说,好像都关系不大了。为什么要放弃保研?我是不是傻!🤔\n\n简单讲讲我的想法吧,首先,我本身对于本科阶段三年的接受的教学是失望的,我所学的只是大多来自自发的学习和实践。这也是目前普通大学的普遍现象,清华北大是否如此我不知道。面对这个现象,我大致可以有两个选择,一是进入更好的学府一探究竟;第二是借助我目前的能力投入更丰富的实践,为这个问题提出申论,甚至和一群志同道合的同学一起为解决这个问题而努力。我选择了后者,因为意义更大!\n\n另外一个原因是,我能够拿到保研名额,一部分功劳是来自于导师发的论文的。在这篇论文与我毫无干系,并不能提现我大学期间所做的事,我不想靠着与我无关的东西保研。我也并不觉得论文、奖项比我参与过的项目、开源的代码要来得有分量。所以可能从我大学期间的方向来看也并不适合读研。\n\n第三点是我的疑问,就是读研与工作,哪一个能够真正地提升我的能力和视野,而不是工作的身价。钱固然重要,但是够了就好,我很知足!我觉得就我目前接受的教育水平来看,读研能够带给我的很有限,当然不包括所有大学的教育!\n\n最后回到第一点申论的话题,这是一个很喜欢的歌手张悬在14年一席的演讲上提到的。她大致讲到,我们这个世代的年轻人,需要找到一个关于自身或者社会问题困惑的申论的方式。我很有感触,但是申论如何提出,如何得到更多人的响应,如何去做实验,拿出一个相对更好的方案,这是我所在思考的!我想借助现有的力量做出这样一个东西,可能是小众的,但是绝对是意义非凡的!帮助我们这个世代的年轻人能够真正团结起来为解决自身的困惑而努力,不碌碌一生!\n\n一点了,就先写到这,熬夜都长痘痘了!😓外链不支持 https,外链暂时就不放了,只放个连接![一点点——周董](http://music.163.com/#/song?id=418603076),因为晚上新声音看到周董,所以回来听了它的新专辑,这首挺喜欢😊。","html":"<p>晚上做完客回到家已是 11 点,洗完澡看了这周的火影已经是 12 点,本来想分享点关于今天看的 《css揭秘》一书中的 css 历史及草案制定的内容的,但是感觉还需要看一遍,再找点资料,才能写的好点,所以放到明天。但还是要写点什么的,养成习惯了,毕竟这不仅仅是一个单纯的技术博客,所以扯点别的也无所谓,金色梦乡嘛!!</p>\n\n<p>晚上饭后,爸妈和叔叔阿姨讨论着那些未来的事,大致也就是考研啊、工作啊什么的!听了很多遍了,也烦了,就管自己看新歌声周董队的 pk 了!</p>\n\n<p>对于已经放弃保研的我来说,好像都关系不大了。为什么要放弃保研?我是不是傻!🤔</p>\n\n<p>简单讲讲我的想法吧,首先,我本身对于本科阶段三年的接受的教学是失望的,我所学的只是大多来自自发的学习和实践。这也是目前普通大学的普遍现象,清华北大是否如此我不知道。面对这个现象,我大致可以有两个选择,一是进入更好的学府一探究竟;第二是借助我目前的能力投入更丰富的实践,为这个问题提出申论,甚至和一群志同道合的同学一起为解决这个问题而努力。我选择了后者,因为意义更大!</p>\n\n<p>另外一个原因是,我能够拿到保研名额,一部分功劳是来自于导师发的论文的。在这篇论文与我毫无干系,并不能提现我大学期间所做的事,我不想靠着与我无关的东西保研。我也并不觉得论文、奖项比我参与过的项目、开源的代码要来得有分量。所以可能从我大学期间的方向来看也并不适合读研。</p>\n\n<p>第三点是我的疑问,就是读研与工作,哪一个能够真正地提升我的能力和视野,而不是工作的身价。钱固然重要,但是够了就好,我很知足!我觉得就我目前接受的教育水平来看,读研能够带给我的很有限,当然不包括所有大学的教育!</p>\n\n<p>最后回到第一点申论的话题,这是一个很喜欢的歌手张悬在14年一席的演讲上提到的。她大致讲到,我们这个世代的年轻人,需要找到一个关于自身或者社会问题困惑的申论的方式。我很有感触,但是申论如何提出,如何得到更多人的响应,如何去做实验,拿出一个相对更好的方案,这是我所在思考的!我想借助现有的力量做出这样一个东西,可能是小众的,但是绝对是意义非凡的!帮助我们这个世代的年轻人能够真正团结起来为解决自身的困惑而努力,不碌碌一生!</p>\n\n<p>一点了,就先写到这,熬夜都长痘痘了!😓外链不支持 https,外链暂时就不放了,只放个连接!<a href=\"http://music.163.com/#/song?id=418603076\">一点点——周董</a>,因为晚上新声音看到周董,所以回来听了它的新专辑,这首挺喜欢😊。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1471245064499,"created_by":1,"updated_at":1472318288846,"updated_by":1,"published_at":1472317683444,"published_by":1},{"id":51,"uuid":"31dbfd34-fafb-452f-a108-a2c81e166de7","title":"金色梦乡","slug":"jin-se-meng-xiang","markdown":"备案上交管局也有一周了吧,但是还毫无音讯啊🙄。提交备案时选择的名称是“金色梦乡”,The Beatles 的一首歌,亦是雅人叔的一部电影。当时还没有看雅人叔的这部电影,仅是看了影评,也有另一个选择,小葵演的“编舟记”。\n\n“编舟记”之意是这个博客是我不断成长的小舟,而我的成长之路亦是编舟之路,不断积累的过程。从这层意思上来讲,“编舟记”也不失为一个恰当的题目。但是最终我还是莫名其妙的选择了“金色梦乡”,这个更加温润的题目。今晚看了雅人叔的 10 年的这部代表作,有种感觉,我是选对了啊!\n\n从披头士讲起吧,之前听披头士是高一的时候了吧,Yesterday、Hey Jude,还有那首充满回忆的 Let It Be。记得还学唱过 Yesterday;记得班长唱 hey jude 很好听;记得教室里放着这首歌,然后和同桌调侃着 let it be 好像方言中一句骂人的话,都是回忆啊!不过不记得当时是否听过 Golden Slumbers 这首了,不过 Golden Slumbers 之于片中的几人,就像 let it be 之于我和同桌吧。\n\n不矫情,继续!《Golden Slumbers》选自Beatles 1969年的专辑《Abbey Road》。虽然从时间上来看,《Let It Be》是发表于1970年,但实际上69年的时候 Beatles 已经走向分裂,而《Abbey Road》才是Beatles 真正的告别。 Golden Slumbers 是保罗麦卡特尼创作的,对四人过去岁月的一种诗意的怀念。\n\n而影片中刚好也是四个人,四个人虽然已经分开过上了各自的生活,但是他们的生活通过青柳的这件事再次联系起来。不管他们的现状如何,在这件事中,他们回忆起的过往都是那么的美妙,他们也没有真正的背叛过青柳。加上剧本的戏剧性,我甚至想要设想这是青柳的一个梦,青柳的金色梦乡。梦中的那一切不幸就像是大学毕业后进入社会,遭受到的束缚与挫折;面对这些,朋友家人的回忆在青柳的脑中构建出了以种种力量,帮助他逃离!\n\n这力量告诉他:听好青柳!快逃!不管多么凄惨!总之要逃走!活下去!能活下来是最好的!快逃啊!青柳!\n\n快逃啊!青柳!脑洞大了点,也不符合影片的细节,但是我想要这么解读。不服打我呀!😜\n\n影片感动、温润的地方还很多,就不更多据透露了!而这个博客之于我,绝不仅仅是一个技术博客那么简单,它记录了我美好的点点滴滴,像个梦但是却又那么的真实。另外还有牵强的一点,就是外部的力量,也就是青柳从朋友、家人甚至陌生人身上得到的力量,这是我不想忽略的东西。\n\n😂12点半了,不行了,不写了,关机睡觉!\n\n<embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=2696215&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed>\n\n","html":"<p>备案上交管局也有一周了吧,但是还毫无音讯啊🙄。提交备案时选择的名称是“金色梦乡”,The Beatles 的一首歌,亦是雅人叔的一部电影。当时还没有看雅人叔的这部电影,仅是看了影评,也有另一个选择,小葵演的“编舟记”。</p>\n\n<p>“编舟记”之意是这个博客是我不断成长的小舟,而我的成长之路亦是编舟之路,不断积累的过程。从这层意思上来讲,“编舟记”也不失为一个恰当的题目。但是最终我还是莫名其妙的选择了“金色梦乡”,这个更加温润的题目。今晚看了雅人叔的 10 年的这部代表作,有种感觉,我是选对了啊!</p>\n\n<p>从披头士讲起吧,之前听披头士是高一的时候了吧,Yesterday、Hey Jude,还有那首充满回忆的 Let It Be。记得还学唱过 Yesterday;记得班长唱 hey jude 很好听;记得教室里放着这首歌,然后和同桌调侃着 let it be 好像方言中一句骂人的话,都是回忆啊!不过不记得当时是否听过 Golden Slumbers 这首了,不过 Golden Slumbers 之于片中的几人,就像 let it be 之于我和同桌吧。</p>\n\n<p>不矫情,继续!《Golden Slumbers》选自Beatles 1969年的专辑《Abbey Road》。虽然从时间上来看,《Let It Be》是发表于1970年,但实际上69年的时候 Beatles 已经走向分裂,而《Abbey Road》才是Beatles 真正的告别。 Golden Slumbers 是保罗麦卡特尼创作的,对四人过去岁月的一种诗意的怀念。</p>\n\n<p>而影片中刚好也是四个人,四个人虽然已经分开过上了各自的生活,但是他们的生活通过青柳的这件事再次联系起来。不管他们的现状如何,在这件事中,他们回忆起的过往都是那么的美妙,他们也没有真正的背叛过青柳。加上剧本的戏剧性,我甚至想要设想这是青柳的一个梦,青柳的金色梦乡。梦中的那一切不幸就像是大学毕业后进入社会,遭受到的束缚与挫折;面对这些,朋友家人的回忆在青柳的脑中构建出了以种种力量,帮助他逃离!</p>\n\n<p>这力量告诉他:听好青柳!快逃!不管多么凄惨!总之要逃走!活下去!能活下来是最好的!快逃啊!青柳!</p>\n\n<p>快逃啊!青柳!脑洞大了点,也不符合影片的细节,但是我想要这么解读。不服打我呀!😜</p>\n\n<p>影片感动、温润的地方还很多,就不更多据透露了!而这个博客之于我,绝不仅仅是一个技术博客那么简单,它记录了我美好的点点滴滴,像个梦但是却又那么的真实。另外还有牵强的一点,就是外部的力量,也就是青柳从朋友、家人甚至陌生人身上得到的力量,这是我不想忽略的东西。</p>\n\n<p>😂12点半了,不行了,不写了,关机睡觉!</p>\n\n<p><embed height=\"415\" width=\"544\" quality=\"high\" allowfullscreen=\"true\" type=\"application/x-shockwave-flash\" src=\"http://static.hdslb.com/miniloader.swf\" flashvars=\"aid=2696215&page=1\" pluginspage=\"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash\"></embed></p>","image":"/content/images/2016/08/d1160924ab18972b76de0bb1e6cd7b899f510ae0.jpg","featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1471361067936,"created_by":1,"updated_at":1473917893125,"updated_by":1,"published_at":1471365183323,"published_by":1},{"id":52,"uuid":"05bcaa58-2e47-4f49-9397-a11fe66598bd","title":"git 代码合并:Merge、Rebase的选择","slug":"git-xiao-jie","markdown":"[原文](https://github.com/geeeeeeeeek/git-recipes/wiki/5.1-%E4%BB%A3%E7%A0%81%E5%90%88%E5%B9%B6%EF%BC%9AMerge%E3%80%81Rebase%E7%9A%84%E9%80%89%E6%8B%A9)\n\n因为原文已经写得很清楚了,所以我就不再写一遍了,仅仅是总结一下。\n\n##### git Merge \n\n- 优势:一个安全的操作,现有的分支不会被更改,可跟踪;\n- 劣势:合并时会引入外来提交,或多或少会污染你的分支历史。\n\n##### git Rebase\n\n- 优势:项目历史会非常整洁;\n- 劣势:安全性和可跟踪性。\n\n##### 应用场景(最佳实践)\n\n什么时候使用 Rebase?(符合黄金法则)\n\n黄金法则:绝不要在公共的分支上使用它。个人总结了一下就是下面几个场景符合黄金法则:\n\n1. 一个人的项目,只有自己提交;\n2. 交互式 Rebase,用于清理本地的提交(原文中有);\n3. 这个分支只有你在工作(不光是主分支,如果一个特性分支上多人在提交,也不能使用 Rebase)。\n\n除了以上几个场景,我认为都建议用 Merge。另外,扯点其他的,首先就是养成比较好的习惯,上班第一件事 pull;其次,和同事沟通好;另外就是尽量先 fetch,再 merge,而不是直接pull,可控一点。\n\n参考资料:\n\n1. [Git Rebase原理以及黄金准则详解](https://segmentfault.com/a/1190000005937408)\n2. [Git 分支 - 分支的衍合](https://git-scm.com/book/zh/v1/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%9A%84%E8%A1%8D%E5%90%88)","html":"<p><a href=\"https://github.com/geeeeeeeeek/git-recipes/wiki/5.1-%E4%BB%A3%E7%A0%81%E5%90%88%E5%B9%B6%EF%BC%9AMerge%E3%80%81Rebase%E7%9A%84%E9%80%89%E6%8B%A9\">原文</a></p>\n\n<p>因为原文已经写得很清楚了,所以我就不再写一遍了,仅仅是总结一下。</p>\n\n<h5 id=\"gitmerge\">git Merge</h5>\n\n<ul>\n<li>优势:一个安全的操作,现有的分支不会被更改,可跟踪;</li>\n<li>劣势:合并时会引入外来提交,或多或少会污染你的分支历史。</li>\n</ul>\n\n<h5 id=\"gitrebase\">git Rebase</h5>\n\n<ul>\n<li>优势:项目历史会非常整洁;</li>\n<li>劣势:安全性和可跟踪性。</li>\n</ul>\n\n<h5 id=\"\">应用场景(最佳实践)</h5>\n\n<p>什么时候使用 Rebase?(符合黄金法则)</p>\n\n<p>黄金法则:绝不要在公共的分支上使用它。个人总结了一下就是下面几个场景符合黄金法则:</p>\n\n<ol>\n<li>一个人的项目,只有自己提交; </li>\n<li>交互式 Rebase,用于清理本地的提交(原文中有); </li>\n<li>这个分支只有你在工作(不光是主分支,如果一个特性分支上多人在提交,也不能使用 Rebase)。</li>\n</ol>\n\n<p>除了以上几个场景,我认为都建议用 Merge。另外,扯点其他的,首先就是养成比较好的习惯,上班第一件事 pull;其次,和同事沟通好;另外就是尽量先 fetch,再 merge,而不是直接pull,可控一点。</p>\n\n<p>参考资料:</p>\n\n<ol>\n<li><a href=\"https://segmentfault.com/a/1190000005937408\">Git Rebase原理以及黄金准则详解</a> </li>\n<li><a href=\"https://git-scm.com/book/zh/v1/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%9A%84%E8%A1%8D%E5%90%88\">Git 分支 - 分支的衍合</a></li>\n</ol>","image":"/content/images/2016/08/1-e-tlWqLwbUd1UmZyC_KbGg.png","featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1471421090540,"created_by":1,"updated_at":1471422860787,"updated_by":1,"published_at":1471422860788,"published_by":1},{"id":53,"uuid":"2aa68f9f-c0b4-4742-8778-42d3156bee1d","title":"备案成功😀","slug":"bei-an-cheng-gong-2","markdown":"早上管局终于来了电话,然后问了几个问题,备案成功的短信就收到了!没想到等了半个月,最后就这么敷衍的成功了!以后就直接访问 https://m2mbob.cn 就行了,原来的端口号,我看看配置下 nginx 能不能成功!😎\n\nnginx 配置 https 失败,证书搞了半天居然没用!!!😷 \n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=28784417&auto=1&height=66\"></iframe>","html":"<p>早上管局终于来了电话,然后问了几个问题,备案成功的短信就收到了!没想到等了半个月,最后就这么敷衍的成功了!以后就直接访问 <a href=\"https://m2mbob.cn\">https://m2mbob.cn</a> 就行了,原来的端口号,我看看配置下 nginx 能不能成功!😎</p>\n\n<p>nginx 配置 https 失败,证书搞了半天居然没用!!!😷 </p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=28784417&auto=1&height=66\"></iframe>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1471917700169,"created_by":1,"updated_at":1473917831639,"updated_by":1,"published_at":1471917809949,"published_by":1},{"id":54,"uuid":"92886ad7-43fc-4222-902c-337c02d7a439","title":"升级博客到 HTTP/2 实录——CentOS升级Nginx到最新版","slug":"sheng-ji-bo-ke-dao-http2shi-lu-centossheng-ji-nginxdao-zui-xin-ban","markdown":"最近看 w3c 的文档都快吐了,尤其是视觉格式化模型,但是收获还是很大的,更加深入的理解了很多东西。这部分的东西会在之后总结完放出来的,而这个系列升级博客到 HTTP/2 实录,则是一边熟悉 HTTP 、 HTTPS 、HTTP/2,一边实践的产物,每天会实践一点,更新一点,直到把博客升级到 HTTP/2 为止。对于博客搞这个虽然是有点大材小用了,但是玩玩加上学习还是很不错的。\n\n这是本系列的第一篇,是关于 CentOS 安装和升级 nginx 的,因为 nginx 要到 1.9.0 才支持 HTTP/2 。废话不多讲,开始。\n\n首先,我的服务器是阿里云 CentOS 7,64位的,所以以下内容都是在这个环境下的。打开命令行,执行 `yum install yum-fastestmirror`,这个插件可以帮助我们自动选择最快的 yum 源。\n\n然后执行 `yum install nginx` ,安装 nginx。一般这时候安装的 nginx 版本是 1.6.3 。而且执行 `yum update nginx`,也无法更新。这就无法满足版本大于 1.9.0 的要求。\n\n在查阅了一些资料后,得到的方案是,在 update 之前,需要配置一下 nginx 源。打开命令行执行 `vim /etc/yum.repos.d/nginx.repo`,然后在 vim 中输入以下内容:\n\n```\n#nginx.repo \n \n[nginx] \nname=nginx repo \nbaseurl=http://nginx.org/packages/centos/7/$basearch/ \ngpgcheck=0 \nenabled=1 \n```\n\n配置完之后,在执行 `yum update nginx` 可以更新 nginx 了。打出 `nginx -v`,可以看到 nginx 已经升级到了 1.10.1 了😁。最后别忘了重启一下 nginx,`service nginx restart`。\n\n![](https://ws3.sinaimg.cn/large/006bH5BKgw1f774ek4suvj30m80bp43m.jpg)\n\n至此,我们的 nginx就升级成功了,明天继续升级!!!🤗\n\n参考资料:\n\n1. [CentOS配置Nginx官方的Yum源](http://zhangzifan.com/centos-nginx-yum-source.html)\n2. [CentOS下安装nginx并且升级nginx到最新版](http://www.centoscn.com/nginx/2015/0505/5363.html)\n\n","html":"<p>最近看 w3c 的文档都快吐了,尤其是视觉格式化模型,但是收获还是很大的,更加深入的理解了很多东西。这部分的东西会在之后总结完放出来的,而这个系列升级博客到 HTTP/2 实录,则是一边熟悉 HTTP 、 HTTPS 、HTTP/2,一边实践的产物,每天会实践一点,更新一点,直到把博客升级到 HTTP/2 为止。对于博客搞这个虽然是有点大材小用了,但是玩玩加上学习还是很不错的。</p>\n\n<p>这是本系列的第一篇,是关于 CentOS 安装和升级 nginx 的,因为 nginx 要到 1.9.0 才支持 HTTP/2 。废话不多讲,开始。</p>\n\n<p>首先,我的服务器是阿里云 CentOS 7,64位的,所以以下内容都是在这个环境下的。打开命令行,执行 <code>yum install yum-fastestmirror</code>,这个插件可以帮助我们自动选择最快的 yum 源。</p>\n\n<p>然后执行 <code>yum install nginx</code> ,安装 nginx。一般这时候安装的 nginx 版本是 1.6.3 。而且执行 <code>yum update nginx</code>,也无法更新。这就无法满足版本大于 1.9.0 的要求。</p>\n\n<p>在查阅了一些资料后,得到的方案是,在 update 之前,需要配置一下 nginx 源。打开命令行执行 <code>vim /etc/yum.repos.d/nginx.repo</code>,然后在 vim 中输入以下内容:</p>\n\n<pre><code>#nginx.repo \n\n[nginx] \nname=nginx repo \nbaseurl=http://nginx.org/packages/centos/7/$basearch/ \ngpgcheck=0 \nenabled=1 \n</code></pre>\n\n<p>配置完之后,在执行 <code>yum update nginx</code> 可以更新 nginx 了。打出 <code>nginx -v</code>,可以看到 nginx 已经升级到了 1.10.1 了😁。最后别忘了重启一下 nginx,<code>service nginx restart</code>。</p>\n\n<p><img src=\"https://ws3.sinaimg.cn/large/006bH5BKgw1f774ek4suvj30m80bp43m.jpg\" alt=\"\" /></p>\n\n<p>至此,我们的 nginx就升级成功了,明天继续升级!!!🤗</p>\n\n<p>参考资料:</p>\n\n<ol>\n<li><a href=\"http://zhangzifan.com/centos-nginx-yum-source.html\">CentOS配置Nginx官方的Yum源</a> </li>\n<li><a href=\"http://www.centoscn.com/nginx/2015/0505/5363.html\">CentOS下安装nginx并且升级nginx到最新版</a></li>\n</ol>","image":"/content/images/2016/08/QQ20160824-0-2x.png","featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1472050275710,"created_by":1,"updated_at":1472192071553,"updated_by":1,"published_at":1472051608652,"published_by":1},{"id":55,"uuid":"3261c781-f0d4-482d-b8d5-584756c661ae","title":"升级博客到 HTTP/2 实录——Nginx启用Let's Encrypt SSL证书","slug":"sheng-ji-bo-ke-dao-http-2-shi-lu-centossheng-ji-nginxdao-zui-xin-ban","markdown":"这篇本来是昨晚发的,但是由于阿里云的学生服务器太渣了、房间网速又不行,一直卡在 letsencrypt 的安装上。早上起来一试,没想到就这么成功了。\n\n[Let's Encrypt](https://letsencrypt.org/) 是一个免费的 SSL/TLS 证书发行机构,证书有效期为90天,到期前30内可续期, 实现永久免费。\n\nLet's Encrypt SSL 证书的的获取并不是像其他网站一样,在页面上填写资申请证书,而是需要在域名所在的服务器上安装一个客户端(python写的)去获取证书和续期。而像 StartSSL 之前搞了半天,弄下来的证书也没办法用。而这个利用 python 客户端的,只要敲敲命令就可以完成一切,方便多了。\n\n本次安装使用的服务器配置:\nAliyun9.9学生服务器 + CentOS 7 + Nginx 1.10.1\n\n##### 客户端\n\nLet's Encrypt 提供了两种客户端。Certbot 和 官方客户端。前者是官方推荐的,不过我使用的是后者,所以前者就不介绍了,感兴趣的可以看参考资料里的链接。\n\nLet's Encrypt 官方的客户端托管在 github 上,每次运行客户端都会先自动升级,再运行最新的客户端,所以需要安装 git。因为是 python 写的程序,所以需要安装 python。\n\n第一步,安装 python 及 git,已经安装的则跳过;\n\n```\nyum install git python\n```\n\n第二步,下载客户端, 放到某路径下;\n\n```\ngit clone https://github.com/letsencrypt/letsencrypt\n```\n\n第三步,运行一次客户端,自动检查升级,请确保内存足够多,大概要几十兆吧。\n\n```\ncd letsencrypt\n./letsencrypt-auto --help\n```\n\n第四步,验证域名所有权,其原理就是申请人在域名所在的服务器上申请证书,然后 Let's Encrypt 会访问绑定的域名与客户端通信成功即可通过。\n\n这个验证的方法有两种,一种需要停止当前的 web server 服务,让出 80 端口,由客户端内置的 web server 启动与 Let's Encrypt 通信. 另一种不需要停止当前 web server,但需要在域名根目录下创建一个临时目录,并要保证外网通过域名可以访问这个目录。\n\n###### 通过客户端 web server 获取证书\n\n```\n#停止nginx\nsystemctl stop nginx\n \n#获取证书,--standalone 参数:使用内置web server。--email 参数:管理员邮箱,证书到期前会发邮件到此邮箱提醒。-d 参数:要绑定的域名,同一域的不同子域都要输入。\n./letsencrypt-auto certonly --standalone --email [email protected] -d m2mbob.cn -d www.m2mbob.cn\n \n#启动nginx\nsystemctl start nginx\n```\n\n###### 通过临时目录获取证书\n\n```\n#创建临时目录,可能要修改nginx rewrite 规则才能从外网访问\nmkdir -p /usr/share/nginx/html/.well-known/acme-challenge\n \n#--webroot 参数:指定使用临时目录的方式。-w 参数:指定后面-d 域名所在的根目录,如果一次申请多个域的, 可以附加更多 -w...-d... 这段。\n./letsencrypt-auto certonly --webroot --email [email protected] -w /usr/sha\n```\n\n执行此命令后会生成证书,保存在 /etc/letsencrypt/live 中对应的域名目录下面,其实这里面并不是真正的证书文件,而是通过链接的形式链到了 /etc/letsencrypt/archive 中对应的域名目录下。\n\n##### 自动更新\n\n完成以上四步,其实就可以配置 nginx ,完成升级了。但是考虑到证书有效期为90天,每次到期需要在申请证书一波很麻烦,所以在这里我们需要写个脚本来自动更新证书。\n\n```\n#!/bin/sh\n#停止 nginx 服务,使用 --standalone 独立服务器验证需要停止当前 web server.\nsystemctl stop nginx\nif ! /path/to/letsencrypt-auto renew -nvv --standalone > /var/log/letsencrypt/renew.log 2>&1 ; then\n echo Automated renewal failed:\n cat /var/log/letsencrypt/renew.log\n exit 1\nfi\n#启动 nginx\nsystemctl start nginx\n```\n\n然后添加执行权限 \n\n```\nchmod +x letsencrypt-renew.sh\n```\n\n最后一步就是编辑 crontab 配置文件或执行 crontab -e 添加 cron 任务\n\n```\nnano /etc/crontab\n```\n\n我这里设置为每月25号23点59分执行此脚本.\n\n```\n#分 时 日 月 星期 执行用户 执行命令\n 59 23 25 * * root /脚本目录/letsencrypt-renew.sh\n```\n\n保存退出即可。\n\n##### 配置 nginx\n\n```\n#设置非安全连接永久跳转到安全连接\nserver{\n listen 80;\n server_name m2mbob.cn www.m2mbob.cn;\n #告诉浏览器有效期内只准用 https 访问\n add_header Strict-Transport-Security max-age=15768000;\n #永久重定向到 https 站点\n return 301 https://$server_name$request_uri;\n}\nserver {\n #启用 https,使用 http/2 协议\n listen 443 ssl http2;\n server_name m2mbob.cn www.m2mbob.cn;\n #告诉浏览器当前页面禁止被frame\n add_header X-Frame-Options DENY;\n #告诉浏览器不要猜测mime类型\n add_header X-Content-Type-Options nosniff;\n \n #证书路径\n ssl_certificate /etc/letsencrypt/live/m2mbob.cn/fullchain.pem;\n #私钥路径\n ssl_certificate_key /etc/letsencrypt/live/m2mbob.cn/privkey.pem;\n #安全链接可选的加密协议\n ssl_protocols TLSv1 TLSv1.1 TLSv1.2;\n #可选的加密算法,顺序很重要,越靠前的优先级越高.\n ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-RC4-SHA:!ECDHE-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:HIGH:!RC4-SHA:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!CBC:!EDH:!kEDH:!PSK:!SRP:!kECDH;\n #在 SSLv3 或 TLSv1 握手过程一般使用客户端的首选算法,如果启用下面的配置,则会使用服务器端的首选算法.\n ssl_prefer_server_ciphers on;\n #储存SSL会话的缓存类型和大小\n ssl_session_cache shared:SSL:10m;\n #缓存有效期\n ssl_session_timeout 60m;\n \n #省略后面与证书无关的设置\n}\n```\n\n最后效果😬:\n\n![](https://ws1.sinaimg.cn/large/006bH5BKgw1f774d06ggdj30gu064t9e.jpg)\n\n可是[极简图床](http://www.yotuku.cn/)挂了,而且由于提供的图片不是 https 的,所以访问一些页面时,chrome 绿色的认证又木有了。下午换用了微博图床,支持 HTTPS 而且是 chrome 插件,很好用。但是b站和网易云音乐外链的问题还解决不了!😂再看看,再看看!\n\n到这里,博客其实已经成功升级到 HTTP/2 了,但是仅仅是实践,对于 HTTP/2 及 HTTPS 理解还不是那么的深刻,需要找时间在学习学习!😳\n\n参考资料:\n\n1. [CentOS 7 Nginx Let’ s Encrypt SSL 证书安装配置](https://blog.itnmg.net/letsencrypt-ssl/)\n2. [Nginx启用Let’s Encrypt SSL证书](https://www.iwwenbo.com/nginx-lets-encrypt-ssl/)","html":"<p>这篇本来是昨晚发的,但是由于阿里云的学生服务器太渣了、房间网速又不行,一直卡在 letsencrypt 的安装上。早上起来一试,没想到就这么成功了。</p>\n\n<p><a href=\"https://letsencrypt.org/\">Let's Encrypt</a> 是一个免费的 SSL/TLS 证书发行机构,证书有效期为90天,到期前30内可续期, 实现永久免费。</p>\n\n<p>Let's Encrypt SSL 证书的的获取并不是像其他网站一样,在页面上填写资申请证书,而是需要在域名所在的服务器上安装一个客户端(python写的)去获取证书和续期。而像 StartSSL 之前搞了半天,弄下来的证书也没办法用。而这个利用 python 客户端的,只要敲敲命令就可以完成一切,方便多了。</p>\n\n<p>本次安装使用的服务器配置:\nAliyun9.9学生服务器 + CentOS 7 + Nginx 1.10.1</p>\n\n<h5 id=\"\">客户端</h5>\n\n<p>Let's Encrypt 提供了两种客户端。Certbot 和 官方客户端。前者是官方推荐的,不过我使用的是后者,所以前者就不介绍了,感兴趣的可以看参考资料里的链接。</p>\n\n<p>Let's Encrypt 官方的客户端托管在 github 上,每次运行客户端都会先自动升级,再运行最新的客户端,所以需要安装 git。因为是 python 写的程序,所以需要安装 python。</p>\n\n<p>第一步,安装 python 及 git,已经安装的则跳过;</p>\n\n<pre><code>yum install git python \n</code></pre>\n\n<p>第二步,下载客户端, 放到某路径下;</p>\n\n<pre><code>git clone https://github.com/letsencrypt/letsencrypt \n</code></pre>\n\n<p>第三步,运行一次客户端,自动检查升级,请确保内存足够多,大概要几十兆吧。</p>\n\n<pre><code>cd letsencrypt \n./letsencrypt-auto --help\n</code></pre>\n\n<p>第四步,验证域名所有权,其原理就是申请人在域名所在的服务器上申请证书,然后 Let's Encrypt 会访问绑定的域名与客户端通信成功即可通过。</p>\n\n<p>这个验证的方法有两种,一种需要停止当前的 web server 服务,让出 80 端口,由客户端内置的 web server 启动与 Let's Encrypt 通信. 另一种不需要停止当前 web server,但需要在域名根目录下创建一个临时目录,并要保证外网通过域名可以访问这个目录。</p>\n\n<h6 id=\"webserver\">通过客户端 web server 获取证书</h6>\n\n<pre><code>#停止nginx\nsystemctl stop nginx\n\n#获取证书,--standalone 参数:使用内置web server。--email 参数:管理员邮箱,证书到期前会发邮件到此邮箱提醒。-d 参数:要绑定的域名,同一域的不同子域都要输入。\n./letsencrypt-auto certonly --standalone --email [email protected] -d m2mbob.cn -d www.m2mbob.cn\n\n#启动nginx\nsystemctl start nginx \n</code></pre>\n\n<h6 id=\"\">通过临时目录获取证书</h6>\n\n<pre><code>#创建临时目录,可能要修改nginx rewrite 规则才能从外网访问\nmkdir -p /usr/share/nginx/html/.well-known/acme-challenge\n\n#--webroot 参数:指定使用临时目录的方式。-w 参数:指定后面-d 域名所在的根目录,如果一次申请多个域的, 可以附加更多 -w...-d... 这段。\n./letsencrypt-auto certonly --webroot --email [email protected] -w /usr/sha\n</code></pre>\n\n<p>执行此命令后会生成证书,保存在 /etc/letsencrypt/live 中对应的域名目录下面,其实这里面并不是真正的证书文件,而是通过链接的形式链到了 /etc/letsencrypt/archive 中对应的域名目录下。</p>\n\n<h5 id=\"\">自动更新</h5>\n\n<p>完成以上四步,其实就可以配置 nginx ,完成升级了。但是考虑到证书有效期为90天,每次到期需要在申请证书一波很麻烦,所以在这里我们需要写个脚本来自动更新证书。</p>\n\n<pre><code>#!/bin/sh\n#停止 nginx 服务,使用 --standalone 独立服务器验证需要停止当前 web server.\nsystemctl stop nginx \nif ! /path/to/letsencrypt-auto renew -nvv --standalone > /var/log/letsencrypt/renew.log 2>&1 ; then \n echo Automated renewal failed:\n cat /var/log/letsencrypt/renew.log\n exit 1\nfi \n#启动 nginx\nsystemctl start nginx \n</code></pre>\n\n<p>然后添加执行权限 </p>\n\n<pre><code>chmod +x letsencrypt-renew.sh \n</code></pre>\n\n<p>最后一步就是编辑 crontab 配置文件或执行 crontab -e 添加 cron 任务</p>\n\n<pre><code>nano /etc/crontab \n</code></pre>\n\n<p>我这里设置为每月25号23点59分执行此脚本.</p>\n\n<pre><code>#分 时 日 月 星期 执行用户 执行命令\n 59 23 25 * * root /脚本目录/letsencrypt-renew.sh\n</code></pre>\n\n<p>保存退出即可。</p>\n\n<h5 id=\"nginx\">配置 nginx</h5>\n\n<pre><code>#设置非安全连接永久跳转到安全连接\nserver{ \n listen 80;\n server_name m2mbob.cn www.m2mbob.cn;\n #告诉浏览器有效期内只准用 https 访问\n add_header Strict-Transport-Security max-age=15768000;\n #永久重定向到 https 站点\n return 301 https://$server_name$request_uri;\n}\nserver { \n #启用 https,使用 http/2 协议\n listen 443 ssl http2;\n server_name m2mbob.cn www.m2mbob.cn;\n #告诉浏览器当前页面禁止被frame\n add_header X-Frame-Options DENY;\n #告诉浏览器不要猜测mime类型\n add_header X-Content-Type-Options nosniff;\n\n #证书路径\n ssl_certificate /etc/letsencrypt/live/m2mbob.cn/fullchain.pem;\n #私钥路径\n ssl_certificate_key /etc/letsencrypt/live/m2mbob.cn/privkey.pem;\n #安全链接可选的加密协议\n ssl_protocols TLSv1 TLSv1.1 TLSv1.2;\n #可选的加密算法,顺序很重要,越靠前的优先级越高.\n ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-RC4-SHA:!ECDHE-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:HIGH:!RC4-SHA:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!CBC:!EDH:!kEDH:!PSK:!SRP:!kECDH;\n #在 SSLv3 或 TLSv1 握手过程一般使用客户端的首选算法,如果启用下面的配置,则会使用服务器端的首选算法.\n ssl_prefer_server_ciphers on;\n #储存SSL会话的缓存类型和大小\n ssl_session_cache shared:SSL:10m;\n #缓存有效期\n ssl_session_timeout 60m;\n\n #省略后面与证书无关的设置\n}\n</code></pre>\n\n<p>最后效果😬:</p>\n\n<p><img src=\"https://ws1.sinaimg.cn/large/006bH5BKgw1f774d06ggdj30gu064t9e.jpg\" alt=\"\" /></p>\n\n<p>可是<a href=\"http://www.yotuku.cn/\">极简图床</a>挂了,而且由于提供的图片不是 https 的,所以访问一些页面时,chrome 绿色的认证又木有了。下午换用了微博图床,支持 HTTPS 而且是 chrome 插件,很好用。但是b站和网易云音乐外链的问题还解决不了!😂再看看,再看看!</p>\n\n<p>到这里,博客其实已经成功升级到 HTTP/2 了,但是仅仅是实践,对于 HTTP/2 及 HTTPS 理解还不是那么的深刻,需要找时间在学习学习!😳</p>\n\n<p>参考资料:</p>\n\n<ol>\n<li><a href=\"https://blog.itnmg.net/letsencrypt-ssl/\">CentOS 7 Nginx Let’ s Encrypt SSL 证书安装配置</a> </li>\n<li><a href=\"https://www.iwwenbo.com/nginx-lets-encrypt-ssl/\">Nginx启用Let’s Encrypt SSL证书</a></li>\n</ol>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1472175887078,"created_by":1,"updated_at":1472200210647,"updated_by":1,"published_at":1472181171990,"published_by":1},{"id":56,"uuid":"5c03c309-e87f-4fb6-a79c-2d4840a8cb6d","title":"升级博客到 HTTP/2 实录——新版Chrome下滚回HTTP/1.1","slug":"sheng-ji-bo-ke-dao-http-2-shi-lu-chromexia-gun-hui-http1-1bug","markdown":"本以为这个系列就要这么结束,但是当我用 Chrome 查看协议版本时,惊奇地发现还是 HTTP/1.1,这又是怎么回事,然后就开始了一路的踩坑。有几个大牛写着 HTTP/2,实际还不知道自己的还是HTTP/1.1,其他的不知是歪打正着,实现了HTTP/2,但是都没有提到这个问题。于是只能从度娘走向谷歌,才找到了问题所在。\n\n首先明确,只有在新版的 Chrome 下才有这个现象,其次造成这个现象的原因与 openssl 有关。然后我们来看一段英文:\n\n> Users of the Google Chrome web browser are seeing some sites that they previously accessed over HTTP/2 falling back to HTTP/1. This is because of a policy change in the most recent update to Chrome, released in late May, which removes support for NPN, one method for upgrading a connection to HTTP/2.\n\n> The only way Chrome users can continue using HTTP/2 to access these websites is by switching to a different browser. Website administrators can restore HTTP/2 support for Chrome users by upgrading their OpenSSL installation to the recently released 1.0.2 version. Unfortunately, this requires either a major operating system upgrade or using a private build of NGINX.\n\n大致意思就是说 Chrome 在最近的更新中放弃了对 NPN 的支持,如果想要继续在 Chrome上支持 HTTP/2 ,则需要安装最新 1.0.2 版的 OpenSSL,并且用 1.0.2 的 OpenSSL 重新编译 Nginx。\n\n知道问题的所在了,后面找日子再试试了,今天已经搞得身心俱疲了,而且明天还要处理 gitlab 上的一大堆 issue。急着解决问题的同学直接看参考链接哈!!!\n\n参考链接:\n\n1. [Supporting HTTP/2 for Google Chrome Users](https://www.nginx.com/blog/supporting-http2-google-chrome-users/)\n2. [Nginx HTTP2 编译](http://www.tuicool.com/articles/3eeIVfi)","html":"<p>本以为这个系列就要这么结束,但是当我用 Chrome 查看协议版本时,惊奇地发现还是 HTTP/1.1,这又是怎么回事,然后就开始了一路的踩坑。有几个大牛写着 HTTP/2,实际还不知道自己的还是HTTP/1.1,其他的不知是歪打正着,实现了HTTP/2,但是都没有提到这个问题。于是只能从度娘走向谷歌,才找到了问题所在。</p>\n\n<p>首先明确,只有在新版的 Chrome 下才有这个现象,其次造成这个现象的原因与 openssl 有关。然后我们来看一段英文:</p>\n\n<blockquote>\n <p>Users of the Google Chrome web browser are seeing some sites that they previously accessed over HTTP/2 falling back to HTTP/1. This is because of a policy change in the most recent update to Chrome, released in late May, which removes support for NPN, one method for upgrading a connection to HTTP/2.</p>\n \n <p>The only way Chrome users can continue using HTTP/2 to access these websites is by switching to a different browser. Website administrators can restore HTTP/2 support for Chrome users by upgrading their OpenSSL installation to the recently released 1.0.2 version. Unfortunately, this requires either a major operating system upgrade or using a private build of NGINX.</p>\n</blockquote>\n\n<p>大致意思就是说 Chrome 在最近的更新中放弃了对 NPN 的支持,如果想要继续在 Chrome上支持 HTTP/2 ,则需要安装最新 1.0.2 版的 OpenSSL,并且用 1.0.2 的 OpenSSL 重新编译 Nginx。</p>\n\n<p>知道问题的所在了,后面找日子再试试了,今天已经搞得身心俱疲了,而且明天还要处理 gitlab 上的一大堆 issue。急着解决问题的同学直接看参考链接哈!!!</p>\n\n<p>参考链接:</p>\n\n<ol>\n<li><a href=\"https://www.nginx.com/blog/supporting-http2-google-chrome-users/\">Supporting HTTP/2 for Google Chrome Users</a> </li>\n<li><a href=\"http://www.tuicool.com/articles/3eeIVfi\">Nginx HTTP2 编译</a></li>\n</ol>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1472229137358,"created_by":1,"updated_at":1472230036207,"updated_by":1,"published_at":1472230018473,"published_by":1},{"id":57,"uuid":"719704a5-6f93-4ea8-9a99-156caf71b95c","title":"转:JS中函数定义和函数表达式的区别","slug":"zhuan-jszhong-han-shu-ding-yi-he-han-shu-biao-da-shi-de-qu-bie","markdown":"Javascript中有2个语法都与function关键字有关,分别是:\n\n> 函数定义:function FunctionName(FormalParameterList) { FunctionBody }\n\n> 函数表达式:function [FunctionName](FormalParameterList) { FunctionBody }\n\n从语法的定义上看,这两者几乎是一模一样的(唯一的区别是函数表达式可以省略函数名称),那么就解释器而言,当遇到这个结构的语句时,判定为函数表达式还是函数定义呢?\n\n就javascript的语法而言,如果一条语句是以function关键字开始,那么这段会被判定为函数定义。而函数定义是不能被立即执行的,这无疑会导致语法的错误(SyntaxError),因此就必须有一个办法,使解析器可以将之识别为函数表达式。\n\n前面已经说到,解析器识别函数定义的条件是以function关键字开始,那么自然,只要在function关键字的前面有任何其他的元素,就会从函数定义转变为函数表达式,以下方法都是可以的,这个大家都知道:\n\n```\n~function() {}();\n\n!function() {}();\n\nvoid function() {}();\n```\n\n但是这几个方法都有一个特点,就是看起来很别扭,所以现在为止,以括号包裹成了比较公认的方案。\n\n回到正题,括号包裹同样有2个方式:(function() {})();和(function(){}());\n\n他们的共通点是:都有括号。而括号在javascript中有2种作用:确立运算优先级,以及分组运算符,从代码上看,显然没有进行数学或逻辑运算,因此我认为这里的括号属于分组运算符。\n\n根据标准,分组运算符的作用是:\n\n> Return the result of evaluating Expression. This may be of type Reference. \n\n返回评估括号中的表达式的结果。结果可能是Reference类型。\n\n抛开像Reference类型这种词汇,这里的一个关键词应当是“ 评估 ”,但是关于分组运算符,又有一个很重要的下文:\n\n> This algorithm does not apply GetValue to the result of evaluating Expression.\n\n这个算法不会对估算的结果使用GetValue。\n\n有很多专用的名词,看起来确实复杂,简而言之,使用括号运算符本身不会让括号中的代码立即执行,只有当括号包含的这个“分组”参与其他运算时,才会执行。因此,(function(){})()这个语句,其实是首先用分组运算符评估了一个函数表达式,随后参与“函数调用”。而(function(){}())这个语句,则是用分组运算符评估了一个函数调用,随后由于语句的结束而被执行。\n\n[原文地址](http://www.zhihu.com/question/20292224)","html":"<p>Javascript中有2个语法都与function关键字有关,分别是:</p>\n\n<blockquote>\n <p>函数定义:function FunctionName(FormalParameterList) { FunctionBody }</p>\n \n <p>函数表达式:function <a href=\"FormalParameterList\">FunctionName</a> { FunctionBody }</p>\n</blockquote>\n\n<p>从语法的定义上看,这两者几乎是一模一样的(唯一的区别是函数表达式可以省略函数名称),那么就解释器而言,当遇到这个结构的语句时,判定为函数表达式还是函数定义呢?</p>\n\n<p>就javascript的语法而言,如果一条语句是以function关键字开始,那么这段会被判定为函数定义。而函数定义是不能被立即执行的,这无疑会导致语法的错误(SyntaxError),因此就必须有一个办法,使解析器可以将之识别为函数表达式。</p>\n\n<p>前面已经说到,解析器识别函数定义的条件是以function关键字开始,那么自然,只要在function关键字的前面有任何其他的元素,就会从函数定义转变为函数表达式,以下方法都是可以的,这个大家都知道:</p>\n\n<pre><code>~function() {}();\n\n!function() {}();\n\nvoid function() {}(); \n</code></pre>\n\n<p>但是这几个方法都有一个特点,就是看起来很别扭,所以现在为止,以括号包裹成了比较公认的方案。</p>\n\n<p>回到正题,括号包裹同样有2个方式:(function() {})();和(function(){}());</p>\n\n<p>他们的共通点是:都有括号。而括号在javascript中有2种作用:确立运算优先级,以及分组运算符,从代码上看,显然没有进行数学或逻辑运算,因此我认为这里的括号属于分组运算符。</p>\n\n<p>根据标准,分组运算符的作用是:</p>\n\n<blockquote>\n <p>Return the result of evaluating Expression. This may be of type Reference. </p>\n</blockquote>\n\n<p>返回评估括号中的表达式的结果。结果可能是Reference类型。</p>\n\n<p>抛开像Reference类型这种词汇,这里的一个关键词应当是“ 评估 ”,但是关于分组运算符,又有一个很重要的下文:</p>\n\n<blockquote>\n <p>This algorithm does not apply GetValue to the result of evaluating Expression.</p>\n</blockquote>\n\n<p>这个算法不会对估算的结果使用GetValue。</p>\n\n<p>有很多专用的名词,看起来确实复杂,简而言之,使用括号运算符本身不会让括号中的代码立即执行,只有当括号包含的这个“分组”参与其他运算时,才会执行。因此,(function(){})()这个语句,其实是首先用分组运算符评估了一个函数表达式,随后参与“函数调用”。而(function(){}())这个语句,则是用分组运算符评估了一个函数调用,随后由于语句的结束而被执行。</p>\n\n<p><a href=\"http://www.zhihu.com/question/20292224\">原文地址</a></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1472875383514,"created_by":1,"updated_at":1472875565217,"updated_by":1,"published_at":1472875537256,"published_by":1},{"id":58,"uuid":"6ea8836d-d787-4574-bdfd-9d217fed6ac7","title":"可执行代码与执行环境","slug":"js-function-context","markdown":"*不行不行,太大了,好难整合。。。还是分开写吧*\n\n这是一个很大的话题,在ECMA262规范(ES5,本文不涉及ES6,因为引入块状作用域后情况会变得更加复杂,下同)的第十章,本文把规范的内容进行了注释,加入了一些个人的理解,不过有点多,写得貌似有点乱!建议在看本文前可以把[汤姆大叔-深入理解JavaScript系列的11-16](http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html)看一下。后面也会针对汤姆大叔这几篇文章的内容结合规范写一些文章的。\n\n#### 可执行代码\n\n首先规范中讲到三种可执行代码类型(三种执行上下文,With和Catch算特例):\n\n- 全局代码:这种类型的代码是在\"程序\"级处理的。例如加载外部的js文件或者本地`<script></script>`标签内的代码。全局代码不包括任何function体内的代码。\n- eval代码:指提供给 eval 内置函数的源代码文本。eval第二个参数可以指定上下文,但是是非规范的,本文不做讨论。\n- 函数代码:作为函数体被解析的源代码文本,但不包括作为其嵌套函数的函数体被解析的源代码文本。另外需要提的是使用Function构造器创建函数时,最后一个参数将被转换为字符串并作为函数体使用,同样不包括嵌套函数的函数体。\n\n在严格模式下,这些代码将被称为严格全局代码、严格 eval 代码和严格函数代码,相应地处理会有一些差异。不过不是本文的重点,不做太多的展开,可以看前面的文章。\n\n#### 词法环境\n\n简单来讲,词法环境对象(后面在部分执行环境会看到词法环境和变量环境,本质都是词法环境对象)基本可以等同于汤姆大叔[变量对象](http://www.cnblogs.com/TomXu/archive/2012/01/16/2309728.html)一文中所讲的变量对象,汤姆大叔已经讲得十分到位了,但是我希望能够把隐藏在变量对象下的知识讲清楚。建议看下面的内容前,先看一下汤姆大叔[变量对象](http://www.cnblogs.com/TomXu/archive/2012/01/16/2309728.html)一文,这样能够更好地做对应和理解。\n\n首先,词法环境包括两个内容:环境记录项和可能为空的外部词法环境引用。通常词法环境会与特定的 ECMAScript 代码诸如 FunctionDeclaration、WithStatement 或者 TryStatement 的 Catch 块这样的语法结构相联系,且类似代码每次执行都会有一个新的语法环境被创建出来。\n\n###### 外部词法环境引用\n\n外部词法环境引用比较简单,就先讲了。它就是用来记录词法环境的嵌套关系的,在创建一个新的词法环境时需要指定它的外部词法环境,`GetIdentifierReference`(根据标识符得到引用函数)就会在这条链上一层层找上去,直到全局词法环境,而全局词法环境的外部词法环境是null,所以这里就是终点,有点感觉了吧。这个其实就是作用域链了,本质就是指向外部词法环境的一个指针。\n\n###### 环境记录项\n\n环境记录项包括声明式环境记录项和对象式环境记录项。声明式环境记录项用于定义那些将标识符与语言值直接绑定的 ECMA 脚本语法元素,例如函数声明,变量声明以及 Catch 语句。对象式环境记录项用于定义那些将标识符 与具体对象的属性绑定的 ECMA 脚本元素,例如程序以及 With 表达式。\n\n*简单来说环境记录项就保存了标识符以及标识符对应的引用。*\n\n全局级别(因为全局环境本质上就是全局对象,这也是汤姆大叔博文中一直强调的)和 With 的词法环境拥有的是对象式环境记录项,函数、和 catch 的词法环境拥有的是声明式环境记录项。\n\n我们可以把环境记录项当做一个抽象类,它上面有一些抽象方法,这些方法都是供语言内部使用的,声明式环境记录项和对象式环境记录项分别实现了这些抽象方法,这些方法就是用来处理标识符和引用的关系的,具体实现可以查看文档。\n\n<table>\n<thead>\n<tr><td width=\"30%\">方法</td><td>作用</td></tr>\n</thead>\n<tbody>\n<tr>\n<td>HasBinding(N)</td><td>判断环境记录项是否包含对某个标识符的绑定。如果包含该绑定则返回 true,反之返回 false。其中字符串 N 是标识符文本。</td></tr>\n<tr>\n<td>CreateMutableBinding(N, D)</td><td>\n在环境记录项中创建一个新的可变绑定。其中字符串 N 指定绑定名称。如果可选参数 D 的值为true,则该绑定在后续操作中可以被删除。</td></tr>\n<tr><td>SetMutableBinding(N,V, S)</td><td>在环境记录项中设置一个已经存在的绑定的值。其中字符串 N 指定绑定名称。V 用于指定绑定的值,可以是任何 ECMA 脚本语言的类型。S 是一个布尔类型的标记,当 S 为 true 并且该绑定不允许赋值时,则抛出一个 TypeError 异常。S 用于指定是否为严格模式。</td></tr>\n<tr><td>GetBindingValue(N,S)</td><td>返回环境记录项中一个已经存在的绑定的值。其中字符串 N 指定绑定的名称。S 用于指定是否为严格模式。如果 S 的值为 true 并且该绑定不存在或未初始化,则抛出一个 ReferenceError 异常。</td></tr>\n<tr><td>DeleteBinding(N)</td><td>从环境记录项中删除一个绑定。其中字符串 N 指定绑定的名称。如果 N 指定的绑定存在,将其删除并返回 true。如果绑定存在但无法删除则返回false。如果绑定不存在则返回 true。</td></tr>\n<tr><td>ImplicitThisValue()</td><td>当从该环境记录项的绑定中获取一个函数对象并且调用时,该方法返回该函数对象使用的 this 对象的值。</td></tr>\n</tbody>\n</table>\n\n声明式环境记录项有两个额外方法,用来创建和初始化不可变的绑定,用在定义严格模式下的 arguments 对象,因为严格模式下要求 arguments 是不和形参进行关联的:\n\n<table>\n<thead>\n<tr><td width=\"30%\">方法</td><td>作用</td></tr>\n</thead>\n<tbody><tr><td>CreateImmutableBinding(N)</td><td>在环境记录项中创建一个未初始化的不可变绑定。其中字符串 N 指定绑定名称。</td></tr>\n<tr><td>\nInitializeImmutableBinding(N,V)</td><td>在环境记录项中设置一个已经创建但未初始化的不可变绑定的值。其中字符串 N 指定绑定名称。V 用于指定绑定的值,可以是任何 ECMA 脚本语言的类型。</td></tr></tbody>\n</table>\n\n对象式环境记录项有一个关联的对象,这个对象被称作绑定对象。对象式环境记录项直接将一系列标识符与其绑定对象的属性名称建立一一对应关系。另外它还有一个 provideThis,用来指定 ImplicitThisValue 对象内部方法的 this,With 环境中 provideThis 为 true,在 ImplicitThisValue算法中,便会根据这个值,将返回值设为 With 语句块绑定的对象。\n\n令人发指了,还有三个词法环境的运算方法:\n\nGetIdentifierReference (lex, name, strict)\n\n*作用是根据标识符,在给定的词法环境获取引用。*当调用 GetIdentifierReference 抽象运算时,需要指定一个 词法环境 lex,一个标识符字符串 name 以及一个布尔型标识 strict。lex 的值可以为 null。\n\nNewDeclarativeEnvironment (E)\n\n*作用是创建词法环境E的子声明式词法环境。*当调用 NewDeclarativeEnvironment 抽象运算时,需指定一个 词法环境 E,其值可以为 null。\n\nNewObjectEnvironment (O, E)\n\n*作用是创建词法环境E的子对象式词法环境。*当调用 NewObjectEnvironmentis 抽象运算时,需指定一个对象 O 及一个 词法环境 E(其值可以为 null)。\n\n###### 全局环境\n\n全局环境是全局代码对应的词法环境,它也是全局对象本身。这里我们就可以解决一个很常见的问题,不用 var 声明时为何就变成全局的了。因为在作用域链上找,直到全局环境时,此时的`b=1`已经变成了在全局对象上的属性赋值,而不再是变量了。也就没有变量提升一说,在之前调用就会报错。\n\n#### 执行环境\n\n执行环境也就是通常我们所说的执行上下文。活动的执行上下文组在逻辑上组成一个堆栈。堆栈底部永远都是全局上下文(global context),而顶部就是当前(活动的)执行上下文。堆栈在EC类型进入和退出上下文的时候被修改(推入或弹出)。\n\n执行环境包含所有用于追踪与其相关的代码的执行进度的状态。共包括三个组件:词法环境、变量环境、this绑定。词法环境和变量环境都是上一部分提到的词法环境对象。当创建一个执行环境时,其词法环境组件和变量环境组件最初是同一个值。在该执行环境相关联的代码的执行过程中,变量环境组件引用永远不变,而词法环境组件引用有可能改变。改变的两个情况就是 With 和 Catch 语句块,为了实现这种改变,所以有了词法环境和变量环境。\n\n#### 建立执行环境\n\n写到这我都快晕了。。。不过进入建立执行环境部分会明朗起来吧!分三种,也就对应最开始讲的三种可执行代码!\n\n###### 全局执行环境\n\n当控制流进入全局代码的执行环境时,执行以下步骤:\n\n1. 将变量环境设置为全局环境;\n2. 将词法环境设置为全局环境;\n3. 将 this 绑定设置为全局对象;\n4. 执行初始化绑定,按下文。\n\n前三步就是把执行环境的三个组件确定,很好理解,初始化绑定比较复杂,并且与 eval 及函数是共用的,因此放在下文。\n\n###### eval \n\n当控制流进入 eval 代码 的执行环境时,执行以下步骤:\n\n1. 如果没有调用环境,或者 eval 代码并非通过[直接调用 eval](http://www.cnblogs.com/_franky/archive/2012/08/18/2645024.html) 函数进行评估的,则\n 1.按描述的初始化全局执行环境的方案,以 eval 代码作为 C 来初始化执行环境。\n2. 否则\n 1. 将 this 绑定设置为当前执行环境下的 this 绑定。\n 2. 将词法环境设置为当前执行环境下的词法环境。\n 3. 将变量环境设置为当前执行环境下的变量环境。\n3. 如果 eval 代码 是 严格模式下的代码 ,则\n 1. 令 strictVarEnv 为以词法环境为参数调用 NewDeclarativeEnvironment 得到的结果。\n 2. 设置词法环境为 strictVarEnv。\n 3. 设置变量环境为 strictVarEnv。\n4. 按下文描述的方案,使用 eval 代码 执行定义绑定初始化步骤。\n\n首先对于严格模式,它的词法环境是独立的,这是严格模式中要求的。而上面步骤中另一个区别是是否直接调用。不过要讲这个东西,又是一篇长文了。简单的来说就是 eval('xxx')这种算是直接调用,(1, eval)('xxx')算是间接调用,具体点开链接。\n\n###### 函数执行环境\n\n当控制流根据一个函数对象 F、调用者提供的 thisArg 以及调用者提供的 argumentList,进入 函数代码 的执行环境时,执行以下步骤:\n\n1. 如果函数代码是严格模式下的代码,设 this 绑定为 thisArg。\n2. 否则如果 thisArg 是 null 或 undefined,则设 this 绑定为 全局对象 。\n3. 否则如果 Type(thisArg) 的结果不为 Object,则设 this 绑定为 ToObject(thisArg)。\n4. 否则设 this 绑定为 thisArg。\n5. 以 F 的 [[Scope]] 内部属性为参数调用 NewDeclarativeEnvironment,并令 localEnv 为调用的结果。\n6. 设词法环境为 localEnv。\n7. 设变量环境为 localEnv。\n8. 令 code 为 F 的 [[Code]] 内部属性的值。\n9. 按下文描述的方案,使用 函数代码 code 和 argumentList 执行定义绑定初始化步骤。\n\n调用者提供的 thisArg,在调用者是引用类型时,就是引用类型的 base;非引用类型时是 null或undefined。非严格模式下es5会把null和undefined自动转化为全局对象,this不是对象就会转为对象。这就是函数this绑定的过程。这部分比起全局和eval来说复杂一些,之后还是老样子,创建设置词法环境,变量环境,初始化绑定。\n\n#### 初始化绑定,放大招了\n\n当进入一个执行环境时,会按以下步骤在变量环境上创建绑定,其中使用到调用者提供的代码设为 code,如果执行的是函数代码,则设 参数列表 为 args:\n\n1. 令 env 是当前运行执行环境的变量环境。\n2. 如果 code 是 eval 代码 ,则令 configurableBindings 为 true,否则令 configurableBindings 为 false。(因此 eval 中的变量是可以 delete 的)\n3. 如果代码是 严格模式下的代码 ,则令 strict 为 true,否则令 strict 为 false。\n4. 如果代码为函数代码,则:(首先绑定的就是参数)\n 1. 令 func 为通过 [[Call]] 内部属性初始化 code 的执行的函数对象。令 names 为 func 的 [[FormalParameters]] 内部属性。\n 2. 令 argCount 为 args 中元素的数量。\n 3. 令 n 为数字类型,其值为 0。\n 4. 按列表顺序遍历 names,对于每一个字符串 argName(这里会把传入的实参对应的形参绑定到变量环境上,严格模式下则不会进行绑定)\n5. 按源码顺序遍历 code,对于每一个 FunctionDeclaration 表达式 f:(其次绑定的是函数声明,这部分还没完全看懂)\n6. 以 arguments 为参数,调用 env 的 HasBinding 具体方法,并令 argumentsAlreadyDeclared 为调用的结果。(针对名字叫arguments的形参或是函数定义?测试了下,名为arguments的形参或者内部函数声明出现时,argumentsAlreadyDeclared会变成 true)\n7. 如果 code 是函数代码,并且 argumentsAlreadyDeclared 为 false,则:(这一部构建arguments对象)\n 1. 以 fn、names、args、env 和 strict 为参数,调用 CreateArgumentsObject 抽象运算函数,并令 argsObj 为调用的结果。\n 2. 如果 strict 为 true,则进行不可变绑定。\n 3. 否则进行可变绑定。\n8. 按源码顺序遍历 code,对于每一个 VariableDeclaration 和VariableDeclarationNoIn 表达式:(最后才是变量声明)\n 1. 令 dn 为 d 中的标识符。\n 2. 以 dn 为参数,调用 env 的 HasBinding 具体方法,并令 varAlreadyDeclared 为调用的结果。\n 3. 如果 varAlreadyDeclared 为 false,则:以 dn 和 configurableBindings 为参数,调用 env 的 CreateMutableBinding 具体方法。以 dn、undefined 和 strict 为参数,调用 env 的 SetMutableBinding 具体方法。\n\n可以看到绑定上下文的顺序是形参、函数声明、arguments对象、变量声明,搞清楚这个流程,那些面试题就都清楚了,不过好像也没有什么卵用。es文档中还有一节讲的是Arguments对象的创建,居然比绑定上下文的创建还要复杂,先跳过了!!\n\n#### 最后扯几句\n\n过了一遍*可执行代码与执行环境*的文档之后,我们来看看那些困扰我们很久的一些js问题。[汤姆大叔-深入理解JavaScript系列的11-16](http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html)分别讲是执行上下文、变量对象、this、作用域链、函数、闭包。从规范的角度再去看汤姆大叔对这些概念的解释,有一种豁然开朗的感觉。当然他没有点到的背后的东西,也可以知晓。\n\n所以还是应该把这些话题用上面的规范再分别讲一遍,等有空吧!\n\n","html":"<p><em>不行不行,太大了,好难整合。。。还是分开写吧</em></p>\n\n<p>这是一个很大的话题,在ECMA262规范(ES5,本文不涉及ES6,因为引入块状作用域后情况会变得更加复杂,下同)的第十章,本文把规范的内容进行了注释,加入了一些个人的理解,不过有点多,写得貌似有点乱!建议在看本文前可以把<a href=\"http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html\">汤姆大叔-深入理解JavaScript系列的11-16</a>看一下。后面也会针对汤姆大叔这几篇文章的内容结合规范写一些文章的。</p>\n\n<h4 id=\"\">可执行代码</h4>\n\n<p>首先规范中讲到三种可执行代码类型(三种执行上下文,With和Catch算特例):</p>\n\n<ul>\n<li>全局代码:这种类型的代码是在\"程序\"级处理的。例如加载外部的js文件或者本地<code><script></script></code>标签内的代码。全局代码不包括任何function体内的代码。</li>\n<li>eval代码:指提供给 eval 内置函数的源代码文本。eval第二个参数可以指定上下文,但是是非规范的,本文不做讨论。</li>\n<li>函数代码:作为函数体被解析的源代码文本,但不包括作为其嵌套函数的函数体被解析的源代码文本。另外需要提的是使用Function构造器创建函数时,最后一个参数将被转换为字符串并作为函数体使用,同样不包括嵌套函数的函数体。</li>\n</ul>\n\n<p>在严格模式下,这些代码将被称为严格全局代码、严格 eval 代码和严格函数代码,相应地处理会有一些差异。不过不是本文的重点,不做太多的展开,可以看前面的文章。</p>\n\n<h4 id=\"\">词法环境</h4>\n\n<p>简单来讲,词法环境对象(后面在部分执行环境会看到词法环境和变量环境,本质都是词法环境对象)基本可以等同于汤姆大叔<a href=\"http://www.cnblogs.com/TomXu/archive/2012/01/16/2309728.html\">变量对象</a>一文中所讲的变量对象,汤姆大叔已经讲得十分到位了,但是我希望能够把隐藏在变量对象下的知识讲清楚。建议看下面的内容前,先看一下汤姆大叔<a href=\"http://www.cnblogs.com/TomXu/archive/2012/01/16/2309728.html\">变量对象</a>一文,这样能够更好地做对应和理解。</p>\n\n<p>首先,词法环境包括两个内容:环境记录项和可能为空的外部词法环境引用。通常词法环境会与特定的 ECMAScript 代码诸如 FunctionDeclaration、WithStatement 或者 TryStatement 的 Catch 块这样的语法结构相联系,且类似代码每次执行都会有一个新的语法环境被创建出来。</p>\n\n<h6 id=\"\">外部词法环境引用</h6>\n\n<p>外部词法环境引用比较简单,就先讲了。它就是用来记录词法环境的嵌套关系的,在创建一个新的词法环境时需要指定它的外部词法环境,<code>GetIdentifierReference</code>(根据标识符得到引用函数)就会在这条链上一层层找上去,直到全局词法环境,而全局词法环境的外部词法环境是null,所以这里就是终点,有点感觉了吧。这个其实就是作用域链了,本质就是指向外部词法环境的一个指针。</p>\n\n<h6 id=\"\">环境记录项</h6>\n\n<p>环境记录项包括声明式环境记录项和对象式环境记录项。声明式环境记录项用于定义那些将标识符与语言值直接绑定的 ECMA 脚本语法元素,例如函数声明,变量声明以及 Catch 语句。对象式环境记录项用于定义那些将标识符 与具体对象的属性绑定的 ECMA 脚本元素,例如程序以及 With 表达式。</p>\n\n<p><em>简单来说环境记录项就保存了标识符以及标识符对应的引用。</em></p>\n\n<p>全局级别(因为全局环境本质上就是全局对象,这也是汤姆大叔博文中一直强调的)和 With 的词法环境拥有的是对象式环境记录项,函数、和 catch 的词法环境拥有的是声明式环境记录项。</p>\n\n<p>我们可以把环境记录项当做一个抽象类,它上面有一些抽象方法,这些方法都是供语言内部使用的,声明式环境记录项和对象式环境记录项分别实现了这些抽象方法,这些方法就是用来处理标识符和引用的关系的,具体实现可以查看文档。</p>\n\n<table> \n<thead> \n<tr><td width=\"30%\">方法</td><td>作用</td></tr> \n</thead> \n<tbody> \n<tr> \n<td>HasBinding(N)</td><td>判断环境记录项是否包含对某个标识符的绑定。如果包含该绑定则返回 true,反之返回 false。其中字符串 N 是标识符文本。</td></tr> \n<tr> \n<td>CreateMutableBinding(N, D)</td><td> \n在环境记录项中创建一个新的可变绑定。其中字符串 N 指定绑定名称。如果可选参数 D 的值为true,则该绑定在后续操作中可以被删除。</td></tr>\n<tr><td>SetMutableBinding(N,V, S)</td><td>在环境记录项中设置一个已经存在的绑定的值。其中字符串 N 指定绑定名称。V 用于指定绑定的值,可以是任何 ECMA 脚本语言的类型。S 是一个布尔类型的标记,当 S 为 true 并且该绑定不允许赋值时,则抛出一个 TypeError 异常。S 用于指定是否为严格模式。</td></tr> \n<tr><td>GetBindingValue(N,S)</td><td>返回环境记录项中一个已经存在的绑定的值。其中字符串 N 指定绑定的名称。S 用于指定是否为严格模式。如果 S 的值为 true 并且该绑定不存在或未初始化,则抛出一个 ReferenceError 异常。</td></tr> \n<tr><td>DeleteBinding(N)</td><td>从环境记录项中删除一个绑定。其中字符串 N 指定绑定的名称。如果 N 指定的绑定存在,将其删除并返回 true。如果绑定存在但无法删除则返回false。如果绑定不存在则返回 true。</td></tr> \n<tr><td>ImplicitThisValue()</td><td>当从该环境记录项的绑定中获取一个函数对象并且调用时,该方法返回该函数对象使用的 this 对象的值。</td></tr> \n</tbody> \n</table>\n\n<p>声明式环境记录项有两个额外方法,用来创建和初始化不可变的绑定,用在定义严格模式下的 arguments 对象,因为严格模式下要求 arguments 是不和形参进行关联的:</p>\n\n<table> \n<thead> \n<tr><td width=\"30%\">方法</td><td>作用</td></tr> \n</thead> \n<tbody><tr><td>CreateImmutableBinding(N)</td><td>在环境记录项中创建一个未初始化的不可变绑定。其中字符串 N 指定绑定名称。</td></tr> \n<tr><td> \nInitializeImmutableBinding(N,V)</td><td>在环境记录项中设置一个已经创建但未初始化的不可变绑定的值。其中字符串 N 指定绑定名称。V 用于指定绑定的值,可以是任何 ECMA 脚本语言的类型。</td></tr></tbody> \n</table>\n\n<p>对象式环境记录项有一个关联的对象,这个对象被称作绑定对象。对象式环境记录项直接将一系列标识符与其绑定对象的属性名称建立一一对应关系。另外它还有一个 provideThis,用来指定 ImplicitThisValue 对象内部方法的 this,With 环境中 provideThis 为 true,在 ImplicitThisValue算法中,便会根据这个值,将返回值设为 With 语句块绑定的对象。</p>\n\n<p>令人发指了,还有三个词法环境的运算方法:</p>\n\n<p>GetIdentifierReference (lex, name, strict)</p>\n\n<p><em>作用是根据标识符,在给定的词法环境获取引用。</em>当调用 GetIdentifierReference 抽象运算时,需要指定一个 词法环境 lex,一个标识符字符串 name 以及一个布尔型标识 strict。lex 的值可以为 null。</p>\n\n<p>NewDeclarativeEnvironment (E)</p>\n\n<p><em>作用是创建词法环境E的子声明式词法环境。</em>当调用 NewDeclarativeEnvironment 抽象运算时,需指定一个 词法环境 E,其值可以为 null。</p>\n\n<p>NewObjectEnvironment (O, E)</p>\n\n<p><em>作用是创建词法环境E的子对象式词法环境。</em>当调用 NewObjectEnvironmentis 抽象运算时,需指定一个对象 O 及一个 词法环境 E(其值可以为 null)。</p>\n\n<h6 id=\"\">全局环境</h6>\n\n<p>全局环境是全局代码对应的词法环境,它也是全局对象本身。这里我们就可以解决一个很常见的问题,不用 var 声明时为何就变成全局的了。因为在作用域链上找,直到全局环境时,此时的<code>b=1</code>已经变成了在全局对象上的属性赋值,而不再是变量了。也就没有变量提升一说,在之前调用就会报错。</p>\n\n<h4 id=\"\">执行环境</h4>\n\n<p>执行环境也就是通常我们所说的执行上下文。活动的执行上下文组在逻辑上组成一个堆栈。堆栈底部永远都是全局上下文(global context),而顶部就是当前(活动的)执行上下文。堆栈在EC类型进入和退出上下文的时候被修改(推入或弹出)。</p>\n\n<p>执行环境包含所有用于追踪与其相关的代码的执行进度的状态。共包括三个组件:词法环境、变量环境、this绑定。词法环境和变量环境都是上一部分提到的词法环境对象。当创建一个执行环境时,其词法环境组件和变量环境组件最初是同一个值。在该执行环境相关联的代码的执行过程中,变量环境组件引用永远不变,而词法环境组件引用有可能改变。改变的两个情况就是 With 和 Catch 语句块,为了实现这种改变,所以有了词法环境和变量环境。</p>\n\n<h4 id=\"\">建立执行环境</h4>\n\n<p>写到这我都快晕了。。。不过进入建立执行环境部分会明朗起来吧!分三种,也就对应最开始讲的三种可执行代码!</p>\n\n<h6 id=\"\">全局执行环境</h6>\n\n<p>当控制流进入全局代码的执行环境时,执行以下步骤:</p>\n\n<ol>\n<li>将变量环境设置为全局环境; </li>\n<li>将词法环境设置为全局环境; </li>\n<li>将 this 绑定设置为全局对象; </li>\n<li>执行初始化绑定,按下文。</li>\n</ol>\n\n<p>前三步就是把执行环境的三个组件确定,很好理解,初始化绑定比较复杂,并且与 eval 及函数是共用的,因此放在下文。</p>\n\n<h6 id=\"eval\">eval</h6>\n\n<p>当控制流进入 eval 代码 的执行环境时,执行以下步骤:</p>\n\n<ol>\n<li>如果没有调用环境,或者 eval 代码并非通过<a href=\"http://www.cnblogs.com/_franky/archive/2012/08/18/2645024.html\">直接调用 eval</a> 函数进行评估的,则 <br />\n1.按描述的初始化全局执行环境的方案,以 eval 代码作为 C 来初始化执行环境。</li>\n<li>否则 <br />\n<ol><li>将 this 绑定设置为当前执行环境下的 this 绑定。</li>\n<li>将词法环境设置为当前执行环境下的词法环境。</li>\n<li>将变量环境设置为当前执行环境下的变量环境。</li></ol></li>\n<li>如果 eval 代码 是 严格模式下的代码 ,则 <br />\n<ol><li>令 strictVarEnv 为以词法环境为参数调用 NewDeclarativeEnvironment 得到的结果。</li>\n<li>设置词法环境为 strictVarEnv。</li>\n<li>设置变量环境为 strictVarEnv。</li></ol></li>\n<li>按下文描述的方案,使用 eval 代码 执行定义绑定初始化步骤。</li>\n</ol>\n\n<p>首先对于严格模式,它的词法环境是独立的,这是严格模式中要求的。而上面步骤中另一个区别是是否直接调用。不过要讲这个东西,又是一篇长文了。简单的来说就是 eval('xxx')这种算是直接调用,(1, eval)('xxx')算是间接调用,具体点开链接。</p>\n\n<h6 id=\"\">函数执行环境</h6>\n\n<p>当控制流根据一个函数对象 F、调用者提供的 thisArg 以及调用者提供的 argumentList,进入 函数代码 的执行环境时,执行以下步骤:</p>\n\n<ol>\n<li>如果函数代码是严格模式下的代码,设 this 绑定为 thisArg。 </li>\n<li>否则如果 thisArg 是 null 或 undefined,则设 this 绑定为 全局对象 。 </li>\n<li>否则如果 Type(thisArg) 的结果不为 Object,则设 this 绑定为 ToObject(thisArg)。 </li>\n<li>否则设 this 绑定为 thisArg。 </li>\n<li>以 F 的 [[Scope]] 内部属性为参数调用 NewDeclarativeEnvironment,并令 localEnv 为调用的结果。 </li>\n<li>设词法环境为 localEnv。 </li>\n<li>设变量环境为 localEnv。 </li>\n<li>令 code 为 F 的 [[Code]] 内部属性的值。 </li>\n<li>按下文描述的方案,使用 函数代码 code 和 argumentList 执行定义绑定初始化步骤。</li>\n</ol>\n\n<p>调用者提供的 thisArg,在调用者是引用类型时,就是引用类型的 base;非引用类型时是 null或undefined。非严格模式下es5会把null和undefined自动转化为全局对象,this不是对象就会转为对象。这就是函数this绑定的过程。这部分比起全局和eval来说复杂一些,之后还是老样子,创建设置词法环境,变量环境,初始化绑定。</p>\n\n<h4 id=\"\">初始化绑定,放大招了</h4>\n\n<p>当进入一个执行环境时,会按以下步骤在变量环境上创建绑定,其中使用到调用者提供的代码设为 code,如果执行的是函数代码,则设 参数列表 为 args:</p>\n\n<ol>\n<li>令 env 是当前运行执行环境的变量环境。 </li>\n<li>如果 code 是 eval 代码 ,则令 configurableBindings 为 true,否则令 configurableBindings 为 false。(因此 eval 中的变量是可以 delete 的) </li>\n<li>如果代码是 严格模式下的代码 ,则令 strict 为 true,否则令 strict 为 false。 </li>\n<li>如果代码为函数代码,则:(首先绑定的就是参数) <br />\n<ol><li>令 func 为通过 [[Call]] 内部属性初始化 code 的执行的函数对象。令 names 为 func 的 [[FormalParameters]] 内部属性。</li>\n<li>令 argCount 为 args 中元素的数量。</li>\n<li>令 n 为数字类型,其值为 0。</li>\n<li>按列表顺序遍历 names,对于每一个字符串 argName(这里会把传入的实参对应的形参绑定到变量环境上,严格模式下则不会进行绑定)</li></ol></li>\n<li>按源码顺序遍历 code,对于每一个 FunctionDeclaration 表达式 f:(其次绑定的是函数声明,这部分还没完全看懂) </li>\n<li>以 arguments 为参数,调用 env 的 HasBinding 具体方法,并令 argumentsAlreadyDeclared 为调用的结果。(针对名字叫arguments的形参或是函数定义?测试了下,名为arguments的形参或者内部函数声明出现时,argumentsAlreadyDeclared会变成 true) </li>\n<li>如果 code 是函数代码,并且 argumentsAlreadyDeclared 为 false,则:(这一部构建arguments对象) <br />\n<ol><li>以 fn、names、args、env 和 strict 为参数,调用 CreateArgumentsObject 抽象运算函数,并令 argsObj 为调用的结果。</li>\n<li>如果 strict 为 true,则进行不可变绑定。</li>\n<li>否则进行可变绑定。</li></ol></li>\n<li>按源码顺序遍历 code,对于每一个 VariableDeclaration 和VariableDeclarationNoIn 表达式:(最后才是变量声明) <br />\n<ol><li>令 dn 为 d 中的标识符。</li>\n<li>以 dn 为参数,调用 env 的 HasBinding 具体方法,并令 varAlreadyDeclared 为调用的结果。</li>\n<li>如果 varAlreadyDeclared 为 false,则:以 dn 和 configurableBindings 为参数,调用 env 的 CreateMutableBinding 具体方法。以 dn、undefined 和 strict 为参数,调用 env 的 SetMutableBinding 具体方法。</li></ol></li>\n</ol>\n\n<p>可以看到绑定上下文的顺序是形参、函数声明、arguments对象、变量声明,搞清楚这个流程,那些面试题就都清楚了,不过好像也没有什么卵用。es文档中还有一节讲的是Arguments对象的创建,居然比绑定上下文的创建还要复杂,先跳过了!!</p>\n\n<h4 id=\"\">最后扯几句</h4>\n\n<p>过了一遍<em>可执行代码与执行环境</em>的文档之后,我们来看看那些困扰我们很久的一些js问题。<a href=\"http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html\">汤姆大叔-深入理解JavaScript系列的11-16</a>分别讲是执行上下文、变量对象、this、作用域链、函数、闭包。从规范的角度再去看汤姆大叔对这些概念的解释,有一种豁然开朗的感觉。当然他没有点到的背后的东西,也可以知晓。</p>\n\n<p>所以还是应该把这些话题用上面的规范再分别讲一遍,等有空吧!</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1472880637599,"created_by":1,"updated_at":1474079566422,"updated_by":1,"published_at":1473842716089,"published_by":1},{"id":59,"uuid":"b60f6f88-f27f-4908-9ea3-63b7afd72950","title":"随笔","slug":"sui-bi-2","markdown":"昨晚寝室没网,看了部存在硬盘中的电影《第八日的蝉》就睡了。挺好的片子,如果后面还记得的话会写写这片子。\n\n早上起得挺早,就准备找个有网能自习的地方去,然而学校图书馆没开,只好跑远点到了浙江图书馆。环境挺不错,就是计算机类的书又少又旧。还好了带了本书,好了,开始学习了!\n\n看了一章,发现普通的书已经解决不了我的问题了,只能上文档了,就是有点厚!","html":"<p>昨晚寝室没网,看了部存在硬盘中的电影《第八日的蝉》就睡了。挺好的片子,如果后面还记得的话会写写这片子。</p>\n\n<p>早上起得挺早,就准备找个有网能自习的地方去,然而学校图书馆没开,只好跑远点到了浙江图书馆。环境挺不错,就是计算机类的书又少又旧。还好了带了本书,好了,开始学习了!</p>\n\n<p>看了一章,发现普通的书已经解决不了我的问题了,只能上文档了,就是有点厚!</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1473385751621,"created_by":1,"updated_at":1473389562246,"updated_by":1,"published_at":1473386005922,"published_by":1},{"id":60,"uuid":"20ff95ab-2c25-491a-a722-17bfc527be8b","title":"MDN js bind 解读","slug":"mdn-js-bind-jie-du","markdown":"#### 基础\n\n###### 语法\n\n>fun.bind(thisArg[, arg1[, arg2[, ...]]])\n\n###### 参数\n\n> thisArg\n\n> 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。\n\n> arg1, arg2, ...\n\n> 当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数。\n\n###### 返回值\n\n> 返回由指定的this值和初始化参数改造的原函数拷贝\n\n注意点:上面bind返回的新函数可以作为构造函数,此时bind指定的this无效,而会使用构造函数产生的对象作为this。其次,就是最终的参数包括两部分,一是bind时指定的参数,二是函数调用是指定的参数,此处涉及了偏函数以及函数科里化的只是后面会补充。\n\n#### 有了call、apply,为何还要bind\n\nbind能够和call、apply一样绑定上下文,但是相较于call和apply,它还有很多别的特点。首先,bind会返回一个新的函数,而不是立即执行;其次,我们在bind时,可以固定一些参数,此时产生的便是一个偏函数。偏函数在js或者python中就是指固定一些参数,返回一个新的函数,来方便函数调用的。举个例子:\n\n```\n// parseInt()支持第二个参数,表示进制,如果我们要指定第一个参数是二进制,并且在很多地方使用,我们就可以把第二个参数固定下来,然后返回一个新的函数,方便调用。\n\nfunction toInt2(x){\n return parseInt(x, 2);\n}\n```\n\n第三是作为构造函数的绑定函数,不过不建议在生产环境中使用。\n\n```\nfunction Point(x, y) {\n this.x = x;\n this.y = y;\n}\n\nPoint.prototype.toString = function() { \n return this.x + ',' + this.y; \n};\n\nvar p = new Point(1, 2);\np.toString(); // '1,2'\n\nvar emptyObj = {};\nvar YAxisPoint = Point.bind(emptyObj, 0/*x*/);\n// 以下这行代码在 polyfill 不支持,\n// 在原生的bind方法运行没问题:\n//(译注:polyfill的bind方法如果加上把bind的第一个参数,即新绑定的this执行Object()来包装为对象,Object(null)则是{},那么也可以支持)\nvar YAxisPoint = Point.bind(null, 0/*x*/);\n\nvar axisPoint = new YAxisPoint(5);\naxisPoint.toString(); // '0,5'\n\naxisPoint instanceof Point; // true\naxisPoint instanceof YAxisPoint; // true\nnew Point(17, 42) instanceof YAxisPoint; // true\n```\n\n上面这段代码使用原生的bind和其他的实现运行的效果有出入,所以在构造函数上还是不建议使用bind吧。\n\n第四是快捷调用,你可以用 Array.prototype.slice 来将一个类似于数组的对象(array-like object)转换成一个真正的数组,就拿它来举例子吧。你可以创建这样一个捷径:\n\n```\nvar slice = Array.prototype.slice;\n//...\nslice.apply(arguments);\n```\n\n但是使用bind,可以使这个过程变得简单。\n\n```\n// same as \"slice\" in the previous example\nvar unboundSlice = Array.prototype.slice;\nvar slice = Function.prototype.call.bind(unboundSlice);\n// ...\nslice(arguments);\n```\n\n主要要讲的是`Function.prototype.call.bind(unboundSlice)`,这是什么写法?其实就是把call执行的时候的this绑定为unboundSlice,然后返回一个新的函数,此时执行`slice(arguments)`,就相当于执行`unboundSlice.call(arguments)`。\n\n#### bind的劣势\n\n目前看到的主要有两个劣势,第一是兼容性,IE9以下不支持;第二是性能,引用一段话:\n\n> 我测试了一下浏览器原生的Function.prototype.bind,发现使用了bind之后,函数的内存占用增加了近2倍!CoffeeScript实现的绑定稍微轻量一点,内存占用也增加了1倍多。\n\n>再顺便测试了下ES6新增的Arrow function(也是=>),因为这个特殊函数是自带绑定技能的,结果惊奇地发现,它的内存占用和普通的Function没啥区别。所以以后需要或者不需要bind的场景如果一定要滥用bind图个安心的话,可以通通上高逼格的箭头函数。:)\n\n>文/寂寞的原子(简书作者)\n原文链接:http://www.jianshu.com/p/45515682be0d\n著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。\n\nbind和箭头函数又是一个新的问题了,这里不展开讨论了。\n\n不过总的来说,bind还是一个比较好的方案,像很多库都在使用,underscore、lodash、jquery也都有对应的实现。\n\n#### bind的实现\n\n最简单的实现:\n\n```\nFunction.prototype.bind = function(context){ \n self = this; //保存this,即调用bind方法的目标函数\n return function(){\n return self.apply(context,arguments);\n };\n};\n```\n\n加入科里化,也就是把两部分参数进行合并:\n\n```\nFunction.prototype.bind = function(context){ \n var args = Array.prototype.slice.call(arguments, 1),\n self = this;\n return function(){\n var innerArgs = Array.prototype.slice.call(arguments);\n var finalArgs = args.concat(innerArgs);\n return self.apply(context,finalArgs);\n };\n};\n```\n\n最后要分析一下Polyfill(兼容旧浏览器),它考虑到了构造函数,但是与原生的实现还是有点区别,可以见构造函数一节的注释:\n\n```\n// 判断是否原生支持\nif (!Function.prototype.bind) {\n Function.prototype.bind = function (oThis) {\n // 如果需要bind的不是函数则报错\n if (typeof this !== \"function\") {\n // closest thing possible to the ECMAScript 5\n // internal IsCallable function\n throw new TypeError(\"Function.prototype.bind - what is trying to be bound is not callable\");\n }\n\n // aArgs是bind时固定下来的参数\n // fToBind保存了要bind函数的引用\n // fNOP作为中介\n // fBound是最后返回的函数\n var aArgs = Array.prototype.slice.call(arguments, 1), \n fToBind = this, \n fNOP = function () {},\n fBound = function () {\n return fToBind.apply(this instanceof fNOP\n ? this\n : oThis || this,\n// 连接两次的参数 aArgs.concat(Array.prototype.slice.call(arguments)));\n };\n\n // 保证bind出来的函数与借用的函数拥有相同的原型链\n fNOP.prototype = this.prototype;\n fBound.prototype = new fNOP();\n\n return fBound;\n };\n}\n```\n\n剩下还有`this instanceof fNOP ? this : oThis || this`是我百思不得其解的,最后在一篇博客上找到了答案,当bind返回的函数用作构造函数时,`this instanceof fNOP`为true,也就是定义中所说的忽略this的情况。\n\n至于为什么需要fNOP作为中介,这是构造函数继承的一种方法,它的好处是防止`this.prototype`和`fBound.prototype`两者会指向同一个引用。\n\n#### 扩展es7绑定函数\n\n该语法还是ES7的一个提案,但是Babel转码器已经支持。\n\n函数绑定运算符是并排的两个双冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。\n\n```\nfoo::bar;\n// 等同于\nbar.bind(foo);\n\nfoo::bar(...arguments);\n// 等同于\nbar.apply(foo, arguments);\n\nconst hasOwnProperty = Object.prototype.hasOwnProperty;\nfunction hasOwn(obj, key) {\n return obj::hasOwnProperty(key);\n}\n```\n\n详情见阮老师的es6入门吧!\n\n#### 最后扯两句\n\n仅仅是bind就有这么多东西,如果把它放到整块知识中肯定能够有更多的收获,不过在晕之前还先把阶段性的结果写下来,到时候好整合!","html":"<h4 id=\"\">基础</h4>\n\n<h6 id=\"\">语法</h6>\n\n<blockquote>\n <p>fun.bind(thisArg[, arg1[, arg2[, ...]]])</p>\n</blockquote>\n\n<h6 id=\"\">参数</h6>\n\n<blockquote>\n <p>thisArg</p>\n \n <p>当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。</p>\n \n <p>arg1, arg2, ...</p>\n \n <p>当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数。</p>\n</blockquote>\n\n<h6 id=\"\">返回值</h6>\n\n<blockquote>\n <p>返回由指定的this值和初始化参数改造的原函数拷贝</p>\n</blockquote>\n\n<p>注意点:上面bind返回的新函数可以作为构造函数,此时bind指定的this无效,而会使用构造函数产生的对象作为this。其次,就是最终的参数包括两部分,一是bind时指定的参数,二是函数调用是指定的参数,此处涉及了偏函数以及函数科里化的只是后面会补充。</p>\n\n<h4 id=\"callapplybind\">有了call、apply,为何还要bind</h4>\n\n<p>bind能够和call、apply一样绑定上下文,但是相较于call和apply,它还有很多别的特点。首先,bind会返回一个新的函数,而不是立即执行;其次,我们在bind时,可以固定一些参数,此时产生的便是一个偏函数。偏函数在js或者python中就是指固定一些参数,返回一个新的函数,来方便函数调用的。举个例子:</p>\n\n<pre><code>// parseInt()支持第二个参数,表示进制,如果我们要指定第一个参数是二进制,并且在很多地方使用,我们就可以把第二个参数固定下来,然后返回一个新的函数,方便调用。\n\nfunction toInt2(x){ \n return parseInt(x, 2);\n}\n</code></pre>\n\n<p>第三是作为构造函数的绑定函数,不过不建议在生产环境中使用。</p>\n\n<pre><code>function Point(x, y) { \n this.x = x;\n this.y = y;\n}\n\nPoint.prototype.toString = function() { \n return this.x + ',' + this.y; \n};\n\nvar p = new Point(1, 2); \np.toString(); // '1,2'\n\nvar emptyObj = {}; \nvar YAxisPoint = Point.bind(emptyObj, 0/*x*/); \n// 以下这行代码在 polyfill 不支持,\n// 在原生的bind方法运行没问题:\n//(译注:polyfill的bind方法如果加上把bind的第一个参数,即新绑定的this执行Object()来包装为对象,Object(null)则是{},那么也可以支持)\nvar YAxisPoint = Point.bind(null, 0/*x*/);\n\nvar axisPoint = new YAxisPoint(5); \naxisPoint.toString(); // '0,5'\n\naxisPoint instanceof Point; // true \naxisPoint instanceof YAxisPoint; // true \nnew Point(17, 42) instanceof YAxisPoint; // true \n</code></pre>\n\n<p>上面这段代码使用原生的bind和其他的实现运行的效果有出入,所以在构造函数上还是不建议使用bind吧。</p>\n\n<p>第四是快捷调用,你可以用 Array.prototype.slice 来将一个类似于数组的对象(array-like object)转换成一个真正的数组,就拿它来举例子吧。你可以创建这样一个捷径:</p>\n\n<pre><code>var slice = Array.prototype.slice; \n//...\nslice.apply(arguments); \n</code></pre>\n\n<p>但是使用bind,可以使这个过程变得简单。</p>\n\n<pre><code>// same as \"slice\" in the previous example\nvar unboundSlice = Array.prototype.slice; \nvar slice = Function.prototype.call.bind(unboundSlice); \n// ...\nslice(arguments); \n</code></pre>\n\n<p>主要要讲的是<code>Function.prototype.call.bind(unboundSlice)</code>,这是什么写法?其实就是把call执行的时候的this绑定为unboundSlice,然后返回一个新的函数,此时执行<code>slice(arguments)</code>,就相当于执行<code>unboundSlice.call(arguments)</code>。</p>\n\n<h4 id=\"bind\">bind的劣势</h4>\n\n<p>目前看到的主要有两个劣势,第一是兼容性,IE9以下不支持;第二是性能,引用一段话:</p>\n\n<blockquote>\n <p>我测试了一下浏览器原生的Function.prototype.bind,发现使用了bind之后,函数的内存占用增加了近2倍!CoffeeScript实现的绑定稍微轻量一点,内存占用也增加了1倍多。</p>\n \n <p>再顺便测试了下ES6新增的Arrow function(也是=>),因为这个特殊函数是自带绑定技能的,结果惊奇地发现,它的内存占用和普通的Function没啥区别。所以以后需要或者不需要bind的场景如果一定要滥用bind图个安心的话,可以通通上高逼格的箭头函数。:)</p>\n \n <p>文/寂寞的原子(简书作者)\n 原文链接:<a href=\"http://www.jianshu.com/p/45515682be0d\">http://www.jianshu.com/p/45515682be0d</a>\n 著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。</p>\n</blockquote>\n\n<p>bind和箭头函数又是一个新的问题了,这里不展开讨论了。</p>\n\n<p>不过总的来说,bind还是一个比较好的方案,像很多库都在使用,underscore、lodash、jquery也都有对应的实现。</p>\n\n<h4 id=\"bind\">bind的实现</h4>\n\n<p>最简单的实现:</p>\n\n<pre><code>Function.prototype.bind = function(context){ \n self = this; //保存this,即调用bind方法的目标函数\n return function(){\n return self.apply(context,arguments);\n };\n};\n</code></pre>\n\n<p>加入科里化,也就是把两部分参数进行合并:</p>\n\n<pre><code>Function.prototype.bind = function(context){ \n var args = Array.prototype.slice.call(arguments, 1),\n self = this;\n return function(){\n var innerArgs = Array.prototype.slice.call(arguments);\n var finalArgs = args.concat(innerArgs);\n return self.apply(context,finalArgs);\n };\n};\n</code></pre>\n\n<p>最后要分析一下Polyfill(兼容旧浏览器),它考虑到了构造函数,但是与原生的实现还是有点区别,可以见构造函数一节的注释:</p>\n\n<pre><code>// 判断是否原生支持\nif (!Function.prototype.bind) { \n Function.prototype.bind = function (oThis) {\n // 如果需要bind的不是函数则报错\n if (typeof this !== \"function\") {\n // closest thing possible to the ECMAScript 5\n // internal IsCallable function\n throw new TypeError(\"Function.prototype.bind - what is trying to be bound is not callable\");\n }\n\n // aArgs是bind时固定下来的参数\n // fToBind保存了要bind函数的引用\n // fNOP作为中介\n // fBound是最后返回的函数\n var aArgs = Array.prototype.slice.call(arguments, 1), \n fToBind = this, \n fNOP = function () {},\n fBound = function () {\n return fToBind.apply(this instanceof fNOP\n ? this\n : oThis || this,\n// 连接两次的参数 aArgs.concat(Array.prototype.slice.call(arguments)));\n };\n\n // 保证bind出来的函数与借用的函数拥有相同的原型链\n fNOP.prototype = this.prototype;\n fBound.prototype = new fNOP();\n\n return fBound;\n };\n}\n</code></pre>\n\n<p>剩下还有<code>this instanceof fNOP ? this : oThis || this</code>是我百思不得其解的,最后在一篇博客上找到了答案,当bind返回的函数用作构造函数时,<code>this instanceof fNOP</code>为true,也就是定义中所说的忽略this的情况。</p>\n\n<p>至于为什么需要fNOP作为中介,这是构造函数继承的一种方法,它的好处是防止<code>this.prototype</code>和<code>fBound.prototype</code>两者会指向同一个引用。</p>\n\n<h4 id=\"es7\">扩展es7绑定函数</h4>\n\n<p>该语法还是ES7的一个提案,但是Babel转码器已经支持。</p>\n\n<p>函数绑定运算符是并排的两个双冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。</p>\n\n<pre><code>foo::bar; \n// 等同于\nbar.bind(foo);\n\nfoo::bar(...arguments); \n// 等同于\nbar.apply(foo, arguments);\n\nconst hasOwnProperty = Object.prototype.hasOwnProperty; \nfunction hasOwn(obj, key) { \n return obj::hasOwnProperty(key);\n}\n</code></pre>\n\n<p>详情见阮老师的es6入门吧!</p>\n\n<h4 id=\"\">最后扯两句</h4>\n\n<p>仅仅是bind就有这么多东西,如果把它放到整块知识中肯定能够有更多的收获,不过在晕之前还先把阶段性的结果写下来,到时候好整合!</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1473729418209,"created_by":1,"updated_at":1474022560461,"updated_by":1,"published_at":1473733465651,"published_by":1},{"id":61,"uuid":"149d7964-a6f5-4ecc-8b5d-4a98d5b2dc1c","title":"有生之年系列之——编剧出来谈人生","slug":"you-sheng-zhi-nian-xi-lie-zhi-bian-ju-chu-lai-tan-ren-sheng","markdown":"大哥搞完事情,编剧又出来搞事情,哈哈哈哈哈哈!\n\n坐等下周大结局!!!\n\n![](https://ws2.sinaimg.cn/large/76fc6301gw1f7rw8zmuxej20x00qcq91.jpg)\n\n![](https://ws1.sinaimg.cn/large/76fc6301gw1f7rw9l9pi4j218w0oogst.jpg)\n\n![](https://ws3.sinaimg.cn/large/76fc6301gw1f7rwa2amjej214u0q2n66.jpg)\n\n![](https://ws4.sinaimg.cn/large/76fc6301gw1f7rwahfw9nj21bs0vg7s0.jpg)","html":"<p>大哥搞完事情,编剧又出来搞事情,哈哈哈哈哈哈!</p>\n\n<p>坐等下周大结局!!!</p>\n\n<p><img src=\"https://ws2.sinaimg.cn/large/76fc6301gw1f7rw8zmuxej20x00qcq91.jpg\" alt=\"\" /></p>\n\n<p><img src=\"https://ws1.sinaimg.cn/large/76fc6301gw1f7rw9l9pi4j218w0oogst.jpg\" alt=\"\" /></p>\n\n<p><img src=\"https://ws3.sinaimg.cn/large/76fc6301gw1f7rwa2amjej214u0q2n66.jpg\" alt=\"\" /></p>\n\n<p><img src=\"https://ws4.sinaimg.cn/large/76fc6301gw1f7rwahfw9nj21bs0vg7s0.jpg\" alt=\"\" /></p>","image":"/content/images/2016/09/76fc6301gw1f7rwahfw9nj21bs0vg7s0.jpg","featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1473744682232,"created_by":1,"updated_at":1473756483186,"updated_by":1,"published_at":1473744849848,"published_by":1},{"id":62,"uuid":"43bc67b4-73d6-4764-b873-1d351631b482","title":"js 正则表达式小结","slug":"js-zheng-ze-biao-da-shi-xiao-jie","markdown":"这是之前欠下的,一直没空总结一下。看完剧,先把之前的总结了,在进行下一步的学习。在讲js的正则之前,先要讲一下正则的基本概念。结合一些例子,应该能快速了解吧!本文只能算扫盲加知识拓展性质,要熟练掌握,还是要靠多练!\n\n#### 基础\n\n##### 字符\n\n1、普通字符(单个字符)\n\n字母、数字、汉字、下划线、以及后边章节中没有特殊定义的标点符号,都是\"普通字符\"。表达式中的普通字符,在匹配一个字符串的时候,匹配与之相同的一个字符。\n\n2、转义字符(单个字符)\n\n一些不便书写的字符,采用在前面加 \"\\\" 的方法。这些字符其实我们都已经熟知了。\n\n<table>\n<thead>\n<tr>\n<td width=\"20%\">表达式</td>\n<td>可匹配</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>\\r, \\n</td>\n<td>代表回车和换行符</td>\n</tr>\n<tr>\n<td>\\t</td>\n<td>制表符</td>\n</tr>\n<tr>\n<td>\\/</td>\n<td>/本身</td>\n</tr>\n<tr>\n<td>...</td>\n<td>...</td>\n</tr>\n</tbody>\n</table>\n\n还有其他一些在后边章节中有特殊用处的标点符号,在前面加 \"\\\" 后,就代表该符号本身。比如:^,$ 都有特殊意义,如果要想匹配字符串中 \"^\" 和 \"$\" 字符,则表达式就需要写成 \"\\^\" 和 \"\\$\"。\n\n<table>\n<thead>\n<tr>\n<td width=\"20%\">表达式</td>\n<td>可匹配</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>\\^</td>\n<td>匹配 ^ 符号本身</td>\n</tr>\n<tr>\n<td>\\$</td>\n<td>匹配 $ 符号本身</td>\n</tr>\n<tr>\n<td>\\.</td>\n<td>匹配小数点(.)本身</td>\n</tr>\n<tr>\n<td>...</td>\n<td>...</td>\n</tr>\n</tbody>\n</table>\n\n这些转义字符的匹配方法与 \"普通字符\" 是类似的。也是匹配与之相同的一个字符。\n\n3、自定义范围类(多个字符)\n\n上面两个匹配的都是单个字符,当我们需要匹配多个字符时,就可以用\"[]\"来表示一个范围,可以匹配范围内的任意字符,另外只匹配一个字符。\n\n<table>\n<thead>\n<tr>\n<td width=\"20%\">表达式</td>\n<td>可匹配</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>[ab5@]</td>\n<td>匹配 \"a\" 或 \"b\" 或 \"5\" 或 \"@\"</td>\n</tr>\n<tr>\n<td>[^abc]</td>\n<td>匹配 \"a\",\"b\",\"c\" 之外的任意一个字符</td>\n</tr>\n<tr>\n<td>[f-k]</td>\n<td>匹配 \"f\"~\"k\" 之间的任意一个字母</td>\n</tr>\n<td>[^A-F0-3]</td>\n<td>匹配 \"A\"~\"F\",\"0\"~\"3\" 之外的任意一个字符</td>\n</tr>\n<tr>\n<td>...</td>\n<td>...</td>\n</tr>\n</tbody>\n</table>\n\n4、预范围类(多个字符)\n\n除了上面的自定义的范围类,为了写起来比较方便,还有一些预定义的范围类。\n\n<table>\n<thead>\n<tr>\n<td width=\"20%\">表达式</td>\n<td>可匹配</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>\\d</td>\n<td>任意一个数字,0~9 中的任意一个</td>\n</tr>\n<tr>\n<td>\\w</td>\n<td>任意一个字母或数字或下划线,也就是 A~Z,a~z,0~9,_ 中任意一个</td>\n</tr>\n<tr>\n<td>\\s</td>\n<td>包括空格、制表符、换页符等空白字符的其中任意一个</td>\n</tr>\n<td>.</td>\n<td>小数点可以匹配除了换行符(\\n)以外的任意一个字符</td>\n</tr>\n<tr>\n<td>...</td>\n<td>...</td>\n</tr>\n</tbody>\n</table>\n\n##### 量词\n\n上文的字符或者范围类匹配的都是原串中的一个字符,如果要重复匹配多次,则需要量词,主要有以下一些:\n\n<table>\n<thead>\n<tr>\n<td width=\"20%\">表达式</td>\n<td>作用</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>{n}</td>\n<td>表达式重复n次,比如:\"\\w{2}\" 相当于 \"\\w\\w\";\"a{5}\" 相当于 \"aaaaa\"</td>\n</tr>\n<tr>\n<td>{m,n}</td>\n<td>表达式至少重复m次,最多重复n次,比如:\"ba{1,3}\"可以匹配 \"ba\"或\"baa\"或\"baaa\"</td>\n</tr>\n<tr>\n<td>{m,}</td>\n<td>表达式至少重复m次,比如:\"\\w\\d{2,}\"可以匹配 \"a12\",\"_456\",\"M12344\"...</td>\n</tr>\n<tr>\n<td>?</td>\n<td>匹配表达式0次或者1次,相当于 {0,1},比如:\"a[cd]?\"可以匹配 \"a\",\"ac\",\"ad\"</td>\n</tr>\n<tr>\n<td>+</td>\n<td>表达式至少出现1次,相当于 {1,},比如:\"a+b\"可以匹配 \"ab\",\"aab\",\"aaab\"...</td>\n</tr>\n<tr>\n<td>*</td>\n<td>表达式不出现或出现任意次,相当于 {0,},比如:\"\\^*b\"可以匹配 \"b\",\"^^^b\"...</td>\n</tr>\n</tbody>\n</table>\n\n##### 边界\n\n我们经常要匹配文件名后缀,这时候我们就会用到边界匹配符号。当然匹配开头、单词边界都很常见。\n\n<table>\n<thead>\n<tr>\n<td width=\"20%\">表达式</td>\n<td>作用</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>^</td>\n<td>与字符串开始的地方匹配,不匹配任何字符</td>\n</tr>\n<tr>\n<td>$</td>\n<td>与字符串结束的地方匹配,不匹配任何字符</td>\n</tr>\n<td>\\b</td>\n<td>匹配一个单词边界,也就是单词和空格之间的位置,不匹配任何字符</td>\n</tr>\n</tbody>\n</table>\n\n##### 分组\n\n分组的符号是\"()\",它的作用是在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰;并且取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到。\n\n另外还有一个\"|\",用于表达两个表达式之间或的关系。\n\n#### 高级\n\n##### 贪婪、非贪婪\n\n量词在匹配的时候是不确定的,例如aaaaa匹配a{1,3},可以匹配一到三次,但是如何选择呢?正则表达式默认规定是使用贪婪模式,也就是越多越好,尽量多地匹配。相反的还有一种非贪婪模式,要启用非贪婪模式只要在量词表达式之后跟上\"?\"即可。\n\n##### 反向引用\n\n表达式在匹配时,表达式引擎会将小括号 \"( )\" 包含的表达式所匹配到的字符串记录下来。并且记录下来的字符串我们可以通过\"$1\",\"$2\"的方式进行引用。举个例子,有电话号码13588884444,我们通常会把中间四位变成\"*\",来保护用户信息,这用正则表达式来做很简单。\n\n```\n'13588884444'.replace(/(\\d{3})\\d{4}(\\d{4})/g, '$1****$2')\n```\n\n上面的表达式将前三位和后四位分组,然后在替换的时候通过\"$1\"和\"$2\"进行反向引用,很神奇吧!\n\n##### 忽略分组\n\n假设上面的例子如果我们把中间四位也进行分组,但是中间四位没有记录下来的必要,此时我们就可以忽略分组,用\"(?:)\"表达式。\n\n##### 前瞻后顾(正向预搜索、反向预搜索)\n\njs目前只支持前瞻,所以只介绍前瞻了,后顾的道理是一样的。有的时候我们会有如下的需求,就是在匹配的时候,字符串的前后需要满足一定的要求。前瞻就是右边满足一定的要求,后顾就是左边满足一定的要求。例子如下:\n\n```\n'windows95,windows98,windows2000'.match(/windows(?=95|98)/g);//[\"windows\", \"windows\"]\n```\n\n上面的表达式匹配要求匹配windows的同时,后边是95或98,匹配的结果也就是[\"windows\", \"windows\"],可以看到条件是不会被匹配进结果的。\n\n#### js 中的正则\n\n##### es6之前\n\n###### 创建正则表达式:\n\n```\n// 构造函数\nvar regex = new RegExp('xyz', 'i');\nvar regex = new RegExp(/xyz/i);\nvar regex = new RegExp(/xyz/, 'i');//es6才支持\n// 字面量\nvar regex = /xyz/i;\n```\n\n###### 修饰符:\n\n<table>\n<thead>\n<tr><td width=\"20%\">修饰符</td><td>描述</td></tr>\n</thead>\n<tbody>\n<tr><td>i</td><td>执行对大小写不敏感的匹配。</td></tr>\n<tr><td>g</td><td>执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。</td></tr>\n<tr><td>m</td><td>执行多行匹配。</td></tr>\n</tbody>\n</table>\n\n###### RegExp 对象属性:\n\n<table>\n<thead>\n<tr><td width=\"20%\">属性</td><td>描述</td></tr>\n</thead>\n<tbody>\n<tr><td>global</td><td>RegExp 对象是否具有标志 g。</td></tr>\n<tr><td>ignoreCase</td><td>RegExp 对象是否具有标志 i。</td></tr>\n<tr><td>lastIndex</td><td>一个整数,标示开始下一次匹配的字符位置。</td></tr>\n<tr><td>multiline</td><td>RegExp 对象是否具有标志 m。</td></tr>\n<tr><td>source</td><td>正则表达式的源文本。</td></tr>\n</tbody>\n</table>\n\n###### Regex.prototype.test\n\ntest() 方法用于检测一个字符串是否匹配某个模式。存在则返回true,否则返回false。表单验证时经常会用到。\n\n###### Regex.prototype.exec \n\n```\n// Match \"quick brown\" followed by \"jumps\", ignoring characters in between\n// Remember \"brown\" and \"jumps\"\n// Ignore case\nvar re = /quick\\s(brown).+?(jumps)/ig;\nvar result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog');\n```\n\n下面的表格展示这个脚本的返回值:\n<table>\n<thead>\n<tr>\n<td width=\"33%\">属性/索引</td>\n<td width=\"33%\">描述</td>\n<td>例子</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>[0]</td><td>匹配的全部字符串</td><td>\tQuick Brown Fox Jumps</td>\n</tr>\n<tr>\n<td>[1], ...[n ]</td><td>括号中的分组捕获</td><td>\t[1] = Brown[2] = Jumps</td>\n</tr>\n<tr>\n<td>index</td><td>匹配到的字符位于原始字符串的基于0的索引值</td><td>4</td>\n</tr>\t\t\n<tr>\n<td>input</td><td>原始字符串</td><td>The Quick Brown Fox Jumps Over The Lazy Dog</td>\n</tr>\n</tbody>\n</table>\t\n\n全局模式下:它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。这就是说,您可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。\n\n> 注意:如果在一个字符串中完成了一次模式匹配之后要开始检索新的字符串,就必须手动地把 lastIndex 属性重置为 0。\n\n###### String.prototype.match \n\n非全局模式:和Regex.prototype.exec返回的信息相同。\n\n全局模式:全局匹配返回的数组的内容与前者大不相同,它的数组元素中存放的是 stringObject 中所有的匹配子串,而且也没有 index 属性或 input 属性。\n\n> 注意:在全局检索模式下,match() 即不提供与子表达式匹配的文本的信息,也不声明每个匹配子串的位置。如果您需要这些全局检索的信息,可以使用 RegExp.prototype.exec()。\n\n###### String.prototype.search\n\nsearch() 方法不执行全局匹配,它将忽略标志 g。它同时忽略 regexp 的 lastIndex 属性,并且总是从字符串的开始进行检索,这意味着它总是返回 stringObject 的第一个匹配的位置。\n\n###### String.prototype.replace\n\n字符串 stringObject 的 replace() 方法执行的是查找并替换的操作。它将在 stringObject 中查找与 regexp 相匹配的子字符串,然后用 replacement 来替换这些子串。如果 regexp 具有全局标志 g,那么 replace() 方法将替换所有匹配的子串。否则,它只替换第一个匹配子串。\nreplacement 可以是字符串,也可以是函数。如果它是字符串,那么每个匹配都将由字符串替换。\n\n但是 replacement 中的 $ 字符具有特定的含义。如下表所示,它说明从模式匹配得到的字符串将用于替换,也就是反向引用。\n\n###### String.prototype.split\n\n这个比较简单,举个例子吧:\n\n```\n'How are you doing today?'.split(/o/); //[\"H\", \"w are y\", \"u d\", \"ing t\", \"day?\"]\n```\n\n另外这个函数还接收第二个参数,表示数组的最大长度。\n\n##### es6正则扩展\n\n1、构造函数支持第一个参数为正则的同时,还可以使用修饰符参数。\n\n```\nvar regex = new RegExp(/xyz/, 'i');\n// ES5 Uncaught TypeError: Cannot supply flags when constructing one RegExp from another\n// ES6支持了这种写法\n```\n\n2、字符串的四个正则相关的方法定义在了Regex对象上。\n\n3、加入了u修饰符,含义为“Unicode模式”,用来正确处理大于\\uFFFF的Unicode字符。也就是说,会正确处理四个字节的UTF-16编码。\n\n4、加入了y修饰符,y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。\n\n```\nvar s = 'aaa_aa_a';\nvar r1 = /a+/g;\nvar r2 = /a+/y;\n\nr1.exec(s) // [\"aaa\"]\nr2.exec(s) // [\"aaa\"]\n\nr1.exec(s) // [\"aa\"]\nr2.exec(s) // null\n```\n\n5、正则对象增加了两个属性,flags和sticky。分别表示修饰符和是否设置y修饰符。\n\n##### es7正则扩展\n\n1、RegExp.escape()方法。\n\n2、加入后顾(反向预搜索)哈哈,迟早要支持的吧!\n\nes6和es7的部分只是列了一下,具体还是看阮一峰老师的[es6入门](http://es6.ruanyifeng.com/#docs/regex)。\n\n参考资料:\n\n1. [es6入门](http://es6.ruanyifeng.com/#docs/regex)\n2. [js正则表达式语法](http://blog.csdn.net/zaifendou/article/details/5746988)\n3. [将正则表达式图形化工具](https://regexper.com/)","html":"<p>这是之前欠下的,一直没空总结一下。看完剧,先把之前的总结了,在进行下一步的学习。在讲js的正则之前,先要讲一下正则的基本概念。结合一些例子,应该能快速了解吧!本文只能算扫盲加知识拓展性质,要熟练掌握,还是要靠多练!</p>\n\n<h4 id=\"\">基础</h4>\n\n<h5 id=\"\">字符</h5>\n\n<p>1、普通字符(单个字符)</p>\n\n<p>字母、数字、汉字、下划线、以及后边章节中没有特殊定义的标点符号,都是\"普通字符\"。表达式中的普通字符,在匹配一个字符串的时候,匹配与之相同的一个字符。</p>\n\n<p>2、转义字符(单个字符)</p>\n\n<p>一些不便书写的字符,采用在前面加 \"\\\" 的方法。这些字符其实我们都已经熟知了。</p>\n\n<table> \n<thead> \n<tr> \n<td width=\"20%\">表达式</td> \n<td>可匹配</td> \n</tr> \n</thead> \n<tbody> \n<tr> \n<td>\\r, \\n</td> \n<td>代表回车和换行符</td> \n</tr> \n<tr> \n<td>\\t</td> \n<td>制表符</td> \n</tr> \n<tr> \n<td>\\/</td> \n<td>/本身</td> \n</tr> \n<tr> \n<td>...</td> \n<td>...</td> \n</tr> \n</tbody> \n</table>\n\n<p>还有其他一些在后边章节中有特殊用处的标点符号,在前面加 \"\\\" 后,就代表该符号本身。比如:^,$ 都有特殊意义,如果要想匹配字符串中 \"^\" 和 \"$\" 字符,则表达式就需要写成 \"\\^\" 和 \"\\$\"。</p>\n\n<table> \n<thead> \n<tr> \n<td width=\"20%\">表达式</td> \n<td>可匹配</td> \n</tr> \n</thead> \n<tbody> \n<tr> \n<td>\\^</td> \n<td>匹配 ^ 符号本身</td> \n</tr> \n<tr> \n<td>\\$</td> \n<td>匹配 $ 符号本身</td> \n</tr> \n<tr> \n<td>\\.</td> \n<td>匹配小数点(.)本身</td> \n</tr> \n<tr> \n<td>...</td> \n<td>...</td> \n</tr> \n</tbody> \n</table>\n\n<p>这些转义字符的匹配方法与 \"普通字符\" 是类似的。也是匹配与之相同的一个字符。</p>\n\n<p>3、自定义范围类(多个字符)</p>\n\n<p>上面两个匹配的都是单个字符,当我们需要匹配多个字符时,就可以用\"[]\"来表示一个范围,可以匹配范围内的任意字符,另外只匹配一个字符。</p>\n\n<table> \n<thead> \n<tr> \n<td width=\"20%\">表达式</td> \n<td>可匹配</td> \n</tr> \n</thead> \n<tbody> \n<tr> \n<td>[ab5@]</td> \n<td>匹配 \"a\" 或 \"b\" 或 \"5\" 或 \"@\"</td> \n</tr> \n<tr> \n<td>[^abc]</td> \n<td>匹配 \"a\",\"b\",\"c\" 之外的任意一个字符</td> \n</tr> \n<tr> \n<td>[f-k]</td> \n<td>匹配 \"f\"~\"k\" 之间的任意一个字母</td> \n</tr> \n<td>[^A-F0-3]</td> \n<td>匹配 \"A\"~\"F\",\"0\"~\"3\" 之外的任意一个字符</td> \n</tr> \n<tr> \n<td>...</td> \n<td>...</td> \n</tr> \n</tbody> \n</table>\n\n<p>4、预范围类(多个字符)</p>\n\n<p>除了上面的自定义的范围类,为了写起来比较方便,还有一些预定义的范围类。</p>\n\n<table> \n<thead> \n<tr> \n<td width=\"20%\">表达式</td> \n<td>可匹配</td> \n</tr> \n</thead> \n<tbody> \n<tr> \n<td>\\d</td> \n<td>任意一个数字,0~9 中的任意一个</td> \n</tr> \n<tr> \n<td>\\w</td> \n<td>任意一个字母或数字或下划线,也就是 A~Z,a~z,0~9,_ 中任意一个</td> \n</tr> \n<tr> \n<td>\\s</td> \n<td>包括空格、制表符、换页符等空白字符的其中任意一个</td> \n</tr> \n<td>.</td> \n<td>小数点可以匹配除了换行符(\\n)以外的任意一个字符</td> \n</tr> \n<tr> \n<td>...</td> \n<td>...</td> \n</tr> \n</tbody> \n</table>\n\n<h5 id=\"\">量词</h5>\n\n<p>上文的字符或者范围类匹配的都是原串中的一个字符,如果要重复匹配多次,则需要量词,主要有以下一些:</p>\n\n<table> \n<thead> \n<tr> \n<td width=\"20%\">表达式</td> \n<td>作用</td> \n</tr> \n</thead> \n<tbody> \n<tr> \n<td>{n}</td> \n<td>表达式重复n次,比如:\"\\w{2}\" 相当于 \"\\w\\w\";\"a{5}\" 相当于 \"aaaaa\"</td> \n</tr> \n<tr> \n<td>{m,n}</td> \n<td>表达式至少重复m次,最多重复n次,比如:\"ba{1,3}\"可以匹配 \"ba\"或\"baa\"或\"baaa\"</td> \n</tr> \n<tr> \n<td>{m,}</td> \n<td>表达式至少重复m次,比如:\"\\w\\d{2,}\"可以匹配 \"a12\",\"_456\",\"M12344\"...</td> \n</tr> \n<tr> \n<td>?</td> \n<td>匹配表达式0次或者1次,相当于 {0,1},比如:\"a[cd]?\"可以匹配 \"a\",\"ac\",\"ad\"</td> \n</tr> \n<tr> \n<td>+</td> \n<td>表达式至少出现1次,相当于 {1,},比如:\"a+b\"可以匹配 \"ab\",\"aab\",\"aaab\"...</td> \n</tr> \n<tr> \n<td>*</td> \n<td>表达式不出现或出现任意次,相当于 {0,},比如:\"\\^*b\"可以匹配 \"b\",\"^^^b\"...</td> \n</tr> \n</tbody> \n</table>\n\n<h5 id=\"\">边界</h5>\n\n<p>我们经常要匹配文件名后缀,这时候我们就会用到边界匹配符号。当然匹配开头、单词边界都很常见。</p>\n\n<table> \n<thead> \n<tr> \n<td width=\"20%\">表达式</td> \n<td>作用</td> \n</tr> \n</thead> \n<tbody> \n<tr> \n<td>^</td> \n<td>与字符串开始的地方匹配,不匹配任何字符</td> \n</tr> \n<tr> \n<td>$</td> \n<td>与字符串结束的地方匹配,不匹配任何字符</td> \n</tr> \n<td>\\b</td> \n<td>匹配一个单词边界,也就是单词和空格之间的位置,不匹配任何字符</td> \n</tr> \n</tbody> \n</table>\n\n<h5 id=\"\">分组</h5>\n\n<p>分组的符号是\"()\",它的作用是在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰;并且取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到。</p>\n\n<p>另外还有一个\"|\",用于表达两个表达式之间或的关系。</p>\n\n<h4 id=\"\">高级</h4>\n\n<h5 id=\"\">贪婪、非贪婪</h5>\n\n<p>量词在匹配的时候是不确定的,例如aaaaa匹配a{1,3},可以匹配一到三次,但是如何选择呢?正则表达式默认规定是使用贪婪模式,也就是越多越好,尽量多地匹配。相反的还有一种非贪婪模式,要启用非贪婪模式只要在量词表达式之后跟上\"?\"即可。</p>\n\n<h5 id=\"\">反向引用</h5>\n\n<p>表达式在匹配时,表达式引擎会将小括号 \"( )\" 包含的表达式所匹配到的字符串记录下来。并且记录下来的字符串我们可以通过\"$1\",\"$2\"的方式进行引用。举个例子,有电话号码13588884444,我们通常会把中间四位变成\"*\",来保护用户信息,这用正则表达式来做很简单。</p>\n\n<pre><code>'13588884444'.replace(/(\\d{3})\\d{4}(\\d{4})/g, '$1****$2') \n</code></pre>\n\n<p>上面的表达式将前三位和后四位分组,然后在替换的时候通过\"$1\"和\"$2\"进行反向引用,很神奇吧!</p>\n\n<h5 id=\"\">忽略分组</h5>\n\n<p>假设上面的例子如果我们把中间四位也进行分组,但是中间四位没有记录下来的必要,此时我们就可以忽略分组,用\"(?:)\"表达式。</p>\n\n<h5 id=\"\">前瞻后顾(正向预搜索、反向预搜索)</h5>\n\n<p>js目前只支持前瞻,所以只介绍前瞻了,后顾的道理是一样的。有的时候我们会有如下的需求,就是在匹配的时候,字符串的前后需要满足一定的要求。前瞻就是右边满足一定的要求,后顾就是左边满足一定的要求。例子如下:</p>\n\n<pre><code>'windows95,windows98,windows2000'.match(/windows(?=95|98)/g);//[\"windows\", \"windows\"] \n</code></pre>\n\n<p>上面的表达式匹配要求匹配windows的同时,后边是95或98,匹配的结果也就是[\"windows\", \"windows\"],可以看到条件是不会被匹配进结果的。</p>\n\n<h4 id=\"js\">js 中的正则</h4>\n\n<h5 id=\"es6\">es6之前</h5>\n\n<h6 id=\"\">创建正则表达式:</h6>\n\n<pre><code>// 构造函数\nvar regex = new RegExp('xyz', 'i'); \nvar regex = new RegExp(/xyz/i); \nvar regex = new RegExp(/xyz/, 'i');//es6才支持 \n// 字面量\nvar regex = /xyz/i; \n</code></pre>\n\n<h6 id=\"\">修饰符:</h6>\n\n<table> \n<thead> \n<tr><td width=\"20%\">修饰符</td><td>描述</td></tr> \n</thead> \n<tbody> \n<tr><td>i</td><td>执行对大小写不敏感的匹配。</td></tr> \n<tr><td>g</td><td>执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。</td></tr> \n<tr><td>m</td><td>执行多行匹配。</td></tr> \n</tbody> \n</table>\n\n<h6 id=\"regexp\">RegExp 对象属性:</h6>\n\n<table> \n<thead> \n<tr><td width=\"20%\">属性</td><td>描述</td></tr> \n</thead> \n<tbody> \n<tr><td>global</td><td>RegExp 对象是否具有标志 g。</td></tr> \n<tr><td>ignoreCase</td><td>RegExp 对象是否具有标志 i。</td></tr> \n<tr><td>lastIndex</td><td>一个整数,标示开始下一次匹配的字符位置。</td></tr> \n<tr><td>multiline</td><td>RegExp 对象是否具有标志 m。</td></tr> \n<tr><td>source</td><td>正则表达式的源文本。</td></tr> \n</tbody> \n</table>\n\n<h6 id=\"regexprototypetest\">Regex.prototype.test</h6>\n\n<p>test() 方法用于检测一个字符串是否匹配某个模式。存在则返回true,否则返回false。表单验证时经常会用到。</p>\n\n<h6 id=\"regexprototypeexec\">Regex.prototype.exec</h6>\n\n<pre><code>// Match \"quick brown\" followed by \"jumps\", ignoring characters in between\n// Remember \"brown\" and \"jumps\"\n// Ignore case\nvar re = /quick\\s(brown).+?(jumps)/ig; \nvar result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog'); \n</code></pre>\n\n<p>下面的表格展示这个脚本的返回值:</p>\n\n<table> \n<thead> \n<tr> \n<td width=\"33%\">属性/索引</td> \n<td width=\"33%\">描述</td> \n<td>例子</td> \n</tr> \n</thead> \n<tbody> \n<tr> \n<td>[0]</td><td>匹配的全部字符串</td><td> Quick Brown Fox Jumps</td> \n</tr> \n<tr> \n<td>[1], ...[n ]</td><td>括号中的分组捕获</td><td> [1] = Brown[2] = Jumps</td> \n</tr> \n<tr> \n<td>index</td><td>匹配到的字符位于原始字符串的基于0的索引值</td><td>4</td> \n</tr> \n<tr> \n<td>input</td><td>原始字符串</td><td>The Quick Brown Fox Jumps Over The Lazy Dog</td> \n</tr> \n</tbody> \n</table> \n\n<p>全局模式下:它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。这就是说,您可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。</p>\n\n<blockquote>\n <p>注意:如果在一个字符串中完成了一次模式匹配之后要开始检索新的字符串,就必须手动地把 lastIndex 属性重置为 0。</p>\n</blockquote>\n\n<h6 id=\"stringprototypematch\">String.prototype.match</h6>\n\n<p>非全局模式:和Regex.prototype.exec返回的信息相同。</p>\n\n<p>全局模式:全局匹配返回的数组的内容与前者大不相同,它的数组元素中存放的是 stringObject 中所有的匹配子串,而且也没有 index 属性或 input 属性。</p>\n\n<blockquote>\n <p>注意:在全局检索模式下,match() 即不提供与子表达式匹配的文本的信息,也不声明每个匹配子串的位置。如果您需要这些全局检索的信息,可以使用 RegExp.prototype.exec()。</p>\n</blockquote>\n\n<h6 id=\"stringprototypesearch\">String.prototype.search</h6>\n\n<p>search() 方法不执行全局匹配,它将忽略标志 g。它同时忽略 regexp 的 lastIndex 属性,并且总是从字符串的开始进行检索,这意味着它总是返回 stringObject 的第一个匹配的位置。</p>\n\n<h6 id=\"stringprototypereplace\">String.prototype.replace</h6>\n\n<p>字符串 stringObject 的 replace() 方法执行的是查找并替换的操作。它将在 stringObject 中查找与 regexp 相匹配的子字符串,然后用 replacement 来替换这些子串。如果 regexp 具有全局标志 g,那么 replace() 方法将替换所有匹配的子串。否则,它只替换第一个匹配子串。\nreplacement 可以是字符串,也可以是函数。如果它是字符串,那么每个匹配都将由字符串替换。</p>\n\n<p>但是 replacement 中的 $ 字符具有特定的含义。如下表所示,它说明从模式匹配得到的字符串将用于替换,也就是反向引用。</p>\n\n<h6 id=\"stringprototypesplit\">String.prototype.split</h6>\n\n<p>这个比较简单,举个例子吧:</p>\n\n<pre><code>'How are you doing today?'.split(/o/); //[\"H\", \"w are y\", \"u d\", \"ing t\", \"day?\"] \n</code></pre>\n\n<p>另外这个函数还接收第二个参数,表示数组的最大长度。</p>\n\n<h5 id=\"es6\">es6正则扩展</h5>\n\n<p>1、构造函数支持第一个参数为正则的同时,还可以使用修饰符参数。</p>\n\n<pre><code>var regex = new RegExp(/xyz/, 'i'); \n// ES5 Uncaught TypeError: Cannot supply flags when constructing one RegExp from another\n// ES6支持了这种写法\n</code></pre>\n\n<p>2、字符串的四个正则相关的方法定义在了Regex对象上。</p>\n\n<p>3、加入了u修饰符,含义为“Unicode模式”,用来正确处理大于\\uFFFF的Unicode字符。也就是说,会正确处理四个字节的UTF-16编码。</p>\n\n<p>4、加入了y修饰符,y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。</p>\n\n<pre><code>var s = 'aaa_aa_a'; \nvar r1 = /a+/g; \nvar r2 = /a+/y;\n\nr1.exec(s) // [\"aaa\"] \nr2.exec(s) // [\"aaa\"]\n\nr1.exec(s) // [\"aa\"] \nr2.exec(s) // null \n</code></pre>\n\n<p>5、正则对象增加了两个属性,flags和sticky。分别表示修饰符和是否设置y修饰符。</p>\n\n<h5 id=\"es7\">es7正则扩展</h5>\n\n<p>1、RegExp.escape()方法。</p>\n\n<p>2、加入后顾(反向预搜索)哈哈,迟早要支持的吧!</p>\n\n<p>es6和es7的部分只是列了一下,具体还是看阮一峰老师的<a href=\"http://es6.ruanyifeng.com/#docs/regex\">es6入门</a>。</p>\n\n<p>参考资料:</p>\n\n<ol>\n<li><a href=\"http://es6.ruanyifeng.com/#docs/regex\">es6入门</a> </li>\n<li><a href=\"http://blog.csdn.net/zaifendou/article/details/5746988\">js正则表达式语法</a> </li>\n<li><a href=\"https://regexper.com/\">将正则表达式图形化工具</a></li>\n</ol>","image":"/content/images/2016/09/QQ20160913-5-2x.png","featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1473745279085,"created_by":1,"updated_at":1473757279089,"updated_by":1,"published_at":1473748641713,"published_by":1},{"id":63,"uuid":"6b0923c3-aa1a-4002-a4b9-1f2303870b36","title":"js 严格模式小结","slug":"js-yan-ge-mo-shi-xiao-jie","markdown":"大部分内容来自MDN,最后个人补充了一些,总结了一下使用严格模式的策略。在node6.2环境测试过程中发现严格模式在ES6可能有部分调整,想要了解的同学看[这里](http://www.ecma-international.org/ecma-262/6.0/#sec-strict-mode-of-ecmascript),我暂时不去折腾了。\n\n#### 简介\n\n严格模式是ES5引入的一种限制性更强的变种方式。严格模式不是一个子集:它在语义上与正常代码有着明显的差异。不支持严格模式的浏览器与支持严格模式的浏览器行为上也不一样,所以不要在未经严格模式特性测试情况下使用严格模式。严格模式可以与非严格模式共存,所以脚本可以逐渐的选择性加入严格模式。\n\n严格模式在语义上与正常的JavaScript有一些不同。 首先,严格模式会将JavaScript陷阱直接变成明显的错误。其次,严格模式修正了一些引擎难以优化的错误:同样的代码有些时候严格模式会比非严格模式下更快。 第三,严格模式禁用了一些有可能在未来版本中定义的语法。\n\n#### 开启\n\n在全局或者函数或者eval代码块所有语句前中加入'use strict',就这该作用域内开启了严格模式。\n\n#### 与正常模式的差别\n\n###### 将问题直接转化为错误(如语法错误或运行时错误,方便找错)\n\n1、 不通过var声明变量,不会变为全局变量,而是报错;\n```\n\"use strict\";\n // 假如有一个全局变量叫做mistypedVariable\nmistypedVaraible = 17; // 因为变量名拼写错误\n // 这一行代码就会抛出 ReferenceError\n```\n\n2、 给不可写属性赋值,给只读属性(getter-only)赋值赋值,给不可扩展对象(non-extensible object)的新属性赋值都会抛出异常;\n\n```\n\"use strict\";\n\n// 给不可写属性赋值\nvar obj1 = {};\nObject.defineProperty(obj1, \"x\", { value: 42, writable: false });\nobj1.x = 9; // 抛出TypeError错误\n\n// 给只读属性赋值\nvar obj2 = { get x() { return 17; } };\nobj2.x = 5; // 抛出TypeError错误\n\n// 给不可扩展对象的新属性赋值\nvar fixed = {};\nObject.preventExtensions(fixed);\nfixed.newProp = \"ohai\"; // 抛出TypeError错误\n```\n\n3、在严格模式下,试图删除不可删除的属性时会抛出异常;\n\n```\n\"use strict\";\ndelete Object.prototype; // 抛出TypeError错误\n```\n\n4、第四,在Gecko版本34之前,严格模式要求一个对象内的所有属性名在对象内必须唯一。正常模式下重名属性是允许的,最后一个重名的属性决定其属性值。因为只有最后一个属性起作用,当代码是要改变属性值而却不是修改的最后一个重名属性的时候,复制这个对象就产生一连串的bug。在严格模式下,重名属性被认为是语法错误:\n\n> 这个问题在ECMAScript6中已经不复存在([bug 1041128](https://bugzilla.mozilla.org/show_bug.cgi?id=1041128))。\n\n```\n\"use strict\";\nvar o = { p: 1, p: 2 }; // !!! 语法错误\n```\n\n5、严格模式要求函数的参数名唯一。在正常模式下,最后一个重名参数名会掩盖之前的重名参数。之前的参数仍然可以通过 arguments[i] 来访问,还不是完全无法访问。然而,这种隐藏毫无意义而且可能是意料之外的 (比如它可能本来是打错了),所以在严格模式下重名参数被认为是语法错误:\n\n```\nfunction sum(a, a, c){ // !!! 语法错误\n \"use strict\";\n return a + b + c; // 代码运行到这里会出错\n}\n```\n\n6、严格模式禁止八进制数字语法。ECMAScript并不包含八进制语法,但所有的浏览器都支持这种以零(0)开头的八进制语法:0644 === 420 还有 \"\\045\" === \"%\"。有些新手开发者认为数字的前导零没有语法意义,所以他们会用作对齐措施 — 但其实这会改变数字的意义!八进制语法很少有用并且可能会错误使用,所以严格模式下八进制语法会引起语法错误:\n\n```\n\"use strict\";\nvar sum = 015 + // !!! 语法错误\n 197 +\n 142;\n```\n\n###### 简化变量的使用(禁用动态绑定,因为js引擎不能很好地优化)\n\n严格模式简化了代码中变量名字映射到变量定义的方式。很多编译器的优化是依赖存储变量X位置的能力:这对全面优化JavaScript代码至关重要。\n\n1、with的使用块内的任何名称可以映射(map)到with传进来的对象的属性,也可以映射到包围这个块的作用域内的变量(甚至是全局变量),这一切都是在运行时决定的:在代码运行之前是无法得知的。造成的问题就是js引擎无法对with的代码块做出优化!\n\n```\n\"use strict\";\nvar x = 17;\nwith (obj) // !!! 语法错误\n{\n // 如果没有开启严格模式,with中的这个x会指向with上面的那个x,还是obj.x?\n // 如果不运行代码,我们无法知道,因此,这种代码让引擎无法进行优化,速度也就会变慢。\n x;\n}\n```\n\n2、严格模式下的 eval 不再为上层范围(surrounding scope,注:包围eval代码块的范围)引入新变量。在正常模式下,代码 eval(\"var x;\") 会给上层函数(surrounding function)或者全局引入一个新的变量 x 。 这意味着,一般情况下,在一个包含 eval 调用的函数内所有没有引用到参数或者局部变量的名称都必须在运行时才能被映射到特定的定义 (因为 eval 可能引入的新变量会覆盖它的外层变量)。在严格模式下 eval 仅仅为被运行的代码创建变量,所以 eval 不会影响到名称映射到外部变量或者其他局部变量:\n\n```\nvar x = 17;\nvar evalX = eval(\"'use strict'; var x = 42; x\");\nassert(x === 17);\nassert(evalX === 42);\n```\n\n相应的,如果函数 eval 被在被严格模式下的eval(...)以表达式的形式调用时,其代码会被当做严格模式下的代码执行。当然也可以在代码中显式开启严格模式,但这样做并不是必须的。\n\n3、严格模式禁止删除声明变量。delete name 在严格模式下会引起语法错误:\n\n```\n\"use strict\";\nvar x;\ndelete x; // !!! 语法错误\neval(\"var x; delete x;\"); // !!! 语法错误\n```\n\n###### 让eval和arguments变的简单(减少出错的机会)\n\n1、名称 eval 和 arguments 不能通过程序语法被绑定(be bound)或赋值。以下的所有尝试将引起语法错误:\n\n```\n\"use strict\";\neval = 17;\narguments++;\n++eval;\nvar obj = { set p(arguments) { } };\nvar eval;\ntry { } catch (arguments) { }\nfunction x(eval) { }\nfunction arguments() { }\nvar y = function eval() { };\nvar f = new Function(\"arguments\", \"'use strict'; return 17;\");\n```\n\n2、严格模式下,参数的值不会随 arguments 对象的值的改变而变化。在正常模式下,对于第一个参数是 arg 的函数,对 arg 赋值时会同时赋值给 arguments[0],反之亦然(除非没有参数,或者 arguments[0] 被删除)。严格模式下,函数的 arguments 对象会保存函数被调用时的原始参数。arguments[i] 的值不会随与之相应的参数的值的改变而变化,同名参数的值也不会随与之相应的 arguments[i] 的值的改变而变化。\n\n```\nfunction f(a)\n{\n \"use strict\";\n a = 42;\n return [a, arguments[0]];//[42,17]严格模式下未发生绑定\n}\nvar pair = f(17);\nconsole.assert(pair[0] === 42);\nconsole.assert(pair[1] === 17);\n```\n\n第三,不再支持 arguments.callee。正常模式下,arguments.callee 指向当前正在执行的函数。这个作用很小:直接给执行函数命名就可以了!此外,arguments.callee 十分不利于优化,例如内联函数,因为 arguments.callee 会依赖对非内联函数的引用。在严格模式下,arguments.callee 是一个不可删除属性,而且赋值和读取时都会抛出异常:\n\n```\n\"use strict\";\nvar f = function() { return arguments.callee; };\nf(); // 抛出类型错误\n```\n\n###### 消除代码运行的一些不安全之处\n\n1、禁止this关键字指向全局对象;\n\n2、第一,在严格模式下通过this传递给一个函数的值不会被强制转换为一个对象;\n\n3、禁止在函数内部遍历调用栈。在普通模式下用这些扩展的话,当一个叫fun的函数正在被调用的时候,fun.caller是最后一个调用fun的函数,而且fun.arguments包含调用fun时用的形参。这两个扩展接口对于“安全”JavaScript而言都是有问题的,因为他们允许“安全的”代码访问\"专有\"函数和他们的(通常是没有经过保护的)形参。如果fun在严格模式下,那么fun.caller和fun.arguments都是不可删除的属性而且在存值、取值时都会报错:\n\n4、严格模式下的arguments不会再提供访问与调用这个函数相关的变量的途径。\n\n###### 为未来的ECMAScript版本铺平道路\n\n1、将implements, interface, let, package, private, protected, public, static和yield作为了保留的关键词,事实也证明是对的,let、static、yield已经出现在了ES6中。\n\n2、严格模式禁止了不在脚本或者函数层面上的函数声明。在浏览器的普通代码中,在“所有地方”的函数声明都是合法的。这并不在ES5规范中(甚至是ES3)!这是一种针对不同浏览器中不同语义的一种延伸。未来的ECMAScript版本很有希望制定一个新的,针对不在脚本函数层面进行函数声明的语法。在严格模式下禁止这样的函数声明对于将来ECMAScript版本的推出扫清了障碍。(测试在node6.2中启用严格模式,在块级作用域内声明函数没有报错,这与阮老师es6入门中描述的不符,个人猜测应该是严格模式在ES6做了调整)。\n\n#### 副作用\n\n1、兼容性,市场上仍然有大量的浏览器版本只部分支持严格模式或者根本就不支持(比如IE10之前的版本),在不支持的浏览器上会把'use strict'当做普通字符串;\n\n2、在为整个script标签开启严格模式后,如果合并开启严格模式的代码和未开启严格模式的代码时便会出现问题。\n\n#### 使用策略\n\n看了下Angular、Vue、Jquery都是在自执行函数内部开启了严格模式,这种方式可以解决合并代码的问题,另外也浏览了阿里云、知乎等网站,都没有在整个script范围启用严格模式,只是部分文件启用,启用的方式都是在匿名自执行函数之内,所以就目前来说在函数级别开启严格模式是比较好的做法,即使要在整个脚本开启,也建议把脚本代码包进自执行的函数内部。\n\n#### 小结\n\n为了能够写出更加安全、可调式的代码,从现在开始开启严格模式吧!\n\n参考资料:\n\n1.[MDN 严格模式](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode)\n2.[Javascript 严格模式详解](http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html)","html":"<p>大部分内容来自MDN,最后个人补充了一些,总结了一下使用严格模式的策略。在node6.2环境测试过程中发现严格模式在ES6可能有部分调整,想要了解的同学看<a href=\"http://www.ecma-international.org/ecma-262/6.0/#sec-strict-mode-of-ecmascript\">这里</a>,我暂时不去折腾了。</p>\n\n<h4 id=\"\">简介</h4>\n\n<p>严格模式是ES5引入的一种限制性更强的变种方式。严格模式不是一个子集:它在语义上与正常代码有着明显的差异。不支持严格模式的浏览器与支持严格模式的浏览器行为上也不一样,所以不要在未经严格模式特性测试情况下使用严格模式。严格模式可以与非严格模式共存,所以脚本可以逐渐的选择性加入严格模式。</p>\n\n<p>严格模式在语义上与正常的JavaScript有一些不同。 首先,严格模式会将JavaScript陷阱直接变成明显的错误。其次,严格模式修正了一些引擎难以优化的错误:同样的代码有些时候严格模式会比非严格模式下更快。 第三,严格模式禁用了一些有可能在未来版本中定义的语法。</p>\n\n<h4 id=\"\">开启</h4>\n\n<p>在全局或者函数或者eval代码块所有语句前中加入'use strict',就这该作用域内开启了严格模式。</p>\n\n<h4 id=\"\">与正常模式的差别</h4>\n\n<h6 id=\"\">将问题直接转化为错误(如语法错误或运行时错误,方便找错)</h6>\n\n<p>1、 不通过var声明变量,不会变为全局变量,而是报错; </p>\n\n<pre><code>\"use strict\";\n // 假如有一个全局变量叫做mistypedVariable\nmistypedVaraible = 17; // 因为变量名拼写错误 \n // 这一行代码就会抛出 ReferenceError\n</code></pre>\n\n<p>2、 给不可写属性赋值,给只读属性(getter-only)赋值赋值,给不可扩展对象(non-extensible object)的新属性赋值都会抛出异常;</p>\n\n<pre><code>\"use strict\";\n\n// 给不可写属性赋值\nvar obj1 = {}; \nObject.defineProperty(obj1, \"x\", { value: 42, writable: false }); \nobj1.x = 9; // 抛出TypeError错误\n\n// 给只读属性赋值\nvar obj2 = { get x() { return 17; } }; \nobj2.x = 5; // 抛出TypeError错误\n\n// 给不可扩展对象的新属性赋值\nvar fixed = {}; \nObject.preventExtensions(fixed); \nfixed.newProp = \"ohai\"; // 抛出TypeError错误 \n</code></pre>\n\n<p>3、在严格模式下,试图删除不可删除的属性时会抛出异常;</p>\n\n<pre><code>\"use strict\";\ndelete Object.prototype; // 抛出TypeError错误 \n</code></pre>\n\n<p>4、第四,在Gecko版本34之前,严格模式要求一个对象内的所有属性名在对象内必须唯一。正常模式下重名属性是允许的,最后一个重名的属性决定其属性值。因为只有最后一个属性起作用,当代码是要改变属性值而却不是修改的最后一个重名属性的时候,复制这个对象就产生一连串的bug。在严格模式下,重名属性被认为是语法错误:</p>\n\n<blockquote>\n <p>这个问题在ECMAScript6中已经不复存在(<a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1041128\">bug 1041128</a>)。</p>\n</blockquote>\n\n<pre><code>\"use strict\";\nvar o = { p: 1, p: 2 }; // !!! 语法错误 \n</code></pre>\n\n<p>5、严格模式要求函数的参数名唯一。在正常模式下,最后一个重名参数名会掩盖之前的重名参数。之前的参数仍然可以通过 arguments[i] 来访问,还不是完全无法访问。然而,这种隐藏毫无意义而且可能是意料之外的 (比如它可能本来是打错了),所以在严格模式下重名参数被认为是语法错误:</p>\n\n<pre><code>function sum(a, a, c){ // !!! 语法错误 \n \"use strict\";\n return a + b + c; // 代码运行到这里会出错\n}\n</code></pre>\n\n<p>6、严格模式禁止八进制数字语法。ECMAScript并不包含八进制语法,但所有的浏览器都支持这种以零(0)开头的八进制语法:0644 <mark>= 420 还有 \"\\045\" </mark>= \"%\"。有些新手开发者认为数字的前导零没有语法意义,所以他们会用作对齐措施 — 但其实这会改变数字的意义!八进制语法很少有用并且可能会错误使用,所以严格模式下八进制语法会引起语法错误:</p>\n\n<pre><code>\"use strict\";\nvar sum = 015 + // !!! 语法错误 \n 197 +\n 142;\n</code></pre>\n\n<h6 id=\"js\">简化变量的使用(禁用动态绑定,因为js引擎不能很好地优化)</h6>\n\n<p>严格模式简化了代码中变量名字映射到变量定义的方式。很多编译器的优化是依赖存储变量X位置的能力:这对全面优化JavaScript代码至关重要。</p>\n\n<p>1、with的使用块内的任何名称可以映射(map)到with传进来的对象的属性,也可以映射到包围这个块的作用域内的变量(甚至是全局变量),这一切都是在运行时决定的:在代码运行之前是无法得知的。造成的问题就是js引擎无法对with的代码块做出优化!</p>\n\n<pre><code>\"use strict\";\nvar x = 17; \nwith (obj) // !!! 语法错误 \n{\n // 如果没有开启严格模式,with中的这个x会指向with上面的那个x,还是obj.x?\n // 如果不运行代码,我们无法知道,因此,这种代码让引擎无法进行优化,速度也就会变慢。\n x;\n}\n</code></pre>\n\n<p>2、严格模式下的 eval 不再为上层范围(surrounding scope,注:包围eval代码块的范围)引入新变量。在正常模式下,代码 eval(\"var x;\") 会给上层函数(surrounding function)或者全局引入一个新的变量 x 。 这意味着,一般情况下,在一个包含 eval 调用的函数内所有没有引用到参数或者局部变量的名称都必须在运行时才能被映射到特定的定义 (因为 eval 可能引入的新变量会覆盖它的外层变量)。在严格模式下 eval 仅仅为被运行的代码创建变量,所以 eval 不会影响到名称映射到外部变量或者其他局部变量:</p>\n\n<pre><code>var x = 17; \nvar evalX = eval(\"'use strict'; var x = 42; x\"); \nassert(x === 17); \nassert(evalX === 42); \n</code></pre>\n\n<p>相应的,如果函数 eval 被在被严格模式下的eval(...)以表达式的形式调用时,其代码会被当做严格模式下的代码执行。当然也可以在代码中显式开启严格模式,但这样做并不是必须的。</p>\n\n<p>3、严格模式禁止删除声明变量。delete name 在严格模式下会引起语法错误:</p>\n\n<pre><code>\"use strict\";\nvar x; \ndelete x; // !!! 语法错误 \neval(\"var x; delete x;\"); // !!! 语法错误 \n</code></pre>\n\n<h6 id=\"evalarguments\">让eval和arguments变的简单(减少出错的机会)</h6>\n\n<p>1、名称 eval 和 arguments 不能通过程序语法被绑定(be bound)或赋值。以下的所有尝试将引起语法错误:</p>\n\n<pre><code>\"use strict\";\neval = 17; \narguments++; \n++eval;\nvar obj = { set p(arguments) { } }; \nvar eval; \ntry { } catch (arguments) { } \nfunction x(eval) { } \nfunction arguments() { } \nvar y = function eval() { }; \nvar f = new Function(\"arguments\", \"'use strict'; return 17;\"); \n</code></pre>\n\n<p>2、严格模式下,参数的值不会随 arguments 对象的值的改变而变化。在正常模式下,对于第一个参数是 arg 的函数,对 arg 赋值时会同时赋值给 arguments[0],反之亦然(除非没有参数,或者 arguments[0] 被删除)。严格模式下,函数的 arguments 对象会保存函数被调用时的原始参数。arguments[i] 的值不会随与之相应的参数的值的改变而变化,同名参数的值也不会随与之相应的 arguments[i] 的值的改变而变化。</p>\n\n<pre><code>function f(a) \n{\n \"use strict\";\n a = 42;\n return [a, arguments[0]];//[42,17]严格模式下未发生绑定\n}\nvar pair = f(17); \nconsole.assert(pair[0] === 42); \nconsole.assert(pair[1] === 17); \n</code></pre>\n\n<p>第三,不再支持 arguments.callee。正常模式下,arguments.callee 指向当前正在执行的函数。这个作用很小:直接给执行函数命名就可以了!此外,arguments.callee 十分不利于优化,例如内联函数,因为 arguments.callee 会依赖对非内联函数的引用。在严格模式下,arguments.callee 是一个不可删除属性,而且赋值和读取时都会抛出异常:</p>\n\n<pre><code>\"use strict\";\nvar f = function() { return arguments.callee; }; \nf(); // 抛出类型错误 \n</code></pre>\n\n<h6 id=\"\">消除代码运行的一些不安全之处</h6>\n\n<p>1、禁止this关键字指向全局对象;</p>\n\n<p>2、第一,在严格模式下通过this传递给一个函数的值不会被强制转换为一个对象;</p>\n\n<p>3、禁止在函数内部遍历调用栈。在普通模式下用这些扩展的话,当一个叫fun的函数正在被调用的时候,fun.caller是最后一个调用fun的函数,而且fun.arguments包含调用fun时用的形参。这两个扩展接口对于“安全”JavaScript而言都是有问题的,因为他们允许“安全的”代码访问\"专有\"函数和他们的(通常是没有经过保护的)形参。如果fun在严格模式下,那么fun.caller和fun.arguments都是不可删除的属性而且在存值、取值时都会报错:</p>\n\n<p>4、严格模式下的arguments不会再提供访问与调用这个函数相关的变量的途径。</p>\n\n<h6 id=\"ecmascript\">为未来的ECMAScript版本铺平道路</h6>\n\n<p>1、将implements, interface, let, package, private, protected, public, static和yield作为了保留的关键词,事实也证明是对的,let、static、yield已经出现在了ES6中。</p>\n\n<p>2、严格模式禁止了不在脚本或者函数层面上的函数声明。在浏览器的普通代码中,在“所有地方”的函数声明都是合法的。这并不在ES5规范中(甚至是ES3)!这是一种针对不同浏览器中不同语义的一种延伸。未来的ECMAScript版本很有希望制定一个新的,针对不在脚本函数层面进行函数声明的语法。在严格模式下禁止这样的函数声明对于将来ECMAScript版本的推出扫清了障碍。(测试在node6.2中启用严格模式,在块级作用域内声明函数没有报错,这与阮老师es6入门中描述的不符,个人猜测应该是严格模式在ES6做了调整)。</p>\n\n<h4 id=\"\">副作用</h4>\n\n<p>1、兼容性,市场上仍然有大量的浏览器版本只部分支持严格模式或者根本就不支持(比如IE10之前的版本),在不支持的浏览器上会把'use strict'当做普通字符串;</p>\n\n<p>2、在为整个script标签开启严格模式后,如果合并开启严格模式的代码和未开启严格模式的代码时便会出现问题。</p>\n\n<h4 id=\"\">使用策略</h4>\n\n<p>看了下Angular、Vue、Jquery都是在自执行函数内部开启了严格模式,这种方式可以解决合并代码的问题,另外也浏览了阿里云、知乎等网站,都没有在整个script范围启用严格模式,只是部分文件启用,启用的方式都是在匿名自执行函数之内,所以就目前来说在函数级别开启严格模式是比较好的做法,即使要在整个脚本开启,也建议把脚本代码包进自执行的函数内部。</p>\n\n<h4 id=\"\">小结</h4>\n\n<p>为了能够写出更加安全、可调式的代码,从现在开始开启严格模式吧!</p>\n\n<p>参考资料:</p>\n\n<p>1.<a href=\"https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode\">MDN 严格模式</a> <br />\n2.<a href=\"http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html\">Javascript 严格模式详解</a></p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1473817012014,"created_by":1,"updated_at":1473835465160,"updated_by":1,"published_at":1473834551640,"published_by":1},{"id":64,"uuid":"462dd77c-e983-4dc6-8f4c-7cb8eaaae5d2","title":"为了N","slug":"wei-liao-n","markdown":"下面那篇可执行代码与执行环境写得我心力交瘁,所以先休息下写点关于前几天看的《为了N》这本书或者这部日剧的一些东西,算不上是影评或书评。写完插音乐时发现只要把网易外链的http改成https音乐就能播放了,副作用是chrome绿色的锁没了。\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=30798011&auto=1&height=66\"></iframe>\n\n之前哪一篇来着,讲着去了浙江图书馆。也就是那天,办了张卡,借了几本书回来,其中就有凑佳苗的《为了N》,就是写《告白》那个,电影版是松隆子演的。花了两天吧,把小说看完了,但是还是不过瘾,于是找来了改编的剧,也是一天刷完。感觉改编的还不错,大部分还原了小说,并在小说的基础上加入了警察夫妇这一对,来推进剧情,对于小说中没有交代的一些东西,编剧也还算合理地做了交代。\n\nN是什么,杉下的N是成濑和安腾;成濑的N是杉下;安腾的N是杉下;西崎的N是奈央子;奈央子的N是野口;警察的N是他妻子;他妻子的N则是他(不是成濑父亲哈);野口呢,也许没有N,硬是要说的话就是奈央子了。\n\nN在剧中是每个人的姓氏开头的字母,也是每个人最重要的人。杉下为了成濑共有罪行;杉下为了安腾则不让他陷入N计划;西崎为了奈央子则打算去救他;奈央子为了野口和杉下分开(本来也没在一起)则向西崎求救;警察为了妻子,调查14年前的火灾;妻子为了警察保守秘密14年。这里的每一个人都有自己要守护的东西,也是这份东西在潜移默化的改变着他们,朝着或好或坏的方向上不断前行。\n\n就是这么一种每个人都可能拥有的东西,用戏剧化的方式表达出来,让我们产生深深地共鸣。最后杉下和母亲和好了、警察妻子能够说话了、杉下回到了岛上;安腾继续为着当初的梦想努力;西崎开始找工作了;成濑在岛上有了店...为了N还在继续,留下的是美好的当下。\n\n不管从前如何,记得不忘初衷,为了N努力向上吧!为来就是那个样子的,闪闪发光!がんばって,加油!\n\n![](https://ws4.sinaimg.cn/large/006bH5BKgw1f7u7i9mb6mj30g4094q38.jpg)\n\n![](https://ws4.sinaimg.cn/large/006bH5BKgw1f7u7gfg2oij30g4092aad.jpg)\n\n![](https://ws3.sinaimg.cn/large/006bH5BKgw1f7u7ioojb1j30g40923yu.jpg)\n\n![](https://ws1.sinaimg.cn/large/006bH5BKgw1f7u7hs6e92j30g4092t8y.jpg)","html":"<p>下面那篇可执行代码与执行环境写得我心力交瘁,所以先休息下写点关于前几天看的《为了N》这本书或者这部日剧的一些东西,算不上是影评或书评。写完插音乐时发现只要把网易外链的http改成https音乐就能播放了,副作用是chrome绿色的锁没了。</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=30798011&auto=1&height=66\"></iframe>\n\n<p>之前哪一篇来着,讲着去了浙江图书馆。也就是那天,办了张卡,借了几本书回来,其中就有凑佳苗的《为了N》,就是写《告白》那个,电影版是松隆子演的。花了两天吧,把小说看完了,但是还是不过瘾,于是找来了改编的剧,也是一天刷完。感觉改编的还不错,大部分还原了小说,并在小说的基础上加入了警察夫妇这一对,来推进剧情,对于小说中没有交代的一些东西,编剧也还算合理地做了交代。</p>\n\n<p>N是什么,杉下的N是成濑和安腾;成濑的N是杉下;安腾的N是杉下;西崎的N是奈央子;奈央子的N是野口;警察的N是他妻子;他妻子的N则是他(不是成濑父亲哈);野口呢,也许没有N,硬是要说的话就是奈央子了。</p>\n\n<p>N在剧中是每个人的姓氏开头的字母,也是每个人最重要的人。杉下为了成濑共有罪行;杉下为了安腾则不让他陷入N计划;西崎为了奈央子则打算去救他;奈央子为了野口和杉下分开(本来也没在一起)则向西崎求救;警察为了妻子,调查14年前的火灾;妻子为了警察保守秘密14年。这里的每一个人都有自己要守护的东西,也是这份东西在潜移默化的改变着他们,朝着或好或坏的方向上不断前行。</p>\n\n<p>就是这么一种每个人都可能拥有的东西,用戏剧化的方式表达出来,让我们产生深深地共鸣。最后杉下和母亲和好了、警察妻子能够说话了、杉下回到了岛上;安腾继续为着当初的梦想努力;西崎开始找工作了;成濑在岛上有了店...为了N还在继续,留下的是美好的当下。</p>\n\n<p>不管从前如何,记得不忘初衷,为了N努力向上吧!为来就是那个样子的,闪闪发光!がんばって,加油!</p>\n\n<p><img src=\"https://ws4.sinaimg.cn/large/006bH5BKgw1f7u7i9mb6mj30g4094q38.jpg\" alt=\"\" /></p>\n\n<p><img src=\"https://ws4.sinaimg.cn/large/006bH5BKgw1f7u7gfg2oij30g4092aad.jpg\" alt=\"\" /></p>\n\n<p><img src=\"https://ws3.sinaimg.cn/large/006bH5BKgw1f7u7ioojb1j30g40923yu.jpg\" alt=\"\" /></p>\n\n<p><img src=\"https://ws1.sinaimg.cn/large/006bH5BKgw1f7u7hs6e92j30g4092t8y.jpg\" alt=\"\" /></p>","image":"/content/images/2016/09/418faf2ddd1f5c03bc9c2c93acbcab93.jpg","featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1473898791496,"created_by":1,"updated_at":1474242566946,"updated_by":1,"published_at":1473917132919,"published_by":1},{"id":65,"uuid":"3596cf79-d838-43af-9f5d-c87de225bd29","title":"随笔","slug":"sui-bi-3","markdown":"早上八点到晚上八点了,快看不下去了,边上这群人真是啊,不会累吗?这样想想当初如果选保研还挺划算,哈哈!\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=26619498&auto=1&height=66\"></iframe>\n\n这几天的收获还挺大的,基本弄清楚了js的执行环境,顺带还把相关的this、作用域链等知识一起解决了,不过这些本来就是一整块的知识。现在看着一段js代码,差不多都能把它在解释器里的样子看出来了,是不是疯了😝。\n\n今天Angular2最终的release版出了,本来想玩玩的,可惜最近没这么多精力了,而且看上去这玩意也不必1的时候简单,就先放放吧,等把手头的东西搞完。\n\n听了一个晚上优叔的歌,顺带原型链开了个头,给人力量啊!这声音!!!!还能写些什么呢?奥,最近的作息算是调整过来了,十点多一点睡,早上7点左右准时起,白天也不会困,挺好!\n\n就是这雨天有点讨厌,跑步的计划泡汤了!!!昨天淘宝了把伞,看样子伞到的时候雨就没了。🙄\n\n瞎扯这么多了,差不多了,军哥哥我们回去吧!\n\n","html":"<p>早上八点到晚上八点了,快看不下去了,边上这群人真是啊,不会累吗?这样想想当初如果选保研还挺划算,哈哈!</p>\n\n<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=26619498&auto=1&height=66\"></iframe>\n\n<p>这几天的收获还挺大的,基本弄清楚了js的执行环境,顺带还把相关的this、作用域链等知识一起解决了,不过这些本来就是一整块的知识。现在看着一段js代码,差不多都能把它在解释器里的样子看出来了,是不是疯了😝。</p>\n\n<p>今天Angular2最终的release版出了,本来想玩玩的,可惜最近没这么多精力了,而且看上去这玩意也不必1的时候简单,就先放放吧,等把手头的东西搞完。</p>\n\n<p>听了一个晚上优叔的歌,顺带原型链开了个头,给人力量啊!这声音!!!!还能写些什么呢?奥,最近的作息算是调整过来了,十点多一点睡,早上7点左右准时起,白天也不会困,挺好!</p>\n\n<p>就是这雨天有点讨厌,跑步的计划泡汤了!!!昨天淘宝了把伞,看样子伞到的时候雨就没了。🙄</p>\n\n<p>瞎扯这么多了,差不多了,军哥哥我们回去吧!</p>","image":"/content/images/2016/09/0b2833ae9667c4cb849b2cb45cd18ef8.jpg","featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1474027770176,"created_by":1,"updated_at":1474028765525,"updated_by":1,"published_at":1474028765526,"published_by":1},{"id":66,"uuid":"fd2bc94b-51ee-4120-beb0-12318e43f98c","title":"函数定义","slug":"han-shu-ding-yi","markdown":"之前转过一篇关于函数声明和函数表达式的文章,今天在研究对象系统的时候,反复地遇到了函数对象这个东西,而且在《可执行代码执行环境一文中》也略过了函数绑定那里部分,所以顺便就把ES5的函数定义这一章看了一遍,应该能够有比较大的收获,以下是规范加上一些个人理解。\n\n#### 语法\n\n```\n// 函数声明\nFunctionDeclaration : function Identifier ( FormalParameterList(可选) ) { FunctionBody } \n```\n\n```\n// 函数表达式 \nFunctionExpression : function Identifier(可选) ( FormalParameterList(可选) ) { FunctionBody } \n```\n\n```\nFormalParameterList : Identifier FormalParameterList , Identifier\n```\n\n```\nFunctionBody : SourceElements(可选)\n```\n\n#### 语义\n\n`FunctionDeclaration : function Identifier ( FormalParameterListopt ) { FunctionBody }`(函数声明)被如下实例化(绑定阶段):\n\n1. 依照下文函数对象创建一节,指定 FormalParameterListopt 为参数,指定 FunctionBody 为 body,创建一个新函数对象,返回结果。运行中的执行环境的 VariableEnvironment 传递作为 Scope。如果 FunctionDeclaration 包含在严格模式代码里或 FunctionBody 是严格模式代码,那么传递 true 为 Strict 标志。\n\n`FunctionExpression : function ( FormalParameterListopt ) { FunctionBody }`(匿名函数表达式)被如下评估(执行阶段):\n\n1. 同上,除了传递给 Scope 的是 LexicalEnvironment,而不是VariableEnvironment。\n\n这也就是为什么函数声明会影响变量环境而函数表达式不会的原因了吧!\n\n`FunctionExpression : function Identifier ( FormalParameterListopt ) { FunctionBody }`(具名函数表达式)被如下评估(执行阶段):\n\n1. 令 funcEnv 为以运行中执行环境的 Lexical Environment 为参数调用 NewDeclarativeEnvironment 的结果。\n2. 令 envRec 为 funcEnv 的环境记录项。\n3. 以 Identifier 的字符串值为参数调用 envRec 的具体方法 CreateImmutableBinding(N)。\n4. 令 closure 为依照下文函数对象创建一节,指定 FormalParameterListopt 为参数,指定 FunctionBody 为 body,创建一个新函数对象的结果。传递 funcEnv 为 Scope。如果 FunctionExpression 包含在严格模式代码 里或 FunctionBody 是严格模式代码,那么传递 true 为 Strict 标志。\n5. 以 Identifier 的字符串值和 closure 为参数调用 envRec 的具体方法 InitializeImmutableBinding(N,V)。\n6. 返回 closure。\n\n这个很重要,具名函数表达式执行前创建了一个新的词法环境在栈顶,然后把具名函数表达式创建的函数对象设置到这个新的词法环境上,并且是不变的。\n\n#### 严格模式的限制\n\n> 这在之前的严格模式一文中已经提到,但是结合《可执行代码和执行环境》一文,在绑定上下文的步骤中提到arguments会被函数声明和变量声明覆盖,这明显是一个错误,在严格模式下有以下的限制,个人觉得很有必要。而下面的第一条限制在绑定上下文的参数初始化中也是有提现的。\n\n* 如果严格模式 FunctionDeclaration 或 FunctionExpression 的 FormalParameterList 里出现多个相同 Identifier 值,那么这是个 SyntaxError。\n\n* 如果严格模式 FunctionDeclaration 或 FunctionExpression 的 FormalParameterList 里出现标识符 \"eval\" 或标识符 \"arguments\",那么这是个 SyntaxError。\n\n* 如果严格模式 FunctionDeclaration 或 FunctionExpression 的 Identifier 是标识符 \"eval\" 或标识符 \"arguments\",那么这是个 SyntaxError。\n\n#### 创建函数对象\n\n指定 FormalParameterList 为可选参数列表,指定 FunctionBody 为函数体,指定 Scope 为 词法环境 ,Strict 为布尔标记,按照如下步骤构建函数对象:\n\n1. 创建一个新的 ECMAScript 原生对象,令 F 为此对象。\n2. 依照 8.12 描述设定 F 的除 [[Get]] 以外的所有内部方法。\n3. 设定 F 的 [[Class]] 内部属性为 \"Function\"。\n4. 设定 F 的 [[Prototype]] 内部属性为 15.3.3.1 指定的标准内置 Function 对象的 prototype 属性。\n5. 依照 15.3.5.4 描述,设定 F 的 [[Get]] 内部属性。\n6. 依照 13.2.1 描述,设定 F 的 [[Call]] 内部属性。\n7. 依照 13.2.2 描述,设定 F 的 [[Construct]] 内部属性。\n8. 依照 15.3.5.3 描述,设定 F 的 [[HasInstance]] 内部属性。\n9. 设定 F 的 [[Scope]] 内部属性为 Scope 的值。\n10. 令 names 为一个列表容器,其中元素是以从左到右的文本顺序对应 FormalParameterList 的标识符的字符串。\n11. 设定 F 的 [[FormalParameters]] 内部属性为 names。\n12. 设定 F 的 [[Code]] 内部属性为 FunctionBody。\n13. 设定 F 的 [[Extensible]] 内部属性为 true。\n14. 令 len 为 FormalParameterList 指定的形式参数的个数。如果没有指定参数,则令 len 为 0。\n15. 以参数 \"length\",属性描述符 {[[Value]]: len, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false},false 调用 F 的 [[DefineOwnProperty]] 内部方法。\n16. 令 proto 为仿佛使用 new Object() 表达式创建新对象的结果,其中 Object 是标准内置构造器名。\n17. 以参数 \"constructor\", 属性描述符 {[[Value]]: F, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}, false 调用 proto 的 [[DefineOwnProperty]] 内部方法。\n18. 以参数 \"prototype\", 属性描述符 {[[Value]]: proto, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}, false 调用 F 的 [[DefineOwnProperty]] 内部方法。\n19. 如果 Strict 是 true,则\n 1. 令 thrower 为 [[ThrowTypeError]] 函数对象 (13.2.3)。\n 2. 以参数 \"caller\", 属性描述符 {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, false 调用 F 的 [[DefineOwnProperty]] 内部方法。\n 3. 以参数 \"caller\", 属性描述符 {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, false 调用 F 的 [[DefineOwnProperty]] 内部方法。\n20. 返回 F。\n\n> 每个函数都会自动创建一个 prototype 属性,以满足函数会被当作构造器的可能性。\n\n好像木有什么卵用,就是设置了一些内部属性。这就尴尬了!!!函数对象好像也没有什么特别的,除了指定了内部[[Prototype]]是Function.prototype,然后新建了一个内部对象,并设置了一个唯一的属性constructor指向函数本身,然后把这个内部对象设置到了函数对象的prototype属性上了。\n\n好像又有点意思了,因为和对象系统的内容接上了!!!可以滚去看对象系统了。😝\n\n\n\n","html":"<p>之前转过一篇关于函数声明和函数表达式的文章,今天在研究对象系统的时候,反复地遇到了函数对象这个东西,而且在《可执行代码执行环境一文中》也略过了函数绑定那里部分,所以顺便就把ES5的函数定义这一章看了一遍,应该能够有比较大的收获,以下是规范加上一些个人理解。</p>\n\n<h4 id=\"\">语法</h4>\n\n<pre><code>// 函数声明\nFunctionDeclaration : function Identifier ( FormalParameterList(可选) ) { FunctionBody } \n</code></pre>\n\n<pre><code>// 函数表达式 \nFunctionExpression : function Identifier(可选) ( FormalParameterList(可选) ) { FunctionBody } \n</code></pre>\n\n<pre><code>FormalParameterList : Identifier FormalParameterList , Identifier \n</code></pre>\n\n<pre><code>FunctionBody : SourceElements(可选) \n</code></pre>\n\n<h4 id=\"\">语义</h4>\n\n<p><code>FunctionDeclaration : function Identifier ( FormalParameterListopt ) { FunctionBody }</code>(函数声明)被如下实例化(绑定阶段):</p>\n\n<ol>\n<li>依照下文函数对象创建一节,指定 FormalParameterListopt 为参数,指定 FunctionBody 为 body,创建一个新函数对象,返回结果。运行中的执行环境的 VariableEnvironment 传递作为 Scope。如果 FunctionDeclaration 包含在严格模式代码里或 FunctionBody 是严格模式代码,那么传递 true 为 Strict 标志。</li>\n</ol>\n\n<p><code>FunctionExpression : function ( FormalParameterListopt ) { FunctionBody }</code>(匿名函数表达式)被如下评估(执行阶段):</p>\n\n<ol>\n<li>同上,除了传递给 Scope 的是 LexicalEnvironment,而不是VariableEnvironment。</li>\n</ol>\n\n<p>这也就是为什么函数声明会影响变量环境而函数表达式不会的原因了吧!</p>\n\n<p><code>FunctionExpression : function Identifier ( FormalParameterListopt ) { FunctionBody }</code>(具名函数表达式)被如下评估(执行阶段):</p>\n\n<ol>\n<li>令 funcEnv 为以运行中执行环境的 Lexical Environment 为参数调用 NewDeclarativeEnvironment 的结果。 </li>\n<li>令 envRec 为 funcEnv 的环境记录项。 </li>\n<li>以 Identifier 的字符串值为参数调用 envRec 的具体方法 CreateImmutableBinding(N)。 </li>\n<li>令 closure 为依照下文函数对象创建一节,指定 FormalParameterListopt 为参数,指定 FunctionBody 为 body,创建一个新函数对象的结果。传递 funcEnv 为 Scope。如果 FunctionExpression 包含在严格模式代码 里或 FunctionBody 是严格模式代码,那么传递 true 为 Strict 标志。 </li>\n<li>以 Identifier 的字符串值和 closure 为参数调用 envRec 的具体方法 InitializeImmutableBinding(N,V)。 </li>\n<li>返回 closure。</li>\n</ol>\n\n<p>这个很重要,具名函数表达式执行前创建了一个新的词法环境在栈顶,然后把具名函数表达式创建的函数对象设置到这个新的词法环境上,并且是不变的。</p>\n\n<h4 id=\"\">严格模式的限制</h4>\n\n<blockquote>\n <p>这在之前的严格模式一文中已经提到,但是结合《可执行代码和执行环境》一文,在绑定上下文的步骤中提到arguments会被函数声明和变量声明覆盖,这明显是一个错误,在严格模式下有以下的限制,个人觉得很有必要。而下面的第一条限制在绑定上下文的参数初始化中也是有提现的。</p>\n</blockquote>\n\n<ul>\n<li><p>如果严格模式 FunctionDeclaration 或 FunctionExpression 的 FormalParameterList 里出现多个相同 Identifier 值,那么这是个 SyntaxError。</p></li>\n<li><p>如果严格模式 FunctionDeclaration 或 FunctionExpression 的 FormalParameterList 里出现标识符 \"eval\" 或标识符 \"arguments\",那么这是个 SyntaxError。</p></li>\n<li><p>如果严格模式 FunctionDeclaration 或 FunctionExpression 的 Identifier 是标识符 \"eval\" 或标识符 \"arguments\",那么这是个 SyntaxError。</p></li>\n</ul>\n\n<h4 id=\"\">创建函数对象</h4>\n\n<p>指定 FormalParameterList 为可选参数列表,指定 FunctionBody 为函数体,指定 Scope 为 词法环境 ,Strict 为布尔标记,按照如下步骤构建函数对象:</p>\n\n<ol>\n<li>创建一个新的 ECMAScript 原生对象,令 F 为此对象。 </li>\n<li>依照 8.12 描述设定 F 的除 [[Get]] 以外的所有内部方法。 </li>\n<li>设定 F 的 [[Class]] 内部属性为 \"Function\"。 </li>\n<li>设定 F 的 [[Prototype]] 内部属性为 15.3.3.1 指定的标准内置 Function 对象的 prototype 属性。 </li>\n<li>依照 15.3.5.4 描述,设定 F 的 [[Get]] 内部属性。 </li>\n<li>依照 13.2.1 描述,设定 F 的 [[Call]] 内部属性。 </li>\n<li>依照 13.2.2 描述,设定 F 的 [[Construct]] 内部属性。 </li>\n<li>依照 15.3.5.3 描述,设定 F 的 [[HasInstance]] 内部属性。 </li>\n<li>设定 F 的 [[Scope]] 内部属性为 Scope 的值。 </li>\n<li>令 names 为一个列表容器,其中元素是以从左到右的文本顺序对应 FormalParameterList 的标识符的字符串。 </li>\n<li>设定 F 的 [[FormalParameters]] 内部属性为 names。 </li>\n<li>设定 F 的 [[Code]] 内部属性为 FunctionBody。 </li>\n<li>设定 F 的 [[Extensible]] 内部属性为 true。 </li>\n<li>令 len 为 FormalParameterList 指定的形式参数的个数。如果没有指定参数,则令 len 为 0。 </li>\n<li>以参数 \"length\",属性描述符 {[[Value]]: len, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false},false 调用 F 的 [[DefineOwnProperty]] 内部方法。 </li>\n<li>令 proto 为仿佛使用 new Object() 表达式创建新对象的结果,其中 Object 是标准内置构造器名。 </li>\n<li>以参数 \"constructor\", 属性描述符 {[[Value]]: F, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}, false 调用 proto 的 [[DefineOwnProperty]] 内部方法。 </li>\n<li>以参数 \"prototype\", 属性描述符 {[[Value]]: proto, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}, false 调用 F 的 [[DefineOwnProperty]] 内部方法。 </li>\n<li>如果 Strict 是 true,则 <br />\n<ol><li>令 thrower 为 [[ThrowTypeError]] 函数对象 (13.2.3)。</li>\n<li>以参数 \"caller\", 属性描述符 {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, false 调用 F 的 [[DefineOwnProperty]] 内部方法。</li>\n<li>以参数 \"caller\", 属性描述符 {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, false 调用 F 的 [[DefineOwnProperty]] 内部方法。</li></ol></li>\n<li>返回 F。</li>\n</ol>\n\n<blockquote>\n <p>每个函数都会自动创建一个 prototype 属性,以满足函数会被当作构造器的可能性。</p>\n</blockquote>\n\n<p>好像木有什么卵用,就是设置了一些内部属性。这就尴尬了!!!函数对象好像也没有什么特别的,除了指定了内部[[Prototype]]是Function.prototype,然后新建了一个内部对象,并设置了一个唯一的属性constructor指向函数本身,然后把这个内部对象设置到了函数对象的prototype属性上了。</p>\n\n<p>好像又有点意思了,因为和对象系统的内容接上了!!!可以滚去看对象系统了。😝</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1474087459334,"created_by":1,"updated_at":1474093338204,"updated_by":1,"published_at":1474092935765,"published_by":1},{"id":67,"uuid":"e8ef7472-1074-424f-a561-adda4bd46568","title":"线香花火","slug":"xian-xiang-hua-huo","markdown":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=829845&auto=1&height=66\"></iframe>\n\n原来线香花火不只是首歌,它就是我们在日剧里经常看到的手上拿着玩的烟火啊![流焰飞花、夏夜独语:日本线香花火的一生](http://www.vccoo.com/v/bb3f0e?source=rss)\n\n据说线香花火是比壁咚更加浪漫的哦!想想也是。\n\n《乐与路》中小葵这段现在在我脑中还是很清晰呢!!!真的很不错哇!!\n\n![](https://ws3.sinaimg.cn/large/006bH5BKgw1f7y1po0h2ij30go090aao.jpg)\n","html":"<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" width=330 height=86 src=\"https://music.163.com/outchain/player?type=2&id=829845&auto=1&height=66\"></iframe>\n\n<p>原来线香花火不只是首歌,它就是我们在日剧里经常看到的手上拿着玩的烟火啊!<a href=\"http://www.vccoo.com/v/bb3f0e?source=rss\">流焰飞花、夏夜独语:日本线香花火的一生</a></p>\n\n<p>据说线香花火是比壁咚更加浪漫的哦!想想也是。</p>\n\n<p>《乐与路》中小葵这段现在在我脑中还是很清晰呢!!!真的很不错哇!!</p>\n\n<p><img src=\"https://ws3.sinaimg.cn/large/006bH5BKgw1f7y1po0h2ij30go090aao.jpg\" alt=\"\" /></p>","image":"/content/images/2016/09/5c2db3db9a89833d71a32c7c215f7c2b.jpg","featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1474095946336,"created_by":1,"updated_at":1474242547521,"updated_by":1,"published_at":1474204036391,"published_by":1},{"id":68,"uuid":"f1568e71-fb89-4186-b2c1-269e724f2e0a","title":"编译原理入门——绪论","slug":"bian-yi-yuan-li-ru-men-xu-lun","markdown":"今天开始重头开始看es5的文档看到第五章,句法和词法部分就懵逼了,什么上下文无关文法??。之前看的第十章和第十三章都不会有这种感觉。所以就去边上的书架上拿来了编译原理的书,了解下,至少能够把es5那几章看懂,毕竟这对于更好地理解语言有很大的好处。不过不会进行很深入的研究,毕竟现在的重点是js。(虽然在计算机学院,但因为不是计算机专业,这门课是不学的)\n\n#### 发展\n\n> 机器语言(二进制)--->汇编语言(助记符)--->高级语言\n\n计算机语言的发展类似于人类语言的进化,而古代人要看懂现代的文字就需要将现代的语言翻译成古代语言,就类似于编译干的事\n\n#### 高级语言的执行\n\n###### 编译型语言程序:\n\n1. 编译阶段:高级语言-(编译)->机器语言或汇编语言\n2. 汇编阶段:如果高级语言编译的目标程序是汇编程序,那么还有一个汇编阶段把汇编程序变换成机器语言目标程序\n3. 运行阶段\n\n###### 解释型语言程序\n\n无需翻译成目标程序,直接逐条运行。\n\n#### 编译阶段简述\n\n编译阶段可以细分为词法分析、语法分析、语义分析、中间代码生成、代码优化、和目标代码生成六个阶段。\n\n1. 词法分析\n\n 把源程序字符串变成单词符号流,单词用同一长度的标准形式表示。简单来说也就是从左到右一个一个的读入源程序,识别一个单词或符号,并进行归类。类似分析汉语语法,例如,我们输入一句话,我是中国人。然后进行分析,读入我可以识别这个单词,为名词,我就归到名词这类;读入是,我们归到动词这类;读入中字,不能构成一个可识别的东东,接着读入,中国人,为名词类。而在计算机中,例如我们写的某行代码,var sum = first + count*10 ; 进行分类,1保留字:var;标识符:2 sum 3 first 4 count 乘号:5*;\n\n2. 语法分析 \n\n 在词法分析的基础上,根据语言的语法规则把单词符号流分解成各类语法单位,如“程序”,“语句”,“表达式”等。例如通过上面的单词“我”“中国人”“是”,可以构成两种形式的语句,我是中国人,中国人是我,都符合<主语><谓语>这样的语法。\n\n3. 语义分析\n\n 审查源程序是否有语义的错误,当不符合语言规范的时候,程序就会报错。例如上面的“我是中国人”和“中国人是我”两个句子,显然后面的中国人是我就不符合语义了。\n\n4. 生成中间代码\n\n 在进行了语法和语义的分析工作之后,编译程序将源程序变成了一种内部表示形式,这种内部表示形式叫做中间语言或中间代码。(比较抽象些),生成一种介于源码和机器语言的形式,常见的有四元式、三元式、逆波兰式。\n\n5. 代码优化\n\n 这个阶段是对前阶段的中间代码进行变换或改造,目的是使生成的目标代码更为高效,即节省时间和空间。\n\n6. 目标代码生成\n\n 也就是把优化后的中间代码变换成机器代码或汇编代码。这是工作的最后阶段,与硬件系统结构,指令系统相关,涉及到硬件系统功能部件运用、机器指令的选择等等。\n\n> 造表和出错处理环绕于这各个阶段!\n\n参考资料:\n\n1. [编译原理学习周入门教程](http://blog.csdn.net/lovesummerforever/article/category/1438807)\n2. 《编译原理(第三版)》科学出版社","html":"<p>今天开始重头开始看es5的文档看到第五章,句法和词法部分就懵逼了,什么上下文无关文法??。之前看的第十章和第十三章都不会有这种感觉。所以就去边上的书架上拿来了编译原理的书,了解下,至少能够把es5那几章看懂,毕竟这对于更好地理解语言有很大的好处。不过不会进行很深入的研究,毕竟现在的重点是js。(虽然在计算机学院,但因为不是计算机专业,这门课是不学的)</p>\n\n<h4 id=\"\">发展</h4>\n\n<blockquote>\n <p>机器语言(二进制)--->汇编语言(助记符)--->高级语言</p>\n</blockquote>\n\n<p>计算机语言的发展类似于人类语言的进化,而古代人要看懂现代的文字就需要将现代的语言翻译成古代语言,就类似于编译干的事</p>\n\n<h4 id=\"\">高级语言的执行</h4>\n\n<h6 id=\"\">编译型语言程序:</h6>\n\n<ol>\n<li>编译阶段:高级语言-(编译)->机器语言或汇编语言 </li>\n<li>汇编阶段:如果高级语言编译的目标程序是汇编程序,那么还有一个汇编阶段把汇编程序变换成机器语言目标程序 </li>\n<li>运行阶段</li>\n</ol>\n\n<h6 id=\"\">解释型语言程序</h6>\n\n<p>无需翻译成目标程序,直接逐条运行。</p>\n\n<h4 id=\"\">编译阶段简述</h4>\n\n<p>编译阶段可以细分为词法分析、语法分析、语义分析、中间代码生成、代码优化、和目标代码生成六个阶段。</p>\n\n<ol>\n<li><p>词法分析</p>\n\n<p>把源程序字符串变成单词符号流,单词用同一长度的标准形式表示。简单来说也就是从左到右一个一个的读入源程序,识别一个单词或符号,并进行归类。类似分析汉语语法,例如,我们输入一句话,我是中国人。然后进行分析,读入我可以识别这个单词,为名词,我就归到名词这类;读入是,我们归到动词这类;读入中字,不能构成一个可识别的东东,接着读入,中国人,为名词类。而在计算机中,例如我们写的某行代码,var sum = first + count<em>10 ; 进行分类,1保留字:var;标识符:2 sum 3 first 4 count 乘号:5</em>;</p></li>\n<li><p>语法分析 </p>\n\n<p>在词法分析的基础上,根据语言的语法规则把单词符号流分解成各类语法单位,如“程序”,“语句”,“表达式”等。例如通过上面的单词“我”“中国人”“是”,可以构成两种形式的语句,我是中国人,中国人是我,都符合<主语><谓语>这样的语法。</p></li>\n<li><p>语义分析</p>\n\n<p>审查源程序是否有语义的错误,当不符合语言规范的时候,程序就会报错。例如上面的“我是中国人”和“中国人是我”两个句子,显然后面的中国人是我就不符合语义了。</p></li>\n<li><p>生成中间代码</p>\n\n<p>在进行了语法和语义的分析工作之后,编译程序将源程序变成了一种内部表示形式,这种内部表示形式叫做中间语言或中间代码。(比较抽象些),生成一种介于源码和机器语言的形式,常见的有四元式、三元式、逆波兰式。</p></li>\n<li><p>代码优化</p>\n\n<p>这个阶段是对前阶段的中间代码进行变换或改造,目的是使生成的目标代码更为高效,即节省时间和空间。</p></li>\n<li><p>目标代码生成</p>\n\n<p>也就是把优化后的中间代码变换成机器代码或汇编代码。这是工作的最后阶段,与硬件系统结构,指令系统相关,涉及到硬件系统功能部件运用、机器指令的选择等等。</p></li>\n</ol>\n\n<blockquote>\n <p>造表和出错处理环绕于这各个阶段!</p>\n</blockquote>\n\n<p>参考资料:</p>\n\n<ol>\n<li><a href=\"http://blog.csdn.net/lovesummerforever/article/category/1438807\">编译原理学习周入门教程</a> </li>\n<li>《编译原理(第三版)》科学出版社</li>\n</ol>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1474175904212,"created_by":1,"updated_at":1474196827403,"updated_by":1,"published_at":1474176633881,"published_by":1},{"id":69,"uuid":"9ebaabb5-790f-43f3-a691-dacb4548c53d","title":"编译原理入门——词法分析","slug":"bian-yi-yuan-li-ru-men-ci-fa-fen-xi","markdown":"词法分析是编译的第一个阶段,其任务是:从左至右逐个字符地对源程序进行扫描,产生一个个单词符号,把字符串形式的源程序改造成单词符号形式的中间程序。执行词法分析的程序称为词法分析程序,也称为词法分析器或扫描器。词法分析器的功能是输入源程序,输出单词符号。\n\n#### 两种处理结构\n\n1. 把词法分析作为主程序(图a)。将词法分析作为独立的过程,由词法分析将字符串形式的源程序改造成单词符号串的形式的中间程序,并以这个中间程序作为语法分析程序的输入。\n2. 把词法分析作为语法分析程序调用的子程序(图b)。在进行语法分析时,每当语法分析程序需要一个单词时,便调用词法分析程序,词法分析程序每一次调用便从字符串源程序中识别出一个单词交给语法分析程序。(通常采用)\n\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f7xwh3z01xj311s0aajsz.jpg)\n\n#### 设计方法\n\n###### 单词符号分类\n\n单词符号是一次词法分析的结果,也是程序语言的基本语法单位,具有确定的语法意义。它通常可以分为下面五种:\n\n- 保留字:if、while、switch等,限制作为标识符;\n- 标识符:变量、函数等;\n- 常数:各种类型的常数;\n- 运算符:加减乘除大于小于等;\n- 界符:逗号、分好、括号这种。\n\n> 一个语言的保留字、运算符、界符数量是确定,标识符和常量数量则是不确定。\n\n###### 输出形式\n\n词法分析程序的输出形式通常是如下的二元式:\n\n```\n(单词种别,单词自身的值)\n```\n\n单词种别顾名思义是单词的种类,一个语言的单词划分种类、分为几类、如何编码都属于技术性问题,主要取决于处理上的方便。通常的做法是让一类单词对应一个整数码。通常情况下,保留字可以一字一种也可以所有一种;标识符和常量统一归为一种;运算符和界符则是一字一种。\n\n单词自身的值是编译中其他阶段需要的信息。如果单词是一字一种的,那么种别就能代表单词自身的值;否则的话给出单词种别的同属还需要给出单词自身的值。标识符自身的值就是标识符字符串,常量自身的值则是常量对应的二进制数值。\n\n###### 状态转换图\n\n初看状态转换图,感觉挺像正则表达式的图形式的。学习下来确实也差不多,因为正则其实也是按一条条规则进行匹配和词法这里的单词匹配其实挺像的。\n\n看几个例子就明白了,下图中a是标识符的状态转换图,首先0是初始状态,0到1匹配字母,因为标识符不能以数字开头哇。然后自己指向自己的箭头表示重复,直到遇到不是字母或数字的字符,匹配结束,结束用两个圈表示。结束符上的*表示的是退回最后多读的一个字符。其它两个图也可以用这种方式去理解。\n\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f7xxu3990kj310s0nqq6m.jpg)\n\n#### 正规表达式、有限自动机理论\n\n###### 正规表达式和正规集\n\n正规表达式同状态转换图,只是相对于状态转换图来说,它是一种形式化的表示法,可以表示单词符号的结构,从而精确地定义单词符号集。\n\n还是标志符的例子,假设字母用letter表示,数字用dight表示,则可得正规表达式:\n\n```\nletter (letter|dight)*\n```\n\nletter表示了标识符的第一个字符必为字母,而*号表示字母或数字重复0到n词。符合这个正规式的字符集就是这个正规式所表示的正规集。\n\n当然也可以通过递归定义,推出一些正规式的性质,这里略过了!\n\n###### 有限自动机 \n\n 能准确的识别正规集,能识别正规文法所定义的语言和正规式表示的集合,也为词法分析程序的自动构造寻找特殊的方法和工具。有限自动机是更加一般化的状态转换图,分为以下两种:\n\n1. 确定有限自动机(DFA)\n\n 一个确定的有穷自动机M是一个五元组:M=(K, ∑,f,S,Z)其中,\n 1. K是一个有穷集,他的每个元素称为一种状态。\n 2. ∑是一个有穷字母表,他的每个元素称为一个输入符号,所以∑称为输入符号表。\n 3. f是转换函数,是KX∑-->K 上的映像,例如f(ki,a)=kj这就意味着,当前状态为k,输入字符a后,将转换到下一状态kj,我们把kj称为ki的一个后继状态;\n 4. S属于K,是唯一的一个出态。\n 5. Z属于K,是一个终态,终态也称为可接受状态或结束状态。\n\n还是放个别人那截来的例子吧:\n\n![](https://ws1.sinaimg.cn/large/006bH5BKgw1f7xzwnloetj3118114784.jpg)\n\n2. 非确定有限自动机(NFA)\n 1. 是一个子集映像。\n 2. S属于K是一个非空出态集;\n 3. Z属于K是一个终态集。\n\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f7y00e152aj30va0x876b.jpg)\n\n两个区别:第一,NFA有若干的初始状态,DFA只有一个;第二,NFA状态转换函数不是单值映射。\n\n我们可以用正规表达式来识别单词,再用有限自动机来识别正规表达式,然后就可以做点有趣的事了!书上还有一章讲到了从正规表达式到有限自动机的转换,这里不展开了!\n\n#### 说了这么多的目的\n\n###### 自动生成词法分析器\n\n说了这么多,正规表达式和有限自动机的理论,归根到底还是程序员们偷懒搞出来的。借助这些理论,自动生成词法分析器成为了可能。只要给出某高级语言各类单词的词法结构的正规表达式以及识别各类单词时词法分析程序应采用的语义动作,系统就可以自动产生此高级程序的词法分析程序。所生成的词法分析程序就如同一台有限自动机,可以用来识别和分析单词符号!!!\n\n参考资料:\n\n1. 《编译原理(第三版)》科学出版社\n2. [编译原理学习周入门教程](http://blog.csdn.net/lovesummerforever/article/category/1438807)\n3. 传说中好像还有龙虎鲸三本经典,然而图书馆并没有🙄","html":"<p>词法分析是编译的第一个阶段,其任务是:从左至右逐个字符地对源程序进行扫描,产生一个个单词符号,把字符串形式的源程序改造成单词符号形式的中间程序。执行词法分析的程序称为词法分析程序,也称为词法分析器或扫描器。词法分析器的功能是输入源程序,输出单词符号。</p>\n\n<h4 id=\"\">两种处理结构</h4>\n\n<ol>\n<li>把词法分析作为主程序(图a)。将词法分析作为独立的过程,由词法分析将字符串形式的源程序改造成单词符号串的形式的中间程序,并以这个中间程序作为语法分析程序的输入。 </li>\n<li>把词法分析作为语法分析程序调用的子程序(图b)。在进行语法分析时,每当语法分析程序需要一个单词时,便调用词法分析程序,词法分析程序每一次调用便从字符串源程序中识别出一个单词交给语法分析程序。(通常采用)</li>\n</ol>\n\n<p><img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f7xwh3z01xj311s0aajsz.jpg\" alt=\"\" /></p>\n\n<h4 id=\"\">设计方法</h4>\n\n<h6 id=\"\">单词符号分类</h6>\n\n<p>单词符号是一次词法分析的结果,也是程序语言的基本语法单位,具有确定的语法意义。它通常可以分为下面五种:</p>\n\n<ul>\n<li>保留字:if、while、switch等,限制作为标识符;</li>\n<li>标识符:变量、函数等;</li>\n<li>常数:各种类型的常数;</li>\n<li>运算符:加减乘除大于小于等;</li>\n<li>界符:逗号、分好、括号这种。</li>\n</ul>\n\n<blockquote>\n <p>一个语言的保留字、运算符、界符数量是确定,标识符和常量数量则是不确定。</p>\n</blockquote>\n\n<h6 id=\"\">输出形式</h6>\n\n<p>词法分析程序的输出形式通常是如下的二元式:</p>\n\n<pre><code>(单词种别,单词自身的值)\n</code></pre>\n\n<p>单词种别顾名思义是单词的种类,一个语言的单词划分种类、分为几类、如何编码都属于技术性问题,主要取决于处理上的方便。通常的做法是让一类单词对应一个整数码。通常情况下,保留字可以一字一种也可以所有一种;标识符和常量统一归为一种;运算符和界符则是一字一种。</p>\n\n<p>单词自身的值是编译中其他阶段需要的信息。如果单词是一字一种的,那么种别就能代表单词自身的值;否则的话给出单词种别的同属还需要给出单词自身的值。标识符自身的值就是标识符字符串,常量自身的值则是常量对应的二进制数值。</p>\n\n<h6 id=\"\">状态转换图</h6>\n\n<p>初看状态转换图,感觉挺像正则表达式的图形式的。学习下来确实也差不多,因为正则其实也是按一条条规则进行匹配和词法这里的单词匹配其实挺像的。</p>\n\n<p>看几个例子就明白了,下图中a是标识符的状态转换图,首先0是初始状态,0到1匹配字母,因为标识符不能以数字开头哇。然后自己指向自己的箭头表示重复,直到遇到不是字母或数字的字符,匹配结束,结束用两个圈表示。结束符上的*表示的是退回最后多读的一个字符。其它两个图也可以用这种方式去理解。</p>\n\n<p><img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f7xxu3990kj310s0nqq6m.jpg\" alt=\"\" /></p>\n\n<h4 id=\"\">正规表达式、有限自动机理论</h4>\n\n<h6 id=\"\">正规表达式和正规集</h6>\n\n<p>正规表达式同状态转换图,只是相对于状态转换图来说,它是一种形式化的表示法,可以表示单词符号的结构,从而精确地定义单词符号集。</p>\n\n<p>还是标志符的例子,假设字母用letter表示,数字用dight表示,则可得正规表达式:</p>\n\n<pre><code>letter (letter|dight)* \n</code></pre>\n\n<p>letter表示了标识符的第一个字符必为字母,而*号表示字母或数字重复0到n词。符合这个正规式的字符集就是这个正规式所表示的正规集。</p>\n\n<p>当然也可以通过递归定义,推出一些正规式的性质,这里略过了!</p>\n\n<h6 id=\"\">有限自动机</h6>\n\n<p>能准确的识别正规集,能识别正规文法所定义的语言和正规式表示的集合,也为词法分析程序的自动构造寻找特殊的方法和工具。有限自动机是更加一般化的状态转换图,分为以下两种:</p>\n\n<ol>\n<li><p>确定有限自动机(DFA)</p>\n\n<p>一个确定的有穷自动机M是一个五元组:M=(K, ∑,f,S,Z)其中,</p>\n\n<ol><li>K是一个有穷集,他的每个元素称为一种状态。</li>\n<li>∑是一个有穷字母表,他的每个元素称为一个输入符号,所以∑称为输入符号表。</li>\n<li>f是转换函数,是KX∑-->K 上的映像,例如f(ki,a)=kj这就意味着,当前状态为k,输入字符a后,将转换到下一状态kj,我们把kj称为ki的一个后继状态;</li>\n<li>S属于K,是唯一的一个出态。</li>\n<li>Z属于K,是一个终态,终态也称为可接受状态或结束状态。</li></ol></li>\n</ol>\n\n<p>还是放个别人那截来的例子吧:</p>\n\n<p><img src=\"https://ws1.sinaimg.cn/large/006bH5BKgw1f7xzwnloetj3118114784.jpg\" alt=\"\" /></p>\n\n<ol>\n<li>非确定有限自动机(NFA) <br />\n<ol><li>是一个子集映像。</li>\n<li>S属于K是一个非空出态集;</li>\n<li>Z属于K是一个终态集。</li></ol></li>\n</ol>\n\n<p><img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f7y00e152aj30va0x876b.jpg\" alt=\"\" /></p>\n\n<p>两个区别:第一,NFA有若干的初始状态,DFA只有一个;第二,NFA状态转换函数不是单值映射。</p>\n\n<p>我们可以用正规表达式来识别单词,再用有限自动机来识别正规表达式,然后就可以做点有趣的事了!书上还有一章讲到了从正规表达式到有限自动机的转换,这里不展开了!</p>\n\n<h4 id=\"\">说了这么多的目的</h4>\n\n<h6 id=\"\">自动生成词法分析器</h6>\n\n<p>说了这么多,正规表达式和有限自动机的理论,归根到底还是程序员们偷懒搞出来的。借助这些理论,自动生成词法分析器成为了可能。只要给出某高级语言各类单词的词法结构的正规表达式以及识别各类单词时词法分析程序应采用的语义动作,系统就可以自动产生此高级程序的词法分析程序。所生成的词法分析程序就如同一台有限自动机,可以用来识别和分析单词符号!!!</p>\n\n<p>参考资料:</p>\n\n<ol>\n<li>《编译原理(第三版)》科学出版社 </li>\n<li><a href=\"http://blog.csdn.net/lovesummerforever/article/category/1438807\">编译原理学习周入门教程</a> </li>\n<li>传说中好像还有龙虎鲸三本经典,然而图书馆并没有🙄</li>\n</ol>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1474187112076,"created_by":1,"updated_at":1474201206584,"updated_by":1,"published_at":1474201046318,"published_by":1},{"id":70,"uuid":"5d2e6a22-c09d-4368-b67a-c37508f5dbcc","title":"爱情洗牌","slug":"ai-qing-xi-pai","markdown":"这篇本来应该是昨晚写的,但是昨晚去和搬出去的两个室友吃了顿饭。大家聚在一起的时间越来越少了,所以要珍惜这样不多的机会了。\n\n吃的时候聊的也都是以前的一些事,相互也调侃一下各自的近况,当然我是被调侃最多的😂。反正三个大男人就那么做了好久,都没怎么吃。吃完走到图书馆,我就和他们分开了。虽然不是再见,但是我们的人生已经开始都慢慢划出了一条新的线,都为对方高兴吧!\n\n好了,扯了点有用没用的,开始写这部刚看完的日记《爱情洗牌》。这也是个脑洞挺大的故事,但是如往常的日剧一样,脑洞给我们带来了对于生活的思考和感动。\n\n大致讲的是同住高级公寓同一层的人,一天同时在电梯被困,从此相互认识。在相互的交谈中,大家对于自己现在的爱情都觉得不满意,于是谷原叔演的医生就提议了爱情洗牌。大致就是每人每周抽牌分配自己的恋人,然后找到真爱或者回归原来的恋人。在经过两轮的爱情洗牌之后(过程还是挺戏剧性的,编剧任性),宏爷饰演的启和爱爱在了一起,绵羊则和吉高妹子在了一起,谕吉和芽衣在了一起。配对成功了三对,真是不可思议!但是确实最后每个人都跟更加适合自己的人在一起了。\n\n我们的经历也是如此吧,爱情会经历像启和芽衣那样的从闪闪发光到渐渐被抹去光芒;也会有出于爱爱对谕吉的那种同情;但是真正能够支撑相互走下去的是什么,应该不是第一眼的闪闪发光、也不是一时的同情,而是相互理解相互依赖吧!\n\n哎,这些不该从我嘴里出来的!毕竟昨天被莫名其妙地冠名了‘国民负心汉’🙄。开玩笑的!\n\n最后,编剧给了我们一个意料之中的甜蜜结局!下面是看的时候的一些截图,大多数是最后一集的甜蜜镜头,还有几张是笑喷我的,over,新的一天开始!\n\n可爱的吉高妹子😁:\n![](https://ws3.sinaimg.cn/large/006bH5BKgw1f7zq2l6umfj30zk0k0dp1.jpg)\n\n绵羊调皮了:\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f7zq2fkzguj30zk0k0q7i.jpg)\n\n宏爷乱入:\n![](https://ws1.sinaimg.cn/large/006bH5BKgw1f7zq2ky451j30zk0k0n5w.jpg)\n\n心疼宏爷:\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f7zq2jabbpj30zk0k0jxa.jpg)\n\n经典的画面:\n![](https://ws3.sinaimg.cn/large/006bH5BKgw1f7zq2nw0dqj30zk0k0thy.jpg)\n\n经典的画面:\n![](https://ws4.sinaimg.cn/large/006bH5BKgw1f7zq2ptsjuj30zk0k0k0o.jpg)\n\n闪闪发光的启:\n![](https://ws2.sinaimg.cn/large/006bH5BKgw1f7zq2qbdnlj30zk0k0n8w.jpg)\n\n全剧终:\n![](https://ws3.sinaimg.cn/large/006bH5BKgw1f7zq2iu7jqj30zk0k0wjg.jpg)","html":"<p>这篇本来应该是昨晚写的,但是昨晚去和搬出去的两个室友吃了顿饭。大家聚在一起的时间越来越少了,所以要珍惜这样不多的机会了。</p>\n\n<p>吃的时候聊的也都是以前的一些事,相互也调侃一下各自的近况,当然我是被调侃最多的😂。反正三个大男人就那么做了好久,都没怎么吃。吃完走到图书馆,我就和他们分开了。虽然不是再见,但是我们的人生已经开始都慢慢划出了一条新的线,都为对方高兴吧!</p>\n\n<p>好了,扯了点有用没用的,开始写这部刚看完的日记《爱情洗牌》。这也是个脑洞挺大的故事,但是如往常的日剧一样,脑洞给我们带来了对于生活的思考和感动。</p>\n\n<p>大致讲的是同住高级公寓同一层的人,一天同时在电梯被困,从此相互认识。在相互的交谈中,大家对于自己现在的爱情都觉得不满意,于是谷原叔演的医生就提议了爱情洗牌。大致就是每人每周抽牌分配自己的恋人,然后找到真爱或者回归原来的恋人。在经过两轮的爱情洗牌之后(过程还是挺戏剧性的,编剧任性),宏爷饰演的启和爱爱在了一起,绵羊则和吉高妹子在了一起,谕吉和芽衣在了一起。配对成功了三对,真是不可思议!但是确实最后每个人都跟更加适合自己的人在一起了。</p>\n\n<p>我们的经历也是如此吧,爱情会经历像启和芽衣那样的从闪闪发光到渐渐被抹去光芒;也会有出于爱爱对谕吉的那种同情;但是真正能够支撑相互走下去的是什么,应该不是第一眼的闪闪发光、也不是一时的同情,而是相互理解相互依赖吧!</p>\n\n<p>哎,这些不该从我嘴里出来的!毕竟昨天被莫名其妙地冠名了‘国民负心汉’🙄。开玩笑的!</p>\n\n<p>最后,编剧给了我们一个意料之中的甜蜜结局!下面是看的时候的一些截图,大多数是最后一集的甜蜜镜头,还有几张是笑喷我的,over,新的一天开始!</p>\n\n<p>可爱的吉高妹子😁:\n<img src=\"https://ws3.sinaimg.cn/large/006bH5BKgw1f7zq2l6umfj30zk0k0dp1.jpg\" alt=\"\" /></p>\n\n<p>绵羊调皮了:\n<img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f7zq2fkzguj30zk0k0q7i.jpg\" alt=\"\" /></p>\n\n<p>宏爷乱入:\n<img src=\"https://ws1.sinaimg.cn/large/006bH5BKgw1f7zq2ky451j30zk0k0n5w.jpg\" alt=\"\" /></p>\n\n<p>心疼宏爷:\n<img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f7zq2jabbpj30zk0k0jxa.jpg\" alt=\"\" /></p>\n\n<p>经典的画面:\n<img src=\"https://ws3.sinaimg.cn/large/006bH5BKgw1f7zq2nw0dqj30zk0k0thy.jpg\" alt=\"\" /></p>\n\n<p>经典的画面:\n<img src=\"https://ws4.sinaimg.cn/large/006bH5BKgw1f7zq2ptsjuj30zk0k0k0o.jpg\" alt=\"\" /></p>\n\n<p>闪闪发光的启:\n<img src=\"https://ws2.sinaimg.cn/large/006bH5BKgw1f7zq2qbdnlj30zk0k0n8w.jpg\" alt=\"\" /></p>\n\n<p>全剧终:\n<img src=\"https://ws3.sinaimg.cn/large/006bH5BKgw1f7zq2iu7jqj30zk0k0wjg.jpg\" alt=\"\" /></p>","image":"/content/images/2016/09/697679ec54e736d16115e77d98504fc2d562693d.png","featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1474242590630,"created_by":1,"updated_at":1474330146640,"updated_by":1,"published_at":1474330075920,"published_by":1},{"id":71,"uuid":"6d4d2299-9faf-4645-982c-e850e9386744","title":"编译原理入门——语法分析","slug":"bian-yi-yuan-li-ru-men-yu-fa-fen-xi","markdown":"语法分析是编译过程的核心部分,其基本任务是根据语言的语法规则进行语法分析,若不存在语法错误则给出正确的语法结构并为语义分析和代码生成做准备。在描述程序语言语法结构时,需借助上下文无关文法,而文法是描述程序语言的依据。\n\n#### 文法和语言\n\n> 话外音:这玩意就是读es5文档第五章把我卡住的东西,要好好看看哇!\n\n文法是程序语言的生成系统,而自动机则是程序语言的识别系统:用文法可以精确地定义一个语言,并依据该文法构造出识别这个语言的自动机。如:程序语言的词法可以用正规文法描述;语法可以用上下文无关文法描述;语义则需要上下午有关文法描述。有意思哇!确实想想也是,语法应当是上下文无关的,语义则需要和上下文关联。\n\n##### 基本概念\n\n###### 语言\n\n- 通常我们用 Σ 表示字母表,程序语言的字母表通常是 ASCLL 字符集。\n- 由字母表中字符组成的有穷序列为字符串或字。\n- 字母表上的所有字符串的集合用 Σ* 表示。\n- 那么 Σ* 的任意一个子集称为语言,记为 L 。\n- L 中的字符串则成为语言 L 的一个语句。\n\n###### 文法\n\n文法通常表示成四元组 G[S] = (Vt,Vn,S,𝞷),其中:\n\n1. Vt 为终结符号集,非空有限集;\n2. Vn 为非终结符号集,非空有限集;\n3. S 为一文法开始符,是一个特殊的非终结符,即 S∈Vn ;\n4. 𝞷 是产生式的非空有限集,通常写作 α → β 或 α::=β。读作‘α是β’或者‘α定义为β’。α和β是由终结符和非终结符组成的字符串,其中α至少有一个非终结符。\n\n这么多概念,是不是蒙了,握草,什么是终结符、什么是非终结符、什么是产生式、是不是翻译错了,这就是当时读es5文档时的感受😂。下面先举一个例子,然后根据例子来解释这些概念。\n\n还是熟悉的标识符,标识符是以字母开头的字母数字串,我们用 L 表示字母类非终结符;D 表示数字类非终结符;T 表示数字字母类非终结符,则有:\n\n```\nL → a|b|...|z\nD → 0|1|...|9\nT → L|D\n```\n\n接下来定义数字字母串 S,S → T|ST 有一种递归的意思,数字字母串可以是单个字符 T ,也可以是 T 左边加上一个数字字母串 S:\n\n```\nS → T|ST\n```\n\n最后推导出标识符的非终结符 I,以字母 L 开头,后面可以空也可以是数字字母串:\n\n```\nI → L|LS\n```\n\n文法 G[I] = ({a,b,...,z,0,...9},{I,S,T,L,D},I,𝞷) 注:𝞷 为上面五个式子。\n\n很好理解吧!那么现在再来看看终结符、非终结符、产生式的概念吧。\n\n终结符就是语言不可再分的基本符号,通常是语言的字母表或者字母表的子集吧!就像例子中 {a,b,...,z,0,...9} 这样。\n\n非终结符也称语法变量,它代表一个一定的语法概念。就像上面的例子中 S 非终结符,它代表的就是数字字母串这样一个确定的语法概念。\n\n产生式呢,更好理解了,就是一个推导式子,用来表示某个非终结符或者非终结符加终结符这种结构的确定意义,说到这里 α 必须包含一个非终结符也就很好理解了,因为终结符已经是最小的语法元素了,没有进行推导的必要了。\n\n#### 形式语言分类\n\n1956 年伟大的语言学家 Noam Chomsky 首先建立了形式语言的描述,定义了四类文法及相应的形式语言,分别与相应的识别系统相联系。\n\n###### 0型文法(图灵机)\n\n规则:α → β 产生式左边 α 至少含有一个非终结符。它的识别系统是图灵机。\n\n###### 1型文法(线性界线自动机、自然语言)\n\n在 0 型文法的基础上增加了字符长度上的限制。αA𝞭 → αβ𝞭,这里的 A 是非终结符号,而 α、β 和 𝞭 是包含非终结符号与终结符号的字串,它显然满足之前的长度限制。并且能够表示出只有在α和β的上下文内,A 才能够替换为 β。所以1型文法也叫作上下文有关文法。\n\n###### 2型文法(下推自动机、程序语言)\n\n规则:A → α,A为非终结符号,α为可空的非终结符和终结符组合串。2型文法也叫作上下文无关文法,因为你只要找到符合产生式右边的串,就可以把它归约为对应的非终结符。\n\n###### 3型文法(有限自动机)\n\n规则:A → α 或 A → αB 或 A → Bα,AB为非终结符,α为可空终结符字串。\n\n###### 关系区别\n\n从0到3,限制在增加,1-3型文法都属于0型;2、3型不一定属于1型;3型属于2型。*不是简单的包含关系。*写到这对于这四类文法还是比较模糊啊!!先继续看下去吧!\n\n###### 正规表达式与上下文无关文法\n\n正规表达式和上下文无关文法是可以相互转换的,转换方法略。另外上下文无关文法即可以描述程序语言的词法也可以描述语法,但是相对于正规表达式来说,还是通常使用后者来描述词法,后者简单、直观、效率高。\n\n> 编译原理及语法分析的内容还很多,不过仅仅是为了读es5的文档补充知识,本系列只到语法分析的这一部分,后面的语法树、抽象语法树等到有必要的时候在进行了解了!\n\n参考资料:\n\n1. 《编译原理(第三版)》科学出版社\n2. [编译原理学习周入门教程](http://blog.csdn.net/lovesummerforever/article/category/1438807)\n3. 传说中好像还有龙虎鲸三本经典,然而图书馆并没有🙄\n","html":"<p>语法分析是编译过程的核心部分,其基本任务是根据语言的语法规则进行语法分析,若不存在语法错误则给出正确的语法结构并为语义分析和代码生成做准备。在描述程序语言语法结构时,需借助上下文无关文法,而文法是描述程序语言的依据。</p>\n\n<h4 id=\"\">文法和语言</h4>\n\n<blockquote>\n <p>话外音:这玩意就是读es5文档第五章把我卡住的东西,要好好看看哇!</p>\n</blockquote>\n\n<p>文法是程序语言的生成系统,而自动机则是程序语言的识别系统:用文法可以精确地定义一个语言,并依据该文法构造出识别这个语言的自动机。如:程序语言的词法可以用正规文法描述;语法可以用上下文无关文法描述;语义则需要上下午有关文法描述。有意思哇!确实想想也是,语法应当是上下文无关的,语义则需要和上下文关联。</p>\n\n<h5 id=\"\">基本概念</h5>\n\n<h6 id=\"\">语言</h6>\n\n<ul>\n<li>通常我们用 Σ 表示字母表,程序语言的字母表通常是 ASCLL 字符集。</li>\n<li>由字母表中字符组成的有穷序列为字符串或字。</li>\n<li>字母表上的所有字符串的集合用 Σ* 表示。</li>\n<li>那么 Σ* 的任意一个子集称为语言,记为 L 。</li>\n<li>L 中的字符串则成为语言 L 的一个语句。</li>\n</ul>\n\n<h6 id=\"\">文法</h6>\n\n<p>文法通常表示成四元组 G[S] = (Vt,Vn,S,𝞷),其中:</p>\n\n<ol>\n<li>Vt 为终结符号集,非空有限集; </li>\n<li>Vn 为非终结符号集,非空有限集; </li>\n<li>S 为一文法开始符,是一个特殊的非终结符,即 S∈Vn ; </li>\n<li>𝞷 是产生式的非空有限集,通常写作 α → β 或 α::=β。读作‘α是β’或者‘α定义为β’。α和β是由终结符和非终结符组成的字符串,其中α至少有一个非终结符。</li>\n</ol>\n\n<p>这么多概念,是不是蒙了,握草,什么是终结符、什么是非终结符、什么是产生式、是不是翻译错了,这就是当时读es5文档时的感受😂。下面先举一个例子,然后根据例子来解释这些概念。</p>\n\n<p>还是熟悉的标识符,标识符是以字母开头的字母数字串,我们用 L 表示字母类非终结符;D 表示数字类非终结符;T 表示数字字母类非终结符,则有:</p>\n\n<pre><code>L → a|b|...|z \nD → 0|1|...|9 \nT → L|D \n</code></pre>\n\n<p>接下来定义数字字母串 S,S → T|ST 有一种递归的意思,数字字母串可以是单个字符 T ,也可以是 T 左边加上一个数字字母串 S:</p>\n\n<pre><code>S → T|ST \n</code></pre>\n\n<p>最后推导出标识符的非终结符 I,以字母 L 开头,后面可以空也可以是数字字母串:</p>\n\n<pre><code>I → L|LS \n</code></pre>\n\n<p>文法 G[I] = ({a,b,...,z,0,...9},{I,S,T,L,D},I,𝞷) 注:𝞷 为上面五个式子。</p>\n\n<p>很好理解吧!那么现在再来看看终结符、非终结符、产生式的概念吧。</p>\n\n<p>终结符就是语言不可再分的基本符号,通常是语言的字母表或者字母表的子集吧!就像例子中 {a,b,...,z,0,...9} 这样。</p>\n\n<p>非终结符也称语法变量,它代表一个一定的语法概念。就像上面的例子中 S 非终结符,它代表的就是数字字母串这样一个确定的语法概念。</p>\n\n<p>产生式呢,更好理解了,就是一个推导式子,用来表示某个非终结符或者非终结符加终结符这种结构的确定意义,说到这里 α 必须包含一个非终结符也就很好理解了,因为终结符已经是最小的语法元素了,没有进行推导的必要了。</p>\n\n<h4 id=\"\">形式语言分类</h4>\n\n<p>1956 年伟大的语言学家 Noam Chomsky 首先建立了形式语言的描述,定义了四类文法及相应的形式语言,分别与相应的识别系统相联系。</p>\n\n<h6 id=\"0\">0型文法(图灵机)</h6>\n\n<p>规则:α → β 产生式左边 α 至少含有一个非终结符。它的识别系统是图灵机。</p>\n\n<h6 id=\"1\">1型文法(线性界线自动机、自然语言)</h6>\n\n<p>在 0 型文法的基础上增加了字符长度上的限制。αA𝞭 → αβ𝞭,这里的 A 是非终结符号,而 α、β 和 𝞭 是包含非终结符号与终结符号的字串,它显然满足之前的长度限制。并且能够表示出只有在α和β的上下文内,A 才能够替换为 β。所以1型文法也叫作上下文有关文法。</p>\n\n<h6 id=\"2\">2型文法(下推自动机、程序语言)</h6>\n\n<p>规则:A → α,A为非终结符号,α为可空的非终结符和终结符组合串。2型文法也叫作上下文无关文法,因为你只要找到符合产生式右边的串,就可以把它归约为对应的非终结符。</p>\n\n<h6 id=\"3\">3型文法(有限自动机)</h6>\n\n<p>规则:A → α 或 A → αB 或 A → Bα,AB为非终结符,α为可空终结符字串。</p>\n\n<h6 id=\"\">关系区别</h6>\n\n<p>从0到3,限制在增加,1-3型文法都属于0型;2、3型不一定属于1型;3型属于2型。<em>不是简单的包含关系。</em>写到这对于这四类文法还是比较模糊啊!!先继续看下去吧!</p>\n\n<h6 id=\"\">正规表达式与上下文无关文法</h6>\n\n<p>正规表达式和上下文无关文法是可以相互转换的,转换方法略。另外上下文无关文法即可以描述程序语言的词法也可以描述语法,但是相对于正规表达式来说,还是通常使用后者来描述词法,后者简单、直观、效率高。</p>\n\n<blockquote>\n <p>编译原理及语法分析的内容还很多,不过仅仅是为了读es5的文档补充知识,本系列只到语法分析的这一部分,后面的语法树、抽象语法树等到有必要的时候在进行了解了!</p>\n</blockquote>\n\n<p>参考资料:</p>\n\n<ol>\n<li>《编译原理(第三版)》科学出版社 </li>\n<li><a href=\"http://blog.csdn.net/lovesummerforever/article/category/1438807\">编译原理学习周入门教程</a> </li>\n<li>传说中好像还有龙虎鲸三本经典,然而图书馆并没有🙄</li>\n</ol>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1474242612596,"created_by":1,"updated_at":1474265057960,"updated_by":1,"published_at":1474249659205,"published_by":1},{"id":72,"uuid":"5d8c5247-3161-4797-ac18-5e6314692f7f","title":"转:js 词法","slug":"zhuan-js-ci-fa","markdown":"补充了部分编译原理的知识,回过头来再看 es5 标准的五到七章,总算大致能够看明白了,主要是还是非终结符、产生式这些东西。然后看到博客园的这篇[Javascript词法](http://www.cnblogs.com/winter-cn/archive/2012/04/17/2454229.html),感觉总结的还不错,就用markdown改写了一下,转过来了!当然自己也稍微加了点料子。对应了标准的第七章。\n\n#### InputElement 输入元素\n\n输入元素是 js 词法扫描程序拿到的最基本元素了,也就是 js 程序源代码中表达特定意义的\"单词\"。输入元素共分为四种:\n\n```\nInputElement ::\n WhiteSpace\n Comment\n Token\n LineTerminator\n```\n\n另外值得注意的是,ES5 规范里面其实定义了两种InputElement ,如下所示:\n\n```\nInputElementDiv ::\n WhiteSpace\n Comment\n Token\n LineTerminator\n DivPunctuator\n```\n\n```\nInputElementRegExp ::\n WhiteSpace\n Comment\n Token\n LineTerminator\n RegularExpressionLiteral\n```\n\n这么做是因为JS的除法运算符和正则表达式直接量都使用了 / 字符,在词法分析阶段,是无法区分二者的。所以 js 的词法分析有两种状态,一种状态是扫描 InputElementDiv,另一种状态是扫描InputElementRegExp,又所以,js的词法分析器应该有两种状态,由语法分析器来设置,js 的词法分析和语法分析必须交错进行。\n\n下面的一个例子说明了除法和正则表达式写法的冲突问题:\n\n```\nif(a+b)/a/g;\n(a+b)/a/g;\n```\n\n可以看到完全相同的/a/g(而且前面一段字符也相同),可能被理解为除法或者正则表达式。因为必须区分所处的语法环境,所以单单靠词法分析无论如何也无法决定该用除法还是正则表达式来理解。\n\n因为基本上没有任何编辑环境会对文本做语法分析,这个问题也造成了很多语法着色系统无法很好地处理 js 正则表达式。以非语言实现者的角度,完全应该按照最上面一段产生式去理解 js 的词法。\n\n#### WhiteSpace 空白符\n\n这个词相信不用细说,所有 js 程序员都比较熟悉。js 接受5种 ASCII 字符为空白符,BOM 以及 Unicode 分类中所有属于 whitespace 分类的字符也可以作为空白符使用:\n\n```\nWhiteSpace ::\n <TAB>\n <VT>\n <FF>\n <SP>\n <NBSP>\n <BOM>\n <USP>\n```\n\n其中,<TAB>是U+0009,是缩进TAB符,也就是字符串中写的'\\t'。\n\n<VT>是U+000B,也就是垂直方向的TAB符'\\v',这个字符在键盘上很难打出来,所以很少用到。\n\n<FF>是U+000C,Form Feed,分页符,字符串直接量中写作'\\f',现代已经很少有打印源程序的事情发生了,所以这个字符在JS源代码中很少用到。\n\n<SP>是U+0020,就是最普通的空格了。\n\n<NBSP>是U+00A0,非断行空格,它是SP的一个变体,在文字排版中,可以避免因为空格在此处发生断行,其它方面和普通空格完全一样。多数的JS编辑环境都会把它当做普通空格(因为一般源代码编辑环境根本就不会自动折行……)\n\n<BOM>是U+FEFF,这是ES5新加入的空白符,是Unicode中的零宽非断行空格,在以UTF格式编码的文件中,常常在文件首插入一个额外的U+FEFF,解析UTF文件的程序可以根据U+FEFF的表示方法猜测文件采用哪种UTF编码方式。这个字符也叫做\"bit order mark\"。\n\n<USP>表示Unicode中所有的\"separator, space(Zs)\"分类中的字符,包括(原文中有,不过不是很重要,本文省略了)。\n\t \n> 注意虽然 js 规范承认这些字符可以被用做空白字符,但是除非对源代码的打印、排版有特别的需求,还是应该尽量使用<SP>,尤其是考虑到,相当一批字体无法支持<USP>中的全部字符。\n\n根据一些团队的编码规范,<TAB>常常用于缩进。编程语言中关于用<TAB>还是四个<SP>做缩进的争论从未停止过,此处就不加讨论了。\n\njs中,WhiteSpace 的大部分用途是分隔 token 和保持代码整齐美观,基本上词法分析器产生的 WhiteSpace 都会被语法分析器直接丢弃。\n\n所以一些 WhiteSpace 能够被去掉而完全不影响程序的执行效果。但是也有一些 WhiteSpace 必须存在的情况,考虑下面代码:\n\n```\n1 .toString();\n1.toString(); //报错\n```\n\n上面一段代码中,空白符分隔了1和.,因此它们被理解为两个token。[具体解释](http://stackoverflow.com/questions/38968598/what-happened-inside-of-1-tostring-and-1-tostring-in-javascript)\n\n```\n1.[\"toString\"]();\n1 .[\"toString\"](); //报错\n```\n相反的情况。\n\n#### LineTerminator 行终结符\n\n这个也是一个非常常见的概念了,js 中只提供了4种字符作为换行符:\n\n```\nLineTerminator ::\n <LF>\n <CR>\n <LS>\n <PS>\n```\n\n其中,<LF>是U+000A,就是最正常换行符,在字符串中的'\\n'。\n\n<CR>是U+000D,这个字符真正意义上的\"回车\",在字符串中是'\\r',在一部分Windows风格文本编辑器中,换行是两个字符\\r\\n。\n\n<LS>是U+2028,是Unicode中的行分隔符。\n\n<PS>是U+2029,是Unicode中的段落分隔符。\n\n大部分 LineTerminator 在被词法分析器扫描出之后,会被语法分析器丢弃,但是换行符会影响 js 的两个重要语法特性:自动插入分号和\"no line terminator\"规则。\n\n> \"no line terminator\"规则:如果产生式的右侧出现“[no LineTerminator here]”,那么它表示此产生式是个受限的产生式:如果 LineTerminator 在输入流的指定位置出现,那么此产生式将不会被适用。例如,产生式:\nThrowStatement : throw [no LineTerminator here] Expression ;\n\n考虑下面三段代码:\n\n```\nvar a = 1 , b = 1;\na\n++\nb\n```\n按照 js 语法的自动插入分号规则,代码解释可能产生歧义。但是因为后自增运算符有no line terminator的限制,所以实际结果等价于:\n\n```\nvar a = 1 , b = 1;\na;\n++b;\n```\n\n考虑以下两段代码:\n\n```\nreturn\n 123;\nreturn 123;\n```\n\n因为return有 no line terminator 的限制,所以第一段代码实际等同于\n\n```\nreturn;\n123;\n```\n\n#### Comment 注释\nJS的注释分为单行注释和多行注释两种: \n\n```\nComment :: \n MultiLineComment \n SingleLineComment\n```\n\n多行注释定义如下:\n\n```\nMultiLineComment :: \n /* MultiLineCommentCharsopt */ \nMultiLineCommentChars :: \n MultiLineNotAsteriskChar MultiLineCommentCharsopt \n * PostAsteriskCommentCharsopt \nPostAsteriskCommentChars :: \n MultiLineNotForwardSlashOrAsteriskChar MultiLineCommentCharsopt \n * PostAsteriskCommentCharsopt \nMultiLineNotAsteriskChar :: \n SourceCharacter but not asterisk * \nMultiLineNotForwardSlashOrAsteriskChar :: \n SourceCharacter but not forward-slash / orasterisk *\n```\n\n这个定义略微有些复杂,实际上这就是我们所熟知的 js 多行注释语法的严格描述。\n\n多行注释中允许自由地出现MultiLineNotAsteriskChar ,也就是除了*之外的所有字符。而每一个*之后,不能出现正斜杠符/\n\n单行注释则比较简单:\n\n```\nSingleLineComment ::\n // SingleLineCommentCharsopt\nSingleLineCommentChars ::\n SingleLineCommentChar SingleLineCommentCharsopt\nSingleLineCommentChar ::\n SourceCharacter but not LineTerminator\n```\n\n除了四种LineTerminator之外,所有字符都可以作为单行注释。\n\n一般情况下,不论是单行还是多行注释都不会影响程序的意义,但是含有LineTerminator的多行注释会影响到自动插入分号规则:\n\n```\nreturn/*\n */123;\n```\n\n```\nreturn /**/ 123;\n```\n\n两者会产生不同的效果。\n\n#### Token\nToken 是 js 中所有能被引擎理解的最小语义单元。\n\njs 中有4种Token:\n\n```\nToken ::\n IdentifierName \n Punctuator \n NumericLiteral \n StringLiteral\n```\n\n如果不考虑除法和正则的冲突问题,Token还应该包括RegularExpressionLiteral,而Punctuator中也应该添加 / 和 /= 两种符号。\n\n###### IdentifierName 标识符名\nIdentifierName的定义为:\n\n```\nIdentifierName ::\n IdentifierStart\n IdentifierName IdentifierPart\nIdentifierStart ::\n UnicodeLetter\n $\n _ \n \\ UnicodeEscapeSequence\nIdentifierPart ::\n IdentifierStart\n UnicodeCombiningMark\n UnicodeDigit\n UnicodeConnectorPunctuation\n <ZWNJ>\n <ZWJ>\n```\n\nIdentifierName 可以以美元符$下划线_ 或者Unicode字母开始,除了开始字符以外,IdentifierName 中还可以使用 Unicode 中的连接标记、数字、以及连接符号。\n\nIdentifierName 的任意字符可以使用 js 的 Unicode 转义写法,使用Unicode 转义写法时,没有任何字符限制。\n\nIdentifierName 可以是 Identifier、NullLiteral、BooleanLiteral 或者 keyword ,在 ObjectLiteral 中,IdentifierName 还可以被直接当做属性名称使用。\n\n仅当不是保留字的时候,IdentifierName 会被解析为 Identifier 。\n\nUnicodeLetter, UnicodeCombiningMark, UnicodeDigit, UnicodeConnectorPunctuation 各自对应几个 Unicode 的分类。\n\n![](https://ws4.sinaimg.cn/large/006bH5BKgw1f7yypaauqsj30nw0fidi6.jpg)\n\n> 注意<ZWNJ>和<ZWJ>是ES5新加入的两个格式控制字符,但是目前为止实测还没有浏览器支持。\n\nJS中的关键字有:\n\n```\nKeyword :: one of\n break do instanceof typeof case else new var catch finally return void continue for switch while debugger function this with default if throw delete in try\n```\n\n还有7个为了未来使用而保留的关键字: \n\n```\nFutureReservedKeyword :: one of\n class enum extends super const export import\n```\n\n在严格模式下,有一些额外的为未来使用而保留的关键字:\n\n```\nimplements let private public interface package protected static yield\n```\n\n除了这些之外,NullLiteral: \n\n```\nNullLiteral ::\n null\n```\n\n和BooleanLiteral:\n\n```\nBooleanLiteral ::\n true false\n```\n\n也是保留字,不能用于Identifier。\n\n###### Punctuator\n\njs 使用48个运算符,因为前面提到的除法和正则问题,/和/=两个运算符被拆分为DivPunctuator。其余的运算符为:\n\n```\n Punctuator :: one of\n { } ( ) [ ] . ; , < > <= >= == != === !== + - * % ++ -- << >> >>> & | ^ ! ~ && || ? : = += -= *= %= <<= >>= >>>= &= |= ^=\n```\n\n所有运算符在语法分析器中作为不同的 symbol 出现。\n\n###### NumericLiteral 数字字面量\n\njs 规范中规定的数字直接量可以支持两种写法:十进制和十六进制整数,尽管标准中没有提到,但是大部分 js 实现还支持以0开头的八进制整数写法。\n\n所以实际上 js 的 NumericLiteral 产生式应该是这样的:\n\n```\nNumericLiteral :: \n DecimalLiteral\n HexIntegerLiteral\n OctalIntegerLiteralnot-standard \n```\n\n只有十进制可以表示浮点数,DecimalLiteral 定义如下:\n\n```\nDecimalLiteral ::\n DecimalIntegerLiteral . DecimalDigitsopt ExponentPartopt\n . DecimalDigits ExponentPartopt\n DecimalIntegerLiteral ExponentPartopt\nDecimalIntegerLiteral ::\n 0 \n NonZeroDigit DecimalDigitsopt\nDecimalDigits ::\n DecimalDigit\n DecimalDigits DecimalDigit\nDecimalDigit :: one of\n 0 1 2 3 4 5 6 7 8 9\nNonZeroDigit:: one of\n 1 2 3 4 5 6 7 8 9\nExponentPart::\n ExponentIndicator SignedInteger\nExponentIndicator :: one of\n e E\nSignedInteger ::\n DecimalDigits\n + DecimalDigits\n - DecimalDigits\n```\n\n十进制数的小数点前和小数点后均可以省略, 所以 1. 和 .1 都是合法的数字直接量,特别地,除了0之外,十进制数不能以0开头(这其实是为了八进制整数预留的)。\n\n.同时还是一个 Punctuator ,在词法分析阶段,.123 应该优先被尝试理解为 NumericLiteral ,而非 Punctuator NumericLiteral。\n\n十六进制整数产生式如下:\n\n```\nHexIntegerLiteral ::\n 0x HexDigit\n 0X HexDigit\n HexIntegerLiteral HexDigit\nHexDigit :: one of\n 0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F\n```\n\njs 中支持0x标记的大小写形式,十六进制数字中的大小写也可以任意使用。\n\n八进制整数是非标准的,但是大多数引擎都支持(严格模式下是禁止的):\n\n```\nOctalIntegerLiteral :: \n 0 OctalDigit\n OctalIntegerLiteral OctalDigit \nOctalDigit :: one of\n 0 1 2 3 4 5 6 7\n```\n\n###### StringLiteral 字符串字面量\n\njs 中的 StringLiteral 支持单引号和双引号两种写法。\n\n```\nStringLiteral ::\n \" DoubleStringCharactersopt \"\n ' SingleStringCharactersopt '\n```\n\n单双引号的区别仅仅在于写法,在双引号字符串直接量中,双引号必须转义,在单引号字符串直接量中,单引号必须转义\n\n```\nDoubleStringCharacters ::\n DoubleStringCharacter DoubleStringCharactersopt\nSingleStringCharacters ::\n SingleStringCharacter SingleStringCharactersopt\nDoubleStringCharacter ::\n SourceCharacter but not double-quote \" or backslash \\ or LineTerminator\n \\ EscapeSequence\n LineContinuation\nSingleStringCharacter ::\n SourceCharacter but not single-quote ' orbackslash \\ or LineTerminator\n \\ EscapeSequence\n LineContinuation\n```\n\n字符串中其他必须转义的字符是 \\ 和所有换行符。\n\njs 中支持四种转义形式,还有一种虽然标准没有定义,但是大部分实现都支持的八进制转义\n\n```\nEscapeSequence ::\n CharacterEscapeSequence\n 0 [lookahead no DecimalDigit]\n HexEscapeSequence\n UnicodeEscapeSequence\n OctalEscapeSequencenot-standard \n```\n\n第一种是单字符转义。即一个反斜杠 \\ 后面跟一个字符这种形式。\n\n```\nCharacterEscapeSequence ::\n SingleEscapeCharacter\n NonEscapeCharacter\nSingleEscapeCharacter :: one of\n ' \" \\ b f n r t v\nNonEscapeCharacter ::\n SourceCharacter but notEscapeCharacter or LineTerminator\n```\n\n有特别意义的字符包括有 SingleEscapeCharacter 所定义的9种。除了这9种字符、数字、x和u以及所有的换行符之外,其它字符经过\\转义都是自身。\n\n十六进制转义只支持两位,也就是说,这种写法只支持ASCII字符:\n\n```\nHexEscapeSequence ::\n x HexDigit HexDigit\n```\n\nUnicode转义可以支持BMP中的所有字符:\n\n```\nUnicodeEscapeSequence ::\n u HexDigit HexDigit HexDigit HexDigit\n```\n\nLineContinuation 可以被理解为一种特别的转义。写字符串直接量时灵活使用 LineContinuation 可以增加可读性。\n\n```\nLineContinuation ::\n \\ LineTerminatorSequence\nLineTerminatorSequence ::\n <LF>\n <CR> [lookahead no <LF> ]\n <LS>\n <PS>\n <CR>\n <CR> <LF> \n```\n\n为了适应 Windows 风格的文本,js 把\"\\r\\n\"作为一个换行符使用。\n\n> 注意因为 CR 在某些 Windows 风格的编辑器中没法显示出来,所以乱用的话会产生奇怪的效果。\n\n###### RegularExpressionLiteral 正则字面量\n\n正则表达式由Body和Flags两部分组成:\n\n```\nRegularExpressionLiteral ::\n / RegularExpressionBody / RegularExpressionFlags\n```\n\n其中 Body 部分至少有一个字符,第一个字符不能是*\n(因为/*跟多行注释有词法冲突。)\n\n```\nRegularExpressionBody ::\n RegularExpressionFirstChar RegularExpressionChars\nRegularExpressionChars ::\n [empty]\n RegularExpressionChars RegularExpressionChar\nRegularExpressionFirstChar ::\n RegularExpressionNonTerminator but not * or \\ or / or [ \n RegularExpressionBackslashSequence\n RegularExpressionClass\nRegularExpressionChar ::\n RegularExpressionNonTerminator but not \\ or / or [ \n RegularExpressionBackslashSequence\n RegularExpressionClass\n```\n\n除了\\ / 和 [ 三个字符之外,js 正则表达式中的字符都是普通字符。\n\n```\nRegularExpressionBackslashSequence ::\n \\ RegularExpressionNonTerminator\nRegularExpressionNonTerminator ::\n SourceCharacter but not LineTerminator\n```\n\n用 \\和一个非换行符可以组成一个RegularExpressionBackslashSequence,这种方式可以用于表示正则表达式中的有特殊意义的字符。\n\n```\nRegularExpressionClass ::\n [ RegularExpressionClassChars ]\n```\n\n正则表达式中,用一对方括号表示class。class中的特殊字符仅仅为]和\\。\n\nclass允许为空。class中也支持转义。\n\n```\nRegularExpressionClassChars ::\n [empty]\n RegularExpressionClassChars RegularExpressionClassChar\nRegularExpressionClassChar ::\n RegularExpressionNonTerminator but not ] or \\ \n RegularExpressionBackslashSequence\n```\n\n正则表达式中的 flag 在词法阶段不会限制字符,虽然只有 ig 几个是有效的,但是任何 IdentifierPart 序列在词法阶段都会被认为是合法的。\n\n```\nRegularExpressionFlags ::\n [empty]\n RegularExpressionFlags IdentifierPart \n``` ","html":"<p>补充了部分编译原理的知识,回过头来再看 es5 标准的五到七章,总算大致能够看明白了,主要是还是非终结符、产生式这些东西。然后看到博客园的这篇<a href=\"http://www.cnblogs.com/winter-cn/archive/2012/04/17/2454229.html\">Javascript词法</a>,感觉总结的还不错,就用markdown改写了一下,转过来了!当然自己也稍微加了点料子。对应了标准的第七章。</p>\n\n<h4 id=\"inputelement\">InputElement 输入元素</h4>\n\n<p>输入元素是 js 词法扫描程序拿到的最基本元素了,也就是 js 程序源代码中表达特定意义的\"单词\"。输入元素共分为四种:</p>\n\n<pre><code>InputElement :: \n WhiteSpace\n Comment\n Token\n LineTerminator\n</code></pre>\n\n<p>另外值得注意的是,ES5 规范里面其实定义了两种InputElement ,如下所示:</p>\n\n<pre><code>InputElementDiv :: \n WhiteSpace\n Comment\n Token\n LineTerminator\n DivPunctuator\n</code></pre>\n\n<pre><code>InputElementRegExp :: \n WhiteSpace\n Comment\n Token\n LineTerminator\n RegularExpressionLiteral\n</code></pre>\n\n<p>这么做是因为JS的除法运算符和正则表达式直接量都使用了 / 字符,在词法分析阶段,是无法区分二者的。所以 js 的词法分析有两种状态,一种状态是扫描 InputElementDiv,另一种状态是扫描InputElementRegExp,又所以,js的词法分析器应该有两种状态,由语法分析器来设置,js 的词法分析和语法分析必须交错进行。</p>\n\n<p>下面的一个例子说明了除法和正则表达式写法的冲突问题:</p>\n\n<pre><code>if(a+b)/a/g; \n(a+b)/a/g;\n</code></pre>\n\n<p>可以看到完全相同的/a/g(而且前面一段字符也相同),可能被理解为除法或者正则表达式。因为必须区分所处的语法环境,所以单单靠词法分析无论如何也无法决定该用除法还是正则表达式来理解。</p>\n\n<p>因为基本上没有任何编辑环境会对文本做语法分析,这个问题也造成了很多语法着色系统无法很好地处理 js 正则表达式。以非语言实现者的角度,完全应该按照最上面一段产生式去理解 js 的词法。</p>\n\n<h4 id=\"whitespace\">WhiteSpace 空白符</h4>\n\n<p>这个词相信不用细说,所有 js 程序员都比较熟悉。js 接受5种 ASCII 字符为空白符,BOM 以及 Unicode 分类中所有属于 whitespace 分类的字符也可以作为空白符使用:</p>\n\n<pre><code>WhiteSpace :: \n <TAB>\n <VT>\n <FF>\n <SP>\n <NBSP>\n <BOM>\n <USP>\n</code></pre>\n\n<p>其中,<TAB>是U+0009,是缩进TAB符,也就是字符串中写的'\\t'。</p>\n\n<p><VT>是U+000B,也就是垂直方向的TAB符'\\v',这个字符在键盘上很难打出来,所以很少用到。</p>\n\n<p><FF>是U+000C,Form Feed,分页符,字符串直接量中写作'\\f',现代已经很少有打印源程序的事情发生了,所以这个字符在JS源代码中很少用到。</p>\n\n<p><SP>是U+0020,就是最普通的空格了。</p>\n\n<p><NBSP>是U+00A0,非断行空格,它是SP的一个变体,在文字排版中,可以避免因为空格在此处发生断行,其它方面和普通空格完全一样。多数的JS编辑环境都会把它当做普通空格(因为一般源代码编辑环境根本就不会自动折行……)</p>\n\n<p><BOM>是U+FEFF,这是ES5新加入的空白符,是Unicode中的零宽非断行空格,在以UTF格式编码的文件中,常常在文件首插入一个额外的U+FEFF,解析UTF文件的程序可以根据U+FEFF的表示方法猜测文件采用哪种UTF编码方式。这个字符也叫做\"bit order mark\"。</p>\n\n<p><USP>表示Unicode中所有的\"separator, space(Zs)\"分类中的字符,包括(原文中有,不过不是很重要,本文省略了)。 <br />\n </p>\n\n<blockquote>\n <p>注意虽然 js 规范承认这些字符可以被用做空白字符,但是除非对源代码的打印、排版有特别的需求,还是应该尽量使用<SP>,尤其是考虑到,相当一批字体无法支持<USP>中的全部字符。</p>\n</blockquote>\n\n<p>根据一些团队的编码规范,<TAB>常常用于缩进。编程语言中关于用<TAB>还是四个<SP>做缩进的争论从未停止过,此处就不加讨论了。</p>\n\n<p>js中,WhiteSpace 的大部分用途是分隔 token 和保持代码整齐美观,基本上词法分析器产生的 WhiteSpace 都会被语法分析器直接丢弃。</p>\n\n<p>所以一些 WhiteSpace 能够被去掉而完全不影响程序的执行效果。但是也有一些 WhiteSpace 必须存在的情况,考虑下面代码:</p>\n\n<pre><code>1 .toString(); \n1.toString(); //报错 \n</code></pre>\n\n<p>上面一段代码中,空白符分隔了1和.,因此它们被理解为两个token。<a href=\"http://stackoverflow.com/questions/38968598/what-happened-inside-of-1-tostring-and-1-tostring-in-javascript\">具体解释</a></p>\n\n<pre><code>1.[\"toString\"](); \n1 .[\"toString\"](); //报错 \n</code></pre>\n\n<p>相反的情况。</p>\n\n<h4 id=\"lineterminator\">LineTerminator 行终结符</h4>\n\n<p>这个也是一个非常常见的概念了,js 中只提供了4种字符作为换行符:</p>\n\n<pre><code>LineTerminator :: \n <LF>\n <CR>\n <LS>\n <PS>\n</code></pre>\n\n<p>其中,<LF>是U+000A,就是最正常换行符,在字符串中的'\\n'。</p>\n\n<p><CR>是U+000D,这个字符真正意义上的\"回车\",在字符串中是'\\r',在一部分Windows风格文本编辑器中,换行是两个字符\\r\\n。</p>\n\n<p><LS>是U+2028,是Unicode中的行分隔符。</p>\n\n<p><PS>是U+2029,是Unicode中的段落分隔符。</p>\n\n<p>大部分 LineTerminator 在被词法分析器扫描出之后,会被语法分析器丢弃,但是换行符会影响 js 的两个重要语法特性:自动插入分号和\"no line terminator\"规则。</p>\n\n<blockquote>\n <p>\"no line terminator\"规则:如果产生式的右侧出现“[no LineTerminator here]”,那么它表示此产生式是个受限的产生式:如果 LineTerminator 在输入流的指定位置出现,那么此产生式将不会被适用。例如,产生式:\n ThrowStatement : throw [no LineTerminator here] Expression ;</p>\n</blockquote>\n\n<p>考虑下面三段代码:</p>\n\n<pre><code>var a = 1 , b = 1; \na \n++\nb \n</code></pre>\n\n<p>按照 js 语法的自动插入分号规则,代码解释可能产生歧义。但是因为后自增运算符有no line terminator的限制,所以实际结果等价于:</p>\n\n<pre><code>var a = 1 , b = 1; \na; \n++b;\n</code></pre>\n\n<p>考虑以下两段代码:</p>\n\n<pre><code>return \n 123;\nreturn 123; \n</code></pre>\n\n<p>因为return有 no line terminator 的限制,所以第一段代码实际等同于</p>\n\n<pre><code>return; \n123; \n</code></pre>\n\n<h4 id=\"comment\">Comment 注释</h4>\n\n<p>JS的注释分为单行注释和多行注释两种: </p>\n\n<pre><code>Comment :: \n MultiLineComment \n SingleLineComment\n</code></pre>\n\n<p>多行注释定义如下:</p>\n\n<pre><code>MultiLineComment :: \n /* MultiLineCommentCharsopt */ \nMultiLineCommentChars :: \n MultiLineNotAsteriskChar MultiLineCommentCharsopt \n * PostAsteriskCommentCharsopt \nPostAsteriskCommentChars :: \n MultiLineNotForwardSlashOrAsteriskChar MultiLineCommentCharsopt \n * PostAsteriskCommentCharsopt \nMultiLineNotAsteriskChar :: \n SourceCharacter but not asterisk * \nMultiLineNotForwardSlashOrAsteriskChar :: \n SourceCharacter but not forward-slash / orasterisk *\n</code></pre>\n\n<p>这个定义略微有些复杂,实际上这就是我们所熟知的 js 多行注释语法的严格描述。</p>\n\n<p>多行注释中允许自由地出现MultiLineNotAsteriskChar ,也就是除了<em>之外的所有字符。而每一个</em>之后,不能出现正斜杠符/</p>\n\n<p>单行注释则比较简单:</p>\n\n<pre><code>SingleLineComment :: \n // SingleLineCommentCharsopt\nSingleLineCommentChars :: \n SingleLineCommentChar SingleLineCommentCharsopt\nSingleLineCommentChar :: \n SourceCharacter but not LineTerminator\n</code></pre>\n\n<p>除了四种LineTerminator之外,所有字符都可以作为单行注释。</p>\n\n<p>一般情况下,不论是单行还是多行注释都不会影响程序的意义,但是含有LineTerminator的多行注释会影响到自动插入分号规则:</p>\n\n<pre><code>return/* \n */123;\n</code></pre>\n\n<pre><code>return /**/ 123; \n</code></pre>\n\n<p>两者会产生不同的效果。</p>\n\n<h4 id=\"token\">Token</h4>\n\n<p>Token 是 js 中所有能被引擎理解的最小语义单元。</p>\n\n<p>js 中有4种Token:</p>\n\n<pre><code>Token :: \n IdentifierName \n Punctuator \n NumericLiteral \n StringLiteral\n</code></pre>\n\n<p>如果不考虑除法和正则的冲突问题,Token还应该包括RegularExpressionLiteral,而Punctuator中也应该添加 / 和 /= 两种符号。</p>\n\n<h6 id=\"identifiername\">IdentifierName 标识符名</h6>\n\n<p>IdentifierName的定义为:</p>\n\n<pre><code>IdentifierName :: \n IdentifierStart\n IdentifierName IdentifierPart\nIdentifierStart :: \n UnicodeLetter\n $\n _ \n \\ UnicodeEscapeSequence\nIdentifierPart :: \n IdentifierStart\n UnicodeCombiningMark\n UnicodeDigit\n UnicodeConnectorPunctuation\n <ZWNJ>\n <ZWJ>\n</code></pre>\n\n<p>IdentifierName 可以以美元符$下划线_ 或者Unicode字母开始,除了开始字符以外,IdentifierName 中还可以使用 Unicode 中的连接标记、数字、以及连接符号。</p>\n\n<p>IdentifierName 的任意字符可以使用 js 的 Unicode 转义写法,使用Unicode 转义写法时,没有任何字符限制。</p>\n\n<p>IdentifierName 可以是 Identifier、NullLiteral、BooleanLiteral 或者 keyword ,在 ObjectLiteral 中,IdentifierName 还可以被直接当做属性名称使用。</p>\n\n<p>仅当不是保留字的时候,IdentifierName 会被解析为 Identifier 。</p>\n\n<p>UnicodeLetter, UnicodeCombiningMark, UnicodeDigit, UnicodeConnectorPunctuation 各自对应几个 Unicode 的分类。</p>\n\n<p><img src=\"https://ws4.sinaimg.cn/large/006bH5BKgw1f7yypaauqsj30nw0fidi6.jpg\" alt=\"\" /></p>\n\n<blockquote>\n <p>注意<ZWNJ>和<ZWJ>是ES5新加入的两个格式控制字符,但是目前为止实测还没有浏览器支持。</p>\n</blockquote>\n\n<p>JS中的关键字有:</p>\n\n<pre><code>Keyword :: one of \n break do instanceof typeof case else new var catch finally return void continue for switch while debugger function this with default if throw delete in try\n</code></pre>\n\n<p>还有7个为了未来使用而保留的关键字: </p>\n\n<pre><code>FutureReservedKeyword :: one of \n class enum extends super const export import\n</code></pre>\n\n<p>在严格模式下,有一些额外的为未来使用而保留的关键字:</p>\n\n<pre><code>implements let private public interface package protected static yield \n</code></pre>\n\n<p>除了这些之外,NullLiteral: </p>\n\n<pre><code>NullLiteral :: \n null\n</code></pre>\n\n<p>和BooleanLiteral:</p>\n\n<pre><code>BooleanLiteral :: \n true false\n</code></pre>\n\n<p>也是保留字,不能用于Identifier。</p>\n\n<h6 id=\"punctuator\">Punctuator</h6>\n\n<p>js 使用48个运算符,因为前面提到的除法和正则问题,/和/=两个运算符被拆分为DivPunctuator。其余的运算符为:</p>\n\n<pre><code> Punctuator :: one of\n { } ( ) [ ] . ; , < > <= >= == != === !== + - * % ++ -- << >> >>> & | ^ ! ~ && || ? : = += -= *= %= <<= >>= >>>= &= |= ^=\n</code></pre>\n\n<p>所有运算符在语法分析器中作为不同的 symbol 出现。</p>\n\n<h6 id=\"numericliteral\">NumericLiteral 数字字面量</h6>\n\n<p>js 规范中规定的数字直接量可以支持两种写法:十进制和十六进制整数,尽管标准中没有提到,但是大部分 js 实现还支持以0开头的八进制整数写法。</p>\n\n<p>所以实际上 js 的 NumericLiteral 产生式应该是这样的:</p>\n\n<pre><code>NumericLiteral :: \n DecimalLiteral\n HexIntegerLiteral\n OctalIntegerLiteralnot-standard \n</code></pre>\n\n<p>只有十进制可以表示浮点数,DecimalLiteral 定义如下:</p>\n\n<pre><code>DecimalLiteral :: \n DecimalIntegerLiteral . DecimalDigitsopt ExponentPartopt\n . DecimalDigits ExponentPartopt\n DecimalIntegerLiteral ExponentPartopt\nDecimalIntegerLiteral :: \n 0 \n NonZeroDigit DecimalDigitsopt\nDecimalDigits :: \n DecimalDigit\n DecimalDigits DecimalDigit\nDecimalDigit :: one of \n 0 1 2 3 4 5 6 7 8 9\nNonZeroDigit:: one of \n 1 2 3 4 5 6 7 8 9\nExponentPart:: \n ExponentIndicator SignedInteger\nExponentIndicator :: one of \n e E\nSignedInteger :: \n DecimalDigits\n + DecimalDigits\n - DecimalDigits\n</code></pre>\n\n<p>十进制数的小数点前和小数点后均可以省略, 所以 1. 和 .1 都是合法的数字直接量,特别地,除了0之外,十进制数不能以0开头(这其实是为了八进制整数预留的)。</p>\n\n<p>.同时还是一个 Punctuator ,在词法分析阶段,.123 应该优先被尝试理解为 NumericLiteral ,而非 Punctuator NumericLiteral。</p>\n\n<p>十六进制整数产生式如下:</p>\n\n<pre><code>HexIntegerLiteral :: \n 0x HexDigit\n 0X HexDigit\n HexIntegerLiteral HexDigit\nHexDigit :: one of \n 0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F\n</code></pre>\n\n<p>js 中支持0x标记的大小写形式,十六进制数字中的大小写也可以任意使用。</p>\n\n<p>八进制整数是非标准的,但是大多数引擎都支持(严格模式下是禁止的):</p>\n\n<pre><code>OctalIntegerLiteral :: \n 0 OctalDigit\n OctalIntegerLiteral OctalDigit \nOctalDigit :: one of \n 0 1 2 3 4 5 6 7\n</code></pre>\n\n<h6 id=\"stringliteral\">StringLiteral 字符串字面量</h6>\n\n<p>js 中的 StringLiteral 支持单引号和双引号两种写法。</p>\n\n<pre><code>StringLiteral :: \n \" DoubleStringCharactersopt \"\n ' SingleStringCharactersopt '\n</code></pre>\n\n<p>单双引号的区别仅仅在于写法,在双引号字符串直接量中,双引号必须转义,在单引号字符串直接量中,单引号必须转义</p>\n\n<pre><code>DoubleStringCharacters :: \n DoubleStringCharacter DoubleStringCharactersopt\nSingleStringCharacters :: \n SingleStringCharacter SingleStringCharactersopt\nDoubleStringCharacter :: \n SourceCharacter but not double-quote \" or backslash \\ or LineTerminator\n \\ EscapeSequence\n LineContinuation\nSingleStringCharacter :: \n SourceCharacter but not single-quote ' orbackslash \\ or LineTerminator\n \\ EscapeSequence\n LineContinuation\n</code></pre>\n\n<p>字符串中其他必须转义的字符是 \\ 和所有换行符。</p>\n\n<p>js 中支持四种转义形式,还有一种虽然标准没有定义,但是大部分实现都支持的八进制转义</p>\n\n<pre><code>EscapeSequence :: \n CharacterEscapeSequence\n 0 [lookahead no DecimalDigit]\n HexEscapeSequence\n UnicodeEscapeSequence\n OctalEscapeSequencenot-standard \n</code></pre>\n\n<p>第一种是单字符转义。即一个反斜杠 \\ 后面跟一个字符这种形式。</p>\n\n<pre><code>CharacterEscapeSequence :: \n SingleEscapeCharacter\n NonEscapeCharacter\nSingleEscapeCharacter :: one of \n ' \" \\ b f n r t v\nNonEscapeCharacter :: \n SourceCharacter but notEscapeCharacter or LineTerminator\n</code></pre>\n\n<p>有特别意义的字符包括有 SingleEscapeCharacter 所定义的9种。除了这9种字符、数字、x和u以及所有的换行符之外,其它字符经过\\转义都是自身。</p>\n\n<p>十六进制转义只支持两位,也就是说,这种写法只支持ASCII字符:</p>\n\n<pre><code>HexEscapeSequence :: \n x HexDigit HexDigit\n</code></pre>\n\n<p>Unicode转义可以支持BMP中的所有字符:</p>\n\n<pre><code>UnicodeEscapeSequence :: \n u HexDigit HexDigit HexDigit HexDigit\n</code></pre>\n\n<p>LineContinuation 可以被理解为一种特别的转义。写字符串直接量时灵活使用 LineContinuation 可以增加可读性。</p>\n\n<pre><code>LineContinuation :: \n \\ LineTerminatorSequence\nLineTerminatorSequence :: \n <LF>\n <CR> [lookahead no <LF> ]\n <LS>\n <PS>\n <CR>\n <CR> <LF> \n</code></pre>\n\n<p>为了适应 Windows 风格的文本,js 把\"\\r\\n\"作为一个换行符使用。</p>\n\n<blockquote>\n <p>注意因为 CR 在某些 Windows 风格的编辑器中没法显示出来,所以乱用的话会产生奇怪的效果。</p>\n</blockquote>\n\n<h6 id=\"regularexpressionliteral\">RegularExpressionLiteral 正则字面量</h6>\n\n<p>正则表达式由Body和Flags两部分组成:</p>\n\n<pre><code>RegularExpressionLiteral :: \n / RegularExpressionBody / RegularExpressionFlags\n</code></pre>\n\n<p>其中 Body 部分至少有一个字符,第一个字符不能是*\n(因为/*跟多行注释有词法冲突。)</p>\n\n<pre><code>RegularExpressionBody :: \n RegularExpressionFirstChar RegularExpressionChars\nRegularExpressionChars :: \n [empty]\n RegularExpressionChars RegularExpressionChar\nRegularExpressionFirstChar :: \n RegularExpressionNonTerminator but not * or \\ or / or [ \n RegularExpressionBackslashSequence\n RegularExpressionClass\nRegularExpressionChar :: \n RegularExpressionNonTerminator but not \\ or / or [ \n RegularExpressionBackslashSequence\n RegularExpressionClass\n</code></pre>\n\n<p>除了\\ / 和 [ 三个字符之外,js 正则表达式中的字符都是普通字符。</p>\n\n<pre><code>RegularExpressionBackslashSequence :: \n \\ RegularExpressionNonTerminator\nRegularExpressionNonTerminator :: \n SourceCharacter but not LineTerminator\n</code></pre>\n\n<p>用 \\和一个非换行符可以组成一个RegularExpressionBackslashSequence,这种方式可以用于表示正则表达式中的有特殊意义的字符。</p>\n\n<pre><code>RegularExpressionClass :: \n [ RegularExpressionClassChars ]\n</code></pre>\n\n<p>正则表达式中,用一对方括号表示class。class中的特殊字符仅仅为]和\\。</p>\n\n<p>class允许为空。class中也支持转义。</p>\n\n<pre><code>RegularExpressionClassChars :: \n [empty]\n RegularExpressionClassChars RegularExpressionClassChar\nRegularExpressionClassChar :: \n RegularExpressionNonTerminator but not ] or \\ \n RegularExpressionBackslashSequence\n</code></pre>\n\n<p>正则表达式中的 flag 在词法阶段不会限制字符,虽然只有 ig 几个是有效的,但是任何 IdentifierPart 序列在词法阶段都会被认为是合法的。</p>\n\n<pre><code>RegularExpressionFlags :: \n [empty]\n RegularExpressionFlags IdentifierPart \n</code></pre>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1474267630475,"created_by":1,"updated_at":1474275112934,"updated_by":1,"published_at":1474274830533,"published_by":1},{"id":73,"uuid":"66447ca1-fc70-4f87-b1bd-d3e8c9bf2aff","title":"js 自动分号插入(ASI)机制","slug":"js-asi-ji-zhi","markdown":"前一篇《js 词法》一文基本以及把规范的第七章介绍了一遍,不过漏下了7.9自动插入这一节。虽然只是一些小细节,但还是有必要记一下,因为通过此改善编码习惯很重要。\n\n我们在写 java 和 c 时,必须要在语句后加分号,否则编译通不过。而 js 不同,存在自动分好插入机制,下文简称 ASI。它会给源代码的 token 流自动插入分号。\n\n#### 规范理论\n\nes5 标准定义了自动分号插入规则,包括以下三个基本规则加两个前置条件:\n\n###### 前置条件\n\n- 如果插入分号后解析结果是空语句,那么不会自动插入分号。\n\n例子:(空语句,else 前不加分好)\n```\nif (a > b)\nelse c = d\n```\n\n- 如果插入分号后它成为 for 语句头部的两个分号之一,那么不会自动插入分号。\n\n例子:(不会加分号)\n```\nfor (a; b\n)\n```\n\n###### 基本规则\n\n- 左到右解析程序,当遇到一个不符合任何文法产生式的 token(叫做 违规 token(offending token)),那么只要满足下面条件之一就在违规 token 前面自动插入分号。\n - 至少一个 LineTerminator 分割了违规 token 和前一个 token。\n - 违规 token 是 }。\n\n例子:(1、2不符合任何产生式,并且之间存在 LineTerminator,因此在违规 token 2前加了分好,2和}则是因为违规 token 是 }所以加了分号)\n\n```\n{ 1\n2 } 3\n```\n\n```\n{ 1\n;2 ;} 3;\n```\n\n- 左到右解析程序,tokens 输入流已经结束,当解析器无法将输入 token 流解析成单个完整 ECMAScript 程序 ,那么就在输入流的结束位置自动插入分号。\n- 对于受限产生式,也就是下面的5个,我们把产生式 [no LineTerminator here]后面的 token 叫做受限 token,如果在 token 和 受限 token 间存在了至少一个 LineTerminator,那么会在受限 token 前自动加上 token。\n\n受限的产生式只限如下5个:\n\n```\nPostfixExpression : \nLeftHandSideExpression [no LineTerminator here] ++ LeftHandSideExpression [no LineTerminator here] --\n```\n\n```\nContinueStatement : \ncontinue [no LineTerminator here] Identifier; \n```\n\n```\nBreakStatement : \nbreak [no LineTerminator here] Identifier; \n```\n\n```\nReturnStatement : \nreturn [no LineTerminator here] Expression; \n```\n\n```\nThrowStatement : throw [no LineTerminator here] Expression;\n```\n\n#### 归纳\n\n###### 避免 ASI 带来的问题\n\n- 后缀运算符 ++ 或 -- 和它的操作数应该出现在同一行。\n- return 或 throw 语句的表达式开始位置应该和 return 或 throw token 同一行。\n- break 或 continue 语句的标示符应该和 break 或 continue token 同一行。\n\n###### 何时加分号\n\n无分号党(懒人党)想要不加分号,那么就需要知道什么时候应该要加分号。[JS魔法堂:ASI(自动分号插入机制)和前置分号](http://www.cnblogs.com/fsjohnhuang/p/4154503.html)归纳了 NO ASI 并且会出现错误的几种情况,在这几种情况下我们是要加分号的。下面是对应的描述(具体了解为什么点击那篇文章):\n\n> 在以 ([/+- 开头的语句前加分号(由于正常写法均不会出现以 .,*% 作为语句开头,因此只需记住前面5个即可,你看能懒则懒哦)\n\n不过这里只考虑了换行的情况,其实 ASI 还存在不换行的情况,这就要根据标准里的三条规则行事了!\n\n知道了这点,其实我们就可以省略大部分的分号了。但是也不强求,因为这还是要根据个人习惯以及团队风格走的。\n\n###### 小补充\n\n为什么自执行函数前要加分号?\n\n主要是应对代码合并压缩时,由于缺少分号;带来的错误。知道了上面的规则,在 ( 开头的行前加分号就可以避免错误了。\n\n\n","html":"<p>前一篇《js 词法》一文基本以及把规范的第七章介绍了一遍,不过漏下了7.9自动插入这一节。虽然只是一些小细节,但还是有必要记一下,因为通过此改善编码习惯很重要。</p>\n\n<p>我们在写 java 和 c 时,必须要在语句后加分号,否则编译通不过。而 js 不同,存在自动分好插入机制,下文简称 ASI。它会给源代码的 token 流自动插入分号。</p>\n\n<h4 id=\"\">规范理论</h4>\n\n<p>es5 标准定义了自动分号插入规则,包括以下三个基本规则加两个前置条件:</p>\n\n<h6 id=\"\">前置条件</h6>\n\n<ul>\n<li>如果插入分号后解析结果是空语句,那么不会自动插入分号。</li>\n</ul>\n\n<p>例子:(空语句,else 前不加分好)</p>\n\n<pre><code>if (a > b) \nelse c = d \n</code></pre>\n\n<ul>\n<li>如果插入分号后它成为 for 语句头部的两个分号之一,那么不会自动插入分号。</li>\n</ul>\n\n<p>例子:(不会加分号)</p>\n\n<pre><code>for (a; b \n)\n</code></pre>\n\n<h6 id=\"\">基本规则</h6>\n\n<ul>\n<li>左到右解析程序,当遇到一个不符合任何文法产生式的 token(叫做 违规 token(offending token)),那么只要满足下面条件之一就在违规 token 前面自动插入分号。\n<ul><li>至少一个 LineTerminator 分割了违规 token 和前一个 token。</li>\n<li>违规 token 是 }。</li></ul></li>\n</ul>\n\n<p>例子:(1、2不符合任何产生式,并且之间存在 LineTerminator,因此在违规 token 2前加了分好,2和}则是因为违规 token 是 }所以加了分号)</p>\n\n<pre><code>{ 1\n2 } 3 \n</code></pre>\n\n<pre><code>{ 1\n;2 ;} 3;\n</code></pre>\n\n<ul>\n<li>左到右解析程序,tokens 输入流已经结束,当解析器无法将输入 token 流解析成单个完整 ECMAScript 程序 ,那么就在输入流的结束位置自动插入分号。</li>\n<li>对于受限产生式,也就是下面的5个,我们把产生式 [no LineTerminator here]后面的 token 叫做受限 token,如果在 token 和 受限 token 间存在了至少一个 LineTerminator,那么会在受限 token 前自动加上 token。</li>\n</ul>\n\n<p>受限的产生式只限如下5个:</p>\n\n<pre><code>PostfixExpression : \nLeftHandSideExpression [no LineTerminator here] ++ LeftHandSideExpression [no LineTerminator here] -- \n</code></pre>\n\n<pre><code>ContinueStatement : \ncontinue [no LineTerminator here] Identifier; \n</code></pre>\n\n<pre><code>BreakStatement : \nbreak [no LineTerminator here] Identifier; \n</code></pre>\n\n<pre><code>ReturnStatement : \nreturn [no LineTerminator here] Expression; \n</code></pre>\n\n<pre><code>ThrowStatement : throw [no LineTerminator here] Expression; \n</code></pre>\n\n<h4 id=\"\">归纳</h4>\n\n<h6 id=\"asi\">避免 ASI 带来的问题</h6>\n\n<ul>\n<li>后缀运算符 ++ 或 -- 和它的操作数应该出现在同一行。</li>\n<li>return 或 throw 语句的表达式开始位置应该和 return 或 throw token 同一行。</li>\n<li>break 或 continue 语句的标示符应该和 break 或 continue token 同一行。</li>\n</ul>\n\n<h6 id=\"\">何时加分号</h6>\n\n<p>无分号党(懒人党)想要不加分号,那么就需要知道什么时候应该要加分号。<a href=\"http://www.cnblogs.com/fsjohnhuang/p/4154503.html\">JS魔法堂:ASI(自动分号插入机制)和前置分号</a>归纳了 NO ASI 并且会出现错误的几种情况,在这几种情况下我们是要加分号的。下面是对应的描述(具体了解为什么点击那篇文章):</p>\n\n<blockquote>\n <p>在以 ([/+- 开头的语句前加分号(由于正常写法均不会出现以 .,*% 作为语句开头,因此只需记住前面5个即可,你看能懒则懒哦)</p>\n</blockquote>\n\n<p>不过这里只考虑了换行的情况,其实 ASI 还存在不换行的情况,这就要根据标准里的三条规则行事了!</p>\n\n<p>知道了这点,其实我们就可以省略大部分的分号了。但是也不强求,因为这还是要根据个人习惯以及团队风格走的。</p>\n\n<h6 id=\"\">小补充</h6>\n\n<p>为什么自执行函数前要加分号?</p>\n\n<p>主要是应对代码合并压缩时,由于缺少分号;带来的错误。知道了上面的规则,在 ( 开头的行前加分号就可以避免错误了。</p>","image":null,"featured":0,"page":0,"status":"published","language":"zh_CN","meta_title":null,"meta_description":null,"author_id":1,"created_at":1474275530868,"created_by":1,"updated_at":1474291198959,"updated_by":1,"published_at":1474290905445,"published_by":1}],"users":[{"id":1,"uuid":"f183c222-672a-47e1-80a4-561830ba1d27","name":"m2mbob","slug":"m2mbob","password":"$2a$10$Xcdj.QcZXVRWoWmvgQTC/.QvFXigb4CHjfLM0kE.44gNKy/j0Z6oq","email":"[email protected]","image":"/content/images/2016/05/113.jpeg","cover":null,"bio":"","website":"https://xxn520.github.io/jiayi520/","location":"杭州","accessibility":null,"status":"active","language":"en_US","meta_title":null,"meta_description":null,"tour":null,"last_login":1474284662275,"created_at":1463567133080,"created_by":1,"updated_at":1474284662276,"updated_by":1},{"id":5,"uuid":"26139dd3-2a1d-41f0-a185-654731c39cef","name":"laowei38ban","slug":"laowei38ban","password":"$2a$10$5k4p6NzBZwU7vcH4zdMtl.mxkc.oXKCywxgDjRGoGXaGc0CVnjDc.","email":"[email protected]","image":null,"cover":null,"bio":null,"website":null,"location":null,"accessibility":null,"status":"invited-pending","language":"zh_CN","meta_title":null,"meta_description":null,"tour":null,"last_login":null,"created_at":1467527925700,"created_by":1,"updated_at":1467528053760,"updated_by":5}],"roles":[{"id":1,"uuid":"a205663c-0051-448b-8d64-c083afc8cd1f","name":"Administrator","description":"Administrators","created_at":1463567128189,"created_by":1,"updated_at":1463567128189,"updated_by":1},{"id":2,"uuid":"f3c3f834-15cf-4992-b37e-9fc241cce9ea","name":"Editor","description":"Editors","created_at":1463567128217,"created_by":1,"updated_at":1463567128217,"updated_by":1},{"id":3,"uuid":"974b0d0e-2a8c-4c45-9932-dbb233bc9cc8","name":"Author","description":"Authors","created_at":1463567128243,"created_by":1,"updated_at":1463567128243,"updated_by":1},{"id":4,"uuid":"e1e437c2-a08b-4152-bb91-130ff9670e31","name":"Owner","description":"Blog Owner","created_at":1463567128271,"created_by":1,"updated_at":1463567128271,"updated_by":1}],"roles_users":[{"id":1,"role_id":4,"user_id":1},{"id":2,"role_id":3,"user_id":2},{"id":3,"role_id":3,"user_id":3},{"id":4,"role_id":3,"user_id":4},{"id":5,"role_id":3,"user_id":5}],"permissions":[{"id":1,"uuid":"17ffb52c-5e47-4520-bb3a-923eee539fae","name":"Export database","object_type":"db","action_type":"exportContent","object_id":null,"created_at":1463567128298,"created_by":1,"updated_at":1463567128298,"updated_by":1},{"id":2,"uuid":"0b603a34-66db-4290-9f1c-17411a32d5af","name":"Import database","object_type":"db","action_type":"importContent","object_id":null,"created_at":1463567128326,"created_by":1,"updated_at":1463567128326,"updated_by":1},{"id":3,"uuid":"63d14f73-132e-4504-9f3c-a5496e529604","name":"Delete all content","object_type":"db","action_type":"deleteAllContent","object_id":null,"created_at":1463567128351,"created_by":1,"updated_at":1463567128351,"updated_by":1},{"id":4,"uuid":"4b3cd8a9-e96e-45f1-8746-ba36e61d837b","name":"Send mail","object_type":"mail","action_type":"send","object_id":null,"created_at":1463567128379,"created_by":1,"updated_at":1463567128379,"updated_by":1},{"id":5,"uuid":"b3a9c3fe-26ef-4f19-895a-bb6b09cfa07a","name":"Browse notifications","object_type":"notification","action_type":"browse","object_id":null,"created_at":1463567128407,"created_by":1,"updated_at":1463567128407,"updated_by":1},{"id":6,"uuid":"7d1c64de-07c8-49e8-87cf-eef785bd3500","name":"Add notifications","object_type":"notification","action_type":"add","object_id":null,"created_at":1463567128431,"created_by":1,"updated_at":1463567128431,"updated_by":1},{"id":7,"uuid":"d490cd6d-e086-4e27-86f0-4a4638f020b0","name":"Delete notifications","object_type":"notification","action_type":"destroy","object_id":null,"created_at":1463567128456,"created_by":1,"updated_at":1463567128456,"updated_by":1},{"id":8,"uuid":"e00cf85a-7d10-4908-9952-164dbb7e76d5","name":"Browse posts","object_type":"post","action_type":"browse","object_id":null,"created_at":1463567128480,"created_by":1,"updated_at":1463567128480,"updated_by":1},{"id":9,"uuid":"388edf22-9724-4936-840b-4972018c83f5","name":"Read posts","object_type":"post","action_type":"read","object_id":null,"created_at":1463567128506,"created_by":1,"updated_at":1463567128506,"updated_by":1},{"id":10,"uuid":"8cafbddb-afdb-4ce8-82be-17dd7dab9b39","name":"Edit posts","object_type":"post","action_type":"edit","object_id":null,"created_at":1463567128534,"created_by":1,"updated_at":1463567128534,"updated_by":1},{"id":11,"uuid":"2f54798f-05bc-45b4-a477-0cae3a8c15fb","name":"Add posts","object_type":"post","action_type":"add","object_id":null,"created_at":1463567128556,"created_by":1,"updated_at":1463567128556,"updated_by":1},{"id":12,"uuid":"b85480a8-ff4d-4ed1-baad-595ea2452fca","name":"Delete posts","object_type":"post","action_type":"destroy","object_id":null,"created_at":1463567128578,"created_by":1,"updated_at":1463567128578,"updated_by":1},{"id":13,"uuid":"4760b85c-cc79-4ee4-87c1-643bc500136e","name":"Browse settings","object_type":"setting","action_type":"browse","object_id":null,"created_at":1463567128602,"created_by":1,"updated_at":1463567128602,"updated_by":1},{"id":14,"uuid":"b60aa8d7-8605-4c2e-84bb-35fd98b9e05b","name":"Read settings","object_type":"setting","action_type":"read","object_id":null,"created_at":1463567128631,"created_by":1,"updated_at":1463567128631,"updated_by":1},{"id":15,"uuid":"a7437212-afe7-4df4-9370-9e4cb8507d64","name":"Edit settings","object_type":"setting","action_type":"edit","object_id":null,"created_at":1463567128655,"created_by":1,"updated_at":1463567128655,"updated_by":1},{"id":16,"uuid":"c334e5b6-c398-4b0b-ad00-9b3c747aa564","name":"Generate slugs","object_type":"slug","action_type":"generate","object_id":null,"created_at":1463567128688,"created_by":1,"updated_at":1463567128688,"updated_by":1},{"id":17,"uuid":"3338699f-c4e5-41b8-9ca4-4d22be830633","name":"Browse tags","object_type":"tag","action_type":"browse","object_id":null,"created_at":1463567128719,"created_by":1,"updated_at":1463567128719,"updated_by":1},{"id":18,"uuid":"b58d136d-763d-4cfc-bcc3-ab32fdc2d4d1","name":"Read tags","object_type":"tag","action_type":"read","object_id":null,"created_at":1463567128746,"created_by":1,"updated_at":1463567128746,"updated_by":1},{"id":19,"uuid":"76384049-8ad7-4233-9259-f7b511018001","name":"Edit tags","object_type":"tag","action_type":"edit","object_id":null,"created_at":1463567128770,"created_by":1,"updated_at":1463567128770,"updated_by":1},{"id":20,"uuid":"2c117ad5-76ab-438a-9e2e-2a707d15b2fa","name":"Add tags","object_type":"tag","action_type":"add","object_id":null,"created_at":1463567128795,"created_by":1,"updated_at":1463567128795,"updated_by":1},{"id":21,"uuid":"94bf77cf-2356-47ba-be2b-6e51ff3b4e0b","name":"Delete tags","object_type":"tag","action_type":"destroy","object_id":null,"created_at":1463567128820,"created_by":1,"updated_at":1463567128820,"updated_by":1},{"id":22,"uuid":"6f3064f3-7109-4049-807b-0b5986a00077","name":"Browse themes","object_type":"theme","action_type":"browse","object_id":null,"created_at":1463567128848,"created_by":1,"updated_at":1463567128848,"updated_by":1},{"id":23,"uuid":"589fe891-4edb-4504-bcdd-443950122687","name":"Edit themes","object_type":"theme","action_type":"edit","object_id":null,"created_at":1463567128876,"created_by":1,"updated_at":1463567128876,"updated_by":1},{"id":24,"uuid":"91e1d531-9886-4cf5-ab90-868e851150ee","name":"Browse users","object_type":"user","action_type":"browse","object_id":null,"created_at":1463567128903,"created_by":1,"updated_at":1463567128903,"updated_by":1},{"id":25,"uuid":"f898d0b8-7a24-4da8-9ae0-d2d71082c0fe","name":"Read users","object_type":"user","action_type":"read","object_id":null,"created_at":1463567128926,"created_by":1,"updated_at":1463567128926,"updated_by":1},{"id":26,"uuid":"69769fb8-0707-41d9-a941-108e78458828","name":"Edit users","object_type":"user","action_type":"edit","object_id":null,"created_at":1463567128950,"created_by":1,"updated_at":1463567128950,"updated_by":1},{"id":27,"uuid":"f077887e-a6bb-4d7e-a5a2-669f2c709db7","name":"Add users","object_type":"user","action_type":"add","object_id":null,"created_at":1463567128976,"created_by":1,"updated_at":1463567128976,"updated_by":1},{"id":28,"uuid":"1a13365e-a47e-4355-92fd-5ca74fa7259e","name":"Delete users","object_type":"user","action_type":"destroy","object_id":null,"created_at":1463567129004,"created_by":1,"updated_at":1463567129004,"updated_by":1},{"id":29,"uuid":"d331b27f-cd04-43f2-8e13-4cc90a9fe017","name":"Assign a role","object_type":"role","action_type":"assign","object_id":null,"created_at":1463567129036,"created_by":1,"updated_at":1463567129036,"updated_by":1},{"id":30,"uuid":"d6a8f870-d4aa-4565-bbb9-08274700fcd7","name":"Browse roles","object_type":"role","action_type":"browse","object_id":null,"created_at":1463567129059,"created_by":1,"updated_at":1463567129059,"updated_by":1}],"permissions_users":[],"permissions_roles":[{"id":1,"role_id":1,"permission_id":1},{"id":2,"role_id":1,"permission_id":2},{"id":3,"role_id":1,"permission_id":3},{"id":4,"role_id":1,"permission_id":4},{"id":5,"role_id":1,"permission_id":5},{"id":6,"role_id":1,"permission_id":6},{"id":7,"role_id":1,"permission_id":7},{"id":8,"role_id":1,"permission_id":8},{"id":9,"role_id":1,"permission_id":9},{"id":10,"role_id":1,"permission_id":10},{"id":11,"role_id":1,"permission_id":11},{"id":12,"role_id":1,"permission_id":12},{"id":13,"role_id":1,"permission_id":13},{"id":14,"role_id":1,"permission_id":14},{"id":15,"role_id":1,"permission_id":15},{"id":16,"role_id":1,"permission_id":16},{"id":17,"role_id":1,"permission_id":17},{"id":18,"role_id":1,"permission_id":18},{"id":19,"role_id":1,"permission_id":19},{"id":20,"role_id":1,"permission_id":20},{"id":21,"role_id":1,"permission_id":21},{"id":22,"role_id":1,"permission_id":22},{"id":23,"role_id":1,"permission_id":23},{"id":24,"role_id":1,"permission_id":24},{"id":25,"role_id":1,"permission_id":25},{"id":26,"role_id":1,"permission_id":26},{"id":27,"role_id":1,"permission_id":27},{"id":28,"role_id":1,"permission_id":28},{"id":29,"role_id":1,"permission_id":29},{"id":30,"role_id":1,"permission_id":30},{"id":31,"role_id":2,"permission_id":8},{"id":32,"role_id":2,"permission_id":9},{"id":33,"role_id":2,"permission_id":10},{"id":34,"role_id":2,"permission_id":11},{"id":35,"role_id":2,"permission_id":12},{"id":36,"role_id":2,"permission_id":13},{"id":37,"role_id":2,"permission_id":14},{"id":38,"role_id":2,"permission_id":16},{"id":39,"role_id":2,"permission_id":17},{"id":40,"role_id":2,"permission_id":18},{"id":41,"role_id":2,"permission_id":19},{"id":42,"role_id":2,"permission_id":20},{"id":43,"role_id":2,"permission_id":21},{"id":44,"role_id":2,"permission_id":24},{"id":45,"role_id":2,"permission_id":25},{"id":46,"role_id":2,"permission_id":26},{"id":47,"role_id":2,"permission_id":27},{"id":48,"role_id":2,"permission_id":28},{"id":49,"role_id":2,"permission_id":29},{"id":50,"role_id":2,"permission_id":30},{"id":51,"role_id":3,"permission_id":8},{"id":52,"role_id":3,"permission_id":9},{"id":53,"role_id":3,"permission_id":11},{"id":54,"role_id":3,"permission_id":13},{"id":55,"role_id":3,"permission_id":14},{"id":56,"role_id":3,"permission_id":16},{"id":57,"role_id":3,"permission_id":17},{"id":58,"role_id":3,"permission_id":18},{"id":59,"role_id":3,"permission_id":20},{"id":60,"role_id":3,"permission_id":24},{"id":61,"role_id":3,"permission_id":25},{"id":62,"role_id":3,"permission_id":30}],"permissions_apps":[],"settings":[{"id":1,"uuid":"07767b0a-31a8-4f7c-9089-0cac6be45867","key":"databaseVersion","value":"004","type":"core","created_at":1463567133162,"created_by":1,"updated_at":1463567133162,"updated_by":1},{"id":2,"uuid":"64a07b8e-9ca6-4464-940a-985f4241f6e7","key":"dbHash","value":"e00711f4-02e7-4f60-a9c5-b870666a2a83","type":"core","created_at":1463567133162,"created_by":1,"updated_at":1463567133836,"updated_by":1},{"id":3,"uuid":"d57d0b4c-9dbf-4810-a010-651b42825bf5","key":"nextUpdateCheck","value":"1474354010","type":"core","created_at":1463567133163,"created_by":1,"updated_at":1474267607628,"updated_by":1},{"id":4,"uuid":"b43fd992-cc8f-4f1e-bc47-c060dfdcae3f","key":"displayUpdateNotification","value":"0.7.0","type":"core","created_at":1463567133163,"created_by":1,"updated_at":1474267607630,"updated_by":1},{"id":5,"uuid":"0b0d23f5-c9f3-4a41-9c47-ba9170f1cec5","key":"title","value":"金色梦乡","type":"blog","created_at":1463567133163,"created_by":1,"updated_at":1473925171068,"updated_by":1},{"id":6,"uuid":"e53b4cef-36a8-4208-9605-9dd6062e7407","key":"description","value":"Thoughts, stories and ideas.","type":"blog","created_at":1463567133163,"created_by":1,"updated_at":1473925171070,"updated_by":1},{"id":7,"uuid":"adccbe11-7283-472b-b363-b517530d874c","key":"logo","value":"","type":"blog","created_at":1463567133163,"created_by":1,"updated_at":1473925171071,"updated_by":1},{"id":8,"uuid":"9ef9dbff-161f-45db-921e-4857f9d617b3","key":"cover","value":"/content/images/2016/08/f1a79dc7f4573842399295901c16baa8.jpg","type":"blog","created_at":1463567133164,"created_by":1,"updated_at":1473925171072,"updated_by":1},{"id":9,"uuid":"c205e750-b7b4-4a65-be94-e33aa231e523","key":"defaultLang","value":"en_US","type":"blog","created_at":1463567133164,"created_by":1,"updated_at":1473925171073,"updated_by":1},{"id":10,"uuid":"d7917f90-456e-42d0-a6fe-429f84898f0b","key":"postsPerPage","value":"5","type":"blog","created_at":1463567133164,"created_by":1,"updated_at":1473925171075,"updated_by":1},{"id":11,"uuid":"66503e4a-acf0-4aca-8dbc-0e36ae39bcdd","key":"forceI18n","value":"true","type":"blog","created_at":1463567133165,"created_by":1,"updated_at":1473925171076,"updated_by":1},{"id":12,"uuid":"8755c9a1-7e8c-42c6-a665-df37701f6072","key":"permalinks","value":"/:year/:month/:day/:slug/","type":"blog","created_at":1463567133165,"created_by":1,"updated_at":1473925171077,"updated_by":1},{"id":13,"uuid":"1b0a112b-d8b5-47b6-8b13-56c323eaa83e","key":"ghost_head","value":"","type":"blog","created_at":1463567133165,"created_by":1,"updated_at":1473925171080,"updated_by":1},{"id":14,"uuid":"9675e29f-a962-49a0-ad3a-f15533d0f7dc","key":"ghost_foot","value":"","type":"blog","created_at":1463567133165,"created_by":1,"updated_at":1473925171081,"updated_by":1},{"id":15,"uuid":"6d4ab5d5-75d4-4b1e-b5ec-6435f411cb3a","key":"labs","value":"{}","type":"blog","created_at":1463567133165,"created_by":1,"updated_at":1473925171082,"updated_by":1},{"id":16,"uuid":"a10d0aed-69e5-4de0-899c-8ad0fbc6c955","key":"navigation","value":"[{\"label\":\"Home\",\"url\":\"/\"},{\"label\":\"admin\",\"url\":\"/ghost/\"},{\"label\":\"mail\",\"url\":\"http://mail.m2mbob.cn/\"},{\"label\":\"github\",\"url\":\"https://github.com/xxn520\"},{\"label\":\"gitosc\",\"url\":\"http://git.oschina.net/m2mbob\"}]","type":"blog","created_at":1463567133166,"created_by":1,"updated_at":1473925171083,"updated_by":1},{"id":17,"uuid":"821e540b-22d6-49b5-acb5-bc963b77100f","key":"activeApps","value":"[]","type":"app","created_at":1463567133166,"created_by":1,"updated_at":1463567133166,"updated_by":1},{"id":18,"uuid":"2a154f41-dcc1-4035-bbe9-886d2f715d8e","key":"installedApps","value":"[]","type":"app","created_at":1463567133167,"created_by":1,"updated_at":1472228447220,"updated_by":1},{"id":19,"uuid":"96f6c4a2-30d4-4490-968d-449261420d65","key":"isPrivate","value":"false","type":"private","created_at":1463567133167,"created_by":1,"updated_at":1473925171084,"updated_by":1},{"id":20,"uuid":"90857a68-2fe1-4798-9654-88a635a54ddb","key":"password","value":"null","type":"private","created_at":1463567133167,"created_by":1,"updated_at":1473925171085,"updated_by":1},{"id":21,"uuid":"fcd59843-2747-411c-8ab4-7e97a1c9c3b4","key":"activeTheme","value":"casper","type":"theme","created_at":1463567133166,"created_by":1,"updated_at":1473925171079,"updated_by":1}],"tags":[{"id":1,"uuid":"ecb77cf1-1be2-443a-b555-f1c1fa8992fb","name":"get-started","slug":"get-started","description":"get-started","image":"","hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463567128109,"created_by":1,"updated_at":1463754330921,"updated_by":1},{"id":2,"uuid":"f73b4d4f-12cc-498c-a159-be13b472fec7","name":"redux源码分析系列","slug":"redux","description":"","image":"/content/images/2016/05/687474703a2f2f692e696d6775722e636f6d2f4a65567164514d2e706e67-1.png","hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463754046804,"created_by":1,"updated_at":1463817817169,"updated_by":1},{"id":3,"uuid":"2ce8db62-0a14-47c4-b1b8-1e3b920c420a","name":"life","slug":"life","description":"关于生活","image":"/content/images/2016/05/u-1687420627-4175839764-fm-21-gp-0.jpg","hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463817797112,"created_by":1,"updated_at":1463817887321,"updated_by":1},{"id":4,"uuid":"495550a6-a385-4ff4-8e4f-afb9870f94fa","name":"乱七八糟","slug":"digression","description":"杂谈","image":"/content/images/2016/05/u-1468361829-4039309630-fm-116-gp-0.jpg","hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463834283409,"created_by":1,"updated_at":1463834926309,"updated_by":1},{"id":5,"uuid":"4723a273-0582-4a37-842d-c771a73f85f8","name":"mongodb","slug":"mongodb","description":"","image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463838088406,"created_by":1,"updated_at":1463838094311,"updated_by":1},{"id":6,"uuid":"31ada047-4501-406d-917f-488e23b03f36","name":"mac","slug":"mac","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463838131596,"created_by":1,"updated_at":1463838131596,"updated_by":1},{"id":7,"uuid":"91b7d980-5ce6-4340-aaf1-9a6b5dc80667","name":"koa","slug":"koa","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463838270749,"created_by":1,"updated_at":1463838270749,"updated_by":1},{"id":8,"uuid":"be14e3b0-fb8f-4924-81b5-0924b220508c","name":"nodejs","slug":"nodejs","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1463838276529,"created_by":1,"updated_at":1463838283586,"updated_by":1},{"id":9,"uuid":"c332f075-6919-4dd1-9b6e-cdd1b93e86db","name":"react","slug":"react","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1464247836082,"created_by":1,"updated_at":1464247836082,"updated_by":1},{"id":10,"uuid":"5d8a3fba-d4a7-4e50-856f-2b3034b30d5d","name":"git","slug":"git","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1464335179221,"created_by":1,"updated_at":1464335179221,"updated_by":1},{"id":11,"uuid":"3dc60c76-7ce0-44e2-93eb-da3879afc085","name":"eslint","slug":"eslint","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1464346513640,"created_by":1,"updated_at":1464346513640,"updated_by":1},{"id":12,"uuid":"5f5ac01a-3d8d-4fde-bf30-52f04398df03","name":"react native","slug":"react-native","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1466558983520,"created_by":1,"updated_at":1466558983520,"updated_by":1},{"id":13,"uuid":"f2a13e9c-f961-4e08-96be-16fcb05eca63","name":"人生","slug":"ren-sheng","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1467111340939,"created_by":1,"updated_at":1467111340939,"updated_by":1},{"id":14,"uuid":"985d4ca6-c298-4e23-83d8-93dac19d7987","name":"有趣视频","slug":"you-qu-shi-pin","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1467111340949,"created_by":1,"updated_at":1467111340949,"updated_by":1},{"id":15,"uuid":"768f7c77-48fa-40ee-9b14-b8dd4d8e7393","name":"翻译","slug":"fan-yi","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1467688909351,"created_by":1,"updated_at":1467688909351,"updated_by":1},{"id":16,"uuid":"f3ad56e4-023f-45ee-bdd1-2bd05ea6f8d2","name":"随笔","slug":"sui-bi","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1468460842999,"created_by":1,"updated_at":1468460842999,"updated_by":1},{"id":17,"uuid":"a3c86f17-4e46-41f9-b79e-e8f12c534a1b","name":"常识","slug":"chang-shi","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1468469618360,"created_by":1,"updated_at":1468469618360,"updated_by":1},{"id":18,"uuid":"257fd3de-50cc-4358-a378-78a6e7d561d0","name":"js","slug":"js","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1468896571000,"created_by":1,"updated_at":1468896571000,"updated_by":1},{"id":19,"uuid":"052b64f0-5ab2-4dad-a3a7-63351c98ff62","name":"策略模式","slug":"ce-lue-mo-shi","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1469029769238,"created_by":1,"updated_at":1469029769238,"updated_by":1},{"id":20,"uuid":"cbb3738c-77e3-43ac-bb21-cc594f49b866","name":"android","slug":"android","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1469436760798,"created_by":1,"updated_at":1469436760798,"updated_by":1},{"id":21,"uuid":"edc646b2-0dd0-4190-b271-7f3ed0b0ee6e","name":"mobx","slug":"mobx","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1470231894998,"created_by":1,"updated_at":1470231894998,"updated_by":1},{"id":22,"uuid":"7d673880-09c5-477c-86a3-67dedc86dea9","name":"业务","slug":"ye-wu","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1470842891947,"created_by":1,"updated_at":1470842891947,"updated_by":1},{"id":23,"uuid":"db41c8ca-827a-46d9-9e20-aaeca92b8431","name":"jersey","slug":"jersey","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1470999361155,"created_by":1,"updated_at":1470999361155,"updated_by":1},{"id":24,"uuid":"b6597668-99d0-4422-9119-85a2d695b7a7","name":"fetch","slug":"fetch","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1471011841089,"created_by":1,"updated_at":1471011841089,"updated_by":1},{"id":25,"uuid":"66f8d61d-43bc-4967-9ab3-3a185db4bf8a","name":"web","slug":"web","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1471071283575,"created_by":1,"updated_at":1471071283575,"updated_by":1},{"id":26,"uuid":"6b8cc1f3-e78a-4b21-8f5c-8c9c4db8e4fb","name":"http","slug":"http","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1471164888580,"created_by":1,"updated_at":1471164888580,"updated_by":1},{"id":27,"uuid":"b7239a66-063e-417f-b648-34a7a3d5c222","name":"缓存","slug":"huan-cun","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1471164888585,"created_by":1,"updated_at":1471164888585,"updated_by":1},{"id":28,"uuid":"279f3c3e-ec4b-4f0d-b919-d2455ac963f9","name":"电影","slug":"dian-ying","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1471365183357,"created_by":1,"updated_at":1471365183357,"updated_by":1},{"id":29,"uuid":"20d5dbf8-355b-4d34-8268-8f7ceb664174","name":"centos","slug":"centos","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1472051602556,"created_by":1,"updated_at":1472051602556,"updated_by":1},{"id":30,"uuid":"60bd7479-a26e-4d5e-a04f-10f4c98c26fd","name":"nginx","slug":"nginx","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1472051602566,"created_by":1,"updated_at":1472051602566,"updated_by":1},{"id":31,"uuid":"5d8b3007-0add-40c2-b757-30a3bfe60f94","name":"正则","slug":"zheng-ze","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1473748641758,"created_by":1,"updated_at":1473748641758,"updated_by":1},{"id":32,"uuid":"a3fe9f4c-c6b1-4c43-8bad-1f724b0293a9","name":"编译原理","slug":"bian-yi-yuan-li","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1474176633919,"created_by":1,"updated_at":1474176633919,"updated_by":1},{"id":33,"uuid":"be306d32-5729-4be5-bc2c-917f7dc2f50f","name":"日剧","slug":"ri-ju","description":null,"image":null,"hidden":0,"parent_id":null,"meta_title":null,"meta_description":null,"created_at":1474242547574,"created_by":1,"updated_at":1474242547574,"updated_by":1}],"posts_tags":[{"id":1,"post_id":1,"tag_id":1,"sort_order":0},{"id":2,"post_id":2,"tag_id":2,"sort_order":0},{"id":3,"post_id":3,"tag_id":4,"sort_order":0},{"id":4,"post_id":3,"tag_id":6,"sort_order":1},{"id":5,"post_id":4,"tag_id":5,"sort_order":0},{"id":6,"post_id":4,"tag_id":6,"sort_order":1},{"id":7,"post_id":5,"tag_id":7,"sort_order":0},{"id":8,"post_id":5,"tag_id":8,"sort_order":1},{"id":9,"post_id":6,"tag_id":4,"sort_order":0},{"id":10,"post_id":9,"tag_id":4,"sort_order":0},{"id":11,"post_id":8,"tag_id":9,"sort_order":0},{"id":12,"post_id":10,"tag_id":10,"sort_order":0},{"id":13,"post_id":11,"tag_id":11,"sort_order":0},{"id":14,"post_id":13,"tag_id":12,"sort_order":0},{"id":15,"post_id":15,"tag_id":12,"sort_order":0},{"id":16,"post_id":19,"tag_id":13,"sort_order":0},{"id":17,"post_id":19,"tag_id":14,"sort_order":1},{"id":18,"post_id":23,"tag_id":12,"sort_order":0},{"id":19,"post_id":12,"tag_id":9,"sort_order":0},{"id":20,"post_id":12,"tag_id":15,"sort_order":1},{"id":21,"post_id":24,"tag_id":9,"sort_order":0},{"id":22,"post_id":24,"tag_id":15,"sort_order":1},{"id":23,"post_id":27,"tag_id":16,"sort_order":0},{"id":24,"post_id":28,"tag_id":17,"sort_order":0},{"id":25,"post_id":30,"tag_id":18,"sort_order":0},{"id":26,"post_id":31,"tag_id":12,"sort_order":0},{"id":27,"post_id":31,"tag_id":18,"sort_order":1},{"id":28,"post_id":31,"tag_id":19,"sort_order":2},{"id":29,"post_id":34,"tag_id":20,"sort_order":1},{"id":30,"post_id":34,"tag_id":10,"sort_order":0},{"id":31,"post_id":33,"tag_id":12,"sort_order":0},{"id":32,"post_id":33,"tag_id":20,"sort_order":1},{"id":33,"post_id":35,"tag_id":12,"sort_order":0},{"id":34,"post_id":35,"tag_id":20,"sort_order":1},{"id":35,"post_id":37,"tag_id":12,"sort_order":0},{"id":36,"post_id":37,"tag_id":20,"sort_order":1},{"id":37,"post_id":39,"tag_id":16,"sort_order":0},{"id":38,"post_id":39,"tag_id":3,"sort_order":1},{"id":39,"post_id":32,"tag_id":13,"sort_order":0},{"id":40,"post_id":32,"tag_id":14,"sort_order":1},{"id":41,"post_id":42,"tag_id":9,"sort_order":0},{"id":42,"post_id":42,"tag_id":21,"sort_order":1},{"id":43,"post_id":44,"tag_id":13,"sort_order":0},{"id":44,"post_id":44,"tag_id":16,"sort_order":1},{"id":45,"post_id":44,"tag_id":3,"sort_order":2},{"id":46,"post_id":45,"tag_id":3,"sort_order":0},{"id":47,"post_id":45,"tag_id":13,"sort_order":1},{"id":48,"post_id":45,"tag_id":16,"sort_order":2},{"id":49,"post_id":36,"tag_id":22,"sort_order":0},{"id":50,"post_id":46,"tag_id":12,"sort_order":0},{"id":51,"post_id":26,"tag_id":23,"sort_order":0},{"id":52,"post_id":7,"tag_id":24,"sort_order":0},{"id":53,"post_id":7,"tag_id":12,"sort_order":1},{"id":54,"post_id":29,"tag_id":13,"sort_order":0},{"id":55,"post_id":29,"tag_id":3,"sort_order":1},{"id":56,"post_id":29,"tag_id":14,"sort_order":2},{"id":57,"post_id":47,"tag_id":25,"sort_order":0},{"id":58,"post_id":48,"tag_id":3,"sort_order":0},{"id":59,"post_id":48,"tag_id":13,"sort_order":1},{"id":60,"post_id":49,"tag_id":26,"sort_order":0},{"id":61,"post_id":49,"tag_id":27,"sort_order":1},{"id":62,"post_id":51,"tag_id":3,"sort_order":0},{"id":63,"post_id":51,"tag_id":13,"sort_order":1},{"id":64,"post_id":51,"tag_id":28,"sort_order":2},{"id":65,"post_id":52,"tag_id":10,"sort_order":0},{"id":66,"post_id":54,"tag_id":26,"sort_order":0},{"id":67,"post_id":54,"tag_id":29,"sort_order":1},{"id":68,"post_id":54,"tag_id":30,"sort_order":2},{"id":69,"post_id":55,"tag_id":26,"sort_order":0},{"id":70,"post_id":55,"tag_id":30,"sort_order":1},{"id":71,"post_id":55,"tag_id":29,"sort_order":2},{"id":72,"post_id":56,"tag_id":30,"sort_order":0},{"id":73,"post_id":56,"tag_id":26,"sort_order":1},{"id":74,"post_id":56,"tag_id":29,"sort_order":2},{"id":75,"post_id":50,"tag_id":16,"sort_order":0},{"id":76,"post_id":50,"tag_id":3,"sort_order":1},{"id":77,"post_id":50,"tag_id":13,"sort_order":2},{"id":78,"post_id":57,"tag_id":18,"sort_order":0},{"id":79,"post_id":60,"tag_id":18,"sort_order":0},{"id":80,"post_id":62,"tag_id":18,"sort_order":0},{"id":81,"post_id":62,"tag_id":31,"sort_order":1},{"id":82,"post_id":63,"tag_id":18,"sort_order":0},{"id":83,"post_id":58,"tag_id":18,"sort_order":0},{"id":84,"post_id":64,"tag_id":13,"sort_order":1},{"id":85,"post_id":64,"tag_id":3,"sort_order":2},{"id":86,"post_id":64,"tag_id":16,"sort_order":3},{"id":87,"post_id":65,"tag_id":16,"sort_order":0},{"id":88,"post_id":65,"tag_id":13,"sort_order":1},{"id":89,"post_id":65,"tag_id":3,"sort_order":2},{"id":90,"post_id":66,"tag_id":18,"sort_order":0},{"id":91,"post_id":68,"tag_id":32,"sort_order":0},{"id":92,"post_id":69,"tag_id":32,"sort_order":0},{"id":93,"post_id":67,"tag_id":33,"sort_order":0},{"id":94,"post_id":64,"tag_id":33,"sort_order":0},{"id":95,"post_id":70,"tag_id":33,"sort_order":0},{"id":96,"post_id":71,"tag_id":32,"sort_order":0},{"id":97,"post_id":72,"tag_id":18,"sort_order":0},{"id":98,"post_id":73,"tag_id":18,"sort_order":0}],"apps":[],"app_settings":[],"app_fields":[],"client_trusted_domains":[]}}]}