<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[一刀空间]]></title><description><![CDATA[哈喽~欢迎光临]]></description><link>https://space.snailuu.cn</link><image><url>https://space.snailuu.cn/innei.svg</url><title>一刀空间</title><link>https://space.snailuu.cn</link></image><generator>Shiro (https://github.com/Innei/Shiro)</generator><lastBuildDate>Thu, 23 Apr 2026 11:38:53 GMT</lastBuildDate><atom:link href="https://space.snailuu.cn/feed" rel="self" type="application/rss+xml"/><pubDate>Thu, 23 Apr 2026 11:38:52 GMT</pubDate><language><![CDATA[zh-CN]]></language><item><title><![CDATA[koa-router 在 ctx 获取不到 params]]></title><description><![CDATA[<link rel="preload" as="image" href="http://oss.snailuu.cn/picgo/1733884885525-040f7190-2550-47b4-9c91-65df62e8facc.png"/><link rel="preload" as="image" href="http://oss.snailuu.cn/picgo/1733885095002-f1256982-b2f4-4829-924b-ca77a2458583.png"/><link rel="preload" as="image" href="http://oss.snailuu.cn/picgo/1733885787718-2b42e065-4959-4ae2-95e4-22e07f64b13d.png"/><div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://space.snailuu.cn/posts/backend/3">https://space.snailuu.cn/posts/backend/3</a></blockquote><div><h2 id="">原因</h2><p><code>ctx.params</code> 是由 <code>koa-router</code>注入的，但 <code>koa-router</code>是一个路由中间件，而 <code>api-params-verify</code>是全局中间件，在执行顺序上全局中间件早于路由中间件，所以获取不到 <code>ctx.params</code></p><h2 id="">解决方案</h2><h3 id="">将全局中间件改为改为路由中间件</h3><p>取消全局中间件的加载</p><p><img src="http://oss.snailuu.cn/picgo/1733884885525-040f7190-2550-47b4-9c91-65df62e8facc.png"/></p><p>在具体需要进行校验的路由控制器绑定参数校验中间件(<code>apiParamsVerify</code>)</p><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript">module.exports = (app, router) =&gt; {
  const { project: projectController } = app.controller;

  const apiParamsVerify = app.middlewares.apiParamsVerify;

  router.get(&quot;/api/project/list/:params_data/list&quot;, apiParamsVerify, projectController.getList.bind(projectController));
}</code></pre><p>简单的发送请求验证结果</p><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript">axios.request({
  method: &#x27;get&#x27;,
  url: &#x27;/api/project/list/2/list&#x27;,
  params: {
    params_data_1: 1
  },
})</code></pre><p><img src="http://oss.snailuu.cn/picgo/1733885095002-f1256982-b2f4-4829-924b-ca77a2458583.png"/></p><p>看到控制台可以分别拿到 <code>path</code>、<code>query</code>等参数的信息</p><p><strong>缺点</strong>：每次想要进行参数校验的时候，在每个控制器路由请求都需要绑定中间件(太麻烦)，可以抽离之前的校验，分成 <code>path</code>参数校验和其他(<code>header</code>、<code>query</code>)参数校验，将 <code>path</code>参数校验逻辑抽离成一个新的中间件 <code>apiPathVerify</code>，在需要进行一些路径参数校验的控制器进行校验即可。</p><h3 id="">调整参数校验中间件执行顺序</h3><p><img src="http://oss.snailuu.cn/picgo/1733885787718-2b42e065-4959-4ae2-95e4-22e07f64b13d.png"/></p><p>问题的产生是因为 洋葱模型 中的加载，全局中间件会比路由中间件先加载，所以可以修改等路由加载完再来进行参数校验。</p>
<h2 id="">总结</h2><ol start="1"><li>如果执行顺序不想改变的可以抽离单独的 <code>path</code> 校验中间件，在单独需要进行校验的 <code>api</code> 进行拦截</li><li>如果不想改动太大代码的修改校验执行时机</li></ol></div><p style="text-align:right"><a href="https://space.snailuu.cn/posts/backend/3#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://space.snailuu.cn/posts/backend/3</link><guid isPermaLink="true">https://space.snailuu.cn/posts/backend/3</guid><dc:creator><![CDATA[snailuu]]></dc:creator><pubDate>Wed, 11 Dec 2024 03:05:49 GMT</pubDate></item><item><title><![CDATA[修行elips-core第一章——了解与实践]]></title><description><![CDATA[<link rel="preload" as="image" href="http://oss.snailuu.cn/picgo/1733756953667-3dd5f52b-6b1f-4765-b6ec-76c143909195.png"/><link rel="preload" as="image" href="http://oss.snailuu.cn/picgo/1733757694683-d405db58-4594-4550-9f92-1e0f2e283fbd.png"/><link rel="preload" as="image" href="http://oss.snailuu.cn/picgo/1733758685725-fbca8c0c-a4ca-4971-8d07-280a9f30ad03.jpeg"/><link rel="preload" as="image" href="http://oss.snailuu.cn/picgo/1733758380498-79c3e07c-8302-4e94-a8ff-1381a766f4f8.png"/><link rel="preload" as="image" href="http://oss.snailuu.cn/picgo/1733760028906-9b65378d-8ae5-4da4-a6fa-d14621929cc3.jpeg"/><div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://space.snailuu.cn/posts/backend/2">https://space.snailuu.cn/posts/backend/2</a></blockquote><div><h2 id="">相识</h2><p>作为一名程序猿👨‍🦲，最熟悉的两个按键那就是 <strong>CV </strong>了，这种 <strong>CV</strong> 不止是从网上或者找 AI 寻找答案后复制粘贴到我们的项目代码中，还有一层意思也是：在一个项目中重复的代码为了节省时间直接 复制粘贴 修改一些字段然后进行接口对接🙋‍♂️，然后就愉快😆的提交代码了。但这样确实一时爽，后面要改功能，增加新功能之类的都会让你如履薄冰🥶。</p><p>之后为了防止我的石山越长越高，就会将重复的功能抽离出来，在基本的功能之外就会对其想一想🤔还有没有可能拓展的功能？尽量就把他抽离出来，后续 <strong>leader</strong> 呼唤要修改功能的时候，就不用“到处跑”了。</p><p>上面提到的是作为在开发阶段，我对我自己的代码进行了一些 “抽离” 来使我后面的开发更简便，但还有一种 “魔法”，这个修改就不是在开发时的  “抽离” 了，转变而成的它 居然😲在编译的时候就进行一些的 “魔法攻击”，它有一个响亮的名字—— Loader。</p><h2 id="">相遇</h2><p>本期的主人公—— Elpis-core。作为一名编译时的魔法攻击者，他会在应用启动之前，读取所有相关<strong>约定好</strong>的规则来进行自动加载组件和各种配置信息👍，除此之外还有其他优点，如：</p><ol start="1"><li>约定大于配置：通过固定文件目录结构和命名规范 -&gt; 自动加载模块，从而减少手动的 <code>import</code></li><li>模块化管理：每个功能模块独立管理，诸如路由模块、中间件、服务层、控制器、配置文档等，便于功能维护与拓展</li><li>灵活性：支持自定义扩展(功能、配置、模块等)</li></ol><h2 id="">相知</h2><p>看到这里，是不是也跟我一样（小白吃惊😮），居然还能这么搞 <strong>懒人工具。</strong></p><blockquote><p>&quot;懒&quot;是一些需求的根源</p><p>——爱偷懒的程序员</p></blockquote>
<p>接下来，我准备与你来分享这个 <strong>魔法</strong> 背后的秘密（一点点吧，主要我也是刚开始学习🤣）</p><h3 id="">目录结构</h3><p><img src="http://oss.snailuu.cn/picgo/1733756953667-3dd5f52b-6b1f-4765-b6ec-76c143909195.png"/></p><p>整个项目的<strong>入口<em></em></strong>🚪<em></em>就是根目录下的 <code>index.js</code>。</p><p><img src="http://oss.snailuu.cn/picgo/1733757694683-d405db58-4594-4550-9f92-1e0f2e283fbd.png"/></p><h3 id="loader">Loader</h3><p>在 <code>ElipisCore</code> 中就是一个加载器系统(<code>Loader</code>)，主要作用就是在应用启动时自动加载和初始化各种配置信息。</p><p><img src="http://oss.snailuu.cn/picgo/1733758685725-fbca8c0c-a4ca-4971-8d07-280a9f30ad03.jpeg" alt="画板"/></p><p>在 <code>Loader</code>下的每个子模块都是在约定下去指定目录寻找指定文件进行加载</p><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript">├── app/                    # 业务代码目录
│   ├── controller/        # 控制器层：处理请求响应
│   ├── service/          # 服务层：业务逻辑
│   ├── middleware/       # 中间件：请求处理管道
│   ├── router-schema/    # API 参数校验规则
│   └── extend/          # 功能扩展
└── elpis-core/           # 框架核心
    └── loader/          # 自动加载器</code></pre><p>比如拿一个我们作为萌新😳最容易理解的 <code>controller loader</code>。</p><p><img src="http://oss.snailuu.cn/picgo/1733758380498-79c3e07c-8302-4e94-a8ff-1381a766f4f8.png"/></p><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript"> /*
 * 例子:
 * app/controller
 *  |
 *  | -- custom-module
 *    | 
 *    | -- custom-controller.js
 * 
 *  =&gt; app.controller.customModule.customController
