-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
220 lines (102 loc) · 238 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>跨域</title>
<link href="/2024/08/02/%E8%B7%A8%E5%9F%9F/"/>
<url>/2024/08/02/%E8%B7%A8%E5%9F%9F/</url>
<content type="html"><![CDATA[<h1 id="浏览器的同源策略"><a href="#浏览器的同源策略" class="headerlink" title="浏览器的同源策略"></a>浏览器的同源策略</h1><h3 id="1-同源策略概述"><a href="#1-同源策略概述" class="headerlink" title="1.同源策略概述"></a>1.同源策略概述</h3><p>同源策略是浏览器为确保资源安全,而遵循的一种策略,该策略对访问资源进行了一些限制。</p><h3 id="2-什么是源(origin)?"><a href="#2-什么是源(origin)?" class="headerlink" title="2.什么是源(origin)?"></a>2.什么是源(origin)?</h3><p><img src="/%E8%B7%A8%E5%9F%9F.assets/33a80a2d737e19650ae7757eccd938f6.png" alt="image.png"></p><p>只有这三个都相同的时候,才符合同源策略,否则就涉及跨域问题</p><h3 id="3-跨域会受到哪些限制"><a href="#3-跨域会受到哪些限制" class="headerlink" title="3.跨域会受到哪些限制"></a>3.跨域会受到哪些限制</h3><ul><li>Cookie,LocalStorage,IndexDB 等存储性内容无法读取</li><li>DOM 节点无法访问</li><li>Ajax 请求发出去了,但是响应被浏览器拦截了</li></ul><h5 id="ps-几个注意点"><a href="#ps-几个注意点" class="headerlink" title="ps:几个注意点"></a>ps:几个注意点</h5><ul><li><p>跨域限制仅存在浏览器端,服务端不存在跨域限制。</p></li><li><p>即使跨域了,Ajax 请求也可以正常发出,但响应数据不会交给开发者。</p></li><li><link>、<script>、<img>...... 这些标签发出的请求也可能跨域,只不过浏览器对标签跨域不做严格限制,对开发几乎无影响<p><img src="/%E8%B7%A8%E5%9F%9F.assets/6ef6a9a11e46dc02ba24e4e6fc18162e.png" alt="image.png"></p></li></ul><h1 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h1><h3 id="CORS"><a href="#CORS" class="headerlink" title="CORS"></a>CORS</h3><h4 id="1-CORS概述"><a href="#1-CORS概述" class="headerlink" title="1.CORS概述"></a>1.CORS概述</h4><p>CORS 全称:Cross-Origin Resource Sharing(跨域资源共享),是用于控制浏览器校验跨域请求的一套规范,服务器依照 CORS 规范,添加特定响应头来控制浏览器校验,大致规则如下:</p><p>1)服务器明确表示拒绝跨域请求,或没有表示,则浏览器校验不通过。<br>2)服务器明确表示允许跨域请求,则浏览器校验通过。</p><p>ps:要求服务器是“自己人”。</p><h4 id="2-简单请求和复杂请求"><a href="#2-简单请求和复杂请求" class="headerlink" title="2.简单请求和复杂请求"></a>2.简单请求和复杂请求</h4><p>CORS 会把请求分为两类,分别是:① 简单请求、② 复杂请求。</p><p><img src="/%E8%B7%A8%E5%9F%9F.assets/image-20240811155004439.png" alt="image-20240811155004439"></p><p><strong>关于预检请求:</strong></p><ul><li>发送时机:预检请求在实际跨域请求之前发出,是由浏览器自动发起的。</li><li>主要作用:用于向服务器确认是否允许接下来的跨域请求。</li><li>基本流程:先发起OPTIONS请求,如果通过预检,继续发起实际的跨域请求。</li><li>请求头内容:一个OPTIONS预检请求,通常会包含如下请求头:</li></ul><p><img src="/%E8%B7%A8%E5%9F%9F.assets/image-20240811155052656.png" alt="image-20240811155052656"></p><h4 id="3-解决简单跨域请求"><a href="#3-解决简单跨域请求" class="headerlink" title="3.解决简单跨域请求"></a>3.解决简单跨域请求</h4><p>整体思路:服务器在给出响应时,通过添加Access-Control-Allow-Origin响应头,来明确表达允许某个源发起跨域请求,随后浏览器在校验时,直接通过。</p><p><img src="/%E8%B7%A8%E5%9F%9F.assets/7fc3d4076e0424a705f91a39b41533fe.png" alt="image.png"></p><h4 id="4-解决复杂跨域请求"><a href="#4-解决复杂跨域请求" class="headerlink" title="4.解决复杂跨域请求"></a>4.解决复杂跨域请求</h4><p>第一步:服务器先通过浏览器的预检请求,服务器需要返回如下响应头:</p><p><img src="/%E8%B7%A8%E5%9F%9F.assets/b518d361e5d112a0b6cf9f118df52c6e.png" alt="image.png"></p><table><thead><tr><th>响应头</th><th>含义</th></tr></thead><tbody><tr><td>Access-Control-Allow-Origin</td><td>允许的源</td></tr><tr><td>Access-Control-Allow-Methods</td><td>允许的方法</td></tr><tr><td>Access-Control-Allow-Headers</td><td>允许的自定义头</td></tr><tr><td>Access-Control-Max-Age</td><td>预检请求的结果缓存时间(可选)</td></tr></tbody></table><p>第二步:处理实际的跨域请求(与处理简单请求跨域的方式相同)</p><h4 id="5-使用cors库"><a href="#5-使用cors库" class="headerlink" title="5.使用cors库"></a>5.使用cors库</h4><p>完整配置cors</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// cors中间件配置</span></span><br><span class="line"><span class="keyword">const</span> corsOptions = {</span><br><span class="line"> <span class="attr">origin</span>: <span class="string">'http://127.0.0.1:5500'</span>, <span class="comment">// 允许的源</span></span><br><span class="line"> <span class="attr">methods</span>: [<span class="string">'GET'</span>, <span class="string">'POST'</span>, <span class="string">'PUT'</span>, <span class="string">'DELETE'</span>, <span class="string">'HEAD'</span>, <span class="string">'OPTIONS'</span>], <span class="comment">// 允许的方法</span></span><br><span class="line"> <span class="attr">allowedHeaders</span>: [<span class="string">'school'</span>], <span class="comment">// 允许的自定义头</span></span><br><span class="line"> <span class="attr">exposedHeaders</span>: [<span class="string">'abc'</span>], <span class="comment">// 要暴露的响应头</span></span><br><span class="line"> <span class="attr">optionsSuccessStatus</span>: <span class="number">200</span> <span class="comment">// 预检请求成功的状态码</span></span><br><span class="line">};</span><br><span class="line"> </span><br><span class="line">app.<span class="title function_">use</span>(<span class="title function_">cors</span>(corsOptions)); <span class="comment">// 使用cors中间件</span></span><br></pre></td></tr></table></figure><h3 id="JSONP"><a href="#JSONP" class="headerlink" title="JSONP"></a>JSONP</h3><p>JSONP 是利用了<script>标签可以跨域加载脚本,且不受严格限制的特性</p><p>基本流程:</p><p>第一步:客户端创建一个<script>标签,并将其src属性设置为包含跨域请求的 URL,同时准备一个回调函数,这个回调函数用于处理返回的数据。<br>第二步:服务端接收到请求后,将数据封装在回调函数中并返回。<br>第三步:客户端的回调函数被调用,数据以参数的形势传入回调函数。</p><p><img src="/%E8%B7%A8%E5%9F%9F.assets/a9b69cb4c148848bc9bd6aa7a0474312.png" alt="image.png"></p><h3 id="配置代理"><a href="#配置代理" class="headerlink" title="配置代理"></a>配置代理</h3><h5 id="自己配置"><a href="#自己配置" class="headerlink" title="自己配置"></a>自己配置</h5><p>运用 http-proxy-middleware</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> { createProxyMiddleware } = <span class="built_in">require</span>(<span class="string">'http-proxy-middleware'</span>);</span><br><span class="line"> </span><br><span class="line">app.<span class="title function_">use</span>(<span class="string">'/api'</span>,<span class="title function_">createProxyMiddleware</span>({</span><br><span class="line"> <span class="attr">target</span>:<span class="string">'https://www.toutiao.com'</span>,</span><br><span class="line"> <span class="attr">changeOrigin</span>:<span class="literal">true</span>,</span><br><span class="line"> <span class="attr">pathRewrite</span>:{</span><br><span class="line"> <span class="string">'^/api'</span>:<span class="string">''</span></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>实际上要访问的是<a href="http://www.toutiao.com/news/today,%E4%BD%86%E6%98%AF%E6%88%91%E4%BB%AC%E5%85%88%E7%BB%99%E4%BB%A3%E7%90%86%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%8F%91http://127.0.0.1:8081/api/news/today">http://www.toutiao.com/news/today,但是我们先给代理服务器发http://127.0.0.1:8081/api/news/today</a> 服务器之间不存在跨域问题</p><h5 id="nginx"><a href="#nginx" class="headerlink" title="nginx"></a>nginx</h5><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">location /api/ {</span><br><span class="line"><span class="meta prompt_"> # </span><span class="language-bash">设置代理目标</span></span><br><span class="line"> proxy_pass http://www.toutiao.com/;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="脚手架"><a href="#脚手架" class="headerlink" title="脚手架"></a>脚手架</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="attr">devServer</span>: {</span><br><span class="line"> <span class="attr">proxy</span>: {</span><br><span class="line"> <span class="string">'/api'</span>: {</span><br><span class="line"> <span class="attr">target</span>: <span class="string">'http://api.example.com'</span>,</span><br><span class="line"> <span class="attr">changeOrigin</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">pathRewrite</span>: {</span><br><span class="line"> <span class="string">'^/api'</span>: <span class="string">''</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>适用于使用webpack构建的项目</p></script></li></ul>]]></content>
</entry>
<entry>
<title>web worker</title>
<link href="/2024/08/01/web-worker/"/>
<url>/2024/08/01/web-worker/</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title>Proxy</title>
<link href="/2024/07/21/Proxy/"/>
<url>/2024/07/21/Proxy/</url>
<content type="html"><![CDATA[<h3 id="为什么要使用代理?"><a href="#为什么要使用代理?" class="headerlink" title="为什么要使用代理?"></a>为什么要使用代理?</h3><p>之所以使用代理,就是不希望用户能够<strong>直接访问</strong>某个对象,<strong>直接操作</strong>对象的某个成员(因为这样是不可控的,我们不知道用户在访问操作哪一个对象)</p><p>通过代理,我们可以<strong>拦截</strong>用户的访问(称为<strong>数据劫持</strong>),拦截住后我们就可以对数据进行一些处理,比如做一些数据的验证,之后再允许用户的访问操作(因为我们拦截了用户的每一次访问,这样用户操作对象就完全是在我们可控的范围内)</p><p>简单来说,就是我们希望用户在访问对象时我们能够清除的知道用户在访问什么并且能够在中间做一些我们自己的操作</p><h3 id="Proxy是什么?"><a href="#Proxy是什么?" class="headerlink" title="Proxy是什么?"></a>Proxy是什么?</h3><p><code>Proxy</code> 是 <code>ES6</code> 中新增的一个构造函数,也可以叫类,通过<code>new</code>操作符调用使用。</p><p><code>Proxy</code> 对象用于<strong>创建目标对象的代理</strong>,从而实现基本操作的<strong>拦截和自定义</strong>(如属性查找、赋值、枚举、函数调用等)</p><p>需要注意的是,<code>Proxy</code><strong>并没有</strong><code>prototype</code>原型对象</p><p>官方说明是因为 <code>Proxy</code> 构造出来的实例对象仅仅是对目标对象的一个<strong>代理</strong>,所以 <code>Proxy</code> 在构造过程中是不需要 <code>prototype</code> 进行初始化的</p><blockquote><p>其他构造函数之所以需要 <code>prototype</code>,是因为构造出来的对象需要一些初始化的成员,所以将这些成员定义到了 <code>protoype</code> 上</p></blockquote><h4 id="基础语法"><a href="#基础语法" class="headerlink" title="基础语法"></a>基础语法</h4><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> proxyTarget = <span class="keyword">new</span> <span class="title class_">Proxy</span>(target, handler)</span><br></pre></td></tr></table></figure><p><strong>参数:</strong></p><ul><li><p><code>target</code>:Proxy 会对 target 对象进行包装。它可以是<strong>任何类型的对象</strong>,包括内置的数组,函数甚至是另一个代理对象。</p></li><li><p>handler:它是一个对象,它的属性提供了某些操作发生时所对应的处理函数。</p><blockquote><p>一个空的 <code>handler</code> 参数将会创建一个与被代理对象行为几乎完全相同的代理对象。通过在 <code>handler</code> 对象上定义一组<strong>处理函数</strong>,你可以自定义被代理对象的一些特定行为。</p></blockquote></li></ul><p> <strong>返回值:</strong></p><ul><li><code>proxyTarget </code>:经过Proxy包装后的target对象</li></ul><p><strong>基础使用:</strong></p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'Ailjx'</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> proxyTarget = <span class="keyword">new</span> <span class="title class_">Proxy</span>(obj, {})</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(proxyTarget);</span><br></pre></td></tr></table></figure><p><img src="/Proxy.assets/6fc283fe47164fbd97d4f7b3a03e9a46tplv-k3u1fbpfcp-zoom-in-crop-mark1512000.awebp" alt="在这里插入图片描述"> </p><h3 id="handler处理函数"><a href="#handler处理函数" class="headerlink" title="handler处理函数"></a>handler处理函数</h3><p><code>Proxy</code>代理的灵魂就在于它的第二个参数:<code>handler</code>对象,在这个对象内我们可以定义一些<strong>处理函数</strong>来进行数据劫持,从而实现一些额外的操作</p><h4 id="1-get-拦截对象属性的读取操作"><a href="#1-get-拦截对象属性的读取操作" class="headerlink" title="1.get() 拦截对象属性的读取操作"></a>1.get() 拦截对象属性的读取操作</h4><p><code>handler.get()</code> 方法用于拦截对象的读取属性操作</p><p> <strong>语法:</strong></p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> proxyTarget = <span class="keyword">new</span> <span class="title class_">Proxy</span>(target, {</span><br><span class="line"> <span class="attr">get</span>: <span class="keyword">function</span>(<span class="params">target, property, receiver</span>) {}</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p> <strong>参数:</strong></p><blockquote><p>以下是传递给 <code>get</code> 方法的参数</p></blockquote><ul><li><code>target</code>:目标对象。</li><li><code>property</code>:被获取的属性名。</li><li><code>receiver</code>:Proxy 或者继承 Proxy 的对象</li></ul><p> <strong>返回值:</strong></p><p><code>get</code> 方法可以返回任何值,这些返回值就是用户真正获取到的属性值</p><p> <strong>使用:</strong></p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"Ailjx"</span>,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> proxy = <span class="keyword">new</span> <span class="title class_">Proxy</span>(obj, {</span><br><span class="line"> <span class="attr">get</span>: <span class="keyword">function</span> (<span class="params">target, property, receiver</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"你访问的属性为:"</span>, property);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(receiver);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"My name is "</span> + target.<span class="property">name</span>;</span><br><span class="line"> },</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(proxy.<span class="property">name</span>);</span><br></pre></td></tr></table></figure><p><img src="/Proxy.assets/e8e42d671882471eb5d7a08e11d83a13tplv-k3u1fbpfcp-zoom-in-crop-mark1512000.awebp" alt="在这里插入图片描述"></p><h4 id="2-set-拦截对象属性的修改-设置操作"><a href="#2-set-拦截对象属性的修改-设置操作" class="headerlink" title="2.set() 拦截对象属性的修改/设置操作"></a>2.set() 拦截对象属性的修改/设置操作</h4><p><code>handler.set()</code> 方法是设置属性值操作的捕获器</p><p> <strong>语法:</strong></p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> p = <span class="keyword">new</span> <span class="title class_">Proxy</span>(target, {</span><br><span class="line"> <span class="attr">set</span>: <span class="keyword">function</span>(<span class="params">target, property, value, receiver</span>) {}</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p><strong>参数:</strong></p><blockquote><p>下面的参数将会传递给 <code>set()</code> 方法</p></blockquote><ul><li><p><code>target</code>:目标对象</p></li><li><p><code>property</code>:将被设置的属性名</p></li><li><p><code>value</code>:新属性值。</p></li><li><p><code>receiver</code>:最初被调用的对象。通常是 <code>proxy</code> 本身,但 <code>handler</code> 的 <code>set</code> 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 <code>proxy</code> 本身)</p><blockquote><p>假设有一段代码执行 <code>obj.name = "Ailjx"</code>, <code>obj</code> 不是一个 <code>proxy</code>,且自身不含 <code>name</code> 属性,但是它的原型链上有一个 <code>proxy</code>,那么,那个 <code>proxy</code> 的 <code>set()</code> 处理器会被调用,而此时,<code>obj</code> 会作为 <code>receiver</code> 参数传进来</p></blockquote></li></ul><p> <strong>返回值:</strong></p><p><code>set()</code> 方法应当返回一个布尔值</p><ul><li>返回 <code>true</code> 代表属性设置成功</li><li>在严格模式下,如果 <code>set()</code> 方法返回 <code>false</code>,那么会抛出一个 <a href="https://link.juejin.cn/?target=https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypeError">TypeError</a> 异常</li></ul><p> <strong>使用:</strong></p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"Ailjx"</span>,</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> handler = {</span><br><span class="line"> <span class="title function_">set</span>(<span class="params">target, property, value</span>) {</span><br><span class="line"> <span class="keyword">if</span> (property === <span class="string">"name"</span> && <span class="keyword">typeof</span> value !== <span class="string">"string"</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"姓名必须是字符串!"</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> target.<span class="property">name</span> = value;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> proxy1 = <span class="keyword">new</span> <span class="title class_">Proxy</span>(obj, handler);</span><br><span class="line"></span><br><span class="line">proxy1.<span class="property">name</span> = <span class="number">1</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(proxy1.<span class="property">name</span>);</span><br><span class="line"></span><br><span class="line">proxy1.<span class="property">name</span> = <span class="string">"Chen"</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(proxy1.<span class="property">name</span>);</span><br></pre></td></tr></table></figure><p><img src="/Proxy.assets/634ffbea0c57475b98324f3a5bf28e2etplv-k3u1fbpfcp-zoom-in-crop-mark1512000.awebp" alt="在这里插入图片描述"></p><h4 id="3-deleteProperty-拦截对象属性的删除操作"><a href="#3-deleteProperty-拦截对象属性的删除操作" class="headerlink" title="3.deleteProperty()拦截对象属性的删除操作"></a>3.deleteProperty()拦截对象属性的删除操作</h4><p><code>handler.deleteProperty()</code> 方法用于拦截对对象属性的删除操作</p><p> <strong>语法:</strong></p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> p = <span class="keyword">new</span> <span class="title class_">Proxy</span>(target, {</span><br><span class="line"> <span class="attr">deleteProperty</span>: <span class="keyword">function</span>(<span class="params">target, property</span>) {}</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p> <strong>参数:</strong></p><blockquote><p><code>deleteProperty</code>方法将会接受以下参数。</p></blockquote><ul><li><code>property</code>:待删除的属性名。</li></ul><p><strong>返回值:</strong></p><p><code>deleteProperty</code> 必须返回一个 <code>Boolean</code> 值,表示了该属性是否被成功删除</p><p><strong>使用:</strong></p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"Ailjx"</span>,</span><br><span class="line">};</span><br><span class="line"><span class="keyword">var</span> p = <span class="keyword">new</span> <span class="title class_">Proxy</span>(obj, {</span><br><span class="line"> <span class="attr">deleteProperty</span>: <span class="keyword">function</span> (<span class="params">target, prop</span>) {</span><br><span class="line"> <span class="keyword">delete</span> target[prop];</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"你删除了"</span> + prop + <span class="string">"属性"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> },</span><br><span class="line">});</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(p.<span class="property">name</span>); <span class="comment">// Ailjx</span></span><br><span class="line"><span class="keyword">delete</span> p.<span class="property">name</span>; <span class="comment">// 你删除了name属性</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(p.<span class="property">name</span>); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure><p><img src="/Proxy.assets/6a966d5a017b4f618d0e16b7ab7c68e2tplv-k3u1fbpfcp-zoom-in-crop-mark1512000.awebp" alt="在这里插入图片描述"></p><h4 id="更多处理函数"><a href="#更多处理函数" class="headerlink" title="更多处理函数"></a>更多处理函数</h4><p><code>handler</code>对象内还有以下处理函数:</p><ul><li>defineProperty():拦截对对象的 <code>Object.defineProperty()</code> 操作</li><li>apply(): 拦截函数的调用</li><li>getOwnPropertyDescriptor():<code>Object.getOwnPropertyDescriptor</code> 调用劫持</li><li>getPrototypeOf() :拦截对象<strong>原型</strong>的读取操作</li><li>isExtensible():拦截对对象的 <code>Object.isExtensible()</code></li><li>ownKeys():<code>Object.getOwnPropertyNames</code> 和<code>Object.getOwnPropertySymbols</code>的调用劫持</li><li>preventExtensions():对<code>Object.preventExtensions()</code>的拦截</li><li>setPrototypeOf]():拦截 <code>Object.setPrototypeOf()</code></li></ul><h3 id="可撤销代理"><a href="#可撤销代理" class="headerlink" title="可撤销代理"></a>可撤销代理</h3><p><code>Proxy.revocable()</code> 方法可以用来创建一个<strong>可撤销</strong>的代理对象</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 创建可撤销的代理</span></span><br><span class="line"><span class="keyword">const</span> revocable = <span class="title class_">Proxy</span>.<span class="title function_">revocable</span>({},{</span><br><span class="line"> <span class="title function_">get</span>(<span class="params">target, name</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"[["</span> + name + <span class="string">"]]"</span>;</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(revocable);</span><br></pre></td></tr></table></figure><p><img src="/Proxy.assets/f2ad79e790484bc9a57cf3db5c5c186dtplv-k3u1fbpfcp-zoom-in-crop-mark1512000.awebp" alt="在这里插入图片描述"> <code>Proxy.revocable()</code> 方法具有和<code>Proxy</code>一样的两个参数:<code>target</code>目标对象和<code>handler</code>对象</p><p>但它的<strong>返回值</strong>有点特殊,它返回一个包含了代理对象本身和它的撤销方法的可撤销 <code>Proxy</code> 对象,其结构为:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">{<span class="string">"proxy"</span>: proxy, <span class="string">"revoke"</span>: revoke}</span><br></pre></td></tr></table></figure><ul><li><p><code>proxy</code>:表示新生成的代理对象本身,和用一般方式 <code>new Proxy(target, handler)</code> 创建的代理对象没什么不同,只是它可以被撤销掉</p></li><li><p><code>revoke</code>:撤销方法,调用的时候不需要加任何参数,就可以撤销掉和它一起生成的那个代理对象</p><blockquote><p>一旦某个代理对象被撤销,它将变得几乎完全不可调用,在它身上执行任何的<strong>可代理操作</strong>都会抛出 <code>TypeError</code> 异常(可代理操作指的就是我们在<code>handler</code>对象函数属性上<strong>能拦截到的操作</strong>,一共有<strong>14种</strong>,执行这14种以外的情况不会报错)</p></blockquote><blockquote><p>一旦被撤销,这个代理对象便不可能被直接恢复到原来的状态,同时和它关联的目标对象以及处理器对象都有可能被垃圾回收掉。再次调用撤销方法 <code>revoke()</code> 则不会有任何效果,但也不会报错</p></blockquote></li></ul><p><strong>示例:</strong></p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 创建可撤销的代理</span></span><br><span class="line"><span class="keyword">const</span> revocable = <span class="title class_">Proxy</span>.<span class="title function_">revocable</span>(</span><br><span class="line"> {},</span><br><span class="line"> {</span><br><span class="line"> <span class="title function_">get</span>(<span class="params">target, name</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"[["</span> + name + <span class="string">"]]"</span>;</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取创建的代理</span></span><br><span class="line"><span class="keyword">const</span> proxy = revocable.<span class="property">proxy</span>;</span><br><span class="line">proxy.<span class="property">foo</span>; <span class="comment">// "[[foo]]"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 撤销代理</span></span><br><span class="line">revocable.<span class="title function_">revoke</span>();</span><br><span class="line"><span class="comment">// 代理撤销后,可代理操作将不能再被使用</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(proxy.<span class="property">foo</span>); <span class="comment">// 抛出 TypeError</span></span><br><span class="line">proxy.<span class="property">foo</span> = <span class="number">1</span>; <span class="comment">// 还是 TypeError</span></span><br><span class="line"><span class="keyword">delete</span> proxy.<span class="property">foo</span>; <span class="comment">// 又是 TypeError</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typeof</span> proxy; <span class="comment">// "object",因为 typeof 不属于可代理操作</span></span><br></pre></td></tr></table></figure><p>这种机制非常有利于一种情况,就是在我们使用一些不信任的第三方库时候。如果必须向一个自己不信任的库传递一个函数,这时候当然不能直接将自己的函数传递过去,我们可以向其传递一个可以撤销的代理,这样同样可以达到效果,当使用完这个库,我们就将这个代理撤销。保证了安全性</p>]]></content>
</entry>
<entry>
<title>浅拷贝与深拷贝</title>
<link href="/2024/07/06/%E6%B5%85%E6%8B%B7%E8%B4%9D%E4%B8%8E%E6%B7%B1%E6%8B%B7%E8%B4%9D/"/>
<url>/2024/07/06/%E6%B5%85%E6%8B%B7%E8%B4%9D%E4%B8%8E%E6%B7%B1%E6%8B%B7%E8%B4%9D/</url>
<content type="html"><![CDATA[<h2 id="直接赋值"><a href="#直接赋值" class="headerlink" title="直接赋值"></a>直接赋值</h2><p>基本数据类型保存在栈里面,可以直接访问它的值,赋值时,会完整复制变量值。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> a = <span class="number">10</span></span><br><span class="line"><span class="keyword">let</span> b = a</span><br><span class="line">a = <span class="number">20</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(b) <span class="comment">//10</span></span><br></pre></td></tr></table></figure><p>引用数据类型保存在<strong>堆</strong>里面,栈里面保存的是地址,通过栈里面的地址去访问堆里面的值。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> obj1 = {</span><br><span class="line"> a : <span class="number">1</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> obj2 = obj1</span><br><span class="line">obj1.<span class="property">a</span> = <span class="number">2</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obj2.<span class="property">a</span>) <span class="comment">//2</span></span><br></pre></td></tr></table></figure><p><img src="/%E6%B5%85%E6%8B%B7%E8%B4%9D%E4%B8%8E%E6%B7%B1%E6%8B%B7%E8%B4%9D.assets/e0e9907960e34656a0f443f51e2eef64.png" alt="img"></p><p>这是直接赋值的情况,要想两个值互不影响,就需要进行拷贝,js中的拷贝分为浅拷贝和深拷贝</p><h2 id="浅拷贝"><a href="#浅拷贝" class="headerlink" title="浅拷贝"></a>浅拷贝</h2><p>我的理解就是,浅拷贝<strong>实际上只拷贝了一层</strong></p><p>具体实现方法有这几种:</p><ol><li>拓展运算符</li></ol><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> a = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]</span><br><span class="line"><span class="keyword">let</span> b = [...a] <span class="comment">//把数组a的内容挨个取出放到b数组中</span></span><br><span class="line">a[<span class="number">0</span>] = <span class="number">0</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(b) <span class="comment">//1,2,3</span></span><br></pre></td></tr></table></figure><ol start="2"><li>slice方法</li></ol><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> a = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]</span><br><span class="line"><span class="keyword">let</span> b = a.<span class="title function_">slice</span>()</span><br><span class="line">a[<span class="number">0</span>] = <span class="number">4</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(b) <span class="comment">//1 2 3</span></span><br></pre></td></tr></table></figure><ol start="3"><li>assign方法</li></ol><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> obj1 = { <span class="attr">a</span>: <span class="number">1</span>, <span class="attr">b</span>: <span class="number">2</span> }</span><br><span class="line"><span class="keyword">let</span> obj2 = <span class="title class_">Object</span>.<span class="title function_">assign</span>({}, obj1)</span><br><span class="line">obj1.<span class="property">a</span> = <span class="number">5</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obj2); <span class="comment">// { a: 1, b: 2 }</span></span><br></pre></td></tr></table></figure><ol start="4"><li>手搓</li></ol><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">shallowCopy</span> (obj){</span><br><span class="line"> <span class="comment">// 只拷贝复杂类型,基本类型或null直接返回</span></span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">typeof</span> obj !== <span class="string">'object'</span> || obj === <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> obj;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 判断是新建一个数组还是对象</span></span><br><span class="line"> <span class="keyword">let</span> newObj = <span class="title class_">Array</span>.<span class="title function_">isArray</span>(obj) ? []: {};</span><br><span class="line"> <span class="comment">//for…in会遍历对象的整个原型链,如果只考虑对象本身的属性,需要搭配hasOwnProperty</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> key <span class="keyword">in</span> obj ){</span><br><span class="line"> <span class="comment">//hasOwnProperty判断是否是对象自身属性,会忽略从原型链上继承的属性</span></span><br><span class="line"> <span class="keyword">if</span>(obj.<span class="title function_">hasOwnProperty</span>(key)){</span><br><span class="line"> newObj[key] = obj[key];<span class="comment">//只拷贝对象本身的属性</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> newObj;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>但是如果有嵌套的话,浅拷贝就会出问题</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> obj1 = { <span class="attr">a</span>: <span class="number">1</span>, <span class="attr">b</span>: { <span class="attr">c</span>: <span class="number">2</span> } }</span><br><span class="line"><span class="keyword">let</span> obj2 = <span class="title class_">Object</span>.<span class="title function_">assign</span>({}, obj1)</span><br><span class="line">obj1.<span class="property">b</span>.<span class="property">c</span> = <span class="number">5</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obj2); <span class="comment">// { a: 1, b: { c: 5 } }</span></span><br></pre></td></tr></table></figure><p>因为这里复制的b的属性是引用类型,所以复制的还是它的内存地址</p><h2 id="深拷贝"><a href="#深拷贝" class="headerlink" title="深拷贝"></a>深拷贝</h2><p>复制对象及其所有嵌套对象和数组,确保新对象与原对象完全独立</p><p>下面是具体实现方法:</p><p>1.使用JSON</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> a = [ [<span class="number">1</span>],[<span class="number">2</span>], [<span class="number">3</span>]]</span><br><span class="line"><span class="keyword">let</span> b = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(<span class="title class_">JSON</span>.<span class="title function_">stringfy</span>(a));</span><br><span class="line">a[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">4</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a) <span class="comment">// [ [4],[2], [3]]</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(b) <span class="comment">// [ [1],[2], [3]]</span></span><br></pre></td></tr></table></figure><p>利用JSON.stringify将对象序列化成为JSON字符串,并将对象里面的内容转换成字符串,再使用JSON.parse来反序列化,将字符串生成一个新的对象</p><p>这个方法虽然简单,但也存在一些问题:</p><ul><li>⽆法解决循环引用问题</li><li>无法拷贝一些特殊的对象,如 RegExp (会变成空对象)、 Date (被转成字符串)</li><li>无法拷贝函数</li><li>无法拷贝undefined</li></ul><p>2.使用第三方库 lodash</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> _ = <span class="built_in">require</span>(<span class="string">'lodash'</span>); <span class="comment">//导入lodash库</span></span><br><span class="line"><span class="keyword">let</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">1</span>,</span><br><span class="line"> <span class="attr">b</span>: {</span><br><span class="line"> <span class="attr">c</span>: <span class="number">2</span></span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"><span class="keyword">let</span> deepCopy = _.<span class="title function_">cloneDeep</span>(obj);</span><br></pre></td></tr></table></figure><p>3.structuredClone</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> a = [ [<span class="number">1</span>],[<span class="number">2</span>], [<span class="number">3</span>]]</span><br><span class="line"><span class="keyword">let</span> b = <span class="title function_">structuredClone</span>(a);</span><br><span class="line">a[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">4</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a) <span class="comment">// [ [4],[2], [3]]</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(b) <span class="comment">// [ [1],[2], [3]]</span></span><br></pre></td></tr></table></figure><p>这个是一个web api,只适用于在浏览器环境下使用</p><p>4.手搓(递归)</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> newObj = {}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">deepClone</span>(<span class="params">newO,old</span>){</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> key <span class="keyword">in</span> old) {</span><br><span class="line"> <span class="keyword">let</span> value = old[key]</span><br><span class="line"> <span class="keyword">if</span>(value <span class="keyword">instanceof</span> <span class="title class_">Array</span>) {</span><br><span class="line"> newO[key] = [] <span class="comment">//被拷贝的是数组,创建一个新数组</span></span><br><span class="line"> <span class="title function_">deepClone</span>(newO[key],value)</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(value <span class="keyword">instanceof</span> <span class="title class_">Object</span>) {</span><br><span class="line"> newO[key] = {} <span class="comment">//创建新对象</span></span><br><span class="line"> <span class="title function_">deepClone</span>(newO[key],value)</span><br><span class="line"> }</span><br><span class="line"> }<span class="keyword">else</span> {</span><br><span class="line"> newO[key] = value <span class="comment">//基础类型,单纯值复制</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="title function_">deepClone</span>(newObj,oldObj)</span><br></pre></td></tr></table></figure><p>循环引用问题</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> oneObj = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'oneObj'</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> twoObj = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'twoObj'</span>,</span><br><span class="line"> <span class="attr">age</span>: <span class="number">15</span>,</span><br><span class="line"> <span class="attr">family</span>: oneObj <span class="comment">//two引用one</span></span><br><span class="line">}</span><br><span class="line">oneObj.<span class="property">a</span> = twoObj <span class="comment">//one引用two造成循环引用</span></span><br></pre></td></tr></table></figure><p>如果直接按照上面的递归来深拷贝,就会陷入无线循环</p><p>解决方法:ES6 的Map对象,它的key可以是任意类型(对象类型)</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> newObj = {}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">deepClone</span>(<span class="params">newO,twoObj,map</span>) {</span><br><span class="line"> <span class="keyword">if</span>(!map){</span><br><span class="line"> map = <span class="keyword">new</span> <span class="title class_">Map</span>()</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> key <span class="keyword">in</span> twoObj) {</span><br><span class="line"> <span class="keyword">let</span> value = twoObj[key]</span><br><span class="line"> <span class="keyword">if</span>(value <span class="keyword">instanceof</span> <span class="title class_">Array</span>) {</span><br><span class="line"> newO[key] = [] </span><br><span class="line"> <span class="title function_">deepClone</span>(newO[key],value)</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(value <span class="keyword">instanceof</span> <span class="title class_">Object</span>) {</span><br><span class="line"> newO[key] = {} </span><br><span class="line"> <span class="keyword">if</span>(!map.<span class="title function_">has</span>(value)) { <span class="comment">//如果这个对象在map里出现过,就不进入递归拷贝</span></span><br><span class="line"> map.<span class="title function_">set</span>(value,<span class="number">1</span>)</span><br><span class="line"> <span class="title function_">deepClone</span>(newO[key],value,map)</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> newO[key] = value</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="title function_">deepClone</span>(newObj,twoObj)</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
</entry>
<entry>
<title>echarts入门</title>
<link href="/2024/05/17/echarts%E5%85%A5%E9%97%A8/"/>
<url>/2024/05/17/echarts%E5%85%A5%E9%97%A8/</url>
<content type="html"><![CDATA[<h2 id="1-echarts的介绍"><a href="#1-echarts的介绍" class="headerlink" title="1.echarts的介绍"></a>1.echarts的介绍</h2><h5 id="1-brief-introduction"><a href="#1-brief-introduction" class="headerlink" title="1.brief introduction"></a>1.brief introduction</h5><p>echarts是一款基于JavaScript的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。ECharts最初由百度团队开源,并于2018年初捐赠给Apache基金会,成为ASF孵化级项目。</p><h5 id="2-echarts的下载"><a href="#2-echarts的下载" class="headerlink" title="2.echarts的下载"></a>2.echarts的下载</h5><p>(1)从 npm 获取<br>npm install echarts –save<br>(2)从 CDN 获取<br>(3)从 GitHub 获取(下载echarts.min.js文件)</p><h2 id="2-echarts语法"><a href="#2-echarts语法" class="headerlink" title="2.echarts语法"></a>2.echarts语法</h2><h5 id="1-常见术语"><a href="#1-常见术语" class="headerlink" title="1.常见术语"></a>1.常见术语</h5><table><thead><tr><th>英文</th><th>汉语</th></tr></thead><tbody><tr><td>title</td><td>标题</td></tr><tr><td>legend</td><td>图例</td></tr><tr><td>tooltip</td><td>提示</td></tr><tr><td>xAxis</td><td>x轴线</td></tr><tr><td>yAxis</td><td>y轴线</td></tr><tr><td>series</td><td>系列</td></tr><tr><td>data</td><td>数据</td></tr></tbody></table><h5 id="2-图表常见类型"><a href="#2-图表常见类型" class="headerlink" title="2.图表常见类型"></a>2.图表常见类型</h5><ol><li>bar 柱状图</li><li>line折线图<br>(1)曲线图<br>加上smooth:true;就会变成曲线图<br>(2)面积图<br>加上areaStyle:{fill:“颜色编号”} 会变成面积图</li><li>pie 饼形图<br>加上radius:[80,50] 会变成环形图 //第一个值是外半径,第二个值是内半径</li></ol><p>开始写一个简单的柱状图:</p><ol><li>准备具有大小的DOM容器</li><li>初始化实例对象 echarts.init(dom容器)</li><li>指定配置项和数据</li><li>将配置项设置给echarts实例对象,使用刚指定的配置项和数据显示图表。</li></ol><h5 id="3-echarts中的样式简介"><a href="#3-echarts中的样式简介" class="headerlink" title="3.echarts中的样式简介"></a>3.echarts中的样式简介</h5><ol><li>color调色盘</li></ol><p>在 option 中设置。可以设置全局的调色盘,也可以设置系列自己专属的调色盘。<br>全局调色盘option.color</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">option.<span class="property">color</span>:<span class="attr">color</span>: [<span class="string">"pink"</span>, <span class="string">"#ff0"</span>, <span class="string">"#f0f"</span>, <span class="string">"#0ff"</span>]</span><br></pre></td></tr></table></figure><p>局部调色盘series.item.color</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="attr">series</span>: [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">type</span>: <span class="string">'bar'</span>,</span><br><span class="line"> <span class="comment">// 此系列自己的调色盘。</span></span><br><span class="line"> <span class="attr">color</span>: [</span><br><span class="line"> <span class="string">'#dd6b66'</span>,</span><br><span class="line"> <span class="string">'#759aa0'</span>,</span><br><span class="line"> <span class="string">'#e69d87'</span>,</span><br><span class="line"> <span class="string">'#8dc1a9'</span>,</span><br><span class="line"> <span class="string">'#ea7e53'</span>,</span><br><span class="line"> <span class="string">'#eedd78'</span>,</span><br><span class="line"> <span class="string">'#73a373'</span>,</span><br><span class="line"> <span class="string">'#73b9bc'</span>,</span><br><span class="line"> <span class="string">'#7289ab'</span>,</span><br><span class="line"> <span class="string">'#91ca8c'</span>,</span><br><span class="line"> <span class="string">'#f49f42'</span></span><br><span class="line"> ]</span><br><span class="line"> </span><br><span class="line"> },</span><br></pre></td></tr></table></figure><p>itemStyle项的颜色</p><ul><li>itemStyle:{color:“#00f” }</li><li>高亮的样式emphasis</li></ul><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="attr">itemStyle</span>:{</span><br><span class="line"><span class="attr">normal</span>:{<span class="attr">color</span>:<span class="string">"#93da6c"</span>},</span><br><span class="line"><span class="attr">emphasis</span>:{<span class="attr">color</span>:<span class="string">"#bcff57"</span>}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="2"><li>特殊样式</li></ol><p>渐变色</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 定义渐变</span></span><br><span class="line"><span class="keyword">let</span> linear = {</span><br><span class="line"> <span class="attr">type</span>: <span class="string">'linear'</span>,</span><br><span class="line"> <span class="attr">x</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">y</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">x2</span>: <span class="number">0</span>,</span><br><span class="line"> <span class="attr">y2</span>:<span class="number">1</span>,</span><br><span class="line"> <span class="attr">colorStops</span>: [{</span><br><span class="line"> <span class="attr">offset</span>: <span class="number">0</span>, <span class="attr">color</span>: <span class="string">'#02bcff'</span> <span class="comment">// 0% 处的颜色</span></span><br><span class="line"> }, {</span><br><span class="line"> <span class="attr">offset</span>: <span class="number">1</span>, <span class="attr">color</span>: <span class="string">'#5555ff'</span> <span class="comment">// 100% 处的颜色</span></span><br><span class="line"> }],</span><br><span class="line"> <span class="attr">global</span>: <span class="literal">false</span> <span class="comment">// 缺省为 false</span></span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="attr">itemStyle</span>:{</span><br><span class="line"><span class="attr">color</span>:linear,</span><br><span class="line"><span class="attr">borderRadius</span>:[<span class="number">30</span>,<span class="number">30</span>,<span class="number">0</span>,<span class="number">0</span>]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="3"><li>label 标签</li></ol><ul><li><p>show:true是否显示</p></li><li><p>position:”insideRight“位置</p></li><li><p>formatter格式<br>formatter:</p></li><li><p><code>formatter</code> 是一个用于格式化标签、提示框等的模板字符串。它可以用在以下几个地方:</p><ul><li><code>tooltip.formatter</code>:提示框内容的格式化。</li><li><code>series.label.formatter</code>:系列中每个数据项的标签格式化。</li><li><code>axisLabel.formatter</code>:坐标轴标签的格式化。</li></ul><p>这里使用字符串模板进行设置,其中模板变量有{a}, {b}, {c}, {d},在不同图表类型下代表含义分别为<br>折线图、柱状图、K线图:{a} (系列名称)、{b}(类目值),{c}(数值),{d}(无)<br>散点图(气泡)图 : {a}(系列名称),{b}(数据名称),{c}(数值数组), {d}(无)<br>地图 : {a}(系列名称),{b}(区域名称),{c}(合并数值), {d}(无)<br>饼图、仪表盘、漏斗图: {a}(系列名称),{b}(数据项名称),{c}(数值), {d}(百分比)</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="attr">formatter</span>: <span class="string">'{name|{b}}\n{time|{c} 小时}'</span> <span class="comment">// 包含两个内容(“{b}”、“{c} 小时”),它们之间使用“\n”分隔</span></span><br></pre></td></tr></table></figure><p>还有一种写法 {classname|content} 配合富文本使用</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="attr">formatter</span>: <span class="string">'{a|这段文本采用样式a}'</span></span><br></pre></td></tr></table></figure></li><li><p>rich富文本</p></li></ul><p>rich 的目的就是给文字加上一些自定义的样式</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="attr">formatter</span>:[</span><br><span class="line"><span class="string">'{名称|...}'</span>,</span><br><span class="line"><span class="string">'...'</span>,</span><br><span class="line">...</span><br><span class="line">].<span class="title function_">join</span>(<span class="string">'\n'</span>),</span><br><span class="line"><span class="attr">rich</span>:{</span><br><span class="line">名称:{</span><br><span class="line">对该名称的文本进行添加样式,且可以看成inline-block</span><br><span class="line"><span class="attr">lineHeight</span>: <span class="number">10</span></span><br><span class="line"><span class="attr">width</span>:<span class="number">10</span>,</span><br><span class="line"><span class="attr">align</span>:<span class="string">'left|center|right'</span>,</span><br><span class="line"><span class="attr">verticalAlign</span>:<span class="string">'top|middle|bottom'</span><span class="comment">//在lineHeight被决定后,竖直位置由verticalAlign来指定</span></span><br><span class="line"><span class="attr">backgroundColor</span>: {使用图片</span><br><span class="line"> <span class="attr">image</span>: <span class="string">'./data/asset/img/weather/sunny_128.png'</span></span><br><span class="line"> },</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="4-缓冲动画"><a href="#4-缓冲动画" class="headerlink" title="4.缓冲动画"></a>4.缓冲动画</h5><p>动画延迟animationDelay </p><p>动画时长animationDuration</p><p>动画缓动函数animationEasing</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="attr">animationDelay</span>: <span class="keyword">function</span>(<span class="params">idx</span>) {</span><br><span class="line"><span class="comment">// 越往后的数据延迟越大</span></span><br><span class="line"><span class="keyword">return</span> idx * <span class="number">200</span>;</span><br><span class="line">},</span><br><span class="line"><span class="attr">animationDuration</span>: <span class="keyword">function</span>(<span class="params">idx</span>) {</span><br><span class="line"><span class="comment">// 每小格动画的时候</span></span><br><span class="line"><span class="keyword">return</span> idx * <span class="number">200</span>;</span><br><span class="line">},</span><br><span class="line"><span class="comment">// 弹性的方式出现动画</span></span><br><span class="line"><span class="attr">animationEasing</span>: <span class="string">"bounceInOut"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
</entry>
<entry>
<title>canvas事件</title>
<link href="/2024/04/28/canvas%E4%BA%8B%E4%BB%B6/"/>
<url>/2024/04/28/canvas%E4%BA%8B%E4%BB%B6/</url>
<content type="html"><![CDATA[<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><h5 id="常用的鼠标事件"><a href="#常用的鼠标事件" class="headerlink" title="常用的鼠标事件"></a>常用的鼠标事件</h5><p>首先咱们还是先回顾一下鼠标的常用事件:</p><ul><li>click(点击)</li><li>dblclick(双击)</li><li>mouseover(鼠标移入)</li><li>mouseout(鼠标移出)</li><li>mouseenter(鼠标移入)</li><li>mouseleave(鼠标移出)</li><li>mouseup(鼠标抬起)</li><li>mousedown(鼠标按下)</li><li>mousemove(鼠标移动)</li></ul><p>以上就是咱们常用的鼠标事件,我们发现其中的<code>mouseover</code>和<code>mouseenter</code>还有<code>mouseout</code>和<code>mouseleave</code>好像是一样的事件,但凭我们的经验虽然看似一样,其实肯定不一样,或者说有区别。</p><p>那么他们有什么区别呢?</p><p><code>mouseover</code>和<code>mouseenter</code>都是鼠标移入时触发,但区别是<code>mouseover</code>支持事件冒泡,而<code>mouseenter</code>不支持事件冒泡。简单说就是 <code>mouseover</code>事件在鼠标指针移入被选元素或者是被选元素的任何子元素,都会触发,而<code>mouseenter</code>事件只有在鼠标指针移入被选元素时,才会触发,移入被选元素的子元素不会触发。</p><p><code>mouseout</code>和<code>mouseleave</code>都是鼠标移入时触发,但区别是<code>mouseout</code>支持事件冒泡,而<code>mouseleave</code>不支持事件冒泡。简单说就是 <code>mouseout</code>事件在鼠标指针离开被选元素或者是被选元素的任何子元素,都会触发,而<code>mouseleave</code>事件只有在鼠标指针离开被选元素时,才会触发,离开被选元素的子元素不会触发。</p><h5 id="键盘事件"><a href="#键盘事件" class="headerlink" title="键盘事件"></a>键盘事件</h5><ul><li>keydown(键盘按下)</li><li>keyup(键盘抬起)</li><li>keypress(紧接着<code>keydown</code>事件触发(只有按下字符键时触发))</li></ul><p>如果用户按下了一个<em>字符键</em>不放,就会重复触发<code>keydown</code>和<code>keypress</code>事件,直到用户松开该键为止,如果用户按下了一个<em>非字符键</em>不放,就会重复触发<code>keydown</code>事件</p><p><code>keypress</code>事件返回的是输入的字符的<strong>ASCII</strong>码(which属性),而<code>keydown</code>事件返回的是键盘码(keyCode属性)。</p><p>ps:<code>keypress</code>支持的系统<em>功能键</em> :</p><p>**<code>Firefox</code>**:<code>Esc、Enter、Backspace、Pause Break、Insert、 Delete、Home、End、Page Up、Page Down、F1 through F12、The Arrow Keys</code>、上下左右键</p><p><strong><code>Chrome / Oprea / Safari</code></strong> :<code>Enter</code></p><p>**<code>IE</code>**:<code>Esc、Entery</code></p><p><strong>除了 <code>Firefox</code>,其他<code>chrome、oprea、safari、IE</code> 上下左右键不会触发<code>keypress</code></strong></p><h3 id="事件的添加和移除"><a href="#事件的添加和移除" class="headerlink" title="事件的添加和移除"></a>事件的添加和移除</h3><p>给Canvas中的元素添加事件我们用的是:<code>addEventListener()</code>方法,移除事件用的是<code>removeEventListener()</code>方法</p><p><code>参数值</code></p><ol><li><p>event 必须。字符串,指定事件名。</p><p>注意: 不要使用 “on” 前缀。 例如,使用 “click” ,而不是使用 “onclick”。</p></li><li><p>function 必须。指定要事件触发时执行的函数。</p><p>当事件对象会作为第一个参数传入函数。 事件对象的类型取决于特定的事件。例如, “click” 事件属于 MouseEvent(鼠标事件) 对象。</p></li><li><p>useCapture 可选。布尔值,指定事件是否在捕获或冒泡阶段执行。</p></li></ol><p>首先看一下添加鼠标事件</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 获取Canvas</span></span><br><span class="line"><span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); </span><br><span class="line"><span class="comment">// 获取绘制上下文</span></span><br><span class="line"><span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); </span><br><span class="line"><span class="comment">// 给canvas添加鼠标移动事件</span></span><br><span class="line">canvas.<span class="title function_">addEventListener</span>(<span class="string">"mousemove"</span>, mouseMoving, <span class="literal">false</span>);</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">mouseMoving</span>(<span class="params">e</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`当前鼠标在Canvas中的位置: x: <span class="subst">${e.clientX}</span> y: <span class="subst">${e.clientY}</span>`</span>);<span class="comment">//e.clientX表示鼠标相对于浏览器窗口的水平位置,e.clientY表示鼠标相对于浏览器窗口的垂直位置。</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="/canvas%E4%BA%8B%E4%BB%B6.assets/8231906139394c4d98ebd81dd808d508tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-17142898943982.awebp" alt="5.gif"></p><p>Canvas支持所有的鼠标事件,但是并不支持键盘事件,那么想要让Canvas支持键盘事件我们就需要自己处理,这边有两种方法可以实现键盘事件。</p><p>方法一:让Canvas自动获取焦点,从而支持键盘事件。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><body></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">canvas</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">id</span>=<span class="string">"canvas"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">width</span>=<span class="string">"550"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">height</span>=<span class="string">"500"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">tabindex</span>=<span class="string">"0"</span>//<span class="attr">使其可聚焦</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">style</span>=<span class="string">"box-shadow: 0px 0px 5px #ccc; border-radius: 8px;"</span>></span></span></span><br><span class="line"><span class="language-xml"> 当前浏览器不支持canvas元素,请升级或更换浏览器!</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">canvas</span>></span></span></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">script</span>></span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="comment">// 获取Canvas</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); </span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="comment">// 获取绘制上下文</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); </span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="comment">// 给canvas添加键盘事件</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> canvas.<span class="title function_">addEventListener</span>(<span class="string">"keydown"</span>, doKeydown, <span class="literal">false</span>);</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> canvas.<span class="title function_">focus</span>();<span class="comment">//自动获取焦点</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">function</span> <span class="title function_">doKeydown</span>(<span class="params">e</span>) {</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">switch</span>(e.<span class="property">keyCode</span>) {</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">case</span> <span class="number">37</span>:</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`按下左键`</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">break</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">case</span> <span class="number">38</span>:</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`按下上键`</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">break</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">case</span> <span class="number">39</span>:</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`按下右键`</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">break</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">case</span> <span class="number">40</span>:</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`按下下键`</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">break</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> }</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> }</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> </span><span class="tag"></<span class="name">script</span>></span></span></span><br><span class="line"></body></span><br></pre></td></tr></table></figure><p><img src="/canvas%E4%BA%8B%E4%BB%B6.assets/9f91c99e848a4a55930f5312e252f005tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-17142931387424.awebp" alt="6.gif"></p><p>需要注意,当鼠标点击别的元素的时候,<code>canvas</code>元素会失去焦点,从而失去键盘事件</p><p>方法二:通过为<code>windows</code>对象添加键盘事件,从而控制<code>canvas</code>元素(更常用)</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><body></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">canvas</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">id</span>=<span class="string">"canvas"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">width</span>=<span class="string">"500"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">height</span>=<span class="string">"500"</span> </span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">tabindex</span>=<span class="string">"0"</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">style</span>=<span class="string">"box-shadow: 0px 0px 5px #ccc; border-radius: 8px;"</span>></span></span></span><br><span class="line"><span class="language-xml"> 当前浏览器不支持canvas元素,请升级或更换浏览器!</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">canvas</span>></span></span></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">script</span>></span><span class="language-javascript"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="comment">// 获取Canvas</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); </span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="comment">// 获取绘制上下文</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); </span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="comment">// 设置填充的颜色为橘色</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> ctx.<span class="property">fillStyle</span>=<span class="string">"orange"</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="comment">// 获取x,y的值</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">let</span> x = canvas.<span class="property">width</span> / <span class="number">2</span> - <span class="number">50</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">let</span> y = canvas.<span class="property">height</span> / <span class="number">2</span> - <span class="number">50</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="comment">// 绘制一个矩形</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> ctx.<span class="title function_">fillRect</span>(x, y, <span class="number">100</span>, <span class="number">50</span>);</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="comment">// 给canvas添加鼠标移动事件</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="variable language_">window</span>.<span class="title function_">addEventListener</span>(<span class="string">"keydown"</span>, doKeydown, <span class="literal">false</span>);</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> </span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">function</span> <span class="title function_">doKeydown</span>(<span class="params">e</span>) {</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> ctx.<span class="title function_">clearRect</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">500</span>, <span class="number">500</span>)<span class="comment">//绘制新图像之前先擦除之前的图像</span></span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">let</span> keyID = e.<span class="property">keyCode</span> ? e.<span class="property">keyCode</span> :e.<span class="property">which</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">switch</span>(keyID) {</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">case</span> <span class="number">37</span>:</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`按下左键`</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> x = x - <span class="number">10</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> ctx.<span class="title function_">fillRect</span>(x, y, <span class="number">100</span>, <span class="number">50</span>);</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">break</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">case</span> <span class="number">38</span>:</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`按下上键`</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> y = y - <span class="number">10</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> ctx.<span class="title function_">fillRect</span>(x, y, <span class="number">100</span>, <span class="number">50</span>);</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">break</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">case</span> <span class="number">39</span>:</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`按下右键`</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> x = x + <span class="number">10</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> ctx.<span class="title function_">fillRect</span>(x, y, <span class="number">100</span>, <span class="number">50</span>);</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">break</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">case</span> <span class="number">40</span>:</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`按下下键`</span>)</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> y = y + <span class="number">10</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> ctx.<span class="title function_">fillRect</span>(x, y, <span class="number">100</span>, <span class="number">50</span>);</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> <span class="keyword">break</span>;</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> }</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> }</span></span></span><br><span class="line"><span class="language-javascript"><span class="language-xml"> </span><span class="tag"></<span class="name">script</span>></span></span></span><br><span class="line"></body></span><br></pre></td></tr></table></figure><p><img src="/canvas%E4%BA%8B%E4%BB%B6.assets/312491f7e3ce4eb5aeb26a932f752359tplv-k3u1fbpfcp-zoom-in-crop-mark1512000.awebp" alt="7.gif"></p><h3 id="内部元素添加事件"><a href="#内部元素添加事件" class="headerlink" title="内部元素添加事件"></a>内部元素添加事件</h3><p>上面的所有事件其实都是添加到<code>canvas</code>元素上的,但往往在平常的需求中我们需要针对<code>canvas</code>元素内部的子元素做单独的事件交互,那么我们就需要考虑如何给<code>canvas</code>元素的内部元素添加事件。</p><p><code>canvas</code>元素本身并没有提供给内部元素添加事件的Api,所以我们直接使用原生的方式和<code>canvas</code>元素的内部元素进行交互</p><p>这里就以拖拽为例,假如<code>canvas</code>元素内部有多个子元素,那么想拖拽其中一个子元素,我们首先得知道,在鼠标按下的时候是否按在<code>canvas</code>元素的子元素上,只有按在<code>canvas</code>元素的子元素上我们才能对它进行拖拽。</p><h5 id="1-准备工作"><a href="#1-准备工作" class="headerlink" title="1.准备工作"></a>1.准备工作</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><script></span><br><span class="line"> <span class="comment">// 获取Canvas</span></span><br><span class="line"> <span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); </span><br><span class="line"> <span class="keyword">const</span> width = canvas.<span class="property">width</span>;</span><br><span class="line"> <span class="keyword">const</span> height = canvas.<span class="property">height</span>;</span><br><span class="line"> <span class="comment">// 获取绘制上下文</span></span><br><span class="line"> <span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); </span><br><span class="line"> <span class="keyword">const</span> images = [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"白月魁"</span>,</span><br><span class="line"> <span class="attr">url</span>: <span class="string">"../images/bailaoban.jpg"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"鸣人"</span>,</span><br><span class="line"> <span class="attr">url</span>: <span class="string">"../images/mingren.jpg"</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"路飞"</span>,</span><br><span class="line"> <span class="attr">url</span>: <span class="string">"../images/lufei.jpg"</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"哪吒"</span>,</span><br><span class="line"> <span class="attr">url</span>: <span class="string">"../images/nazha.jpg"</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"千寻"</span>,</span><br><span class="line"> <span class="attr">url</span>: <span class="string">"../images/qianxun.jpg"</span>,</span><br><span class="line"> },</span><br><span class="line"> ];</span><br><span class="line"> <span class="comment">//遍历</span></span><br><span class="line"> images.<span class="title function_">forEach</span>(<span class="function">(<span class="params">item</span>)=></span>{</span><br><span class="line"> <span class="comment">// 创建image元素</span></span><br><span class="line"> <span class="keyword">const</span> image = <span class="keyword">new</span> <span class="title class_">Image</span>()</span><br><span class="line"> image.<span class="property">src</span> = item.<span class="property">url</span>;</span><br><span class="line"> <span class="keyword">const</span> name = item.<span class="property">name</span>;</span><br><span class="line"> image.<span class="property">onload</span> = <span class="function">() =></span> {</span><br><span class="line"> <span class="comment">// 控制宽度为200(等宽)</span></span><br><span class="line"> <span class="keyword">const</span> w = <span class="number">200</span>;</span><br><span class="line"> <span class="comment">// 高度按宽度200的比例缩放</span></span><br><span class="line"> <span class="keyword">const</span> h = <span class="number">200</span> / image.<span class="property">width</span> * image.<span class="property">height</span>;</span><br><span class="line"> <span class="keyword">const</span> x = <span class="title class_">Math</span>.<span class="title function_">random</span>() * (width - w) ;<span class="comment">//保证能在画布里面正常显示</span></span><br><span class="line"> <span class="keyword">const</span> y = <span class="title class_">Math</span>.<span class="title function_">random</span>() * (height - h);</span><br><span class="line"> <span class="keyword">const</span> imageObj = { image, name, x, y, w, h }</span><br><span class="line"> <span class="title function_">draw</span>(imageObj)</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 渲染图片</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">draw</span>(<span class="params">imageObj</span>) {</span><br><span class="line"> ctx.<span class="title function_">drawImage</span>(imageObj.<span class="property">image</span>, imageObj.<span class="property">x</span>, imageObj.<span class="property">y</span>, imageObj.<span class="property">w</span>, imageObj.<span class="property">h</span>);</span><br><span class="line"> }</span><br><span class="line"> </script></span><br></pre></td></tr></table></figure><p><img src="/canvas%E4%BA%8B%E4%BB%B6.assets/abf5e7c11a794ea9b22fa68de96619c5tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-17142952232837.awebp" alt="image.png"></p><h5 id="2-添加事件"><a href="#2-添加事件" class="headerlink" title="2.添加事件"></a>2.添加事件</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 为canvas添加鼠标按下事件</span></span><br><span class="line">canvas.<span class="title function_">addEventListener</span>(<span class="string">"mousedown"</span>, mousedownFn, <span class="literal">false</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 鼠标按下触发的方法</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">mousedownFn</span>(<span class="params">e</span>) {</span><br><span class="line"> <span class="comment">// 为canvas添加鼠标移动和鼠标抬起事件</span></span><br><span class="line"> canvas.<span class="title function_">addEventListener</span>(<span class="string">"mousemove"</span>, mousemoveFn, <span class="literal">false</span>)</span><br><span class="line"> canvas.<span class="title function_">addEventListener</span>(<span class="string">"mouseup"</span>, mouseupFn, <span class="literal">false</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 鼠标移动触发</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">mousemoveFn</span>(<span class="params"></span>) {}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 鼠标抬起触发</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">mouseupFn</span>(<span class="params"></span>) {}</span><br></pre></td></tr></table></figure><h5 id="3-判断选中元素"><a href="#3-判断选中元素" class="headerlink" title="3.判断选中元素"></a>3.判断选中元素</h5><p>定义完事件以后,我们就需要判断每次点击的元素是其中的哪一个,这样我们才能针对这个元素做交互。</p><p>通过计算,如上面布局的代码,每个图片绘制的x、y、width和height我们都是知道的,那么当我们每次点击下去的时候就可以遍历图片的数据,看我们是否点击到元素上。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 获取Canvas</span></span><br><span class="line"> <span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); </span><br><span class="line"> <span class="keyword">const</span> width = canvas.<span class="property">width</span>;</span><br><span class="line"> <span class="keyword">const</span> height = canvas.<span class="property">height</span>;</span><br><span class="line"> <span class="comment">// 获取绘制上下文</span></span><br><span class="line"> <span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); </span><br><span class="line"> <span class="keyword">const</span> images = [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"白月魁"</span>,</span><br><span class="line"> <span class="attr">url</span>: <span class="string">"../images/bailaoban.jpg"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"鸣人"</span>,</span><br><span class="line"> <span class="attr">url</span>: <span class="string">"../images/mingren.jpg"</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"路飞"</span>,</span><br><span class="line"> <span class="attr">url</span>: <span class="string">"../images/lufei.jpg"</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"哪吒"</span>,</span><br><span class="line"> <span class="attr">url</span>: <span class="string">"../images/nazha.jpg"</span>,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"千寻"</span>,</span><br><span class="line"> <span class="attr">url</span>: <span class="string">"../images/qianxun.jpg"</span>,</span><br><span class="line"> },</span><br><span class="line"> ];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> imagesData = []<span class="comment">//存储图片信息对象</span></span><br><span class="line"> <span class="keyword">let</span> clickCoordinate = { <span class="attr">x</span>: <span class="number">0</span>, <span class="attr">y</span>: <span class="number">0</span> } <span class="comment">//存储鼠标按下时的坐标信息</span></span><br><span class="line"> <span class="keyword">let</span> target;<span class="comment">//存储index</span></span><br><span class="line"></span><br><span class="line"> images.<span class="title function_">forEach</span>(<span class="function">(<span class="params">item</span>)=></span>{</span><br><span class="line"> <span class="comment">// 创建image元素</span></span><br><span class="line"> <span class="keyword">const</span> image = <span class="keyword">new</span> <span class="title class_">Image</span>()</span><br><span class="line">image.<span class="property">src</span> = item.<span class="property">url</span>;</span><br><span class="line"> <span class="keyword">const</span> name = item.<span class="property">name</span>;</span><br><span class="line"> image.<span class="property">onload</span> = <span class="function">() =></span> {</span><br><span class="line"> <span class="comment">// 控制宽度为200(等宽)</span></span><br><span class="line"> <span class="keyword">const</span> w = <span class="number">200</span>;</span><br><span class="line"> <span class="comment">// 高度按宽度200的比例缩放</span></span><br><span class="line"> <span class="keyword">const</span> h = <span class="number">200</span> / image.<span class="property">width</span> * image.<span class="property">height</span>;</span><br><span class="line"> <span class="keyword">const</span> x = <span class="title class_">Math</span>.<span class="title function_">random</span>() * (width - w) ;</span><br><span class="line"> <span class="keyword">const</span> y = <span class="title class_">Math</span>.<span class="title function_">random</span>() * (height - h);</span><br><span class="line"> <span class="keyword">const</span> imageObj = { image, name, x, y, w, h }</span><br><span class="line"> imagesData.<span class="title function_">push</span>(imageObj)</span><br><span class="line"> <span class="title function_">draw</span>(imageObj)</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 渲染图片</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">draw</span>(<span class="params">imageObj</span>) {</span><br><span class="line"> ctx.<span class="title function_">drawImage</span>(imageObj.<span class="property">image</span>, imageObj.<span class="property">x</span>, imageObj.<span class="property">y</span>, imageObj.<span class="property">w</span>, imageObj.<span class="property">h</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 为canvas添加鼠标按下事件</span></span><br><span class="line"> canvas.<span class="title function_">addEventListener</span>(<span class="string">"mousedown"</span>, mousedownFn, <span class="literal">false</span>)</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 鼠标按下触发的方法</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">mousedownFn</span>(<span class="params">e</span>) {</span><br><span class="line"> <span class="comment">// 获取元素按下时的坐标</span></span><br><span class="line"> clickCoordinate.<span class="property">x</span> = e.<span class="property">pageX</span> - canvas.<span class="property">offsetLeft</span>;<span class="comment">//e.pageX 鼠标相对于左侧边缘的水平偏移量</span></span><br><span class="line"> clickCoordinate.<span class="property">y</span> = e.<span class="property">pageY</span> - canvas.<span class="property">offsetTop</span>;</span><br><span class="line"> <span class="comment">// 判断选中的元素是哪一个</span></span><br><span class="line"> <span class="title function_">checkElement</span>()</span><br><span class="line"> <span class="comment">// 为canvas添加鼠标移动和鼠标抬起事件</span></span><br><span class="line"> canvas.<span class="title function_">addEventListener</span>(<span class="string">"mousemove"</span>, mousemoveFn, <span class="literal">false</span>)</span><br><span class="line"> canvas.<span class="title function_">addEventListener</span>(<span class="string">"mouseup"</span>, mouseupFn, <span class="literal">false</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 鼠标移动触发</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">mousemoveFn</span>(<span class="params"></span>) {}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 鼠标抬起触发</span></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">mouseupFn</span>(<span class="params"></span>) {}</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">checkElement</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">//遍历去一一对应</span></span><br><span class="line"> imagesData.<span class="title function_">forEach</span>(<span class="function">(<span class="params">item, index</span>)=></span>{</span><br><span class="line"> <span class="keyword">const</span> minX = item.<span class="property">x</span></span><br><span class="line"> <span class="keyword">const</span> maxX = item.<span class="property">x</span> + item.<span class="property">w</span></span><br><span class="line"> <span class="keyword">const</span> minY = item.<span class="property">y</span></span><br><span class="line"> <span class="keyword">const</span> maxY = item.<span class="property">y</span> + item.<span class="property">h</span></span><br><span class="line"> <span class="keyword">if</span>(minX <= clickCoordinate.<span class="property">x</span> && clickCoordinate.<span class="property">x</span> <= maxX && minY <= clickCoordinate.<span class="property">y</span> && clickCoordinate.<span class="property">y</span> <= maxY) {</span><br><span class="line"> target = index</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"点击的元素是:"</span>, item.<span class="property">name</span>)</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> </span><br><span class="line"> }</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="/canvas%E4%BA%8B%E4%BB%B6.assets/af94ba6fd4af40a78783e869fe97afc2tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-17142984212399.awebp" alt="1.gif"></p><h5 id="4-移动"><a href="#4-移动" class="headerlink" title="4.移动"></a>4.移动</h5><p>知道选中的元素以后,我们就需要在移动的时候把移动的坐标赋值给选中的元素,让选中的元素跟着鼠标移动。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 鼠标移动触发</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">mousemoveFn</span>(<span class="params">e</span>) {</span><br><span class="line"> <span class="keyword">const</span> moveX = e.<span class="property">pageX</span> <span class="comment">//鼠标移动结束的位置</span></span><br><span class="line"> <span class="keyword">const</span> moveY = e.<span class="property">pageY</span> <span class="comment">//鼠标移动结束的位置</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(moveX,moveY)</span><br><span class="line"> <span class="comment">// 计算移动元素的坐标</span></span><br><span class="line"> imagesData[target].<span class="property">x</span> = imagesData[target].<span class="property">x</span> + ( moveX - clickCoordinate.<span class="property">x</span> );<span class="comment">//移动位移</span></span><br><span class="line"> imagesData[target].<span class="property">y</span> = imagesData[target].<span class="property">y</span> + ( moveY - clickCoordinate.<span class="property">y</span> ); </span><br><span class="line"> <span class="comment">// 清空画布</span></span><br><span class="line"> ctx.<span class="title function_">clearRect</span>(<span class="number">0</span>, <span class="number">0</span>, width, height);</span><br><span class="line"> <span class="comment">// 清空画布以后重新绘制</span></span><br><span class="line"> imagesData.<span class="title function_">forEach</span>(<span class="function">(<span class="params">i</span>) =></span> <span class="title function_">draw</span>(i))</span><br><span class="line"> <span class="comment">// 赋值 更新clickCoordinate的坐标</span></span><br><span class="line"> clickCoordinate.<span class="property">x</span> = moveX; </span><br><span class="line"> clickCoordinate.<span class="property">y</span> = moveY;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 鼠标抬起触发</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">mouseupFn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 鼠标抬起以后移除事件</span></span><br><span class="line"> canvas.<span class="title function_">removeEventListener</span>(<span class="string">"mousemove"</span>, mousemoveFn, <span class="literal">false</span>)</span><br><span class="line"> canvas.<span class="title function_">removeEventListener</span>(<span class="string">"mouseup"</span>, mouseupFn, <span class="literal">false</span>)</span><br><span class="line"> <span class="comment">// 销毁选中元素</span></span><br><span class="line"> target = <span class="literal">undefined</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="/canvas%E4%BA%8B%E4%BB%B6.assets/00671f0ee4f3421a977e6ec22bd06ca1tplv-k3u1fbpfcp-zoom-in-crop-mark1512000.awebp" alt="2.gif"></p>]]></content>
</entry>
<entry>
<title>canvas进阶</title>
<link href="/2024/04/12/canvas%E8%BF%9B%E9%98%B6/"/>
<url>/2024/04/12/canvas%E8%BF%9B%E9%98%B6/</url>
<content type="html"><![CDATA[<h2 id="1-动画"><a href="#1-动画" class="headerlink" title="1.动画"></a>1.动画</h2><p>在Canvas中,动画其实也就是一些基础的<code>几何变换</code>,因此想做动画第一步咱们需要先了解有哪些<code>几何变换</code>。</p><h4 id="几何变换"><a href="#几何变换" class="headerlink" title="几何变换"></a>几何变换</h4><p>几何变换的类型其实和CSS动画中的类型差不多,也就是:移动、旋转、缩放</p><h5 id="移动"><a href="#移动" class="headerlink" title="移动"></a>移动</h5><p>语法:<code>translate(x, y)</code>,其中 x 是左右偏移量,y 是上下偏移量。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); <span class="comment">// 获取Canvas</span></span><br><span class="line"><span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); <span class="comment">// 获取绘制上下文</span></span><br><span class="line">ctx.<span class="property">fillStyle</span>=<span class="string">"#ff0000"</span></span><br><span class="line"><span class="comment">// 向x轴和y轴平移200像素(移的是画布的原点)</span></span><br><span class="line">ctx.<span class="title function_">translate</span>(<span class="number">200</span>, <span class="number">200</span>);</span><br><span class="line"><span class="comment">// 在(0,0)坐标点绘制一个宽:200,高:100的矩形</span></span><br><span class="line">ctx.<span class="title function_">fillRect</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">200</span>, <span class="number">100</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><h5 id="旋转"><a href="#旋转" class="headerlink" title="旋转"></a>旋转</h5><p>语法:<code>rotate(angle)</code>,其中 angle 是旋转的角度,以弧度为单位,顺时针旋转。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); <span class="comment">// 获取Canvas</span></span><br><span class="line"><span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); <span class="comment">// 获取绘制上下文</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">9</span>; i++) {</span><br><span class="line"> <span class="comment">// 旋转弧度设置,角度和弧度的转换公式:1° = Math.PI / 180</span></span><br><span class="line"> ctx.<span class="title function_">rotate</span>(i * <span class="number">2</span> * <span class="title class_">Math</span>.<span class="property">PI</span> / <span class="number">180</span>);</span><br><span class="line"> <span class="comment">// 在(0,0)坐标点绘制一个宽:200,高:100的矩形</span></span><br><span class="line"> ctx.<span class="title function_">fillRect</span>(<span class="number">100</span>, <span class="number">0</span>, <span class="number">200</span>, <span class="number">100</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>每次调用 <code>rotate()</code> 方法都是基于当前绘图上下文的状态进行旋转,就是在上一次旋转的角度基础上再进行旋转。用closePath()也不能重置旋转的角度为0°</p><h5 id="缩放"><a href="#缩放" class="headerlink" title="缩放"></a>缩放</h5><p>语法:<code>scale(x, y)</code>,其中 x 为水平缩放的值,y 为垂直缩放得值。x和y的值小于1则为缩小,大于1则为放大。默认值为 1。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); <span class="comment">// 获取Canvas</span></span><br><span class="line"><span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); <span class="comment">// 获取绘制上下文</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">9</span>; i++) {</span><br><span class="line"> ctx.<span class="property">fillStyle</span>=<span class="string">`#<span class="subst">${i}</span><span class="subst">${i}</span><span class="subst">${i}</span>`</span><span class="comment">//颜色渐变</span></span><br><span class="line"> ctx.<span class="title function_">beginPath</span>()</span><br><span class="line"> ctx.<span class="title function_">scale</span>(<span class="number">2</span> / i, <span class="number">2</span> / i);</span><br><span class="line"> <span class="comment">// 绘制圆</span></span><br><span class="line"> ctx.<span class="title function_">arc</span>(<span class="number">250</span>, <span class="number">250</span>, <span class="number">50</span>, <span class="number">0</span>, <span class="number">360</span> * <span class="title class_">Math</span>.<span class="property">PI</span>/<span class="number">180</span>);</span><br><span class="line"> ctx.<span class="title function_">fill</span>();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="/canvas%E8%BF%9B%E9%98%B6.assets/eHeUg9Vk.webp" alt="4becc49ca733443cbac3b2b70a26baa3~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp"></p><h5 id="状态的保存和恢复"><a href="#状态的保存和恢复" class="headerlink" title="状态的保存和恢复"></a>状态的保存和恢复</h5><p>什么是状态的保存和恢复呢?我们这么理解,当我们在Canvas中绘制时,每次绘制完都会是一个Canvas的快照,而每个快照时的状态,我们可以保存起来,当我们需要再次使用时,又把这个快照恢复。</p><p>状态的保存和恢复 用到的方法是 <code>save()</code> 和 <code>restore()</code>, 分别是保存和恢复。方法不需要参数,直接调用就OK。</p><p>绘画的状态有哪些呢(就是我们可以保存和恢复的状态有哪些)?我们列举一下:</p><ul><li>应用的变形:移动、旋转、缩放、strokeStyle、fillStyle、globalAlpha、lineWidth、lineCap、lineJoin、miterLimit、lineDashOffset、shadowOffsetX、shadowOffsetY、shadowBlur、shadowColor、globalCompositeOperation、font、textAlign、textBaseline、direction、imageSmoothingEnabled等。</li><li>应用的裁切路径(clipping path)</li></ul><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); <span class="comment">// 获取Canvas</span></span><br><span class="line"><span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); <span class="comment">// 获取绘制上下文</span></span><br><span class="line"></span><br><span class="line">ctx.<span class="property">fillStyle</span> = <span class="string">"gray"</span>;</span><br><span class="line">ctx.<span class="title function_">fillRect</span>(<span class="number">10</span>, <span class="number">10</span>, <span class="number">200</span>, <span class="number">100</span>);</span><br><span class="line"><span class="comment">// 保存状态</span></span><br><span class="line">ctx.<span class="title function_">save</span>(); </span><br><span class="line">ctx.<span class="property">fillStyle</span> = <span class="string">"orange"</span>;</span><br><span class="line">ctx.<span class="title function_">fillRect</span>(<span class="number">10</span>, <span class="number">150</span>, <span class="number">200</span>, <span class="number">100</span>);</span><br><span class="line"><span class="comment">// 恢复上次保存的状态</span></span><br><span class="line">ctx.<span class="title function_">restore</span>(); </span><br><span class="line">ctx.<span class="title function_">fillRect</span>(<span class="number">10</span>, <span class="number">300</span>, <span class="number">200</span>, <span class="number">200</span>);</span><br></pre></td></tr></table></figure><p><img src="/canvas%E8%BF%9B%E9%98%B6.assets/nmTfC03K-17129954736242.webp" alt="28e1c9899a1749fab7893b7d21f057b9~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp"></p><p>如上图我们可以看出,最开始我们设置了填充颜色为灰色,并绘制了一个矩形,然后我们执行了状态保存,上面我们已经列举了哪些状态可以保存,所以这里我们知道此次的状态保存的是:<code>fillStyle</code>状态,保存完以后我们又设置了填充颜色为橘色,并且又绘制了一个矩形,最后我们执行了一次状态恢复,接着直接绘制一个正方形。我们知道如果没有状态保存和恢复的方法,正常情况下正方形应该是使用橘色来填充,但正因为我们保存了<code>fillStyle</code>状态的灰色,又在绘制正方形之前恢复了<code>fillStyle</code>状态为灰色,因此绘制出来的正方形为灰色。</p><h2 id="2-动画"><a href="#2-动画" class="headerlink" title="2.动画"></a>2.动画</h2><p>Canvas呈现的东西都是绘制完了以后才能看到,因此想通过Canvas自己提供的Api来实现动画是做不到的。</p><p>那么想在 Canvas 中实现动画就得借助别的东西,那么借助啥呢?</p><p>在我们的 windows 对象上有三个方法:</p><ul><li>setInterval(function, delay) :定时器,当设定好间隔时间后,function 会定期执行。</li><li>setTimeout(function, delay):延时器,在设定好的时间之后执行函数</li><li>requestAnimationFrame(callback):告诉浏览器你希望执行一个动画,并在重绘之前,请求浏览器执行一个特定的函数来更新动画。</li></ul><p>那么这三个方法有什么区别呢?</p><p>正常情况下,当我们需要自动去展示动画而不需要和用户交互的情况下,我们会选择 <code>setInterval()</code>方法,因为我们只需要把执行动画的代码丢在 <code>setInterval()</code>方法中,他就会自动执行绘制我们想要的动画。如果我们做一些交互性的动画,那么使用 <code>setTimeout()</code> 方法和键盘或者鼠标事件配合会更简单一些。相对于前两个方法,<code>requestAnimationFrame()</code>方法可能会显得陌生一些,<code>requestAnimationFrame()</code>方法提供了更加平缓且有效率的方式来执行动画,当我们准备好动画以后,把动画交给<code>requestAnimationFrame()</code>方法就能绘制动画帧。</p><h5 id="setInterval-setTimeout"><a href="#setInterval-setTimeout" class="headerlink" title="setInterval setTimeout"></a>setInterval setTimeout</h5><p>这里先使用 <code>setInterval()</code>方法实现一个元素的位移效果。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); <span class="comment">// 获取Canvas</span></span><br><span class="line"><span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); <span class="comment">// 获取绘制上下文</span></span><br><span class="line">ctx.<span class="property">fillStyle</span> = <span class="string">"#ccc"</span>;</span><br><span class="line"><span class="keyword">let</span> num = <span class="number">0</span></span><br><span class="line"><span class="built_in">setInterval</span>(<span class="function">()=></span>{</span><br><span class="line"> num += <span class="number">1</span></span><br><span class="line"> <span class="keyword">if</span>(num <= <span class="number">400</span>) {</span><br><span class="line"> ctx.<span class="title function_">fillRect</span>(num, <span class="number">0</span>, <span class="number">100</span>, <span class="number">100</span>);</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p><img src="/canvas%E8%BF%9B%E9%98%B6.assets/dM0SvrA9-17129955247194.webp" alt="7fc69c0842034fe6a423d696e6a365f0~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp"><img src="/canvas%E8%BF%9B%E9%98%B6.assets/dM0SvrA9.webp" alt="1.gif"></p><p>如图我们可以看出,元素确实动了,但是似乎不是我们想要的那个样子,我们想实现的是元素的位移,但看样子实现的是元素的变宽。</p><p>那么我们看一下问题出在哪里?</p><p>经过我们的一番思考,我们发现,Canvas 绘制时把元素一帧一帧的绘制到画布上,比如上面的例子我们把一个元素从(0,0)移动到(400,0),也就是横向移动400像素。既然是一帧一帧绘制的,那么我们看到的就是连续的从(0,0)绘制到(400,0)的效果,也就是我们看到的是所有的帧组合在一起的效果,而不是从(0,0)移动到(400,0)的效果。</p><p>那么想要看到移动的效果就需要我们只看此时此刻的那一帧,而不看之前的帧。因此我们在绘制下一帧的同时我们需要把上一帧清除掉。</p><h5 id="画布清空"><a href="#画布清空" class="headerlink" title="画布清空"></a>画布清空</h5><p>语法:<code>clearRect(x, y, width, height)</code></p><p>参数:</p><ul><li>x为要清除的矩形区域左上角的x坐标,</li><li>y为要清除的矩形区域左上角的y坐标</li><li>width为要清除的矩形区域的宽度</li><li>height为要清除的矩形区域的高度</li></ul><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); <span class="comment">// 获取Canvas</span></span><br><span class="line"><span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); <span class="comment">// 获取绘制上下文</span></span><br><span class="line">ctx.<span class="property">fillStyle</span> = <span class="string">"#ccc"</span>;</span><br><span class="line"><span class="keyword">const</span> width = canvas.<span class="property">width</span></span><br><span class="line"><span class="keyword">const</span> height = canvas.<span class="property">height</span></span><br><span class="line"><span class="keyword">let</span> num = <span class="number">0</span></span><br><span class="line"><span class="built_in">setInterval</span>(<span class="function">()=></span>{</span><br><span class="line"> num += <span class="number">1</span></span><br><span class="line"> <span class="keyword">if</span>(num <= <span class="number">400</span>) {</span><br><span class="line"> ctx.<span class="title function_">clearRect</span>(<span class="number">0</span>, <span class="number">0</span>, width, height) <span class="comment">//清除整张画布</span></span><br><span class="line"> ctx.<span class="title function_">fillRect</span>(num, <span class="number">0</span>, <span class="number">100</span>, <span class="number">100</span>);</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p><img src="/canvas%E8%BF%9B%E9%98%B6.assets/uZo4vH0a-17129955494136.webp" alt="4f3b8056d42d4e5dade50336be33da54~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp"></p><h4 id="requestAnimationFrame"><a href="#requestAnimationFrame" class="headerlink" title="requestAnimationFrame"></a>requestAnimationFrame</h4><p><code>requestAnimationFrame()</code>方法的整体性能要比<code>setInterval()</code>方法好很多,打个比方,当我们用<code>setInterval()</code>方法来做动画,我们需要设置一下多长时间执行一次<code>setInterval()</code>方法里面的代码块,而这个时间我们只要设定了,那么就会强行这个时间执行,而如果我们的浏览器显示频率和<code>setInterval()</code>方法执行的绘制请求不一致,就会导致一些帧率消失,从而造成卡顿的效果。因此使用<code>requestAnimationFrame()</code>方法做动画会更加平缓且有效率。</p><p>同时在<code>requestAnimationFrame()</code>方法的使用中我们需要注意,一般每秒钟回调函数执行次数为60次,但也可能会被降低,因为通常情况下<code>requestAnimationFrame()</code>方法会遵循W3C的建议,在浏览器中的回调函数执行次数需要和浏览器屏幕刷新次数相匹配。还有就是为了提高性能和电池使用寿命,<code>requestAnimationFrame()</code> 方法运行在后台标签页或者隐藏在 <code><iframe></code>标签里时,<code>requestAnimationFrame()</code>方法会暂停调用以提升性能和电池使用寿命。</p><p><code>requestAnimationFrame()</code>方法不能自循环,那怎么让他实时触发渲染呢?</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">callbackFn</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// 放入需要执行的代码块</span></span><br><span class="line"> <span class="title function_">requestAnimationFrame</span>(callbackFn);</span><br><span class="line">}</span><br><span class="line"><span class="title function_">requestAnimationFrame</span>(callbackFn);</span><br></pre></td></tr></table></figure><p>这样就形成一个递归,当执行完以后会自动调用它自己。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"> <span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); </span><br><span class="line"> <span class="comment">// 获取绘制上下文</span></span><br><span class="line"> <span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); </span><br><span class="line"> <span class="comment">// globalCompositeOperation 属性设置或返回如何将一个源(新的)图像绘制到目标(已有的)的图像上。</span></span><br><span class="line"> <span class="comment">// 这里主要是为了让飞机压在运行轨迹上</span></span><br><span class="line"> ctx.<span class="property">globalCompositeOperation</span> = <span class="string">'destination-over'</span>;</span><br><span class="line"><span class="comment">//globalCompositeOperation 设置或返回如何将一个源(新的)图像绘制到目标(已有的)的图像上</span></span><br><span class="line"><span class="comment">//destination-over 把源图像绘制到目标图像的上面(也就是源图像盖到目标图像的上面)</span></span><br><span class="line"> <span class="keyword">const</span> width = canvas.<span class="property">width</span></span><br><span class="line"> <span class="keyword">const</span> height = canvas.<span class="property">height</span></span><br><span class="line"> <span class="keyword">let</span> num = <span class="number">0</span></span><br><span class="line"> ctx.<span class="property">strokeStyle</span> = <span class="string">"#ccc"</span></span><br><span class="line"> <span class="keyword">const</span> img = <span class="keyword">new</span> <span class="title class_">Image</span>()</span><br><span class="line"> img.<span class="property">src</span>=<span class="string">"../images/plane.png"</span></span><br><span class="line"> img.<span class="property">onload</span> = <span class="function">()=></span>{</span><br><span class="line"> <span class="title function_">requestAnimationFrame</span>(planeRun);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">planeRun</span>(<span class="params"></span>){</span><br><span class="line"> <span class="comment">// 清空画布</span></span><br><span class="line"> ctx.<span class="title function_">clearRect</span>(<span class="number">0</span>, <span class="number">0</span>, width, height)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 保存画布状态</span></span><br><span class="line"> ctx.<span class="title function_">save</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 把圆心移到画布中间</span></span><br><span class="line"> ctx.<span class="title function_">translate</span>(<span class="number">250</span>, <span class="number">250</span>); </span><br><span class="line"></span><br><span class="line"> <span class="comment">// 绘制飞机和飞机动画</span></span><br><span class="line"> num += <span class="number">0.01</span></span><br><span class="line"> ctx.<span class="title function_">rotate</span>(-num);</span><br><span class="line"> ctx.<span class="title function_">translate</span>(<span class="number">0</span>, <span class="number">200</span>);</span><br><span class="line"> ctx.<span class="title function_">drawImage</span>(img, -<span class="number">20</span>, -<span class="number">25</span>, <span class="number">40</span>, <span class="number">40</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 恢复状态</span></span><br><span class="line"> ctx.<span class="title function_">restore</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 飞机运行的轨迹</span></span><br><span class="line"> ctx.<span class="title function_">beginPath</span>();</span><br><span class="line"> ctx.<span class="title function_">arc</span>(<span class="number">250</span>, <span class="number">250</span>, <span class="number">200</span>, <span class="number">0</span>, <span class="title class_">Math</span>.<span class="property">PI</span> * <span class="number">2</span>, <span class="literal">false</span>);</span><br><span class="line"> ctx.<span class="title function_">stroke</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 执行完以后继续调用</span></span><br><span class="line"> <span class="title function_">requestAnimationFrame</span>(planeRun);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p><img src="/canvas%E8%BF%9B%E9%98%B6.assets/RBjG0eBf-17129955680378.webp" alt="d4ccea40300842079c5755adb3dea5fa~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp"></p>]]></content>
</entry>
<entry>
<title>canvas基础</title>
<link href="/2024/04/06/canvas%E5%9F%BA%E7%A1%80/"/>
<url>/2024/04/06/canvas%E5%9F%BA%E7%A1%80/</url>
<content type="html"><![CDATA[<h2 id="绘制"><a href="#绘制" class="headerlink" title="绘制"></a>绘制</h2><h5 id="1-创建"><a href="#1-创建" class="headerlink" title="1.创建"></a>1.创建</h5><p>Canvas的创建其实就是一个HTML标签</p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"IE=edge"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>canvas - 创建<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">canvas</span></span></span><br><span class="line"><span class="tag"> <span class="attr">id</span>=<span class="string">"canvas"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">width</span>=<span class="string">"500"</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">height</span>=<span class="string">"500"</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">style</span>=<span class="string">"box-shadow: 0px 0px 5px #ccc; border-radius: 8px;"</span>></span></span><br><span class="line"> 当前浏览器不支持canvas元素,请升级或更换浏览器!</span><br><span class="line"> <span class="tag"></<span class="name">canvas</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><h5 id="2-属性"><a href="#2-属性" class="headerlink" title="2.属性"></a>2.属性</h5><p>id:画布的唯一标识,获取画布的时候可以用它</p><p>width:画布的宽度,这里需要说明在不设置画布宽度的时候,默认宽度为300像素</p><p>height:画布的高度,这里需要说明在不设置画布高度的时候,默认高度为150像素</p><p>style:画布的样式,例如正常情况下画布是没有边框或者阴影的,但上面示例中我们通过<code>style</code>属性给<code><canvas></code>标签设置了阴影和圆角样式。</p><h5 id="3-绘制上下文"><a href="#3-绘制上下文" class="headerlink" title="3.绘制上下文"></a>3.绘制上下文</h5><p>在Canvas的绘制中,Canvas本身是没有绘制能力的,它的所有绘制都是通过JavaScript来实现的</p><p>在Canvas没有绘制能力的情况下,JavaScript又如何在Canvas中实现元素的绘制呢?</p><p>这还得说到一个东西:<strong>绘制上下文</strong>。</p><p>获取Canvas 绘制上下文的方法为:<code>getContext()</code>,正是这个方法让Canvas具备绘制的能力。</p><h6 id="语法结构"><a href="#语法结构" class="headerlink" title="语法结构"></a>语法结构</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">canvas.<span class="title function_">getContext</span>(contextType, contextAttributes)</span><br></pre></td></tr></table></figure><p>参数说明:</p><ol><li>contextType 为绘制上下文的类型,类型参数有:</li></ol><ul><li>2d:用于创建一个 CanvasRenderingContext2D 2D绘制上下文。</li><li>webgl:用于创建一个 WebGLRenderingContext 3D渲染上下文对象。且该类型只支持在实现WebGL版本1的浏览器上可用也就是 OpenGL ES 2.0。</li><li>webgl2:用于创建一个 WebGL2RenderingContext 3D渲染上下文对象。且该类型只支持在实现WebGL版本2的浏览器上可用也就是 OpenGL ES 3.0。</li><li>bitmaprenderer:用于创建一个只提供将 canvas 内容替换为指定ImageBitmap功能的ImageBitmapRenderingContext。</li></ul><ol start="2"><li>contextAttributes 为绘制上下文的属性,这些属性相对比较多,可以设置单个也可以同时设置多个,下面列一下,方便大家了解:</li></ol><ul><li>2D类型的参数有: (1)、<code>alpha</code> 它的值为Boolean类型,如果设置为false, 浏览器将认Canvas背景总是不透明的,这样可以做到一些性能提效。(2)、<code>willReadFrequently</code>,值也为Boolean类型,用于表明是否要重复操作,频繁调用<code>getImageData()</code>方法时能节省内存,但是仅Gecko内核浏览器支持。(3)、<code>storage</code>用于表明使用哪种方式存储,默认值 persisten,表示持久化存储。</li><li>3D类型的参数有: (1)、<code>alpha</code> 值为Boolean类型,指示画布是否包含alpha缓冲区。 (2)、<code>antialias</code> 值为Boolean类型,指示是否开启抗锯齿。 (3)、<code>depth</code> 值为Boolean类型,表示绘图缓冲区的深度缓冲区至少为16位。 (4)、<code>failIfMajorPerformanceCaveat</code>值为Boolean类型,指示如果系统性能较低,是否创建上下文。 (5)、<code>powerPreference</code>:对用户代理的提示,指示GPU的哪种配置适合WebGL上下文。可能的值是: <code>default</code>: 自动选择模式,自动决定哪种GPU配置最合适,为默认值。 <code>high-performance</code>: 高性能模式,优先考虑渲染性能而不是功耗。 <code>low-power</code>: 节能模式,优先考虑节能而不是渲染性能。 (6)、<code>premultipliedAlpha</code> 值为Boolean类型,表示页面合成器将假定绘图缓冲区包含具有预乘alpha的颜色。 (7)、<code>preserveDrawingBuffer</code> 值为Boolean类型,如果值为true,则不会清除缓冲区并保留其值,直到被清除或被使用者覆盖。 (8)、<code>stencil</code> 值为Boolean类型,表示绘图缓冲区具有至少8位的模板缓冲区。</li></ul><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>);</span><br><span class="line"><span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>);<span class="comment">//获取绘制上下文</span></span><br></pre></td></tr></table></figure><h5 id="4-绘制方式"><a href="#4-绘制方式" class="headerlink" title="4.绘制方式"></a>4.绘制方式</h5><p>stroke() 描边</p><p>fill() 填充</p><p>strokeStyle 描边的样式</p><p>fillStyle 填充的样式</p><p>值得注意的是,<code>strokeStyle</code> 和 <code>fillStyle</code> 属性的设置是一次设置永久有效的,想要改变必须重新设置其他值来覆盖原有的值。</p><h5 id="5-基本绘制形状"><a href="#5-基本绘制形状" class="headerlink" title="5.基本绘制形状"></a>5.基本绘制形状</h5><h6 id="直线"><a href="#直线" class="headerlink" title="直线"></a>直线</h6><p>直线的绘制就是设置两个点,然后连接这两个点形成一条直线,那么这两个点如何设置呢?</p><h3 id="moveTo-x-y"><a href="#moveTo-x-y" class="headerlink" title="moveTo(x, y)"></a>moveTo(x, y)</h3><p>设置初始位置,参数为初始位置x和y的坐标点</p><h3 id="lineTo-x-y"><a href="#lineTo-x-y" class="headerlink" title="lineTo(x, y)"></a>lineTo(x, y)</h3><p>设置指定位置,参数为指定位置x和y的坐标点</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> canvas = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'canvas'</span>); <span class="comment">// 获取Canvas</span></span><br><span class="line"> <span class="keyword">const</span> ctx = canvas.<span class="title function_">getContext</span>(<span class="string">'2d'</span>); <span class="comment">// 获取绘制上下文</span></span><br><span class="line"> ctx.<span class="property">strokeStyle</span> = <span class="string">"#f00"</span> <span class="comment">// 描边样式设置为红色</span></span><br><span class="line"> <span class="comment">// 画一条(50, 50) 到 (400, 50)的直线</span></span><br><span class="line"> ctx.<span class="title function_">moveTo</span>(<span class="number">50</span>, <span class="number">50</span>) </span><br><span class="line"> ctx.<span class="title function_">lineTo</span>(<span class="number">400</span>, <span class="number">50</span>)</span><br><span class="line"> ctx.<span class="title function_">stroke</span>() </span><br><span class="line"></span><br><span class="line"> <span class="comment">// 连续的直线只需要设置一个起始点</span></span><br><span class="line"> ctx.<span class="title function_">moveTo</span>(<span class="number">50</span>, <span class="number">100</span>) </span><br><span class="line"> ctx.<span class="title function_">lineTo</span>(<span class="number">50</span>, <span class="number">400</span>)</span><br><span class="line"> ctx.<span class="title function_">lineTo</span>(<span class="number">400</span>, <span class="number">400</span>)</span><br><span class="line"> ctx.<span class="title function_">stroke</span>() </span><br><span class="line"></span><br></pre></td></tr></table></figure><p>直线样式</p><h4 id="lineWidth"><a href="#lineWidth" class="headerlink" title="lineWidth"></a>lineWidth</h4><p>设置直线的粗细,默认值为1,且属性值必须为正数。</p><h4 id="lineCap"><a href="#lineCap" class="headerlink" title="lineCap"></a>lineCap</h4><p>设置直线端点显示的样式。可选值为:butt,round 和 square(通过添加一个宽度相等且高度为线条粗细一半的框,使线条末端呈方形)。默认是 butt。</p><p><img src="/canvas%E5%9F%BA%E7%A1%80.assets/e9ef1310875a4b5a8b8f12e07aaca8e7tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-17124660987172-17124661132204.awebp" alt="image.png"></p><h4 id="lineJoin"><a href="#lineJoin" class="headerlink" title="lineJoin"></a>lineJoin</h4><p>设置两线段连接处所显示的样子。可选值为:round, bevel 和 miter。默认是 miter。</p><p><img src="/canvas%E5%9F%BA%E7%A1%80.assets/4521845c798e4631b6ea17f3017ee0a2tplv-k3u1fbpfcp-zoom-in-crop-mark1512000.awebp" alt="image.png"></p><h6 id="三角形"><a href="#三角形" class="headerlink" title="三角形"></a>三角形</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 绘制一个三角形</span></span><br><span class="line"> ctx.<span class="title function_">moveTo</span>(<span class="number">50</span>, <span class="number">100</span>) </span><br><span class="line"> ctx.<span class="title function_">lineTo</span>(<span class="number">50</span>, <span class="number">400</span>)</span><br><span class="line"> ctx.<span class="title function_">lineTo</span>(<span class="number">400</span>, <span class="number">400</span>)</span><br><span class="line"> ctx.<span class="title function_">lineTo</span>(<span class="number">50</span>, <span class="number">100</span>) </span><br><span class="line"> ctx.<span class="title function_">stroke</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果是填充一个三角形,则只需两条直线就行,它会默认闭合。</span></span><br><span class="line"> ctx.<span class="title function_">beginPath</span>()</span><br><span class="line"> ctx.<span class="title function_">moveTo</span>(<span class="number">200</span>, <span class="number">200</span>) </span><br><span class="line"> ctx.<span class="title function_">lineTo</span>(<span class="number">400</span>, <span class="number">200</span>)</span><br><span class="line"> ctx.<span class="title function_">lineTo</span>(<span class="number">400</span>, <span class="number">370</span>)</span><br><span class="line"> ctx.<span class="title function_">fill</span>();</span><br><span class="line"></span><br></pre></td></tr></table></figure><h6 id="矩形"><a href="#矩形" class="headerlink" title="矩形"></a>矩形</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 矩形描边</span></span><br><span class="line"><span class="title function_">rect</span>(x, y, width, height)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绘制矩形</span></span><br><span class="line"><span class="title function_">strokeRect</span>(x, y, width, height)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 填充矩形</span></span><br><span class="line"><span class="title function_">fillRect</span>(x, y, width, height)</span><br></pre></td></tr></table></figure><p>上述的三个方法都可以用来绘制矩形,他们的传参都是一样的,其中x和y是起始点的x坐标和y坐标,width为矩形的宽,height为矩形的高。</p><p>合成版 strokeRect() fillRect()</p><h6 id="圆弧和圆"><a href="#圆弧和圆" class="headerlink" title="圆弧和圆"></a>圆弧和圆</h6><p>Canvas提供了绘制圆弧或圆的方法:<code>arc()</code>,该方法提供了给定坐标值(x,y)以后,按给定的半径(r)大小,从给定的起始点(startAngle)位置,默认顺时针方向绘制圆弧或圆到给定的终点(endAngle)。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.<span class="title function_">arc</span>(x, y, radius, startAngle, endAngle, anticlockwise);</span><br></pre></td></tr></table></figure><p>参数说明:</p><ul><li>x,y 为圆弧中心或圆的圆心坐标、</li><li>radius 为圆弧的半径或圆的半径、</li><li>startAngle 为圆弧或圆的起始点,从x轴方向开始计算,且单位为弧度、</li><li>endAngle 为圆弧或圆的终点,单位也是为弧度</li><li>anticlockwise 是一个可选参数,可选值为Boolean类型,用它来表示圆弧或圆的绘制方向,默认为false,顺时针绘制圆弧或圆,true是逆时针</li></ul><p>角度转弧度的公式为:<code>弧度 = 角度 * Math.PI / 180</code></p><h6 id="贝塞尔曲线"><a href="#贝塞尔曲线" class="headerlink" title="贝塞尔曲线"></a>贝塞尔曲线</h6><p>二次贝塞尔曲线<img src="/canvas%E5%9F%BA%E7%A1%80.assets/167c7a62849d43d0948f2707c020048ctplv-k3u1fbpfcp-zoom-in-crop-mark1512000.awebp" alt="image.png"></p><p>如图就是一个二次贝塞尔曲线,他的绘制需要一个控制点来控制曲线,具体的语法为:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">quadraticCurveTo(cp1x, cp1y, x, y)</span><br></pre></td></tr></table></figure><p>参数:</p><ul><li>cp1x和cp1y为控制点坐标</li><li>x和y为结束点坐标</li></ul><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.<span class="title function_">beginPath</span>();</span><br><span class="line">ctx.<span class="title function_">moveTo</span>(<span class="number">100</span>, <span class="number">250</span>);</span><br><span class="line">ctx.<span class="title function_">quadraticCurveTo</span>(<span class="number">250</span>, <span class="number">100</span>, <span class="number">400</span>, <span class="number">250</span>);</span><br><span class="line">ctx.<span class="title function_">stroke</span>();</span><br></pre></td></tr></table></figure><p><img src="/canvas%E5%9F%BA%E7%A1%80.assets/9a18a871e84c42468595aa09989497d7tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-17124668552188.awebp" alt="image.png"></p><p>三次贝塞尔曲线</p><p>三次贝塞尔曲线和二次贝塞尔曲线不同的是三次贝塞尔曲线有两个控制点。具体的语法为:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">ctx.bezierCurveTo(cp1x,cp1y, cp2x,cp2y, x, y)</span><br></pre></td></tr></table></figure><p>参数为:</p><ul><li>cp1x和cp1y为第一个控制点坐标</li><li>cp2x和cp2y为第二个控制点坐标</li><li>x和y为结束点坐标</li></ul><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.<span class="title function_">beginPath</span>();</span><br><span class="line">ctx.<span class="title function_">moveTo</span>(<span class="number">100</span>, <span class="number">250</span>);</span><br><span class="line">ctx.<span class="title function_">bezierCurveTo</span>(<span class="number">150</span>, <span class="number">100</span>, <span class="number">350</span>, <span class="number">100</span>, <span class="number">400</span>, <span class="number">250</span>);</span><br><span class="line">ctx.<span class="title function_">stroke</span>();</span><br></pre></td></tr></table></figure><p><img src="/canvas%E5%9F%BA%E7%A1%80.assets/f581609289004cf187857eba31a87dbftplv-k3u1fbpfcp-zoom-in-crop-mark1512000-171246692279710.awebp" alt="image.png"><img src="/canvas%E5%9F%BA%E7%A1%80.assets/9b6508146ea944a398a2c497c34b51f5tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-171246693675912.awebp" alt="20190809163503842.gif"></p><p>一次贝塞尔曲线:</p><p><img src="/canvas%E5%9F%BA%E7%A1%80.assets/b93f24fcb7e04ebc89959f9dfd9b6708tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-171246696719314.awebp" alt="20190809163745169.gif"></p><p>二次贝塞尔曲线:</p><p><img src="/canvas%E5%9F%BA%E7%A1%80.assets/eb035911f33541ef962436c1afaf6773tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-171246698547116.awebp" alt="20190809163835500.gif"></p><p>三次贝塞尔曲线:</p><p><img src="/canvas%E5%9F%BA%E7%A1%80.assets/95800539044e48eabd40f243f59aee35tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-171246700459218.awebp" alt="20190809163955483.gif"></p><p>四次贝塞尔曲线:</p><p><img src="/canvas%E5%9F%BA%E7%A1%80.assets/2326b343bc2143548e424e0235785233tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-171246701805020.awebp" alt="20190809164018347.gif"></p><p>五次贝塞尔曲线:</p><h5 id="6-路径的开启和闭合"><a href="#6-路径的开启和闭合" class="headerlink" title="6.路径的开启和闭合"></a>6.路径的开启和闭合</h5><p>beginPath()</p><p><code>beginPath()</code>方法用于开始一条路径或重置当前的路径</p><p>当我们不重新开启一条新路径,两条路径就会默认为是一条路径来绘制,因此就得不到我们想要的效果。想分开绘制我们就需要重新开启新的路径。</p><p>closePath()</p><p><code>closePath()</code>方法和<code>beginPath()</code>方法正好相反,用来关闭一条路径,规范的用法其实是他们两个搭配使用,每次绘制都先开启一条新路径,完事关闭该路径</p><p>因此每次我们连续绘制时,若需要分开绘制,那么一定要在每次绘制前重新开启新的路径。</p><h5 id="7-绘制文本"><a href="#7-绘制文本" class="headerlink" title="7.绘制文本"></a>7.绘制文本</h5><p>strokeText()方法是以描边的方式绘制文字的</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.<span class="title function_">strokeText</span>(txt, x, y, maxWidth)</span><br></pre></td></tr></table></figure><p>参数:</p><ul><li>txt:是绘制的文本内容</li><li>x、y:为绘制文本的起始位置坐标</li><li>maxWidth:可选参数,为文本绘制的最大宽度。</li></ul><p>当文案大于最大宽度时不是裁剪或者换行,而是缩小。</p><p>fillText()方法是以填充的方式绘制文字的</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.<span class="title function_">fillText</span>(txt, x, y, maxWidth)</span><br></pre></td></tr></table></figure><p>参数:</p><ul><li>txt:是绘制的文本内容</li><li>x、y:为绘制文本的起始位置坐标</li><li>maxWidth:可选参数,为文本绘制的最大宽度。</li></ul><h6 id="font"><a href="#font" class="headerlink" title="font"></a>font</h6><p><code>font</code>属性的设置可以改变字号和字体,不设置的情况下默认是:<code>10px sans-serif</code>。</p><h6 id="textAlign"><a href="#textAlign" class="headerlink" title="textAlign"></a>textAlign</h6><p><code>textAlign</code>属性的设置可以改变文本对齐的方式。默认值是 <code>start</code>,可选值有:<code>left</code>、<code>right</code>、<code>center</code>、<code>start</code>和<code>end</code>。</p><p><img src="/canvas%E5%9F%BA%E7%A1%80.assets/40b13ba4e7a04a7ba6b541881923bad6tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-171246747696522.awebp" alt="image.png"></p><p>如图我们可以看到各个参数对应的效果,我们会发现<code>start</code>和<code>left</code>的效果基本一样,还有<code>end</code>和<code>right</code>的效果也基本一样,那么<code>start</code>和<code>left</code>,<code>end</code>和<code>right</code>是不是等价的呢?答案肯定是否定的。因为如果是等价的那就没必要设置两个属性了。</p><p>那他们既然不一样,那他们有什么区别呢?这我们需要结合另一个属性:<code>direction</code> 来看。</p><h6 id="direction"><a href="#direction" class="headerlink" title="direction"></a>direction</h6><p><code>direction</code>属性可以设置文本的方向。默认值是 <code>inherit</code>, 可选值为:<code>ltr</code>(文本方向从左向右)、<code>rtl</code>(文本方向从右向左)、<code>inherit</code>(根据情况继承 Canvas元素或者 Document 。)。</p><p> <code>direction</code> 属性会对 <code>textAlign</code> 属性产生影响。</p><p>如果 <code>direction</code> 属性设置为 <code>ltr</code>,则<code>textAlign</code>属性的 <code>left</code> 和 <code>start</code> 的效果相同,<code>right</code> 和 <code>end</code> 的效果相同,</p><p>如果 <code>direction</code> 属性设置为 <code>rtl</code>,则 <code>textAlign</code>属性的 <code>left</code> 和 <code>end</code> 的效果相同,<code>right</code> 和 <code>start</code> 的效果相同。</p><h6 id="textBaseline"><a href="#textBaseline" class="headerlink" title="textBaseline"></a>textBaseline</h6><p><code>textBaseline</code>属性设置基于基线对齐的文字垂直方向的对齐方式。默认值是<code>alphabetic</code>,可选值为:<code>top</code>、<code>hanging</code>、<code>middle</code>、<code>alphabetic</code>、<code>ideographic</code>和<code>bottom</code>。</p><p><img src="/canvas%E5%9F%BA%E7%A1%80.assets/8ceff6bea0e142aba3853e8362176284tplv-k3u1fbpfcp-zoom-in-crop-mark1512000-171246772220024.awebp" alt="image.png"></p><h5 id="8-绘制图像"><a href="#8-绘制图像" class="headerlink" title="8.绘制图像"></a>8.绘制图像</h5><h6 id="语法:"><a href="#语法:" class="headerlink" title="语法:"></a>语法:</h6><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)</span><br></pre></td></tr></table></figure><h6 id="参数:"><a href="#参数:" class="headerlink" title="参数:"></a>参数:</h6><ul><li>image:绘制的元素(图像)。</li><li>sx、sy:裁剪框左上角的坐标。</li><li>sWidth、sHeight:裁剪框的宽度和高度。</li><li>dx、dy:绘制元素(图像)时左上角的坐标。</li><li>dWidth、dHeight:绘制元素(图像)的宽度和高度。如果不设置,则在绘制时image宽度和高度不会缩放,超出画布则图片不完整</li></ul><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> img = <span class="keyword">new</span> <span class="title class_">Image</span>();</span><br><span class="line">img.<span class="property">src</span> = <span class="string">'//图片url'</span>;</span><br><span class="line">img.<span class="property">onload</span> = <span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> ctx.<span class="title function_">drawImage</span>(img, <span class="number">0</span>, <span class="number">150</span>, <span class="number">1650</span>, <span class="number">700</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">550</span>, <span class="number">500</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
</entry>
<entry>
<title>js继承</title>
<link href="/2024/03/31/js%E7%BB%A7%E6%89%BF/"/>
<url>/2024/03/31/js%E7%BB%A7%E6%89%BF/</url>
<content type="html"><![CDATA[<h4 id="1、原型链继承"><a href="#1、原型链继承" class="headerlink" title="1、原型链继承"></a>1、原型链继承</h4><p>构造函数、原型和实例之间的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个原型对象的指针。</p><p>继承的本质就是<strong>复制,即重写原型对象,代之以一个新类型的实例</strong>。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">SuperType</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">property</span> = <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title class_">SuperType</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getSuperValue</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">property</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">SubType</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">subproperty</span> = <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这里是关键,创建SuperType的实例,并将该实例赋值给SubType.prototype</span></span><br><span class="line"><span class="title class_">SubType</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="keyword">new</span> <span class="title class_">SuperType</span>(); </span><br><span class="line"></span><br><span class="line"><span class="title class_">SubType</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">getSubValue</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">subproperty</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> instance = <span class="keyword">new</span> <span class="title class_">SubType</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(instance.<span class="title function_">getSuperValue</span>()); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p><img src="/js%E7%BB%A7%E6%89%BF.assets/166c2c0107fd80c7tplv-t2oaga2asx-zoom-in-crop-mark1512000.awebp" alt="img"></p><p>原型链方案存在的缺点:多个实例对引用类型的操作会被篡改。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">SuperType</span>(<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">colors</span> = [<span class="string">"red"</span>, <span class="string">"blue"</span>, <span class="string">"green"</span>];</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">SubType</span>(<span class="params"></span>){}</span><br><span class="line"></span><br><span class="line"><span class="title class_">SubType</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="keyword">new</span> <span class="title class_">SuperType</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> instance1 = <span class="keyword">new</span> <span class="title class_">SubType</span>();</span><br><span class="line">instance1.<span class="property">colors</span>.<span class="title function_">push</span>(<span class="string">"black"</span>);</span><br><span class="line"><span class="title function_">alert</span>(instance1.<span class="property">colors</span>); <span class="comment">//"red,blue,green,black"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> instance2 = <span class="keyword">new</span> <span class="title class_">SubType</span>(); </span><br><span class="line"><span class="title function_">alert</span>(instance2.<span class="property">colors</span>); <span class="comment">//"red,blue,green,black"</span></span><br></pre></td></tr></table></figure><h4 id="2、借用构造函数继承"><a href="#2、借用构造函数继承" class="headerlink" title="2、借用构造函数继承"></a>2、借用构造函数继承</h4><p>使用父类的构造函数来增强子类<strong>实例</strong>,等同于复制父类的实例给子类(不使用原型)</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">SuperType</span>(<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">color</span>=[<span class="string">"red"</span>,<span class="string">"green"</span>,<span class="string">"blue"</span>];</span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">SubType</span>(<span class="params"></span>){</span><br><span class="line"> <span class="comment">//继承自SuperType</span></span><br><span class="line"> <span class="title class_">SuperType</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> instance1 = <span class="keyword">new</span> <span class="title class_">SubType</span>();</span><br><span class="line">instance1.<span class="property">color</span>.<span class="title function_">push</span>(<span class="string">"black"</span>);</span><br><span class="line"><span class="title function_">alert</span>(instance1.<span class="property">color</span>);<span class="comment">//"red,green,blue,black"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> instance2 = <span class="keyword">new</span> <span class="title class_">SubType</span>();</span><br><span class="line"><span class="title function_">alert</span>(instance2.<span class="property">color</span>);<span class="comment">//"red,green,blue"</span></span><br></pre></td></tr></table></figure><p>核心代码是<code>SuperType.call(this)</code>,创建子类实例时调用<code>SuperType</code>构造函数,于是<code>SubType</code>的每个实例都会将SuperType中的属性复制一份。</p><p>缺点:</p><ul><li>只能继承父类的<strong>实例</strong>属性和方法,不能继承原型属性/方法</li><li>无法实现复用,每个子类都有父类实例函数的副本,影响性能</li></ul><h4 id="3、组合继承"><a href="#3、组合继承" class="headerlink" title="3、组合继承"></a>3、组合继承</h4><p>组合上述两种方法就是组合继承。用原型链实现对<strong>原型</strong>属性和方法的继承,用借用构造函数技术来实现<strong>实例</strong>属性的继承。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">SuperType</span>(<span class="params">name</span>){</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">colors</span> = [<span class="string">"red"</span>, <span class="string">"blue"</span>, <span class="string">"green"</span>];</span><br><span class="line">}</span><br><span class="line"><span class="title class_">SuperType</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">sayName</span> = <span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="title function_">alert</span>(<span class="variable language_">this</span>.<span class="property">name</span>);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">SubType</span>(<span class="params">name, age</span>){</span><br><span class="line"> <span class="comment">// 继承属性</span></span><br><span class="line"> <span class="comment">// 第二次调用SuperType()</span></span><br><span class="line"> <span class="title class_">SuperType</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>, name);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 继承方法</span></span><br><span class="line"><span class="comment">// 构建原型链</span></span><br><span class="line"><span class="comment">// 第一次调用SuperType()</span></span><br><span class="line"><span class="title class_">SubType</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="keyword">new</span> <span class="title class_">SuperType</span>(); </span><br><span class="line"><span class="comment">// 重写SubType.prototype的constructor属性,指向自己的构造函数SubType</span></span><br><span class="line"><span class="title class_">SubType</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">constructor</span> = <span class="title class_">SubType</span>; </span><br><span class="line"><span class="title class_">SubType</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">sayAge</span> = <span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="title function_">alert</span>(<span class="variable language_">this</span>.<span class="property">age</span>);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> instance1 = <span class="keyword">new</span> <span class="title class_">SubType</span>(<span class="string">"Nicholas"</span>, <span class="number">29</span>);</span><br><span class="line">instance1.<span class="property">colors</span>.<span class="title function_">push</span>(<span class="string">"black"</span>);</span><br><span class="line"><span class="title function_">alert</span>(instance1.<span class="property">colors</span>); <span class="comment">//"red,blue,green,black"</span></span><br><span class="line">instance1.<span class="title function_">sayName</span>(); <span class="comment">//"Nicholas";</span></span><br><span class="line">instance1.<span class="title function_">sayAge</span>(); <span class="comment">//29</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> instance2 = <span class="keyword">new</span> <span class="title class_">SubType</span>(<span class="string">"Greg"</span>, <span class="number">27</span>);</span><br><span class="line"><span class="title function_">alert</span>(instance2.<span class="property">colors</span>); <span class="comment">//"red,blue,green"</span></span><br><span class="line">instance2.<span class="title function_">sayName</span>(); <span class="comment">//"Greg";</span></span><br><span class="line">instance2.<span class="title function_">sayAge</span>(); <span class="comment">//27</span></span><br></pre></td></tr></table></figure><p><img src="/js%E7%BB%A7%E6%89%BF.assets/166c2c010c537ff8tplv-t2oaga2asx-zoom-in-crop-mark1512000.awebp" alt="img"></p><p>缺点:</p><ul><li>第一次调用<code>SuperType()</code>:给<code>SubType.prototype</code>写入两个属性name,color。</li><li>第二次调用<code>SuperType()</code>:给<code>instance1</code>写入两个属性name,color。</li></ul><p>实例对象<code>instance1</code>上的两个属性就屏蔽了其原型对象SubType.prototype的两个同名属性。所以,组合模式的缺点就是在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法。</p><h4 id="4、原型式继承"><a href="#4、原型式继承" class="headerlink" title="4、原型式继承"></a>4、原型式继承</h4><p>利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">object</span>(<span class="params">obj</span>){</span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">F</span>(<span class="params"></span>){}</span><br><span class="line"> F.<span class="property"><span class="keyword">prototype</span></span> = obj;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title function_">F</span>();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>object()对传入其中的对象执行了一次<code>浅复制</code>,将构造函数F的原型直接指向传入的对象。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"Nicholas"</span>,</span><br><span class="line"> <span class="attr">friends</span>: [<span class="string">"Shelby"</span>, <span class="string">"Court"</span>, <span class="string">"Van"</span>]</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> anotherPerson = <span class="title function_">object</span>(person);</span><br><span class="line">anotherPerson.<span class="property">name</span> = <span class="string">"Greg"</span>;</span><br><span class="line">anotherPerson.<span class="property">friends</span>.<span class="title function_">push</span>(<span class="string">"Rob"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> yetAnotherPerson = <span class="title function_">object</span>(person);</span><br><span class="line">yetAnotherPerson.<span class="property">name</span> = <span class="string">"Linda"</span>;</span><br><span class="line">yetAnotherPerson.<span class="property">friends</span>.<span class="title function_">push</span>(<span class="string">"Barbie"</span>);</span><br><span class="line"></span><br><span class="line"><span class="title function_">alert</span>(person.<span class="property">friends</span>); <span class="comment">//"Shelby,Court,Van,Rob,Barbie"</span></span><br></pre></td></tr></table></figure><p>缺点:</p><ul><li>原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。</li><li>无法传递参数</li></ul><p>另外,ES5中存在<code>Object.create()</code>的方法,能够代替上面的object方法。</p><h4 id="5、寄生式继承"><a href="#5、寄生式继承" class="headerlink" title="5、寄生式继承"></a>5、寄生式继承</h4><p>核心:在原型式继承的基础上,增强对象,返回构造函数</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">createAnother</span>(<span class="params">original</span>){</span><br><span class="line"> <span class="keyword">var</span> clone = <span class="title function_">object</span>(original); <span class="comment">// 通过调用 object() 函数创建一个新对象</span></span><br><span class="line"> clone.<span class="property">sayHi</span> = <span class="keyword">function</span>(<span class="params"></span>){ <span class="comment">// 以某种方式来增强对象</span></span><br><span class="line"> <span class="title function_">alert</span>(<span class="string">"hi"</span>);</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> clone; <span class="comment">// 返回这个对象</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>函数的主要作用是为构造函数新增属性和方法,以<strong>增强函数</strong></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"Nicholas"</span>,</span><br><span class="line"> <span class="attr">friends</span>: [<span class="string">"Shelby"</span>, <span class="string">"Court"</span>, <span class="string">"Van"</span>]</span><br><span class="line">};</span><br><span class="line"><span class="keyword">var</span> anotherPerson = <span class="title function_">createAnother</span>(person);</span><br><span class="line">anotherPerson.<span class="title function_">sayHi</span>(); <span class="comment">//"hi"</span></span><br></pre></td></tr></table></figure><p>缺点(同原型式继承):</p><ul><li>原型链继承多个实例的引用类型属性指向相同,存在篡改的可能。</li><li>无法传递参数</li></ul><h4 id="6、寄生组合式继承"><a href="#6、寄生组合式继承" class="headerlink" title="6、寄生组合式继承"></a>6、寄生组合式继承</h4><p>结合借用构造函数传递参数和寄生模式实现继承</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">inheritPrototype</span>(<span class="params">subType, superType</span>){</span><br><span class="line"> <span class="keyword">var</span> prototype = <span class="title class_">Object</span>.<span class="title function_">create</span>(superType.<span class="property"><span class="keyword">prototype</span></span>); <span class="comment">// 创建对象,创建父类原型的一个副本</span></span><br><span class="line"> prototype.<span class="property">constructor</span> = subType; <span class="comment">// 增强对象,弥补因重写原型而失去的默认的constructor 属性</span></span><br><span class="line"> subType.<span class="property"><span class="keyword">prototype</span></span> = prototype; <span class="comment">// 指定对象,将新创建的对象赋值给子类的原型</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 父类初始化实例属性和原型属性</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">SuperType</span>(<span class="params">name</span>){</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">colors</span> = [<span class="string">"red"</span>, <span class="string">"blue"</span>, <span class="string">"green"</span>];</span><br><span class="line">}</span><br><span class="line"><span class="title class_">SuperType</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">sayName</span> = <span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="title function_">alert</span>(<span class="variable language_">this</span>.<span class="property">name</span>);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">SubType</span>(<span class="params">name, age</span>){</span><br><span class="line"> <span class="title class_">SuperType</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>, name);</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将父类原型指向子类</span></span><br><span class="line"><span class="title function_">inheritPrototype</span>(<span class="title class_">SubType</span>, <span class="title class_">SuperType</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新增子类原型属性</span></span><br><span class="line"><span class="title class_">SubType</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">sayAge</span> = <span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="title function_">alert</span>(<span class="variable language_">this</span>.<span class="property">age</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> instance1 = <span class="keyword">new</span> <span class="title class_">SubType</span>(<span class="string">"xyc"</span>, <span class="number">23</span>);</span><br><span class="line"><span class="keyword">var</span> instance2 = <span class="keyword">new</span> <span class="title class_">SubType</span>(<span class="string">"lxy"</span>, <span class="number">23</span>);</span><br><span class="line"></span><br><span class="line">instance1.<span class="property">colors</span>.<span class="title function_">push</span>(<span class="string">"2"</span>); <span class="comment">// ["red", "blue", "green", "2"]</span></span><br><span class="line">instance2.<span class="property">colors</span>.<span class="title function_">push</span>(<span class="string">"3"</span>); <span class="comment">// ["red", "blue", "green", "3"]</span></span><br></pre></td></tr></table></figure><p><img src="/js%E7%BB%A7%E6%89%BF.assets/166c2c0109df5438tplv-t2oaga2asx-zoom-in-crop-mark1512000.awebp" alt="img"></p><p>这个例子的高效率体现在它只调用了一次<code>SuperType</code> 构造函数,并且因此避免了在<code>SubType.prototype</code> 上创建不必要的、多余的属性。于此同时,原型链还能保持不变;因此,还能够正常使用<code>instanceof</code> 和<code>isPrototypeOf()</code></p><p><strong>这是最成熟的方法,也是现在库实现的方法</strong></p><h4 id="7、混入方式继承多个对象"><a href="#7、混入方式继承多个对象" class="headerlink" title="7、混入方式继承多个对象"></a>7、混入方式继承多个对象</h4><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">MyClass</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title class_">SuperClass</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>);</span><br><span class="line"> <span class="title class_">OtherSuperClass</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 继承一个类</span></span><br><span class="line"><span class="title class_">MyClass</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="title class_">Object</span>.<span class="title function_">create</span>(<span class="title class_">SuperClass</span>.<span class="property"><span class="keyword">prototype</span></span>);</span><br><span class="line"><span class="comment">// 混合其它</span></span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">assign</span>(<span class="title class_">MyClass</span>.<span class="property"><span class="keyword">prototype</span></span>, <span class="title class_">OtherSuperClass</span>.<span class="property"><span class="keyword">prototype</span></span>);</span><br><span class="line"><span class="comment">// 重新指定constructor</span></span><br><span class="line"><span class="title class_">MyClass</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">constructor</span> = <span class="title class_">MyClass</span>;</span><br><span class="line"></span><br><span class="line"><span class="title class_">MyClass</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">myMethod</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p><code>Object.assign</code>会把 <code>OtherSuperClass</code>原型上的函数拷贝到 <code>MyClass</code>原型上,使 MyClass 的所有实例都可用 OtherSuperClass 的方法。</p><h4 id="8、ES6类继承extends"><a href="#8、ES6类继承extends" class="headerlink" title="8、ES6类继承extends"></a>8、ES6类继承extends</h4><p><code>extends</code>关键字主要用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类。其中<code>constructor</code>表示构造函数,一个类中只能有一个构造函数,有多个会报出<code>SyntaxError</code>错误,如果没有显式指定构造方法,则会添加默认的 <code>constructor</code>方法,使用例子如下。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Rectangle</span> {</span><br><span class="line"> <span class="comment">// constructor</span></span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">height, width</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">height</span> = height;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">width</span> = width;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Getter</span></span><br><span class="line"> <span class="keyword">get</span> <span class="title function_">area</span>() {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">calcArea</span>()</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Method</span></span><br><span class="line"> <span class="title function_">calcArea</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">height</span> * <span class="variable language_">this</span>.<span class="property">width</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> rectangle = <span class="keyword">new</span> <span class="title class_">Rectangle</span>(<span class="number">10</span>, <span class="number">20</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(rectangle.<span class="property">area</span>);</span><br><span class="line"><span class="comment">// 输出 200</span></span><br><span class="line"></span><br><span class="line">-----------------------------------------------------------------</span><br><span class="line"><span class="comment">// 继承</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Square</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Rectangle</span> {</span><br><span class="line"></span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">length</span>) {</span><br><span class="line"> <span class="variable language_">super</span>(length, length);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 如果子类中存在构造函数,则需要在使用“this”之前首先调用 super()。</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = <span class="string">'Square'</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">get</span> <span class="title function_">area</span>() {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">height</span> * <span class="variable language_">this</span>.<span class="property">width</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> square = <span class="keyword">new</span> <span class="title class_">Square</span>(<span class="number">10</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(square.<span class="property">area</span>);</span><br><span class="line"><span class="comment">// 输出 100</span></span><br></pre></td></tr></table></figure><p><code>extends</code>继承的核心代码如下,其实现和上述的寄生组合式继承方式一样</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">_inherits</span>(<span class="params">subType, superType</span>) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 创建对象,创建父类原型的一个副本</span></span><br><span class="line"> <span class="comment">// 增强对象,弥补因重写原型而失去的默认的constructor 属性</span></span><br><span class="line"> <span class="comment">// 指定对象,将新创建的对象赋值给子类的原型</span></span><br><span class="line"> subType.<span class="property"><span class="keyword">prototype</span></span> = <span class="title class_">Object</span>.<span class="title function_">create</span>(superType && superType.<span class="property"><span class="keyword">prototype</span></span>, {</span><br><span class="line"> <span class="attr">constructor</span>: {</span><br><span class="line"> <span class="attr">value</span>: subType,</span><br><span class="line"> <span class="attr">enumerable</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">writable</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">configurable</span>: <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (superType) {</span><br><span class="line"> <span class="title class_">Object</span>.<span class="property">setPrototypeOf</span> </span><br><span class="line"> ? <span class="title class_">Object</span>.<span class="title function_">setPrototypeOf</span>(subType, superType) </span><br><span class="line"> : subType.<span class="property">__proto__</span> = superType;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>1、函数声明和类声明的区别</p><p>函数声明会提升,类声明不会。首先需要声明你的类,然后访问它,否则像下面的代码会抛出一个ReferenceError。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> p = <span class="keyword">new</span> <span class="title class_">Rectangle</span>(); </span><br><span class="line"><span class="comment">// ReferenceError</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Rectangle</span> {}</span><br></pre></td></tr></table></figure><p>2、ES5继承和ES6继承的区别</p><ul><li>ES5的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上(Parent.call(this)).</li><li>ES6的继承有所不同,实质上是先创建父类的实例对象this,然后再用子类的构造函数修改this。因为子类没有自己的this对象,所以必须先调用父类的super()方法,否则新建实例报错。</li></ul>]]></content>
</entry>
<entry>
<title>节流与防抖</title>
<link href="/2024/03/16/%E8%8A%82%E6%B5%81%E4%B8%8E%E9%98%B2%E6%8A%96/"/>
<url>/2024/03/16/%E8%8A%82%E6%B5%81%E4%B8%8E%E9%98%B2%E6%8A%96/</url>
<content type="html"><![CDATA[<p>因为要做一个记忆翻牌配对小游戏,然后在学习时偶然看到“节流”二字,然后就顺便去看了看所谓的“节流”</p><h3 id="节流"><a href="#节流" class="headerlink" title="节流"></a>节流</h3><h5 id="1-啥是节流"><a href="#1-啥是节流" class="headerlink" title="1.啥是节流"></a>1.啥是节流</h5><p> n 秒内只执行一次事件,即使n 秒内事件重复触发,也只有一次生效。</p><p>如果这不好理解,那就假设一个场景:</p><p>1.你设计了一个表单,这个表单提交的数据内容很多。</p><p>2.你的有些用户闲得很无聊,写完表单以后疯狂点击提交按钮。</p><p>3.你的后端同事走到你面前指着崩溃的服务器来向你抱怨。</p><p>这就像打游戏放技能一样,放完一次技能后就有一段的冷却时间</p><p><strong>所以我的第一想法肯定是给这个button一个冷却时间</strong></p><p>现在在我面前的有两个东西:一个按钮提交的功能,一个冷却时间,假设为5秒</p><p>牵扯到时间会让我第一时间想到 setTimeout和setInterval,首先排除setInterval,因为提交表单这个功能我不希望它循环执行,所以我选择setTimeout。</p><p>假设你现在正在玩游戏,游戏有一个技能,它的技能CD是两秒,那么我们就需要判断用户是否在两秒内多处点击了这个技能,如果多次点击,那么无事发生(返回一个空函数),如果不在CD,那么返回这个技能的特效(技能执行的函数)</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">fnOnclick</span> = (<span class="params"></span>)=> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"技能已经开启!"</span>)</span><br><span class="line"><button onClick ={<span class="title function_">cdTime</span>(fnOnclick,<span class="number">2000</span>)}>技能</button></span><br></pre></td></tr></table></figure><p>在这里我们需要知道cdTime这个函数只是一个外壳函数,它真正的意义在于传递我们需要的参数,它并不是我们希望onclick真正要执行的函数!cdTime返回的那个函数才是我们真正想执行的函数</p><p>现在我们设计cdTime,也就是我们限制 技能 只能在两秒之内释放。</p><h5 id="2-代码实现"><a href="#2-代码实现" class="headerlink" title="2.代码实现"></a>2.代码实现</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">fn0nClick</span> = (<span class="params"></span>) => <span class="variable language_">console</span>. <span class="title function_">log</span>(<span class="string">"技能已经开启!!"</span>) </span><br><span class="line"><span class="comment">//这就是你按钮提交的onclick事件</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">cdTime</span>(<span class="params">fn, delay</span>) {</span><br><span class="line"><span class="keyword">let</span> <span class="variable constant_">CD</span> =<span class="literal">false</span>; <span class="comment">//首先你的技能刚开始是没有冷却时间的</span></span><br><span class="line"><span class="keyword">return</span> <span class="keyword">function</span> (<span class="params"></span>)</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable constant_">CD</span>) { <span class="comment">//ok, 当你想放技能的时候,你需要判断是否在冷却时间内,如果在,对不起不能放!</span></span><br><span class="line"> <span class="variable language_">console</span>. log (<span class="string">"不行,cd中"</span> )</span><br><span class="line"><span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">//这里写null, 空值都可以</span></span><br><span class="line">}</span><br><span class="line"> <span class="variable constant_">CD</span> = <span class="literal">true</span>; <span class="comment">//这步我的技能还没放,即将要释放。因为setTimout是异步执行, 所以cd在两秒以后才会被修改,这一步是限制用户频繁点击技能键时,让函数返回一个空值。</span></span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> { </span><br><span class="line"> <span class="comment">//现在没有进入cd,当你放技能的时候然后开始启用</span></span><br><span class="line"> <span class="variable language_">console</span>. <span class="title function_">log</span>(<span class="string">"技能成功释放"</span>);</span><br><span class="line"> <span class="variable constant_">CD</span> = <span class="literal">false</span>; <span class="comment">//ok, 现在我技能释放完毕,把cd的属性清空。</span></span><br><span class="line"> <span class="title function_">fn</span>();</span><br><span class="line"></span><br><span class="line"> }, delay);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然后我在看完节流后又摸过去看了看防抖</p><h3 id="防抖"><a href="#防抖" class="headerlink" title="防抖"></a>防抖</h3><h5 id="1-啥是防抖"><a href="#1-啥是防抖" class="headerlink" title="1.啥是防抖"></a>1.啥是防抖</h5><p>在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。</p><p>我们拿一个点击按钮来举例子。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">getSearch</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"发请求"</span>)</span><br><span class="line">}</span><br><span class="line"><button onClick =<span class="function">()=></span>{getSearch}>点击</button></span><br></pre></td></tr></table></figure><p>现在我们尝试疯狂点击按钮就会疯狂发送请求。</p><p><img src="/1.gif" alt="img"></p><p>我们现在来修改一下这个函数,我们思考一下🤔,假设我们不借助 debounce 可以实现一个伪防抖的功能吗?答案是百分百可以的。我们先在这个文件下设定一个变量叫做 timerID</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> timerId = <span class="number">0</span></span><br></pre></td></tr></table></figure><p>然后我们设定一个定时器,来使这个 console.log(“发请求”) 在 1.5s 后执行。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> timerId = <span class="number">0</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getSearch</span>(<span class="params"></span>){</span><br><span class="line"> timerId = <span class="built_in">setTimeout</span>(<span class="function">()=></span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"发请求"</span>)</span><br><span class="line"> },<span class="number">1500</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然后这里要补充讲一下:setTimeout的返回值</p><p><img src="/2.png" alt="img"><img src="/3.png" alt="img"></p><p>其实 setTimeout 会在 setTimeout 执行的时候返回一个大于 0 的正整数。 所以我们这句话其实是在给 timerID 赋值! 并不是将 setTimeout 函数本身赋值给 timerID 这个变量。</p><p><img src="/4.png" alt="img"></p><p>这里我们需要特别搞清楚 setTimout函数本身执行的时候,是马上赋值的,并不是等到 1.5s 后再赋值的。什么意思呢? 我设置了大约在10几年后再执行的一个函数,千万不要觉得 timerID 是会在10年以后才会被赋值。</p><p>为什么要这样设计呢?因为如果这样执行的话,就会给我们一个反悔的机会。还说上面的例子。假设我在 5 年后突然反悔不想执行了。我只需要取消这个 timerID 就可以中途放弃执行。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">cancelSearch</span>(<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"我取消的ID是"</span>,timerId)</span><br><span class="line"> <span class="built_in">clearTimeout</span>(timerId)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这个 timerID 就是每一个 setTimeout 的身份证。每当你执行一次 setTimout 后,setTimout 所接收的回调函数就会被分配一个唯一 ID,来被放进任务队列。 而 clearTimeout 的功能恰好就是清除位于任务队列里指定的 id 所绑定的那个回调函数。</p><h5 id="2-代码实现-1"><a href="#2-代码实现-1" class="headerlink" title="2.代码实现"></a>2.代码实现</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> timerId = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getSearch</span>(<span class="params"></span>){</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span>(timerId){</span><br><span class="line"> <span class="title function_">clreaTimeout</span>(timerId)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//当我们每次执行 getSearch 之前,如果当前任务队列里有上一次同样的任务,我们就先清除掉</span></span><br><span class="line"> </span><br><span class="line"> timerId = <span class="built_in">setTimeout</span>(<span class="function">()=></span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"发请求"</span>)</span><br><span class="line"> },<span class="number">1200</span>)</span><br><span class="line"> <span class="comment">//然后再去开启一个定时器任务推进任务队列</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
</entry>
<entry>
<title>React实现图片懒加载</title>
<link href="/2024/03/02/React%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E6%87%92%E5%8A%A0%E8%BD%BD/"/>
<url>/2024/03/02/React%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E6%87%92%E5%8A%A0%E8%BD%BD/</url>
<content type="html"><![CDATA[<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>图片懒加载是一种优化网页性能的技术,它允许在用户滚动到图片位置之前延迟加载图片。通过懒加载,可以在用户需要查看图片时才加载图片,避免了不必要的图片加载,从而提高了网页的加载速度和用户体验。</p><h3 id="方法一"><a href="#方法一" class="headerlink" title="方法一"></a>方法一</h3><h5 id="实现思路"><a href="#实现思路" class="headerlink" title="实现思路"></a>实现思路</h5><p>在说明思路之前,先了解几个常见的视图属性。</p><ol><li><p><strong>clientHeight</strong>:元素的像素高度,包含元素的高度+内边距,不包含水平滚动条,边框和外边距。</p></li><li><p><strong>scrollTop</strong>:滚动条滚动的高度,它指的是内容区的顶部到可视区域顶部的距离。</p></li><li><p><strong>offsetParent</strong>:距离元素最近的一个具有定位的祖宗元素(relative,absolute,fixed),若祖宗都不符合条件,offsetParent为body。</p></li><li><p><strong>offsetTop</strong>:元素到offsetParent顶部的距离。</p><p><img src="https://www.runoob.com/wp-content/uploads/2021/10/L0hUTUw15byA5Y-R5paH5qGjL2ltYWdlcy9Dc3NCb3hNb2RlbC5wbmc.png" alt="img"></p></li></ol><p><strong>滚动条的滚动高度</strong>:表示滚动条已经向下滚动的距离,即页面顶部到可视区域顶部的距离。</p><p><strong>可视区域的高度</strong>:表示当前浏览器窗口或容器中可见的部分的高度。</p><p><strong>当前图片的 offsetTop</strong>:表示图片顶部相对于文档顶部的距离。</p><p>根据上面的图解可知,当图片的滚动条滚动的高度加上可视区域的高度大于当前的图片的offsetTop,那么说明图片正在进入可视区域。这个时候便可以加载当前图片。</p><h5 id="第一步"><a href="#第一步" class="headerlink" title="第一步"></a>第一步</h5><p>模拟后台返回的图片url,遍历产生一个url集合,用于后面的懒加载使用。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title function_">imgUrls</span> = (<span class="params">num = <span class="number">10</span></span>) => {</span><br><span class="line"> <span class="keyword">const</span> urls = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < num; i++) {</span><br><span class="line"> <span class="keyword">const</span> url = <span class="string">`https://robohash.org/<span class="subst">${i}</span>.png`</span>;</span><br><span class="line"> urls.<span class="title function_">push</span>(url);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> urls;</span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></table></figure><h5 id="第二步"><a href="#第二步" class="headerlink" title="第二步"></a>第二步</h5><p>遍历图片url集合,渲染loading图片</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><div className={styles[<span class="string">'box-one'</span>]} ref={scrollRef}></span><br><span class="line"> {<span class="title function_">imgUrls</span>(<span class="number">100</span>).<span class="title function_">map</span>(<span class="function">(<span class="params">item</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">img</span> <span class="attr">data-src</span>=<span class="string">{item}</span> <span class="attr">key</span>=<span class="string">{item}</span> <span class="attr">src</span>=<span class="string">{loadingUrl}</span> <span class="attr">alt</span>=<span class="string">""</span> /></span></span>;</span><br><span class="line"> })}</span><br><span class="line"> </div></span><br></pre></td></tr></table></figure><p><img src="/React%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E6%87%92%E5%8A%A0%E8%BD%BD.assets/2b9b931cc08a426cbd0a1788ed89dbddtplv-k3u1fbpfcp-jj-mark3024000q75.awebp#" alt="image.png"></p><h5 id="第三步"><a href="#第三步" class="headerlink" title="第三步"></a>第三步</h5><p>监听容器的滚动事件,当容器滚动时计算容器的高度加上滚动条的高度大于当前图片的offsetTop时加载当前的图片。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> loadingUrl <span class="keyword">from</span> <span class="string">'@/assets/imgs/loading.jpg'</span>;</span><br><span class="line"><span class="keyword">import</span> { useEffect, useRef } <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'../index.less'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 图片url</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">imgUrls</span> = (<span class="params">num = <span class="number">10</span></span>) => {</span><br><span class="line"> <span class="keyword">const</span> urls = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < num; i++) {</span><br><span class="line"> <span class="keyword">const</span> url = <span class="string">`https://robohash.org/<span class="subst">${i}</span>.png`</span>;</span><br><span class="line"> urls.<span class="title function_">push</span>(url);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> urls;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">LazyLoading</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="keyword">const</span> scrollRef = <span class="title function_">useRef</span>({} <span class="keyword">as</span> any);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 滚动事件</span></span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">changeScroll</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="keyword">const</span> clientHeight = scrollRef?.<span class="property">current</span>.<span class="property">clientHeight</span>; <span class="comment">//可视区域高度</span></span><br><span class="line"> <span class="keyword">const</span> scrollTop = scrollRef?.<span class="property">current</span>.<span class="property">scrollTop</span>; <span class="comment">//滚动条滚动高度</span></span><br><span class="line"> <span class="keyword">const</span> childNodes = scrollRef?.<span class="property">current</span>.<span class="property">childNodes</span>; <span class="comment">// 获取所有图片集合</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j < childNodes.<span class="property">length</span>; j++) {</span><br><span class="line"> <span class="keyword">const</span> element = childNodes[j];</span><br><span class="line"> <span class="keyword">if</span> (scrollTop + clientHeight > element.<span class="property">offsetTop</span>) {</span><br><span class="line"> element.<span class="property">src</span> = element.<span class="title function_">getAttribute</span>(<span class="string">'data-src'</span>); <span class="comment">// 替换当前的src</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="title function_">useEffect</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">changeScroll</span>(); <span class="comment">// 第一次渲染的时候替换loading图片</span></span><br><span class="line"> }, []);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">{styles[</span>'<span class="attr">box-one</span>']} <span class="attr">ref</span>=<span class="string">{scrollRef}</span> <span class="attr">onScroll</span>=<span class="string">{changeScroll}</span>></span></span></span><br><span class="line"><span class="language-xml"> {imgUrls(100).map((item) => {</span></span><br><span class="line"><span class="language-xml"> return <span class="tag"><<span class="name">img</span> <span class="attr">data-src</span>=<span class="string">{item}</span> <span class="attr">key</span>=<span class="string">{item}</span> <span class="attr">src</span>=<span class="string">{loadingUrl}</span> <span class="attr">alt</span>=<span class="string">""</span> /></span>;</span></span><br><span class="line"><span class="language-xml"> })}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">LazyLoading</span>;</span><br></pre></td></tr></table></figure><p><img src="/React%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E6%87%92%E5%8A%A0%E8%BD%BD.assets/59cabfcd5a6049bab860b480c72bd557tplv-k3u1fbpfcp-jj-mark3024000q75.awebp#" alt="image.png"></p><h3 id="方法二"><a href="#方法二" class="headerlink" title="方法二"></a>方法二</h3><p>方案二的实现思路利用浏览器提供的 <code>IntersectionObserver</code> API实现。<code>IntersectionObserver</code> API提供了一种方便的方式来监视目标元素和其祖先元素或视窗之间的交叉状态变化。当目标元素进入或离开视口时,可以触发回调函数,进行相应的操作。它的原理是通过注册一个回调函数来观察特定元素的交叉状态变化,并在满足条件时执行相应的操作。</p><p>使用 <code>IntersectionObserver</code> API非常简单,可以通过创建一个 <code>IntersectionObserver</code> 实例,并传入回调函数和选项对象来实现。回调函数会在目标元素的交叉状态发生变化时被调用,并接收一个参数,包含有关交叉状态的信息。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> loadingUrl <span class="keyword">from</span> <span class="string">'@/assets/imgs/loading.jpg'</span>;</span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'../index.less'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">React</span>, { useRef, useEffect, useState } <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="comment">// 图片url</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">imgUrls</span> = (<span class="params">num = <span class="number">10</span></span>) => {</span><br><span class="line"> <span class="keyword">const</span> urls = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < num; i++) {</span><br><span class="line"> <span class="keyword">const</span> url = <span class="string">`https://robohash.org/<span class="subst">${i}</span>.png`</span>;</span><br><span class="line"> urls.<span class="title function_">push</span>(url);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> urls;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">LazyLoadImage</span> = (<span class="params">{ src, alt }</span>) => {</span><br><span class="line"> <span class="keyword">const</span> [imageSrc, setImageSrc] = <span class="title function_">useState</span>(loadingUrl);</span><br><span class="line"> <span class="keyword">const</span> imgRef = <span class="title function_">useRef</span>(<span class="literal">null</span> <span class="keyword">as</span> any);</span><br><span class="line"></span><br><span class="line"> <span class="title function_">useEffect</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">let</span> <span class="attr">observer</span>: <span class="title class_">IntersectionObserver</span>;</span><br><span class="line"> <span class="keyword">if</span> (imgRef.<span class="property">current</span>) {</span><br><span class="line"> <span class="comment">// 创建IntersectionObserver实例</span></span><br><span class="line"> observer = <span class="keyword">new</span> <span class="title class_">IntersectionObserver</span>(</span><br><span class="line"> <span class="function">(<span class="params">[entry]</span>) =></span> {</span><br><span class="line"> <span class="comment">// 当图片进入可视区域时,设置图片地址进行加载</span></span><br><span class="line"> <span class="keyword">if</span> (entry.<span class="property">isIntersecting</span>) {</span><br><span class="line"> <span class="title function_">setImageSrc</span>(src);</span><br><span class="line"> observer.<span class="title function_">unobserve</span>(imgRef.<span class="property">current</span>);</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">rootMargin</span>: <span class="string">'0px 0px 200px 0px'</span>, <span class="comment">// 可视区域的上边距设置为200px</span></span><br><span class="line"> <span class="comment">//rootMargin 是 IntersectionObserver 的配置项之一,用于指定根元素边界框的外边距,以便扩大或缩小视口的大小</span></span><br><span class="line"> },</span><br><span class="line"> );</span><br><span class="line"> observer.<span class="title function_">observe</span>(imgRef.<span class="property">current</span>); <span class="comment">//开始观察目标元素</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">if</span> (observer && observer.<span class="property">unobserve</span>) {</span><br><span class="line"> observer.<span class="title function_">unobserve</span>(imgRef.<span class="property">current</span>);</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> }, [src]);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">img</span> <span class="attr">ref</span>=<span class="string">{imgRef}</span> <span class="attr">src</span>=<span class="string">{imageSrc}</span> <span class="attr">alt</span>=<span class="string">{alt}</span> /></span></span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">LazyLoading</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">{styles[</span>'<span class="attr">box-two</span>']}></span></span></span><br><span class="line"><span class="language-xml"> {imgUrls(100).map((item) => {</span></span><br><span class="line"><span class="language-xml"> return <span class="tag"><<span class="name">LazyLoadImage</span> <span class="attr">src</span>=<span class="string">{item}</span> <span class="attr">alt</span>=<span class="string">"lazy load image"</span> /></span>;</span></span><br><span class="line"><span class="language-xml"> })}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">LazyLoading</span>;</span><br></pre></td></tr></table></figure><p><img src="/React%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E6%87%92%E5%8A%A0%E8%BD%BD.assets/f5725e4841804cc0a4cf6d9938c2484btplv-k3u1fbpfcp-jj-mark3024000q75.awebp#" alt="2024-01-08 16.17.31.gif"></p><h5 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h5><p>在初始化的时候,需要给imageSrc设置一个初始化的loading地址,如果没有的话,初始化的时候会加载多张图片。</p><h3 id="方法三"><a href="#方法三" class="headerlink" title="方法三"></a>方法三</h3><p>利用react的懒加载库<code>react-lazyload</code>,这里介绍几个它的常见属性:</p><ol><li>scrollContainer: 指定的滚动的区域,默认值是undefined,如果没有指定默认是窗口的视图作为滚动区域。</li><li>offset: 元素距离视口顶部的距离,当达到这个距离时,元素将被加载。</li><li>scroll: 是否监听滚动</li><li>height: 渲染元素的占位符的高度。</li><li>overflow : 如果溢出容器,延迟加载组件</li></ol><p>因为这里实现的图片懒加载是局部懒加载,所以需要指定 <code>scrollContainer</code>,<code>scrollContainer</code> 的值DOM对象。在实现的过程中,同时需要设置overflow为true,以及height的值。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> react, { useRef, useEffect } <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">LazyLoad</span> <span class="keyword">from</span> <span class="string">'react-lazyload'</span>;</span><br><span class="line"><span class="keyword">import</span> styles <span class="keyword">from</span> <span class="string">'../index.less'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 图片url</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">imgUrls</span> = (<span class="params">num = <span class="number">10</span></span>) => {</span><br><span class="line"> <span class="keyword">const</span> urls = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < num; i++) {</span><br><span class="line"> <span class="keyword">const</span> url = <span class="string">`https://robohash.org/<span class="subst">${i}</span>.png`</span>;</span><br><span class="line"> urls.<span class="title function_">push</span>(url);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> urls;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">LazyLoading</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="keyword">const</span> scrollRef = <span class="title function_">useRef</span>({} <span class="keyword">as</span> any);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">className</span>=<span class="string">{styles[</span>'<span class="attr">box-three</span>']} <span class="attr">ref</span>=<span class="string">{scrollRef}</span>></span></span></span><br><span class="line"><span class="language-xml"> {imgUrls(100).map((item) => {</span></span><br><span class="line"><span class="language-xml"> return (</span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">LazyLoad</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">height</span>=<span class="string">{200}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">overflow</span>=<span class="string">{true}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">offset</span>=<span class="string">{0}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">key</span>=<span class="string">{item}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">scroll</span>=<span class="string">{true}</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> <span class="attr">scrollContainer</span>=<span class="string">{scrollRef.current}</span> // <span class="attr">DOM</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml"> ></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">{item}</span> <span class="attr">alt</span>=<span class="string">""</span> /></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">LazyLoad</span>></span></span></span><br><span class="line"><span class="language-xml"> );</span></span><br><span class="line"><span class="language-xml"> })}</span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> );</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">LazyLoading</span>;</span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> react </tag>
</tags>
</entry>
<entry>
<title>闭包</title>
<link href="/2023/12/04/%E9%97%AD%E5%8C%85/"/>
<url>/2023/12/04/%E9%97%AD%E5%8C%85/</url>
<content type="html"><![CDATA[<h2 id="闭包(Closure)"><a href="#闭包(Closure)" class="headerlink" title="闭包(Closure)"></a>闭包(Closure)</h2><h3 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h3><ol><li>避免变量全局污染</li><li>使数据私有化,外部无法修改内部数据</li><li>可以让外部可以使用内部的私有数据</li></ol><p>以上三点其实都是函数的作用,而不是闭包的独特作用</p><p>!闭包的核心作用是:使变量可以驻留在内存,不被回收</p><h3 id="代码讲解"><a href="#代码讲解" class="headerlink" title="代码讲解"></a>代码讲解</h3><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> a = <span class="number">10</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>){</span><br><span class="line"> a++;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br><span class="line">}</span><br><span class="line"><span class="title function_">fn</span>();</span><br><span class="line"><span class="title function_">fn</span>();</span><br><span class="line"><span class="title function_">fn</span>();</span><br></pre></td></tr></table></figure><p>结果输出为11,12,13</p><p>但是此时a为全局变量,如果别人不小心对a又重复赋值了,那么结果就不是我们预期的结果</p><p>那此时我们就不让a成为全局变量,把a的定义放在函数内部,让a成为函数的私有变量</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>){</span><br><span class="line"> <span class="keyword">let</span> a = <span class="number">10</span>;</span><br><span class="line"> a++;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br><span class="line">}</span><br><span class="line"><span class="title function_">fn</span>();</span><br><span class="line"><span class="title function_">fn</span>();</span><br><span class="line"><span class="title function_">fn</span>();</span><br></pre></td></tr></table></figure><p>此时就可以满足上面提到的函数的作用(第三条只要加个return就可以实现)</p><p>但是问题出现了:这次的结果三次都为11,这是因为函数在执行完一次后里面的变量就会被释放掉,下一次执行时又会重新对a进行赋值,所以结果一直为11</p><h4 id="创建闭包"><a href="#创建闭包" class="headerlink" title="创建闭包"></a>创建闭包</h4><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">fn</span>(<span class="params"></span>){</span><br><span class="line"> <span class="keyword">let</span> a = <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span> <span class="title function_">b</span>(<span class="params"></span>){</span><br><span class="line"> a++;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(a);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> f = <span class="title function_">fn</span>();</span><br><span class="line"><span class="title function_">f</span>();</span><br></pre></td></tr></table></figure><p>条件:</p><ol><li>必须有个子函数 </li><li>子函数必须要调用父函数里面的变量</li></ol><p>实现自增的原因:</p><ol><li>执行的是f(),所以实际上执行的是子函数b,fn只在给f定义的时候执行了一次,所以后面let a = 10也没有被执行</li><li>闭包函数内的变量可以保存下来</li></ol><p>我的疑惑:</p><ol><li>为什么要在下面重新定义一个f函数</li><li>为什么要加return A :1.因为要返回b函数? 2.最后f调用的是a变量,(如果外部想要使用闭包里面的变量,就要用return?)</li></ol><h3 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h3><h4 id="内存泄漏"><a href="#内存泄漏" class="headerlink" title="内存泄漏"></a>内存泄漏</h4><p>变量长时间驻留在内存,如果处理不当可能造成内存泄漏,所以用完可以手动清除</p><p>在最后加上下面的代码即可</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">f = <span class="literal">null</span>;</span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> javascript </tag>
</tags>
</entry>
<entry>
<title>this(普通函数)————初版</title>
<link href="/2023/12/03/this%E6%99%AE%E9%80%9A%E5%87%BD%E6%95%B0/"/>
<url>/2023/12/03/this%E6%99%AE%E9%80%9A%E5%87%BD%E6%95%B0/</url>
<content type="html"><![CDATA[<h2 id="this(普通函数"><a href="#this(普通函数" class="headerlink" title="this(普通函数)"></a>this(普通函数)</h2><h4 id="this是什么"><a href="#this是什么" class="headerlink" title="this是什么"></a>this是什么</h4><p>this———用于访问当前方法所属的对象</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">12</span>,</span><br><span class="line"> fn:<span class="keyword">function</span> (<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>===obj)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">obj.<span class="title function_">fn</span>();</span><br></pre></td></tr></table></figure><p>当前这个方法fn是属于obj,所以this就是obj,所以结果为true</p><p>这时用另一种写法</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> obj = {</span><br><span class="line"> <span class="attr">a</span>: <span class="number">12</span>,</span><br><span class="line">}</span><br><span class="line">obj.<span class="property">fn</span> = <span class="keyword">function</span> (<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>===obj)</span><br><span class="line">}</span><br><span class="line">obj.<span class="title function_">fn</span>();</span><br></pre></td></tr></table></figure><p>这种后添的写法结果也是完全一样</p><h4 id="this绑定方式"><a href="#this绑定方式" class="headerlink" title="this绑定方式"></a>this绑定方式</h4><p>this和函数的定义无关,和函数的调用有关,也可以说同一个函数的this可以有很多种值,这取决于怎么调用这个函数</p><p>1.直接调用——window || undefined PS:在node环境里为global或者undefined</p><p>下面是直接调用函数</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">show</span>(<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>)</span><br><span class="line">}</span><br><span class="line"><span class="title function_">show</span>()</span><br></pre></td></tr></table></figure><p>结果为window,但这是个错误,因为show不属于任何对象,和this的定义有所冲突</p><p>在这里window相当于是一个兜底的角色,就是找不到this的时候,就用window来兜底</p><p>后面经过修改,如果加上了严格模式</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="meta">'use strict'</span></span><br></pre></td></tr></table></figure><p>则结果为undefined</p><p>2.挂载在对象上,然后执行方法,那this就指向对象</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> arr=[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]</span><br><span class="line">arr.<span class="property">fn</span>=show</span><br><span class="line">arr.<span class="title function_">fn</span>()</span><br></pre></td></tr></table></figure><p>此时输出的结果就是arr,因为此时show挂在了arr上面,属于对象arr,所以this输出的值就是arr</p><p>3.定时器——window</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(show,<span class="number">100</span>)</span><br></pre></td></tr></table></figure><p>在加了严格模式下输出结果仍为window</p><p>因为定时器本来就属于window,window来执行定时器,定时器来执行show,所以最终show是被window调用的</p><p>4.工具函数</p><p>使用call强制改变this的值 ps:apply </p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">show.<span class="title function_">call</span>(<span class="number">12</span>)</span><br><span class="line">show.<span class="title function_">call</span>(<span class="string">'sdjlfhkajhf'</span>)<span class="comment">//这里要求变成个字符串</span></span><br><span class="line">show.<span class="title function_">call</span>(<span class="keyword">new</span> <span class="title class_">Date</span>())<span class="comment">//这里要求变成个日期对象</span></span><br><span class="line">show.<span class="title function_">call</span>({<span class="attr">a</span>: <span class="number">12</span>})<span class="comment">//这里要求变成个json</span></span><br></pre></td></tr></table></figure><p>使用forEach</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> arr=[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]</span><br><span class="line">arr.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">item</span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>,item)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>这里的情况相当于第一种情况</p><p>函数并没有挂在arr上面,只是交给了forEach去调用</p><p>forEach实际上是</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">arr.<span class="property">forEach</span>=<span class="keyword">function</span> (<span class="params">cb</span>){</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i=<span class="number">0</span>;i<<span class="variable language_">this</span>.<span class="property">length</span>;i++){</span><br><span class="line"> <span class="title function_">cb</span>(<span class="variable language_">this</span>[i])<span class="comment">//这里就是直接调用</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>但是forEach有第二个参数,这个参数就有绑定this的作用</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> arr=[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]</span><br><span class="line">arr.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">item</span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>,item)</span><br><span class="line">},<span class="variable language_">this</span>)</span><br></pre></td></tr></table></figure><p>这里的用法就相当于是call的用法</p>]]></content>
<tags>
<tag> javascript </tag>
</tags>
</entry>
<entry>
<title>first</title>
<link href="/2023/11/20/first/"/>
<url>/2023/11/20/first/</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title>Hello World</title>
<link href="/2023/11/20/hello-world/"/>
<url>/2023/11/20/hello-world/</url>
<content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content>
</entry>
</search>