mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-16 01:01:38 +00:00
303 lines
50 KiB
HTML
303 lines
50 KiB
HTML
<!DOCTYPE html><html lang="zh-CN" data-default-color-scheme=""auto""><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="/"> <strong>🍭Defectink</strong> </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"> <i class="iconfont icon-search"></i> </a></li><li class="nav-item" id="color-toggle-btn"><a class="nav-link" href="javascript:"> <i class="iconfont icon-dark" id="color-toggle-icon"></i> </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>{
|
||
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Hi, there'</span>);
|
||
}</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>{
|
||
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Hi, there'</span>);
|
||
}</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>{
|
||
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Yo, there'</span>);
|
||
}</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>{
|
||
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'something...'</span>);
|
||
}</code></pre><p>在早期的环境里,函数声明和if语句可能还不能工作的和契合。不过现在多数浏览器与运行环境都能正常执行如下代码:</p><pre><code class="hljs js"><span class="hljs-keyword">if</span> (<span class="hljs-number">1</span>) {
|
||
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hi</span>(<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'hi'</span>);
|
||
}
|
||
} <span class="hljs-keyword">else</span> {
|
||
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hi</span>(<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'yo'</span>);
|
||
}
|
||
}</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>) {
|
||
hi = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'hi'</span>);
|
||
}
|
||
} <span class="hljs-keyword">else</span> {
|
||
hi = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'yo'</span>);
|
||
}
|
||
}</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>{
|
||
<span class="hljs-keyword">if</span> (num <= <span class="hljs-number">1</span>) {
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
|
||
}
|
||
<span class="hljs-keyword">return</span> num + add(num - <span class="hljs-number">1</span>);
|
||
}</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>{
|
||
<span class="hljs-keyword">if</span> (num <= <span class="hljs-number">1</span>) {
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
|
||
}
|
||
<span class="hljs-keyword">return</span> num + add(num - <span class="hljs-number">1</span>);
|
||
}
|
||
|
||
<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>{
|
||
<span class="hljs-keyword">if</span> (num <= <span class="hljs-number">1</span>) {
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
|
||
}
|
||
<span class="hljs-keyword">return</span> num + <span class="hljs-built_in">arguments</span>.callee(num - <span class="hljs-number">1</span>);
|
||
}
|
||
|
||
<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>{
|
||
<span class="hljs-keyword">if</span> (num <= <span class="hljs-number">1</span>) {
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
|
||
}
|
||
<span class="hljs-keyword">return</span> num + a(num - <span class="hljs-number">1</span>);
|
||
})</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>{
|
||
<span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">obj1, obj2</span>) </span>{
|
||
<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] < obj2[prep]) {
|
||
<span class="hljs-keyword">return</span> -<span class="hljs-number">1</span>;
|
||
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (obj1[prep] > obj2[prep]) {
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
|
||
} <span class="hljs-keyword">else</span> {
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>
|
||
}
|
||
}
|
||
}</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>{
|
||
<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 < <span class="hljs-number">10</span>; i++) {
|
||
arr[i] = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-built_in">console</span>.log(i);
|
||
}
|
||
}
|
||
<span class="hljs-keyword">return</span> arr;
|
||
}
|
||
<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>{
|
||
<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 < <span class="hljs-number">10</span>; i++) {
|
||
(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">o</span>) </span>{
|
||
arr[o] = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-built_in">console</span>.log(o);
|
||
}
|
||
})(i);
|
||
}
|
||
<span class="hljs-keyword">return</span> arr;
|
||
}</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>{
|
||
<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 < <span class="hljs-number">10</span>; i++) {
|
||
arr[i] = (<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">o</span>) </span>{
|
||
<span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-built_in">console</span>.log(o);
|
||
}
|
||
})(i);
|
||
}
|
||
<span class="hljs-keyword">return</span> arr;
|
||
}
|
||
<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>{
|
||
<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 < <span class="hljs-number">10</span>; i++) {
|
||
arr[i] = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-built_in">console</span>.log(i);
|
||
}
|
||
}
|
||
<span class="hljs-keyword">return</span> arr;
|
||
}</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">'xfy'</span>;
|
||
<span class="hljs-comment">// window.name = 'xfy';</span>
|
||
<span class="hljs-keyword">let</span> obj = {
|
||
name: <span class="hljs-string">'xxxfy'</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>{
|
||
<span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">this</span>.name);
|
||
}
|
||
}
|
||
}
|
||
|
||
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">'xfy'</span>;
|
||
<span class="hljs-comment">// window.name = 'xfy';</span>
|
||
<span class="hljs-keyword">let</span> obj = {
|
||
name: <span class="hljs-string">'xxxfy'</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>{
|
||
<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>{
|
||
<span class="hljs-built_in">console</span>.log(that.name);
|
||
}
|
||
}
|
||
}
|
||
|
||
obj.say()();</code></pre><p>有几种特殊的情况,会导致this发生意外的变化。</p><pre><code class="hljs js"><span class="hljs-keyword">let</span> obj = {
|
||
name: <span class="hljs-string">'xfy'</span>,
|
||
feature: <span class="hljs-string">'handsome'</span>,
|
||
say: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">this</span>.name);
|
||
}
|
||
}
|
||
<span class="hljs-built_in">global</span>.name = <span class="hljs-string">'dfy'</span>; <span class="hljs-comment">// 非严格模式下;</span>
|
||
<span class="hljs-comment">// let name = 'dfy';</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">// 'dfy';</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>{
|
||
<span class="hljs-keyword">let</span> element = docuemnt.getElementById(<span class="hljs-string">'test'</span>);
|
||
element.onclick = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
alert(element.id);
|
||
}
|
||
}</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>{
|
||
<span class="hljs-keyword">let</span> element = docuemnt.getElementById(<span class="hljs-string">'test'</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>{
|
||
alert(id);
|
||
}
|
||
element = <span class="hljs-literal">null</span>;
|
||
}</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>{
|
||
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i < num; i++) {
|
||
<span class="hljs-built_in">console</span>.log(i);
|
||
}
|
||
<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>
|
||
}
|
||
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>{
|
||
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < num; i++) {
|
||
<span class="hljs-built_in">console</span>.log(i);
|
||
}
|
||
<span class="hljs-built_in">console</span>.log(i); <span class="hljs-comment">// i is not defined</span>
|
||
}
|
||
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>{
|
||
(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i < num; i++) {
|
||
<span class="hljs-built_in">console</span>.log(i);
|
||
}
|
||
})()
|
||
<span class="hljs-built_in">console</span>.log(i); <span class="hljs-comment">// i is not defined</span>
|
||
}
|
||
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>{
|
||
<span class="hljs-comment">// </span>
|
||
}(); <span class="hljs-comment">// 语法错误;</span>
|
||
|
||
(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-comment">// </span>
|
||
})();</code></pre><p>模仿的块级作用域通常作用于函数外部的全局作用域,从而向全局作用域添加变量和函数,而不污染全局作用域。</p><pre><code class="hljs js"><span class="hljs-keyword">let</span> name = <span class="hljs-string">'xfy'</span>;
|
||
(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-keyword">let</span> name = <span class="hljs-string">'yyy'</span>;
|
||
<span class="hljs-built_in">console</span>.log(name);
|
||
})() <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>{
|
||
<span class="hljs-keyword">let</span> result = num1 + num2;
|
||
<span class="hljs-keyword">return</span> result;
|
||
}</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>{
|
||
<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>{
|
||
age++;
|
||
<span class="hljs-built_in">console</span>.log(age);
|
||
}
|
||
<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>{
|
||
<span class="hljs-built_in">console</span>.log(name);
|
||
<span class="hljs-comment">// 通过公有方法执行私有方法</span>
|
||
pv();
|
||
}
|
||
}
|
||
<span class="hljs-keyword">let</span> xfy = <span class="hljs-keyword">new</span> Person(<span class="hljs-string">'xfy'</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>{
|
||
<span class="hljs-meta"> 'use strict'</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>{
|
||
v++;
|
||
<span class="hljs-built_in">console</span>.log(v);
|
||
}
|
||
|
||
Person = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{};
|
||
Person.prototype.say = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-built_in">console</span>.log(v);
|
||
}
|
||
Person.prototype.cha = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
pv();
|
||
}
|
||
|
||
})();
|
||
|
||
<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>{
|
||
<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>{
|
||
name = value;
|
||
}
|
||
Person.prototype.get = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-built_in">console</span>.log(name);
|
||
}
|
||
Person.prototype.set = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">value</span>) </span>{
|
||
name = value;
|
||
}
|
||
})()
|
||
|
||
<span class="hljs-keyword">let</span> xfy = <span class="hljs-keyword">new</span> Person(<span class="hljs-string">'xfy'</span>);
|
||
xfy.get();
|
||
xfy.set(<span class="hljs-string">'dfy'</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 = {
|
||
name: value,
|
||
method: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-keyword">return</span> name;
|
||
}
|
||
}</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>{
|
||
<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>{
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
|
||
}
|
||
<span class="hljs-keyword">return</span> {
|
||
pubp: <span class="hljs-literal">true</span>,
|
||
prip: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
pv++;
|
||
<span class="hljs-keyword">return</span> pv;
|
||
}
|
||
}
|
||
}()
|
||
|
||
<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>{
|
||
<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> {
|
||
getCon: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-keyword">return</span> con;
|
||
},
|
||
regCon: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">con</span>) </span>{
|
||
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> con == <span class="hljs-string">'obejct'</span>) {
|
||
con.push(con)
|
||
}
|
||
}
|
||
};
|
||
}();</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>{
|
||
<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> {
|
||
getCon: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
|
||
<span class="hljs-keyword">return</span> con;
|
||
},
|
||
regCon: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">con</span>) </span>{
|
||
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> con == <span class="hljs-string">'obejct'</span>) {
|
||
con.push(con)
|
||
}
|
||
}
|
||
};
|
||
}();</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> 目录</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">×</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的函数 "],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> |