*/</code></pre><p>至此，我们的装备🗡已经做好归类，想要什么类型就能快速定位，但面对强大的怪物，还得再做一套完整的计划。</p><h3 id="app">app</h3><p>从 <code>Loader</code>出来之后我们就已经是又有🛡又有🗡，但不会用呀💭</p><p>这时候就要介绍我们的好邻居们（中间件、路由、控制器、服务层等），他们每个家族的🏠住址就在 <code>app</code>中，而且他们每个家族里面也都有好多性（类）格（型）的兄弟姐妹，比如中间价家族就还有分<em>异常处理部落</em>、<em>api校验部落</em>和<em>api签名认证部落</em>等，为什么说他们是我的好邻居呢？因为在外头有人要挑事他们总会挡在我前面，其中就<strong>中间件家族</strong>最仗义了，总是冲在最前面。</p><p><img src="http://oss.snailuu.cn/picgo/1733760028906-9b65378d-8ae5-4da4-a6fa-d14621929cc3.jpeg" alt="画板"/></p><h2 id="">相伴</h2><p>由上面介绍应该可以知道我们面对不同等级的困难也有了一定的应对措施，但回到现实(🐂🐎)，这对开发又有什么好处呢？</p><p>问题又回到了一开始的我们的<strong>相遇</strong>，优点已经大概罗列的，但</p><blockquote><p>规矩是死的，人是活的。</p></blockquote>
<p>虽然有了良好的开发规范和约定，但还是得靠我们自己遵守，其次就是👆不要逼得太紧，🐕急了都会跳墙呢，更何况人呢？所以约定在紧急关头上🤔，我觉得还是不太管用的。</p><p>但至少我们知道能这样做！可以来提高我们的开发效率和降低维护成本，这已经就相当不错啦~</p></div><p style="text-align:right"><a href="https://space.snailuu.cn/posts/backend/2#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://space.snailuu.cn/posts/backend/2</link><guid isPermaLink="true">https://space.snailuu.cn/posts/backend/2</guid><dc:creator><![CDATA[snailuu]]></dc:creator><pubDate>Mon, 09 Dec 2024 16:15:15 GMT</pubDate></item><item><title><![CDATA[我对js隐式转换终于不用再靠_猜_了！]]></title><description><![CDATA[<link rel="preload" as="image" href="https://cdn.nlark.com/yuque/0/2024/jpeg/29265191/1723990372349-1a87f168-e29b-468c-b7f8-785c797a4878.jpeg"/><div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://space.snailuu.cn/posts/language/1">https://space.snailuu.cn/posts/language/1</a></blockquote><div><h1 id="">一、前言</h1><p>在项目开发或者面试中经常出现下面这种判断：</p><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript">console.log({} - {})
console.log([] - [])
console.log([] + [1, 2])
console.log([] == ![])
console.log({} == {})</code></pre><p>结果分别是：<code>NaN、0、1,2、true、false</code></p><h1 id="">二、包装类</h1><font style="color:rgb(37, 41, 51)">包装类是我们理解隐式转化的基础工具,是理解隐式转化的根基。</font><p>js中包装类分为：Boolean()、Number()、String()。</p><h2 id="boolean">Boolean()</h2><p><code>Boolean()</code>只有两种值：<code>true</code>和<code>false</code></p><p>其中值为<code>false</code>的称为<code>&lt;font style=&quot;color:rgb(37, 41, 51);&quot;&gt;falsey&lt;/font&gt;</code><font style="color:rgb(37, 41, 51)">或<code>虚值</code>，分别包括0、null、undefined、false、&#x27;&#x27;，NaN；</font></p><font style="color:rgb(37, 41, 51)">而值为</font>`<font style="color:rgb(37, 41, 51)">true</font>`<font style="color:rgb(37, 41, 51)">的称为</font>`<font style="color:rgb(37, 41, 51)">truth</font>`<font style="color:rgb(37, 41, 51)">，除了</font>`<font style="color:rgb(37, 41, 51)">falsey</font>`<font style="color:rgb(37, 41, 51)">其他的返回都是</font>`<font style="color:rgb(37, 41, 51)">true</font>`
<h2 id="number">Number()</h2><p><code>Number()</code>的处理就分为对 基础类型 的处理和 引用类型 的处理。</p><h3 id="">基础类型</h3><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript">console.log(Number(null)) // 0
console.log(Number(undefined)) // MaM
console.log(Number(&#x27;1&#x27;)) // 1
console.log(Number(&#x27;&#x27;)) // 0
console.log(Number(NaN)) // NaN
console.log(Number(&#x27;1ab&#x27;) // NaN
console.log(Number(false)) // 0
console.log(Number(true)) // 1</code></pre><p>其中为<code>NaN</code>的数值分别有<code>undefined</code>、<code>null</code>、<code>&#x27;1ab&#x27;</code></p><p><code>null</code>表示&quot;没有对象&quot;，即该处不应该有值。 <code>undefined</code>表示&quot;缺少值&quot;，就是此处应该有一个值，但是还没有定义。</p><p>而<code>&#x27;1ab&#x27;</code>为<code>NaN</code>是因为字符串转换中出现了非数字的就会转换成<code>NaN</code></p><h3 id="">引用类型</h3><p>假如我们现在有下面这么一个对象：</p><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript">  const obj = {
    toString() {
      return 2
    },
    valueOf() {
      return 1
    }
  }</code></pre><p>如果用<code>Number()</code>去取值的时候就会调用到<code>valueOf()</code>方法，此时会返回 1 ，但如果改写 <code>valueOf() { return {} }</code>的话就会去调用 <code>toString()</code>， 此时返回 2</p><p>所以<code>Number(引用类型)</code>的逻辑过程就是：</p><p><img src="https://cdn.nlark.com/yuque/0/2024/jpeg/29265191/1723990372349-1a87f168-e29b-468c-b7f8-785c797a4878.jpeg" alt="画板"/></p><h3 id="string">String()</h3><p><code>String()</code>就是跟<code>Number()</code>相反，先调用<code>toString()</code>然后再看<code>valueOf()</code></p><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript">const obj = {
    toString() {
      return {}
    },
    valueOf() {
      return 1    
    }
}
console.log(String(obj)) // 1
console.log(String({})) // [object Object]</code></pre><p><strong>3 &lt; 2 &lt; 1 和 2 &lt; 1 &lt; 1</strong></p><p>两个输出都是<code>true</code>，第一个符合数学公式，第二个就涉及到隐式转换了</p><ol start="1"><li>比较 2 &lt; 1  结果为 fasle</li><li>现在就是 false &lt; 1 ，Number(false) 为0</li><li>现在就是 0 &lt; 1， 结果为 true</li></ol><h1 id="">三、隐式转换规则</h1><h2 id="boolean-">boolean 隐式转换触发</h2><ol start="1"><li>if</li><li>switch</li><li>while</li><li>for(;;)</li><li>&amp;&amp;</li><li>||</li><li>!</li><li>!!</li><li>? : 三元运算</li></ol><h2 id="number-">number 隐式转换触发</h2><p>用到数字运算符的地方都会进行转换：<code>+ - * / == ~~ &amp; | ~ ^ &lt;&lt; &lt;&lt;&lt;</code></p><h2 id="string-">string 隐式转换触发</h2><p><code>+</code> 且两边大于等于1个<code>string</code>类型. 除了有 <code>symbol</code>类型之外</p><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript">console.log(1 + &#x27;2&#x27; + &#x27;2&#x27;) // &#x27;122&#x27;
console.log(1 + + &#x27;2&#x27; + &#x27;2&#x27;) // &#x27;32&#x27;
console.log(&#x27;A&#x27; - &#x27;B&#x27; + &#x27;2&#x27;) // &#x27;NaN2&#x27;
console.log(&#x27;A&#x27; - &#x27;B&#x27; + 2) // NaN</code></pre><ol start="1"><li>用 + 包裹，全部就转换为字符串后相加操作</li><li>+&#x27;2&#x27; 会先进行 number 转换，变成1 + 2 = 3，然后遇到 + 在进行string转换</li><li>&#x27;A&#x27; - &#x27;B&#x27; 会先进行 number 转换，变成 NaN，然后string转换</li><li>同上</li></ol><h1 id="">四、需要注意的点</h1><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript">console.log((123).toString()) // 123
console.log(undefined.toString()) // 报错
console.log(null.toString()) // 报错</code></pre><p><code>undefined</code>和<code>null</code>并没有包装类,它们是基础类型,所以没有<code>toString()</code>方法</p>
<h1 id="">五、面试题</h1><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript">console.log([] == ![]) // true</code></pre><ol start="1"><li>一看到 <code>==</code> 就会想到两边会进行 <code>Number</code>转换</li><li><code>Number([])</code> 会调用到 <code>Array.prototype.toSting()</code>，得到的是一个空字符串 <code>&#x27;&#x27;</code> ，用<code>Number</code>转换后就会变成0</li><li>右边 <code>![]</code>会先进行 <code>Boolean</code> 转换，<code>[]</code> 不是 <code>falsey</code>，所以<code>Boolean([])</code>为 <code>true</code></li><li><code>!true</code> 再进行<code>Numebr</code> 转换就会变成 0</li><li>所以最终就是<code>0 == 0</code>，结果为true</li></ol><h1 id="">六、奇奇怪怪的题目</h1><pre class="language-javascript lang-javascript"><code class="language-javascript lang-javascript">66.toString() // 报错: Invalid or unexpected token
66..toString() // &#x27;66&#x27;
66.6.toString() // &#x27;66.6&#x27;
66...toString() // 报错: Unexpected token &#x27;.&#x27;</code></pre></div><p style="text-align:right"><a href="https://space.snailuu.cn/posts/language/1#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://space.snailuu.cn/posts/language/1</link><guid isPermaLink="true">https://space.snailuu.cn/posts/language/1</guid><dc:creator><![CDATA[snailuu]]></dc:creator><pubDate>Sat, 07 Dec 2024 07:26:07 GMT</pubDate></item></channel></rss>