Files
DefectingCat.github.io/defect/function-of-javascript.html
DefectingCat 8c7085f18f
2020-11-02 02:17:54 +00:00

303 lines
50 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html><html lang="zh-CN" data-default-color-scheme="&#34;auto&#34;"><head><meta charset="UTF-8"><link rel="apple-touch-icon" sizes="76x76" href="/images/img/apple-touch-icon.webp"><link rel="icon" type="image/png" href="/images/img/favicon.webp"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,shrink-to-fit=no"><meta http-equiv="x-ua-compatible" content="ie=edge"><meta name="theme-color" content="#9DC8C8"><meta name="description" content=""><meta name="author" content="Defectink"><meta name="keywords" content=""><title>JavaScript的函数 - 🍭Defectink</title><link rel="stylesheet" href="https://cdn.defectink.com/static/twitter-bootstrap/4.5.3/css/bootstrap.min.css"><link rel="stylesheet" href="https://cdn.defectink.com/static/github-markdown-css/4.0.0/github-markdown.min.css"><link rel="stylesheet" href="/lib/hint/hint.min.css"><link rel="stylesheet" href="https://cdn.defectink.com/static/highlight.js/10.0.0/styles/github-gist.min.css"><link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_ba1fz6golrf.css"><link rel="stylesheet" href="https://cdn.defectink.com/static/t/font_1736178_kmeydafke9r.css"><link rel="stylesheet" href="/css/main.css"><link rel="stylesheet" href="/css/xfy.css"><script src="/js/utils.js"></script><script src="/js/color-schema.js"></script><meta name="generator" content="Hexo 5.2.0"><link rel="alternate" href="/xml/atom.xml" title="🍭Defectink" type="application/atom+xml"><link rel="alternate" href="/xml/rss.xml" title="🍭Defectink" type="application/rss+xml"></head><body><header style="height:75vh"><nav id="navbar" class="navbar fixed-top navbar-expand-lg navbar-dark scrolling-navbar"><div class="container"><a class="navbar-brand" href="/">&nbsp;<strong>🍭Defectink</strong>&nbsp;</a> <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><div class="animated-icon"><span></span><span></span><span></span></div></button><div class="collapse navbar-collapse" id="navbarSupportedContent"><ul class="navbar-nav ml-auto text-center"><li class="nav-item"><a class="nav-link" href="/">🏠 首页</a></li><li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">📕 索引</a><div class="dropdown-menu" aria-labelledby="navbarDropdown"><a class="dropdown-item" href="/categories/">🎁 分类</a> <a class="dropdown-item" href="/tags/">🎐 标签</a></div></li><li class="nav-item"><a class="nav-link" href="/archives/">📂 归档</a></li><li class="nav-item"><a class="nav-link" href="/about/">🎃 关于</a></li><li class="nav-item"><a class="nav-link" href="/links/">🙆‍♀️ 小伙伴</a></li><li class="nav-item"><a class="nav-link" href="/pgp/">🔐 PGP</a></li><li class="nav-item" id="search-btn"><a class="nav-link" data-toggle="modal" data-target="#modalSearch">&nbsp;<i class="iconfont icon-search"></i>&nbsp;</a></li><li class="nav-item" id="color-toggle-btn"><a class="nav-link" href="javascript:">&nbsp;<i class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a></li></ul></div></div></nav><div class="banner intro-2" id="background" parallax="true" style="background:url(/images/img/post.webp) no-repeat center center;background-size:cover"><div class="full-bg-img"><div class="mask flex-center" style="background-color:rgba(0,0,0,.3)"><div class="container page-header text-center fade-in-up"><span class="h2" id="subtitle"></span><div class="mt-3"><span class="post-meta mr-2"><i class="iconfont icon-author" aria-hidden="true"></i> Defectink</span><span class="post-meta"><i class="iconfont icon-date-fill" aria-hidden="true"></i> <time datetime="2020-08-07 11:26" pubdate>2020年8月7日 上午</time></span></div><div class="mt-1"><span class="post-meta mr-2"><i class="iconfont icon-chart"></i> 5.5k 字</span><span class="post-meta mr-2"><i class="iconfont icon-clock-fill"></i> 65 分钟</span></div></div></div></div></div></header><main><div class="container-fluid"><div class="row"><div class="d-none d-lg-block col-lg-2"></div><div class="col-lg-8 nopadding-md"><div class="container nopadding-md" id="board-ctn"><div class="py-5" id="board"><article class="post-content mx-auto" id="post"><h1 style="display:none">JavaScript的函数</h1><p class="note note-info">本文最后水于2020年11月2日 凌晨</p><div class="markdown-body" id="post-body"><p>函数表达式是JavaScript中一个强大同时容易令人困惑的特性。</p><p>定义函数的方式有两种:函数声明和函数表达式。</p><p>函数声明首先是<code>function</code>关键字,其后是函数的名字,这就是指定函数名字的方式。</p><pre><code class="hljs js"><span class="hljs-comment">// 函数声明</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">&#x27;Hi, there&#x27;</span>);
&#125;</code></pre><p>关于函数声明,其一个重要特征就是<strong>函数声明体提升</strong>。它和用<code>var</code>声明提升类似,但他是整体提升,所以可以在声明前使用。</p><pre><code class="hljs js"><span class="hljs-comment">// 函数声明提升</span>
test();
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">&#x27;Hi, there&#x27;</span>);
&#125;</code></pre><p>函数表达式创建函数有一点不同,它类似于声明一个变量,使用<code>var</code>或者<code>let</code>来声明一个函数体。</p><p><code>let</code>关键字后的变量名就是函数的名字,而函数后没有名称,这个函数称为匿名函数。可以理解为将这个匿名函数赋值给了这个变量。</p><pre><code class="hljs js"><span class="hljs-comment">// 函数表达式</span>
<span class="hljs-keyword">let</span> test = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">&#x27;Yo, there&#x27;</span>);
&#125;</code></pre><p>函数表达式与其他表达式一样,需要先声明才能使用。尽管<code>var</code>创建的变量会声明提升,但是赋值并不会提升。</p><pre><code class="hljs js">test(); <span class="hljs-comment">// 函数还未声明</span>
<span class="hljs-keyword">let</span> test = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">&#x27;something...&#x27;</span>);
&#125;</code></pre><p>在早期的环境里函数声明和if语句可能还不能工作的和契合。不过现在多数浏览器与运行环境都能正常执行如下代码</p><pre><code class="hljs js"><span class="hljs-keyword">if</span> (<span class="hljs-number">1</span>) &#123;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hi</span>(<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">&#x27;hi&#x27;</span>);
&#125;
&#125; <span class="hljs-keyword">else</span> &#123;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hi</span>(<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">&#x27;yo&#x27;</span>);
&#125;
&#125;</code></pre><p>我的猜测可能是和函数声明提升有关因为js没有块级作用域所以在if语句里根据条件的函数声明都提升了导致就算条件满足最后声明的还是下面的函数声明。</p><p>如果真的有不能运行的环境,可以做这样的尝试。因为提前声明了变量,在将匿名函数赋值给变量,所以就不存在函数声明提升这个特性了。</p><pre><code class="hljs js"><span class="hljs-keyword">let</span> hi;
<span class="hljs-keyword">if</span> (<span class="hljs-number">1</span>) &#123;
hi = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">&#x27;hi&#x27;</span>);
&#125;
&#125; <span class="hljs-keyword">else</span> &#123;
hi = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">&#x27;yo&#x27;</span>);
&#125;
&#125;</code></pre><h2 id="递归"><a href="#递归" class="headerlink" title="递归"></a>递归</h2><p>递归函数是在一个函数通过名字调用自身的情况下构成的:</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add</span>(<span class="hljs-params">num</span>) </span>&#123;
<span class="hljs-keyword">if</span> (num &lt;= <span class="hljs-number">1</span>) &#123;
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
&#125;
<span class="hljs-keyword">return</span> num + add(num - <span class="hljs-number">1</span>);
&#125;</code></pre><p>这是一个经典的递归实例,但是如果将函数名更换一下。并将前一个函数名给解除引用,就会导致函数出错。这是因为函数内部的递归是通过函数名来引用这个函数的。如果更换了函数名,就会导致这种错误。</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add</span>(<span class="hljs-params">num</span>) </span>&#123;
<span class="hljs-keyword">if</span> (num &lt;= <span class="hljs-number">1</span>) &#123;
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
&#125;
<span class="hljs-keyword">return</span> num + add(num - <span class="hljs-number">1</span>);
&#125;
<span class="hljs-keyword">let</span> minus = add;
add = <span class="hljs-literal">null</span>;
<span class="hljs-built_in">console</span>.log(minus(<span class="hljs-number">5</span>));
<span class="hljs-comment">// return num + add(num - 1);</span>
<span class="hljs-comment">// ^</span>
<span class="hljs-comment">// TypeError: add is not a function</span>
</code></pre><p>但这并不影响递归,可以换种方式来继续引用函数自身。<code>arguments.callee</code>就是指向正在执行的函数的指针,因此可以用它来实现对函数递归的调用。从而与函数名拜托关系。</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add</span>(<span class="hljs-params">num</span>) </span>&#123;
<span class="hljs-keyword">if</span> (num &lt;= <span class="hljs-number">1</span>) &#123;
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
&#125;
<span class="hljs-keyword">return</span> num + <span class="hljs-built_in">arguments</span>.callee(num - <span class="hljs-number">1</span>);
&#125;
<span class="hljs-keyword">let</span> minus = add;
add = <span class="hljs-literal">null</span>;
<span class="hljs-built_in">console</span>.log(minus(<span class="hljs-number">5</span>));</code></pre><p><code>arguments.callee</code>不能在strict下使用。不过可以通过命名函数表达式来达到相同的效果。</p><p>在使用函数表达式时,使用小括号可以为函数体添加一个任然有效的名称。再将这个函数赋值给这个变量,无论函数怎么更换引用,函数名任然有效。这种方式再严格模式和非严格模式都行得通。</p><pre><code class="hljs js"><span class="hljs-keyword">let</span> add = (<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">a</span>(<span class="hljs-params">num</span>) </span>&#123;
<span class="hljs-keyword">if</span> (num &lt;= <span class="hljs-number">1</span>) &#123;
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
&#125;
<span class="hljs-keyword">return</span> num + a(num - <span class="hljs-number">1</span>);
&#125;)</code></pre><h2 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h2><p>闭包就是在一个函数的内部返回另一个函数返回的函数将还能访问外部函数的AO。</p><p>在compare这个函数里返回了另一个内部函数一个匿名函数这个匿名函数使用了compare函数的<code>prep</code>参数。即使这个函数被返回到了外部使用依然能访问到prep变量。这涉及到作用域链的细节。</p><p>当一个函数运行时被调用时会创建一个执行环境execution context及相应的作用域链然后使用arguments和其他参数的值来初始化函数的活动对象Activation Object。这个AO会随着作用域链链给内部的函数并使内部函数可以访问外部函数的变量。</p><p><a href="https://www.defectink.com/defect/javascript-scope-and-chain.html">JavaScript的作用域与链</a></p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">compare</span>(<span class="hljs-params">prep</span>) </span>&#123;
<span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">obj1, obj2</span>) </span>&#123;
<span class="hljs-keyword">let</span> o1 = obj1[prep];
<span class="hljs-keyword">let</span> o2 = obj2[prep];
<span class="hljs-keyword">if</span> (obj1[prep] &lt; obj2[prep]) &#123;
<span class="hljs-keyword">return</span> -<span class="hljs-number">1</span>;
&#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (obj1[prep] &gt; obj2[prep]) &#123;
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
&#125; <span class="hljs-keyword">else</span> &#123;
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>
&#125;
&#125;
&#125;</code></pre><p>当创建compare函数时会创建一个包含全局对象的作用域链这个作用域链被保存在内部的[[scope]]属性中。当调用<code>compare()</code>函数时,会为函数创建一个执行环境,然后赋值[[scope]]属性中的对象构建起执行环境的作用域链。</p><p>作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。</p><p>无论什么时候访问函数中的一个变量时就会从作用域链中搜索具有相应名字的变量。一般来说当函数执行完后局部活动对象就会被销毁内存中仅保留全局作用域全局执行环境的活动对象GO</p><p>但闭包的情况有所不同,当外部函数执行完后,其活动对象也不会被销毁,内部函数依然引用着外部函数的作用域链,。在将内部函数返回后,其执行环境的作用域链会被销毁,但是它的活动对象依然会留在内存中,直到匿名函数被销毁。</p><blockquote><p>由于闭包会携带着包含它的函数的作用域所以会占用更多的内存。虽然2020年不差内存且像v8等js引擎会尝试回收闭包的内存但还是推荐谨慎使用。</p></blockquote><h3 id="闭包与变量"><a href="#闭包与变量" class="headerlink" title="闭包与变量"></a>闭包与变量</h3><p>作用域链的机制以及没有块级作用域的特性引出了一个副作用即闭包只能取得外部函数AO中变量任何最后一个值。</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">cycle</span>(<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">let</span> arr = [];
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++) &#123;
arr[i] = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(i);
&#125;
&#125;
<span class="hljs-keyword">return</span> arr;
&#125;
<span class="hljs-keyword">let</span> test = cycle();
test[<span class="hljs-number">1</span>]();</code></pre><p>数组随着for循环将函数赋值到自身变量i会随着循环的增加而增加。但是函数被赋值时并没有执行等到函数被返回后在外部被执行时访问到的i已经是AO里的10了。</p><p>可以使用立即执行函数让i变量成为实参传入数组的函数内因为<strong>参数是按值传递的</strong>,这样在外部执行时,每个匿名函数中的变量都有一个副本。</p><p>立即执行函数在执行后会被销毁但是它的AO依然被内部的函数所引用。所以对应次数循环的函数内j就对应立即执行函数AO中的j且每个立即执行函数AO中的j都对应i因为每次的立即执行函数都不同。</p><p>可以使用立即执行函数来为内部的匿名函数再封装一个AO来解决此问题。</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">cycle</span>(<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">let</span> arr = [];
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++) &#123;
(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">o</span>) </span>&#123;
arr[o] = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(o);
&#125;
&#125;)(i);
&#125;
<span class="hljs-keyword">return</span> arr;
&#125;</code></pre><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">cycle</span>(<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">let</span> arr = [];
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++) &#123;
arr[i] = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">o</span>) </span>&#123;
<span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(o);
&#125;
&#125;)(i);
&#125;
<span class="hljs-keyword">return</span> arr;
&#125;
<span class="hljs-keyword">let</span> test = cycle();</code></pre><p>或者直接使用let来声明变量产生块级作用域在根本上解决问题。</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">cycle</span>(<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">let</span> arr = [];
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++) &#123;
arr[i] = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(i);
&#125;
&#125;
<span class="hljs-keyword">return</span> arr;
&#125;</code></pre><h3 id="关于this对象"><a href="#关于this对象" class="headerlink" title="关于this对象"></a>关于this对象</h3><p>在闭包中使用this对象可能会出现一些小问题。由于闭包返回的匿名函数是在外部执行的所以匿名函数的执行环境具有全局性因此它的this对象通常指向window。</p><pre><code class="hljs js"><span class="hljs-built_in">global</span>.name = <span class="hljs-string">&#x27;xfy&#x27;</span>;
<span class="hljs-comment">// window.name = &#x27;xfy&#x27;;</span>
<span class="hljs-keyword">let</span> obj = &#123;
name: <span class="hljs-string">&#x27;xxxfy&#x27;</span>,
age: <span class="hljs-number">18</span>,
say: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">this</span>.name);
&#125;
&#125;
&#125;
obj.say()();</code></pre><p>这个例子在非严格模式下返回的是全局对象的属性。那为什么匿名函数没有取得外部函数的this的值呢</p><p>每个函数在被调用时都会自动取得两个特殊的变量this和arguments。内部函数在搜索这两个变量时只会搜索到其活动对象为止因此永远不可能直接访问外部函数中的这两个变量在匿名函数没有定义形参时可以访问外部函数的实参</p><p>可以将this实体化保持在外部函数的AO中再由匿名函数去访问外部AO中的变量。即使在函数返回之后that也任然引用着obj。</p><pre><code class="hljs js"><span class="hljs-built_in">global</span>.name = <span class="hljs-string">&#x27;xfy&#x27;</span>;
<span class="hljs-comment">// window.name = &#x27;xfy&#x27;;</span>
<span class="hljs-keyword">let</span> obj = &#123;
name: <span class="hljs-string">&#x27;xxxfy&#x27;</span>,
age: <span class="hljs-number">18</span>,
say: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">let</span> that = <span class="hljs-built_in">this</span>;
<span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(that.name);
&#125;
&#125;
&#125;
obj.say()();</code></pre><p>有几种特殊的情况会导致this发生意外的变化。</p><pre><code class="hljs js"><span class="hljs-keyword">let</span> obj = &#123;
name: <span class="hljs-string">&#x27;xfy&#x27;</span>,
feature: <span class="hljs-string">&#x27;handsome&#x27;</span>,
say: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">this</span>.name);
&#125;
&#125;
<span class="hljs-built_in">global</span>.name = <span class="hljs-string">&#x27;dfy&#x27;</span>; <span class="hljs-comment">// 非严格模式下;</span>
<span class="hljs-comment">// let name = &#x27;dfy&#x27;;</span></code></pre><p>第一行就是平时最平常的调用了对象的方法this得到正确的引用。第二行在方法执行前加上了括号虽然加上了括号之后好像是在引用一个函数但this的值得到了维持。而第三行代码先执行了一条赋值语句然后再调用赋值后的结果。因为这个赋值表达式的值是函数本身所以this的值不能得到维持返回的就是全局对象里的属性了。</p><pre><code class="hljs js">obj.say(); <span class="hljs-comment">// xfy;</span>
(obj.say)(); <span class="hljs-comment">// xfy;</span>
(obj.say = obj.say)(); <span class="hljs-comment">// &#x27;dfy&#x27;;</span></code></pre><h3 id="内存泄漏"><a href="#内存泄漏" class="headerlink" title="内存泄漏"></a>内存泄漏</h3><p>在高贵的IE9之前的版本JScript对象和COM对象使用不同的辣鸡收集程序例程。那么在这些版本中的ie使用闭包就会导致一些特殊的问题。如果闭包的作用域中保存着一个HTML元素那么就意味着该元素无法被销毁。</p><p>这样的一个简单的示例为element元素事件创建了一个闭包而这个闭包又创建了一个循环引用。由于匿名函数保存了对getid函数的活动对象AO的引用因此就会导致无法减少element的引用数。只要匿名函数存在element引用数至少为1。所以它占用的内存就不会被回收。</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getid</span>(<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">let</span> element = docuemnt.getElementById(<span class="hljs-string">&#x27;test&#x27;</span>);
element.onclick = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
alert(element.id);
&#125;
&#125;</code></pre><p>不过只需要稍微改下就能够解决这个问题。将<code>element.id</code>保存在一个变量中并且在闭包中引用这个变量消除循环引用。但仅仅还不能解决内存泄漏的问题。闭包会引用包含函数的整个活动对象而其中包含着element。即使闭不引用element包含函数的活动对象中也仍然会保存一个引用。因此有必要把element设置为null。</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getid</span>(<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">let</span> element = docuemnt.getElementById(<span class="hljs-string">&#x27;test&#x27;</span>);
<span class="hljs-keyword">let</span> id = element.id;
element.onclick = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
alert(id);
&#125;
element = <span class="hljs-literal">null</span>;
&#125;</code></pre><h2 id="模仿块级作用域"><a href="#模仿块级作用域" class="headerlink" title="模仿块级作用域"></a>模仿块级作用域</h2><p>JavaScript是没有块级作用域的<code>let</code><code>const</code>声明的变量/常量是有的)。这就意味着块语句中定义的变量,实际上是在包含函数中而非语句中创建的。</p><p>在C艹、Java等语言中变量i只会在for循环的语句块中有定义一旦循环结束变量i就会被销毁。而在JS中变量i是由函数的活动对象所定义的使用<code>var</code>声明的变量将会在函数的AO里。并且在函数的任何位置都能访问它即使重新声明一次它的值也不会变。</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params">num</span>) </span>&#123;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; num; i++) &#123;
<span class="hljs-built_in">console</span>.log(i);
&#125;
<span class="hljs-keyword">var</span> i;
<span class="hljs-built_in">console</span>.log(i); <span class="hljs-comment">// 10</span>
<span class="hljs-built_in">console</span>.log(i); <span class="hljs-comment">// 10</span>
&#125;
test(<span class="hljs-number">10</span>);</code></pre><p>在现在看来,当然是推荐使用<code>let</code>来解决这个问题了。在for循环中使用<code>let</code>声明变量就会产生块级作用域。</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params">num</span>) </span>&#123;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; num; i++) &#123;
<span class="hljs-built_in">console</span>.log(i);
&#125;
<span class="hljs-built_in">console</span>.log(i); <span class="hljs-comment">// i is not defined</span>
&#125;
test(<span class="hljs-number">10</span>);</code></pre><p>不过在没有<code>let</code>关键字的时候,可以使用立即执行函数来模拟块级作用域(私有作用域)。与<code>let</code>的效果一样!</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params">num</span>) </span>&#123;
(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; num; i++) &#123;
<span class="hljs-built_in">console</span>.log(i);
&#125;
&#125;)()
<span class="hljs-built_in">console</span>.log(i); <span class="hljs-comment">// i is not defined</span>
&#125;
test(<span class="hljs-number">10</span>);</code></pre><p>JS将<code>function</code>关键字当作一个函数声明的开始,而函数声明后面不能跟园括号。然而函数表达式后面可以跟圆括号,要将函数声明转换为函数表达式,只需要加一对括号即可。</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-comment">// </span>
&#125;(); <span class="hljs-comment">// 语法错误;</span>
(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-comment">// </span>
&#125;)();</code></pre><p>模仿的块级作用域通常作用于函数外部的全局作用域,从而向全局作用域添加变量和函数,而不污染全局作用域。</p><pre><code class="hljs js"><span class="hljs-keyword">let</span> name = <span class="hljs-string">&#x27;xfy&#x27;</span>;
(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">let</span> name = <span class="hljs-string">&#x27;yyy&#x27;</span>;
<span class="hljs-built_in">console</span>.log(name);
&#125;)() <span class="hljs-comment">// yyy</span></code></pre><blockquote><p>这种做法可以减少闭包占用内存的问题,没有匿名函数的引用,只要函数执行完毕,就可以立即销毁其作用域了。</p></blockquote><h2 id="私有变量"><a href="#私有变量" class="headerlink" title="私有变量"></a>私有变量</h2><p>严格来说JS是没有私有成员的概念所有对象属性都是公开的。不过可以使用函数来实现私有变量的概念。任何在函数中定义的变量都可以认为是私有化变量因为在函数外部无法访问这些变量。私有变量包含函数的参数、局部变量和在函数内定义的其他函数。</p><p>在这个函数内部,有两个参数和一个局部变量。在函数的内部可以访问这些变量,而在函数外部除了主动返回之外则不能访问他们。如果在这个函数内创建一个闭包,那么这个闭包可以通过作用域链来访问到这些变量。利用这一点,就可以创建访问私有变量的共有方法。</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add</span>(<span class="hljs-params">num1, num2</span>) </span>&#123;
<span class="hljs-keyword">let</span> result = num1 + num2;
<span class="hljs-keyword">return</span> result;
&#125;</code></pre><p>可以利用构造函数的模式来创建私有和公有属性。带有this的属性将因为构造函数被返回到外部从而形成闭包。而构造函数内部其他的没有this的属性则不会被赋予到实例上。此时就只能通过定义的公有方法来访问私有的属性。</p><p>这种有权访问私有属性的公有方法称之为<strong>特权方法</strong>privileged method</p><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span>(<span class="hljs-params">name</span>) </span>&#123;
<span class="hljs-comment">// 私有变量</span>
<span class="hljs-keyword">let</span> age = <span class="hljs-number">18</span>;
<span class="hljs-comment">// 私有方法</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pv</span>(<span class="hljs-params"></span>) </span>&#123;
age++;
<span class="hljs-built_in">console</span>.log(age);
&#125;
<span class="hljs-comment">// 公有方法</span>
<span class="hljs-built_in">this</span>.say = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(name);
<span class="hljs-comment">// 通过公有方法执行私有方法</span>
pv();
&#125;
&#125;
<span class="hljs-keyword">let</span> xfy = <span class="hljs-keyword">new</span> Person(<span class="hljs-string">&#x27;xfy&#x27;</span>);
<span class="hljs-built_in">console</span>.log(xfy.name);
xfy.say();</code></pre><p>因为必须要使用构造函数,且还需要在构造函数内定义方法,所以这种方法一样有着和构造函数一样的缺点,每个实例都会创建一组同样的新方法。</p><h3 id="静态私有变量"><a href="#静态私有变量" class="headerlink" title="静态私有变量"></a>静态私有变量</h3><p>直接使用构造函数会导致方法被重新定义到每个实例。解决构造函数的这个问题的方法就是将方法定义在其原型上,而为了使原型上的方法能够访问到私有化的变量,可以通过在私有作用域中定义方法。</p><p>这个方法通过在私有作用域中创建一个全局的构造函数,并且将其方法定义在原型上。当构造函数在全局时,其原型的方法依然能访问私有作用域内的私有变量。</p><p>未经声明的创建变量可以提升到全局作用域,但是在严格模式下未经声明的变量则会报错。在目前的版本,可以在全局作用域中将变量先声明,再在私有作用域中赋值。可以到达同样的效果,其原型上的方法依然继承私有作用域的活动对象。</p><pre><code class="hljs js"><span class="hljs-keyword">let</span> Person;
(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-meta"> &#x27;use strict&#x27;</span>
<span class="hljs-keyword">let</span> v = <span class="hljs-number">10</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pv</span>(<span class="hljs-params"></span>) </span>&#123;
v++;
<span class="hljs-built_in">console</span>.log(v);
&#125;
Person = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;&#125;;
Person.prototype.say = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(v);
&#125;
Person.prototype.cha = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
pv();
&#125;
&#125;)();
<span class="hljs-keyword">let</span> xfy = <span class="hljs-keyword">new</span> Person();
xfy.say();</code></pre><p>除了上述的静态私有变量,还有一种写法就是在构造函数内为私有变量赋值。这样创建的实例就能够共享私有变量,并且在创建是就为其赋值。</p><p>这种方式创建静态私有变量会因为使用原型而增进代码复用,但每个实例都没有自己私有变量。要使用实例变量,还是静态私有变量。最终视需求而定。</p><pre><code class="hljs js"><span class="hljs-keyword">let</span> Person;
(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">let</span> name;
Person = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">value</span>) </span>&#123;
name = value;
&#125;
Person.prototype.get = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-built_in">console</span>.log(name);
&#125;
Person.prototype.set = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">value</span>) </span>&#123;
name = value;
&#125;
&#125;)()
<span class="hljs-keyword">let</span> xfy = <span class="hljs-keyword">new</span> Person(<span class="hljs-string">&#x27;xfy&#x27;</span>);
xfy.get();
xfy.set(<span class="hljs-string">&#x27;dfy&#x27;</span>);
xfy.get();</code></pre><h3 id="模块模式"><a href="#模块模式" class="headerlink" title="模块模式"></a>模块模式</h3><p>前面所述的方法都是为自定义类型创建私有变量和特权方法的。而道格拉斯所说的模块模式module pattern是为单例创建私有变量和特权方法的。所谓单例singleton指的就是只有一个实例的对象。</p><p>通常JS都是以对象字面量来创建单例对象的。</p><pre><code class="hljs js"><span class="hljs-keyword">let</span> singleton = &#123;
name: value,
method: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">return</span> name;
&#125;
&#125;</code></pre><p>模块模式通过为单例添加私有变量和特权方法能够让其得到增强。</p><p>这个模块模式使用了一个返回对象的匿名函数。在这个匿名函数内部定义了私有变量和函数。然后,将一个对象字面量作为函数的值返回。在返回的字面量中定义了公开的属性和方法。因为这个字面量是在匿名函数内定义的,所以它有权访问匿名的方法和属性。从本质上来讲,这个对象字面量定义的是单例的公共接口。</p><pre><code class="hljs js"><span class="hljs-keyword">let</span> singleton = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">let</span> pv = <span class="hljs-number">10</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pf</span>(<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
&#125;
<span class="hljs-keyword">return</span> &#123;
pubp: <span class="hljs-literal">true</span>,
prip: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
pv++;
<span class="hljs-keyword">return</span> pv;
&#125;
&#125;
&#125;()
<span class="hljs-built_in">console</span>.log(singleton);
<span class="hljs-built_in">console</span>.log(singleton.prip());</code></pre><p>这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时时非常有用的。</p><pre><code class="hljs js"><span class="hljs-keyword">let</span> app = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-comment">// 私有变量和函数</span>
<span class="hljs-keyword">let</span> con = [];
<span class="hljs-comment">// 初始化</span>
con.push(<span class="hljs-keyword">new</span> BaseCon());
<span class="hljs-comment">// 公共方法</span>
<span class="hljs-keyword">return</span> &#123;
getCon: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">return</span> con;
&#125;,
regCon: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">con</span>) </span>&#123;
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> con == <span class="hljs-string">&#x27;obejct&#x27;</span>) &#123;
con.push(con)
&#125;
&#125;
&#125;;
&#125;();</code></pre><h3 id="增强的模块模式"><a href="#增强的模块模式" class="headerlink" title="增强的模块模式"></a>增强的模块模式</h3><p>有人改进了模块模式,即在返回对象之前就加入对其增强的代码。这种增强的模块模式适合那些单例必须是某种类型的实例。同时还必须添加某些属性/方法对其加以增强的情况。</p><pre><code class="hljs js"><span class="hljs-keyword">let</span> app = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-comment">// 私有变量和函数</span>
<span class="hljs-keyword">let</span> con = [];
<span class="hljs-comment">// 初始化</span>
con.push(<span class="hljs-keyword">new</span> BaseCon());
<span class="hljs-comment">// 公共方法</span>
<span class="hljs-keyword">return</span> &#123;
getCon: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>&#123;
<span class="hljs-keyword">return</span> con;
&#125;,
regCon: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">con</span>) </span>&#123;
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> con == <span class="hljs-string">&#x27;obejct&#x27;</span>) &#123;
con.push(con)
&#125;
&#125;
&#125;;
&#125;();</code></pre><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>在JavaScript编程中函数表达式是一种非常有用的技术。使用函数表达式可以无须对函数命名从而实现动态编程。匿名函数也称为拉姆达函数是一种使用JavaScript函数的强大方式。以下总结了函数表达式的特点。</p><ul><li>函数表达式不同于函数声明。函数声明要求有名字,但函数表达式不需要。没有名字的函数表达式也叫匿名函数。</li><li>在无法确定如何引用函数的情况下,递归函数就会变得比较复杂;</li><li>递归函数应该始终使用<code>arguments.callee</code>来递归地调用自身,不要使用函数名———函数名可能会发生变化。</li></ul><p>当在函数内部定义了其他函数时,就创建了闭包。闭包有权访问包含函数内部的所有变量,原理如下。</p><ul><li>在后台执行环境中,闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域。</li><li>通常,当函数返回一个闭包时,这个函数的作用域将会一直在内存中保存到闭包不存在为止。</li></ul><p>使用闭包可以在JavaScript中模仿块级作用域JavaScript本身没有块级作用域的概念要点如下。</p><ul><li>创建并立即调用一个函数,这样既可以执行其中的代码,又不会在内存中留下对该函数的引用。</li><li>结果就是函数内部的所有变量都会被立即销毁——除非某些变量赋值给了包含作用域(即外部作用域)中的变量。</li></ul><p>闭包还可以用于在对象中创建私有变量,相关概念和要点如下。</p><ul><li>即使JavaScript中没有正式的私有对象属性的概念但可以使用闭包来实现公有方法而通过公有方法可以访问在包含作用域中定义的变量。</li><li>有权访问私有变量的公有方法叫做特权方法。</li><li>可以使用构造函数模式、原型模式来实现自定义类型的特权方法,也可以使用模块模式、增强的模块模式来实现单例的特权方法。</li></ul><p>JavaScript中的函数表达式和闭包都是极其有用的特性利用它们可以实现很多功能。不过因为创建闭包必须维护额外的作用域所以过度使用它们可能会占用大量内存。</p></div><hr><div><div class="post-metas mb-3"><div class="post-meta mr-3"><i class="iconfont icon-category"></i> <a class="hover-with-bg" href="/categories/%E7%AC%94%E8%AE%B0/">笔记</a></div><div class="post-meta"><i class="iconfont icon-tags"></i> <a class="hover-with-bg" href="/tags/JavaScript/">JavaScript</a></div></div><p class="note note-warning"><a target="_blank" href="https://zh.wikipedia.org/wiki/Wikipedia:CC_BY-SA_3.0%E5%8D%8F%E8%AE%AE%E6%96%87%E6%9C%AC" rel="nofollow noopener noopener">CC BY-SA 3.0❤</a></p><div class="post-prevnext row"><article class="post-prev col-6"><a href="/defect/irc-getting-started.html"><i class="iconfont icon-arrowleft"></i> <span class="hidden-mobile">入坑IRC</span> <span class="visible-mobile">上一篇</span></a></article><article class="post-next col-6"><a href="/defect/cant-install-gifsicle.html"><span class="hidden-mobile">Can't install gifsicle</span> <span class="visible-mobile">下一篇</span><i class="iconfont icon-arrowright"></i></a></article></div></div><article class="comments" id="comments"><div id="vcomments"></div><script type="text/javascript">function loadValine(){addScript("https://cdn.defectink.com/static/valine/1.4.14/Valine.min.js",function(){new Valine({el:"#vcomments",app_id:"dD9t7mcIBVzJWag5ez6GPy2v-MdYXbMMI",app_key:"bWG6pmKsEscrH4JjrpNNAAy6",placeholder:"嘤嘤嘤???",path:window.location.pathname,avatar:"retro",meta:["nick","mail","link"],pageSize:"10",lang:"zh-CN",highlight:!0,recordIP:!1,serverURLs:""})})}waitElementVisible("vcomments",loadValine)</script><noscript>Please enable JavaScript to view the <a target="_blank" href="https://valine.js.org" rel="nofollow noopener noopener">comments powered by Valine.</a></noscript></article></article></div></div></div><div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn"><div id="toc"><p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p><div id="tocbot"></div></div></div></div></div></main><a id="scroll-top-button" href="#" role="button"><i class="iconfont icon-arrowup" aria-hidden="true"></i></a><div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel" aria-hidden="true"><div class="modal-dialog modal-dialog-scrollable modal-lg" role="document"><div class="modal-content"><div class="modal-header text-center"><h4 class="modal-title w-100 font-weight-bold">搜索</h4><button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><div class="modal-body mx-3"><div class="md-form mb-5"><input type="text" id="local-search-input" class="form-control validate"> <label data-error="x" data-success="v" for="local-search-input">关键词</label></div><div class="list-group" id="local-search-result"></div></div></div></div></div><footer class="text-center mt-5 py-3"><div class="footer-content"><a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a><i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a></div><div class="beian"><a href="http://beian.miit.gov.cn/" target="_blank" rel="nofollow noopener">皖ICP备17017808号</a></div></footer><script src="https://cdn.defectink.com/static/jquery/3.4.1/jquery.min.js"></script><script src="https://cdn.defectink.com/static/twitter-bootstrap/4.5.3/js/bootstrap.min.js"></script><script src="/js/debouncer.js"></script><script src="/js/main.js"></script><script src="/js/lazyload.js"></script><script defer="defer" src="https://cdn.defectink.com/static/clipboard.js/2.0.6/clipboard.min.js"></script><script src="/js/clipboard-use.js"></script><script src="/js/xfy.js"></script><script src="https://cdn.defectink.com/static/tocbot/4.11.1/tocbot.min.js"></script><script>$(document).ready(function(){var t=$("#board-ctn").offset().top;tocbot.init({tocSelector:"#tocbot",contentSelector:"#post-body",headingSelector:"h1,h2,h3,h4,h5,h6",linkClass:"tocbot-link",activeLinkClass:"tocbot-active-link",listClass:"tocbot-list",isCollapsedClass:"tocbot-is-collapsed",collapsibleClass:"tocbot-is-collapsible",collapseDepth:3,scrollSmooth:!0,headingsOffset:-t}),0<$(".toc-list-item").length&&$("#toc").css("visibility","visible")})</script><script src="https://cdn.defectink.com/static/typed.js/2.0.11/typed.min.js"></script><script>var typed=new Typed("#subtitle",{strings:[" ","JavaScript的函数&nbsp;"],cursorChar:"❤",typeSpeed:70,loop:!1});typed.stop(),$(document).ready(function(){$(".typed-cursor").addClass("h2"),typed.start()})</script><script src="/js/local-search.js"></script><script>var path="/xml/local-search.xml",inputArea=document.querySelector("#local-search-input");inputArea.onclick=function(){searchFunc(path,"local-search-input","local-search-result"),this.onclick=null}</script><script src="https://cdn.defectink.com/static/fancybox/3.5.7/jquery.fancybox.min.js"></script><link rel="stylesheet" href="https://cdn.defectink.com/static/fancybox/3.5.7/jquery.fancybox.min.css"><script>$("#post img:not(.no-zoom img, img[no-zoom]), img[zoom]").each(function(){var t=document.createElement("a");$(t).attr("data-fancybox","images"),$(t).attr("href",$(this).attr("src")),$(this).wrap(t)})</script><script src="https://cdn.defectink.com/static/mermaid/8.5.0/mermaid.min.js"></script><script>window.mermaid&&mermaid.initialize({theme:"default"})</script></body></html>