<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; JavaScript</title><link>https://academy.hsoub.com/programming/javascript/page/6/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; JavaScript</description><language>ar</language><item><title>&#x627;&#x633;&#x62A;&#x639;&#x645;&#x627;&#x644;&#x627;&#x62A; &#x645;&#x62A;&#x642;&#x62F;&#x645;&#x629; &#x644;&#x644;&#x648;&#x627;&#x62C;&#x647;&#x629; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; Fetch &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D8%A7%D8%AA-%D9%85%D8%AA%D9%82%D8%AF%D9%85%D8%A9-%D9%84%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-fetch-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1297/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/61193effdd7d1_--Fetch-API.png.f0ffc40e9a7e73f81c6eabad7c9e7907.png" /></p>

<p>
	لقد أخذنا فكرةً لا بأس بها عن <code>fetch</code> في المقالات السابقة من هذه السلسلة (بدءًا من مقال <a data-ss1634992164="1" href="https://academy.hsoub.com/programming/javascript/%D8%A5%D8%B1%D8%B3%D8%A7%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D8%B3%D8%AA%D9%84%D8%A7%D9%85%D9%87%D8%A7-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1294/" rel="">إرسال البيانات واستلامها عبر الشبكة</a> وحتى مقال <a data-ss1634992164="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-fetch-%D9%85%D8%B9-%D8%A7%D9%84%D8%B7%D9%84%D8%A8%D8%A7%D8%AA-%D8%B0%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B5%D9%84-%D8%A7%D9%84%D9%85%D8%AE%D8%AA%D9%84%D8%B7-cross-origin-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1296/" rel="">استخدام Fetch مع الطلبات ذات الأصل المختلط Cross-Origin</a>)، والآن لنلق نظرةً على بقية مكوّنات الواجهة البرمجية لنغطي كل إمكاناتها.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		لاحظ أنك لا تستخدم معظم الخيارات التي سنستعرضها إلا نادرًا، وبالتالي لن يحدّ تخطي هذا الفصل من قدرتك على الاستخدام الجيد لـ <code>fetch</code>، ومع ذلك من المفيد معرفة إمكانيات <code>fetch</code> بحيث يمكنك العودة إليها عند الحاجة وقراءة التفاصيل.
	</p>
</blockquote>

<p>
	إليك قائمةً كاملةً بكل خيارت <code>fetch</code> الممكنة مع قيمها الافتراضية (وضعنا البدائل في تعليقات):
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9733_7" style="">
<span class="pln">let promise </span><span class="pun">=</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">"GET"</span><span class="pun">,</span><span class="pln"> </span><span class="com">// POST, PUT, DELETE, etc.</span><span class="pln">
  headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// the content type header value is usually auto-set</span><span class="pln">
    </span><span class="com">// depending on the request body</span><span class="pln">
    </span><span class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain;charset=UTF-8"</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  body</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">undefined</span><span class="pln"> </span><span class="com">// string, FormData, Blob, BufferSource, or URLSearchParams</span><span class="pln">
  referrer</span><span class="pun">:</span><span class="pln"> </span><span class="str">"about:client"</span><span class="pun">,</span><span class="pln"> </span><span class="com">// or "" to send no Referer header,</span><span class="pln">
  </span><span class="com">// or an url from the current origin</span><span class="pln">
  referrerPolicy</span><span class="pun">:</span><span class="pln"> </span><span class="str">"no-referrer-when-downgrade"</span><span class="pun">,</span><span class="pln"> </span><span class="com">// no-referrer, origin, same-origin...</span><span class="pln">
  mode</span><span class="pun">:</span><span class="pln"> </span><span class="str">"cors"</span><span class="pun">,</span><span class="pln"> </span><span class="com">// same-origin, no-cors</span><span class="pln">
  credentials</span><span class="pun">:</span><span class="pln"> </span><span class="str">"same-origin"</span><span class="pun">,</span><span class="pln"> </span><span class="com">// omit, include</span><span class="pln">
  cache</span><span class="pun">:</span><span class="pln"> </span><span class="str">"default"</span><span class="pun">,</span><span class="pln"> </span><span class="com">// no-store, reload, no-cache, force-cache, or only-if-cached</span><span class="pln">
  redirect</span><span class="pun">:</span><span class="pln"> </span><span class="str">"follow"</span><span class="pun">,</span><span class="pln"> </span><span class="com">// manual, error</span><span class="pln">
  integrity</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pun">,</span><span class="pln"> </span><span class="com">// a hash, like "sha256-abcdef1234567890"</span><span class="pln">
  keepalive</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln"> </span><span class="com">// true</span><span class="pln">
  signal</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">undefined</span><span class="pun">,</span><span class="pln"> </span><span class="com">// AbortController to abort request</span><span class="pln">
  window</span><span class="pun">:</span><span class="pln"> window </span><span class="com">// null</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	لقد غطينا المفاهيم <code>method</code> و<code>headers</code> و<code>body</code> في مقال <a data-ss1634992164="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-fetch-%D9%85%D8%B9-%D8%A7%D9%84%D8%B7%D9%84%D8%A8%D8%A7%D8%AA-%D8%B0%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B5%D9%84-%D8%A7%D9%84%D9%85%D8%AE%D8%AA%D9%84%D8%B7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1296/" rel="">استخدام Fetch</a>، كما غطينا <code>signal</code> في مقال <a data-ss1634992164="1" href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AA%D8%A8%D8%B9-%D8%AA%D9%82%D8%AF%D9%85-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D8%B2%D9%8A%D9%84-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-fetch-%D9%88%D8%A5%D9%84%D8%BA%D8%A7%D8%A1-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A9-fetch-r1295/" rel="">إيقاف تنفيذ Fetch</a>، وسنتعرف الآن على بقية الإمكانات.
</p>

<h2>
	خيارا المحيل referrer وسياسة المحيل referrerPolicy
</h2>

<p>
	يتحكم هذان الخياران بكيفية ضبط <code>fetch</code> للترويسة <code>Referrer</code>، وهي إحدى ترويسات HTTP، وتُضبط تلقائيًا لتحتوي على عنوان الصفحة التي ولّدت الطلب، ولا تعَد هامةً في معظم الأحيان، وقد يكون من المنطقي أحيانًا إزالتها أو تقصيرها لأسباب تتعلق بالأمان.
</p>

<p>
	يسمح الخيار <code>referrer</code> بتسمية أي مُحيل، وهو الصفحة أو الرابط الذي أحالك إلى الصفحة الحالية التي تعمل عليها، أو إزالته على أن يشترك بالأصل مع الصفحة الحالية، وإذا لم ترغب بإرسال أي محيل فأسند إليه نصًا فارغًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9733_11" style="">
<span class="pln">fetch</span><span class="pun">(</span><span class="str">'/page'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  referrer</span><span class="pun">:</span><span class="pln"> </span><span class="str">""</span><span class="pln"> </span><span class="com">//  لا توجد توريسة محيل     </span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	ولوضع عنوان مورد آخر من الأصل ذاته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9733_13" style="">
<span class="pln">fetch</span><span class="pun">(</span><span class="str">'/page'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// https://javascript.info بفرض أننا في </span><span class="pln">
  </span><span class="com">// نستطيع ضبط أي ترويسة محيل، لكن ضمن الأصل الحالي </span><span class="pln">
  referrer</span><span class="pun">:</span><span class="pln"> </span><span class="str">"https://javascript.info/anotherpage"</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يضبط الخيار <code>referrerPolicy</code> بعض القواعد العامة للمُحيل <code>Referer</code>.
</p>

<p>
	تنقسم الطلبات إلى ثلاث مجموعات، هي الآتية:
</p>

<ol>
<li>
		الطلب إلى مورد من الأصل ذاته.
	</li>
	<li>
		الطلب إلى مورد من أصل مختلف.
	</li>
	<li>
		الطلب من بروتوكول HTTPS إلى بروتوكول HTTP: أي من بروتوكول النقل الآمن إلى غير الآمن.
	</li>
</ol>
<p>
	يدل الخيار <code>referrerPolicy</code> المتصفح على القواعد الخاصة باستخدام المُحيل في كل مجموعة من الطلبات، ولا يسمح بضبط القيمة الدقيقة للمحيل.
</p>

<p>
	ستجد جميع القيم الممكنة في <a data-ss1634992164="1" href="https://w3c.github.io/webappsec-referrer-policy/" rel="external nofollow">توصيف سياسة المحيل</a>:
</p>

<ul>
<li>
		<code>no-referrer-when-downgrade</code>: قيمتها الافتراضية "full"، حيث تُرسل قيمة المحُيل دومًا عدا الحالة التي يُرسَل فيها الطلب من HTTPS إلى HTTP (إلى بروتوكول أقل أمانًا).
	</li>
	<li>
		<code>no-referrer</code>: لا يُرسَل المُحيل.
	</li>
	<li>
		<code>origin</code>: يُرسل الأصل فقط ضمن المُحيل وليس عنوان الصفحة المُحيلة الكامل، أي يُرسل العنوان على الشكل <code><a data-ss1634992164="1" href="http://site.com" ipsnoembed="true" rel="external nofollow">http://site.com</a></code> وليس على الشكل <code><a data-ss1634992164="1" href="http://site.com/path" ipsnoembed="true" rel="external nofollow">http://site.com/path</a></code>.
	</li>
	<li>
		<code>origin-when-cross-origin</code>: يُرسل العنوان الكامل للمحيل إلى المواقع ذات الأصل المشترك، بينما يُرسَل الأصل فقط إلى المواقع ذات الأصل المختلط.
	</li>
	<li>
		<code>same-origin</code>: يُرسل المُحيل كاملًا إلى المواقع التي تنتمي إلى نفس الأصل، ولايرُسل أبدًا إلى المواقع ذات الأصول المختلطة.
	</li>
	<li>
		<code>strict-origin</code>: يُرسل الأصل فقط وليس المُحيل كاملًا في الطلبات من HTTPS إلى HTTP.
	</li>
	<li>
		<code>strict-origin-when-cross-origin</code>: يُرسل المُحيل كاملًا إلى المواقع التي تنتمي إلى نفس الأصل، ويُرسل الأصل فقط إلى المواقع ذات الأصول المختلطة، عدا الحالة التي يُرسَل فيها الطلب من HTTPS إلى HTTP، فلا يُرسل شيء.
	</li>
	<li>
		<code>unsafe-url</code>: يُرسل عنوان المُحيل كاملًا، حتى في الحالة التي يُرسَل فيها الطلب من HTTPS إلى HTTP.
	</li>
</ul>
<p>
	يوضح الجدول التالي جميع الخيارات:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th style="text-align:center">
				القيمة
			</th>
			<th style="text-align:center">
				إلى نفس الأصل
			</th>
			<th style="text-align:center">
				إلى أصل مختلف
			</th>
			<th style="text-align:center">
				HTTPS→HTTP
			</th>
		</tr></thead>
<tbody>
<tr>
<td style="text-align:center">
				<code>"no-referrer"</code>
			</td>
			<td style="text-align:center">
				-
			</td>
			<td style="text-align:center">
				-
			</td>
			<td style="text-align:center">
				-
			</td>
		</tr>
<tr>
<td style="text-align:center">
				<code>no-referrer-when-downgrade</code> أو <code>""</code> وهي القيمة الافتراضية
			</td>
			<td style="text-align:center">
				كاملًا
			</td>
			<td style="text-align:center">
				كاملًا
			</td>
			<td style="text-align:center">
				-
			</td>
		</tr>
<tr>
<td style="text-align:center">
				<code>"origin"</code>
			</td>
			<td style="text-align:center">
				الأصل
			</td>
			<td style="text-align:center">
				الأصل
			</td>
			<td style="text-align:center">
				الأصل
			</td>
		</tr>
<tr>
<td style="text-align:center">
				<code>"origin-when-cross-origin"</code>
			</td>
			<td style="text-align:center">
				كاملًا
			</td>
			<td style="text-align:center">
				الأصل
			</td>
			<td style="text-align:center">
				الأصل
			</td>
		</tr>
<tr>
<td style="text-align:center">
				<code>"same-origin"</code>
			</td>
			<td style="text-align:center">
				كاملًا
			</td>
			<td style="text-align:center">
				-
			</td>
			<td style="text-align:center">
				-
			</td>
		</tr>
<tr>
<td style="text-align:center">
				<code>"strict-origin"</code>
			</td>
			<td style="text-align:center">
				الأصل
			</td>
			<td style="text-align:center">
				الأصل
			</td>
			<td style="text-align:center">
				-
			</td>
		</tr>
<tr>
<td style="text-align:center">
				<code>"strict-origin-when-cross-origin"</code>
			</td>
			<td style="text-align:center">
				كاملًا
			</td>
			<td style="text-align:center">
				الأصل
			</td>
			<td style="text-align:center">
				-
			</td>
		</tr>
<tr>
<td style="text-align:center">
				<code>"unsafe-url"</code>
			</td>
			<td style="text-align:center">
				كاملًا
			</td>
			<td style="text-align:center">
				كاملًا
			</td>
			<td style="text-align:center">
				كاملًا
			</td>
		</tr>
</tbody>
</table>
<p>
	لنفترض وجود صفحة بصلاحيات مدير، ولا ينبغي كشف عنوانها خارج نطاق الموقع، لذا فعند إرسال <code>fetch</code>، فسترسَل الترويسة <code>Referer</code> افتراضيًا مع عنوان صفحتنا كاملًا، عدا الحالة التي يُرسَل فيها الطلب من HTTPS إلى HTTP. حيث لا توجد أي ترويسة <code>Referer</code>، فإذا كان العنوان هو <code>Referer: <a data-ss1634992164="1" href="https://javascript.info/admin/secret/paths" ipsnoembed="true" rel="external nofollow">https://javascript.info/admin/secret/paths</a></code> مثلًا، وأردنا إرسال الأصل فقط وليس العنوان الكامل، فيمكن أن نرسل الخيار التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9733_15" style="">
<span class="pln">fetch</span><span class="pun">(</span><span class="str">'https://another.com/page'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  referrerPolicy</span><span class="pun">:</span><span class="pln"> </span><span class="str">"origin-when-cross-origin"</span><span class="pln"> </span><span class="com">// Referer: https://javascript.info</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يمكن وضع الخيار السابق لكل استدعاءات <code>fetch</code>، كما يمكن أيضًا دمجه في مكتبة JavaScript التي نستخدمها في مشروعنا، والتي تنفّذ كل الطلبات التي تستخدم <code>fetch</code>، ويقتصر الفرق الوحيد بينه وبين الخيار الافتراضي في أنه يرسِل الجزء الأصلي من عنوان الموقع المُحيل، مثلًا: <code><a data-ss1634992164="1" href="https://javascript.info" ipsnoembed="true" rel="external nofollow">https://javascript.info</a></code> ولا يُرسل المسار الكلي، وسنحصل على العنوان الكامل في الطلبات المُرسَلة إلى مواقع من نفس الأصل، فربما تكون مفيدةً لأغراض التنقيح.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>لا تُستخدم سياسة المحيل مع <code>fetch</code> فقط.</strong> فمن الممكن ضبط السياسة الافتراضية للصفحة كاملةً باستخدام <code>Referrer-Policy</code> في ترويسة HTTP، أو لرابط مفرد باستخدام <code>&lt;"a rel="noreferrer&gt;</code>.
	</p>
</blockquote>

<h2>
	الخيار mode
</h2>

<p>
	ويمثل هذا الخيار الحارس الذي يمنع الطلبات ذات الأصل المختلط التي تحدث فجأةً.
</p>

<ul>
<li>
		<code>cors</code>: وهي القيمة الافتراضية، وتسمح بالطلبات ذات الأصل المختلط كما ورد في فصل استخدام Fetch في الطلبات ذات الأصل المختلط.
	</li>
	<li>
		<code>same-origin</code>: يمنع استخدام الطلبات ذات الأصل المختلط.
	</li>
	<li>
		<code>no-cors</code>: يسمح فقط لطلبات الأصل المختلط الآمنة.
	</li>
</ul>
<p>
	قد تظهر أهمية هذا الخيار عندما يأتي العنوان القادم مع <code>fetch</code> من طرف ثالث، ونريد آليةً للحد من الإمكانات المسموحة للأصول المختلطة.
</p>

<h2>
	الخيار credentials
</h2>

<p>
	ويحدد ما إذا كان على <code>fetch</code> إرسال ملفات تعريف الارتباط cookies، وترويسات استيثاق HTTP مع الطلب.
</p>

<ul>
<li>
		<code>same-origin</code>: وهي القيمة الافتراضية، لا تُرسل الثبوتيات مع الطلبات ذات الأصول المختلطة.
	</li>
	<li>
		<code>include</code>: تُرسل الثبوتيات دومًا، ونحتاج إلى الترويسة <code>Access-Control-Allow-Credentials</code> من الخادم ذي الأصل المختلط لتتمكن جافا سكربت من الوصول إلى الاستجابة، وقد شرحنا ذلك في فصل استخدام Fetch في الطلبات ذات الأصل المختلط.
	</li>
	<li>
		<code>omit</code>: لا تُرسل الثبوتيات أبدًا، حتى للطلبات من الأصل نفسه.
	</li>
</ul>
<h2>
	الخيار cache
</h2>

<p>
	تستخدم طلبات <code>fetch</code> افتراضيًا ذاكرة HTTP المؤقتة المعيارية HTTP-cache، فهي تحترم الترويستين <code>Expires</code> و<code>Cache-Control</code>، وترسل الترويسة <code>If-Modified-Since</code> تمامًا كما تفعله طلبات HTTP النظامية.
</p>

<p>
	يسمح الخيار <code>cache</code> بتجاهل "HTTP-cache" أو يضبط استخدامه:
</p>

<ul>
<li>
		<code>default</code>: تستخدم <code>fetch</code> ترويسات وقواعد "HTTP-cache" المعيارية.
	</li>
	<li>
		<code>no-store</code>: يتجاهل الطلب قواعد "HTTP-cache" كليًا، وتصبح هذه القيمة افتراضيةً عند إرسال إحدى الترويسات التالية: <code>If-Modified-Since</code> أو <code>If-None-Match</code> أو <code>If-Unmodified-Since</code> أو <code>If-Match</code> أو <code>If-Range</code>.
	</li>
	<li>
		<code>reload</code>: لا يأخذ النتيجة من "HTTP-cache" -إن وجدت-، بل ينشر محتويات الذاكرة المؤقتة مع الاستجابة، إذا سمحت ترويسات الاستجابة بذلك.
	</li>
	<li>
		<code>no-cache</code>: يُنشئ طلبًا شرطيًا عند وجود استجابة مخزنة في الذاكرة المؤقتة، وطلبًا عاديًا في غير تلك الحالة، وينشر "HTTP-cache" مع الاستجابة.
	</li>
	<li>
		<code>force-cache</code>: يستخدم الاستجابة الموجودة في "HTTP-cache" حتى لو كانت قديمة، وسينشئ طلب HTTP نظاميًا إذا لم تحتوي على استجابة، كما سيسلك الطلب السلوك الطبيعي.
	</li>
	<li>
		<code>only-if-cached</code>: يستخدم الاستجابة الموجودة في "HTTP-cache" حتى لو كانت قديمةً، وسيرمي خطأً إذا لم تحتوي على استجابة، وتعمل فقط مع القيمة <code>same-origin</code> للخيار <code>mode</code>.
	</li>
</ul>
<h2>
	الخيار redirect
</h2>

<p>
	تخضع <code>fetch</code> بكل شفافية لإعادة التوجيه "HTTP-redirect" مثل الحالتين 301 (النقل النهائي لمحتوى) و302 (موجود ولكن يفضل الانتقال إلى العنوان الجديد).
</p>

<ul>
<li>
		<code>follow</code>: وهي القيمة الافتراضية، ويخضع الطلب عندها لحالات إعادة التوجيه.
	</li>
	<li>
		<code>error</code>: يرمي خطأً عند محاولة إعادة توجيه الطلب.
	</li>
	<li>
		<code>manual</code>: يسمح بالتعامل مع إعادة توجيه الطلب يدويًا، وسنحصل عندها على كائن استجابة خاص من النوع <code>"response.type="opaqueredirect</code>، وتكون قيمة خاصية الحالة <code>response.status</code> صفرًا، وكذلك قيم أغلب خصائصه.
	</li>
</ul>
<h2>
	الخيار integrity
</h2>

<p>
	يسمح هذا الخيار بالتحقق من مطابقة الاستجابة للقيم الاختبارية Checksum المحددة مسبقًا، كما هو محدد في <a data-ss1634992164="1" href="https://w3c.github.io/webappsec-subresource-integrity/" rel="external nofollow">التوصيفات</a>. وتُدعم دوال "hash" التالية: SHA-256 وSHA-384 وSHA-512، كما قد تتطلب بعض الاعتماديات وفقًا للمتصفح، فإذا كنا بصدد تنزيل ملف مثلًا، ونعلم أنّ القيمة الاختبارية له وفق SHA-256 هي "abcdef"، والتي ستكون أطول في الواقع، فيمكننا وضعها قيمةً للخيار <code>integrity</code> بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9733_17" style="">
<span class="pln">fetch</span><span class="pun">(</span><span class="str">'http://site.com/file'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  integrity</span><span class="pun">:</span><span class="pln"> </span><span class="str">'sha256-abcdef'</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	ستحسب <code>fetch</code> قيمة SHA-256 بنفسها وتوازنها مع القيمة التي وضعناها، وسترمي خطأً عند عدم تطابق القيمتين.
</p>

<h2>
	الخيار keepalive
</h2>

<p>
	يسمح هذا الخيار ببقاء الطلب فعالًا خارج الصفحة التي أنشأتها. لنفترض مثلًا أننا نجمع إحصائيات عن سلوك المستخدم الحالي لصفحتنا (عدد نقرات الفأرة وأجزاء الصفحة التي زارها) لتحليل تجربة المستخدم وتطويرها، ونرغب بحفظ البيانات على الخادم عندما يغادر هذا المستخدم الصفحة، حيث يمكن أن ننفذ ذلك باستخدام الحدث <code>window.onunload</code> بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9733_19" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">onunload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  fetch</span><span class="pun">(</span><span class="str">'/analytics'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
    body</span><span class="pun">:</span><span class="pln"> </span><span class="str">"statistics"</span><span class="pun">,</span><span class="pln">
    keepalive</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	لكن ستُغلق كل طلبات الشبكة المتعلقة بالمستند عند إزالته، وهنا تظهر أهمية الخيار <code>keepalive</code> الذي يخبر المتصفح بإبقاء الطلبات حيةً في الخلفية حتى بعد أن يغادر الزائر الصفحة، لأن هذا الخيار أساسي لاستمرار الطلب ونجاحه. لكن بالطبع هناك بعض التقييدات في استخدامه، والمتمثلة في الآتي:
</p>

<ul>
<li>
		لا يمكن إرسال أحجام بالميجابايت: لأن الحد الأعلى لحجم جسم الطلب مع خيار <code>keepalive</code> هو 64 كيلوبابت.
	</li>
	<li>
		إذا أردنا جمع إحصائيات كثيرةً عن الزائر، فلا بدّ من إرسالها بانتظام ضمن حزم متتالية، لكي لا تبقى الكثير من المعلومات التي لم ترسل بعد عند تنفيذ الطلب الأخير مع الحدث <code>onunload</code>.
	</li>
	<li>
		تطبق هذه التقييدات على كل الطلبات التي تحمل الخيار <code>keepalive</code> معًا، أي يمكن تنفيذ عدة طلبات من هذا النوع في الوقت نفسه، لكن يجب ألا يتجاوز مجموع أحجام أجسام هذه الطلبات حد 64 كيلوبايت.
	</li>
	<li>
		لا يمكن التعامل مع استجابة الخادم عند إزالة المستند، لذا سينجح استخدام <code>fetch</code> في مثالنا بوجود <code>keepalive</code>، لكن بالطبع لن تُنفَّذ الدوال اللاحقة.
	</li>
	<li>
		لن تظهر المشاكل في أغلب الأحيان عند إرسال بيانات مثل الإحصائيات، لأنّ الخادم سيقبل هذه البيانات وسيعيد غالبًا استجابةً فارغةً لطلبات مثل هذه.
	</li>
</ul>
<p>
	ترجمة -وبتصرف- للفصل <a data-ss1634992164="1" href="https://javascript.info/fetch-api" rel="external nofollow">Fetch: <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> من سلسلة <a href="https://javascript.info/" rel="external nofollow">The Modern JavaScript Tutorial</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق:  <a data-ss1634992164="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-fetch-%D9%85%D8%B9-%D8%A7%D9%84%D8%B7%D9%84%D8%A8%D8%A7%D8%AA-%D8%B0%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B5%D9%84-%D8%A7%D9%84%D9%85%D8%AE%D8%AA%D9%84%D8%B7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1296/" rel="">استخدام Fetch مع الطلبات ذات الأصل المختلط في جافاسكريبت </a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1297</guid><pubDate>Tue, 24 Aug 2021 15:08:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; Fetch &#x645;&#x639; &#x627;&#x644;&#x637;&#x644;&#x628;&#x627;&#x62A; &#x630;&#x627;&#x62A; &#x627;&#x644;&#x623;&#x635;&#x644; &#x627;&#x644;&#x645;&#x62E;&#x62A;&#x644;&#x637; Cross-Origin &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-fetch-%D9%85%D8%B9-%D8%A7%D9%84%D8%B7%D9%84%D8%A8%D8%A7%D8%AA-%D8%B0%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B5%D9%84-%D8%A7%D9%84%D9%85%D8%AE%D8%AA%D9%84%D8%B7-cross-origin-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1296/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/6119366e11a9e_-Fetch-----.png.289d53ea3f89768b95bdf787aa2e13ba.png" /></p>

<p>
	من المحتمل أن يخفق الطلب <code>fetch</code> المرسَل إلى موقع ويب آخر، مثلًا: ستخفق محاولة الحصول على <code><a data-ss1634991696="1" href="http://example.com" ipsnoembed="true" rel="external nofollow">http://example.com</a></code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_9" style="">
<span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  await fetch</span><span class="pun">(</span><span class="str">'http://example.com'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Failed to fetch</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لا بدّ من الإشارة أولًا إلى المفاهيم البنيوية لموضوعنا:
</p>

<ul>
<li>
		الأصل origin: وهو الثلاثية (نطاق ومنفذ وبروتوكول).
	</li>
	<li>
		الطلبات ذات الأصل المختلط cross-origin requests: وهي الطلبات المرسَلة إلى نطاق (أو نطاق فرعي) آخر أو عبر منفذ آخر أو باستخدام بروتوكول آخر، وتتطلب ترويسات خاصةً من الجانب البعيد.
	</li>
</ul>
<p>
	تدعى هذه السياسة "CROS" وهو اختصار للعبارة "Cross-Origin Resource Sharing"، وتعني مشاركة الموارد ذات الأصول المختلطة.
</p>

<h2>
	لماذا نحتاج إلى CROS؟ لمحة تاريخية موجزة
</h2>

<p>
	وُجِدت هذه السياسة لحماية الإنترنت من المخترقين، فلسنوات عديدة لم يكن مسموحًا لسكربت من موقع ما أن يصل إلى محتوى موقع آخر، حيث لا يمكن لسكربت مشبوه من الموقع <code>hacker.com</code> مثلًا الوصول إلى صندوق البريد الإلكتروني لمستخدم على الموقع <code>gmail.com</code>، مما أشعَر مستخدمي الإنترنت في ذلك الوقت بالأمان لعدم امتلاك جافاسكريبت JavaScript، أي توابع خاصة لتنفيذ طلبات عبر الشبكة، فقد كانت عبارةً عن لغة للتسلية وتزيين صفحات الويب، إلا أن مطوري الويب احتاجوا سريعًا إلى قوة أكبر للتحكم بالصفحات، فاخترعوا أساليب متنوعةً للالتفاف على محدودية اللغة وإرسال الطلبات إلى مواقع أخرى.
</p>

<h3>
	استخدام النماذج
</h3>

<p>
	لقد كانت إحدى طرق التواصل مع خادم آخر هي إرسال نموذج <code>&lt;form&gt;</code> إليه، وذلك باستخدام الإطارات <code>&lt;iframe&gt;</code> لإبقاء الزوار ضمن الصفحة نفسها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_11" style="">
<span class="pun">&lt;!--</span><span class="pln"> form target </span><span class="pun">--&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">iframe name</span><span class="pun">=</span><span class="str">"iframe"</span><span class="pun">&gt;&lt;/</span><span class="pln">iframe</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;!--</span><span class="pln"> a form could be dynamically generated and submited by </span><span class="typ">JavaScript</span><span class="pln"> </span><span class="pun">--&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">form target</span><span class="pun">=</span><span class="str">"iframe"</span><span class="pln"> method</span><span class="pun">=</span><span class="str">"POST"</span><span class="pln"> action</span><span class="pun">=</span><span class="str">"http://another.com/…"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span></pre>

<p>
	وهكذا تمكن الناس من إرسال طلبات GET/POST إلى مواقع أخرى دون وجود توابع لتنفيذ ذلك، لأنّ النماذج قادرة على إرسال البيانات إلى أي مكان، لكن لم يكن بالإمكان الحصول على الاستجابة لأنّ الوصول إلى محتويات الإطار <code>&lt;iframe&gt;</code> غير مسموح، ولنكون دقيقين؛ وُجِدت بعض الحيل للالتفاف على ذلك أيضًا، لكنها تطلبت سكربتًا خاصًا يوضع ضمن الإطار والصفحة، أي صار التواصل بينهما ممكنًا من الناحية التقنية.
</p>

<h3>
	استخدام السكربتات
</h3>

<p>
	اعتمدت إحدى الحيل المستخدَمة أيضًا على المعرّف <code>&lt;script&gt;</code>، إذ يمكن أن تكون قيمة الخاصية <code>src</code> لسكربت هي اسم أي نطاق أو موقع مثل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_13" style="">
<span class="pun">&lt;</span><span class="pln">script src</span><span class="pun">=</span><span class="str">"http://another.com/…"</span><span class="pun">&gt;</span></pre>

<p>
	وبالتالي يمكن تنفيذ سكربت أيًا كان مصدره، فإذا أراد موقع ما مثل <code>another.com</code> إتاحة أمكانية الوصول لبياناته، فسيستخدم البروتوكول "JSON with padding" واختصاره JSNOP، وإليك آلية عمله:
</p>

<p>
	لنفترض أننا نريد الوصول إلى بيانات الطقس على الموقع <code><a data-ss1634991696="1" href="http://another.com" ipsnoembed="true" rel="external nofollow">http://another.com</a></code> إنطلاقًا من موقعنا:
</p>

<ol>
<li>
		نعرّف في البداية دالةً عامةً لاستقبال البيانات ولتكن <code>gotWeather</code>.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_15" style="">
<span class="com">// صرح عن الدالة التي ستعالج البيانات المطلوبة</span><span class="pln">
 </span><span class="kwd">function</span><span class="pln"> gotWeather</span><span class="pun">({</span><span class="pln"> temperature</span><span class="pun">,</span><span class="pln"> humidity </span><span class="pun">})</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="pln">temperature</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">temperature</span><span class="pun">},</span><span class="pln"> humidity</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">humidity</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">}</span></pre>

<ol start="2">
<li>
		ننشئ <code>&lt;script&gt;</code>، وتكون قيمة الخاصية <code>src</code> فيه هي
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_18" style="">
<span class="pln">src</span><span class="pun">=</span><span class="str">"http://another.com/weather.json?callback=gotWeather"</span></pre>

<p>
	وللمستخدمين اسم الدالة العامة كقيمة للمعامل <code>callback</code> الخاص بالعنوان URL.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_20" style="">
<span class="pln">let script </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'script'</span><span class="pun">);</span><span class="pln">
script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">http</span><span class="pun">:</span><span class="com">//another.com/weather.json?callback=gotWeather`;</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span></pre>

<ol start="3">
<li>
		يوّلد الخادم البعيد <code>another.com</code> ديناميكيًا سكربتًا يستدعي الدالة <code>()gotWeather</code>بالبيانات التي يريدنا أن نحصل عليها.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_25" style="">
<span class="com">// ستبدو الاستجابة التي نتوقعها من الخادم كالتالي</span><span class="pln">
gotWeather</span><span class="pun">({</span><span class="pln">
  temperature</span><span class="pun">:</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln">
  humidity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">78</span><span class="pln">
</span><span class="pun">});</span></pre>

<ol start="4">
<li>
		عندما يُحمَّل السكربت الذي يولده الخادم ويُنفَّذ، ستُنفَّذ الدالة <code>gotWeather</code> ونحصل على البيانات.
	</li>
</ol>
<p>
	سيعمل الأسلوب السابق ولن يشكل خرقًا لأمن الموقع البعيد بسبب اتفاق كلا الطرفين على تبادل المعلومات بهذا الشكل، ولهذا لن تُعَدَّ العملية عندها اختراقًا، ولا زالت بعض الخدمات تتبع نفس الأسلوب في الوصول إلى البيانات البعيدة وتعمل حتى على المتصفحات القديمة جدًا.
</p>

<p>
	ظهرت بعد فترة من الزمن ضمن لغة JavaScript توابع الطلبات عبر الشبكة والتي ينفذها المتصفح، وقد رفضت الطلبات ذات الأصول المختلطة في البداية، إلا أنه سُمح باستخدامها نتيجة نقاشات طويلة، بشرط الحصول على سماحيات صريحة من الخادم لتنفيذ أي متطلبات، ويُعبَّر عنها من خلال ترويسات خاصة.
</p>

<h3>
	الطلبات الآمنة
</h3>

<p>
	هنالك نوعان من الطلبات ذات الأصل المختلط:
</p>

<ol>
<li>
		الطلبات الآمنة safe requests.
	</li>
	<li>
		بقية الأنواع.
	</li>
</ol>
<p>
	من السهل إنشاء الطلبات الآمنة لذلك سنبدأ بها، إذ يُعَد الطلب آمنًا إذا حقق الشرطين التاليين:
</p>

<ol>
<li>
		يستخدم <a data-ss1634991696="1" href="https://fetch.spec.whatwg.org/#cors-safelisted-method" rel="external nofollow">نوعًا آمنًا</a> مثل GET أو POST أو HEAD.
	</li>
	<li>
		يستخدم ترويسات آمنةً.
	</li>
</ol>
<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74592" data-ss1634991696="1" href="https://academy.hsoub.com/uploads/monthly_2021_08/cors_safe_request_01.png.9682513fc4153a986ed256382fee9297.png" rel=""><img alt="cors_safe_request_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74592" data-unique="k55rnihzj" src="https://academy.hsoub.com/uploads/monthly_2021_08/cors_safe_request_01.png.9682513fc4153a986ed256382fee9297.png"></a>
</p>

<p>
	ويسمح بالترويسات المخصصة التالية فقط:
</p>

<ul>
<li>
		<code>Accept</code>.
	</li>
	<li>
		<code>Accept-Language</code>.
	</li>
	<li>
		<code>Content-Language</code>.
	</li>
	<li>
		<code>Content-Type</code>: بحيث تحمل إحدى القيم التالية <code>application/x-www-form-urlencoded</code> أو <code>multipart/form-data</code> أو <code>text/plain</code>.
	</li>
</ul>
<p>
	وتُعَد بقية الطلبات "غير آمنة"، حيث لا تطابق الطلبات باستخدام <code>PUT</code> أو باستخدام الترويسة <code><abbr title="Application Programming Interface | واجهة برمجية">API</abbr>-Key</code> معايير الأمان السابقة مثلًا. ويكمن الفرق الجوهري في إمكانية تنفيذ الطلبات الآمنة باستخدام معرِّف النموذج <code>&lt;form&gt;</code> أو معرّف السكربت <code>&lt;script&gt;</code> دون الحاجة لأي توابع خاصة، وبالتالي ستكون أقدم الخوادم قادرةً على استقبالها.
</p>

<p>
	لا يمكن في المقابل استخدام الطلبات التي لا تمتلك ترويسات معياريةً مثل <code>DELETE</code> بهذه الطريقة، ولفترة طويلة لم تكن JavaScript قادرةً على استخدام هذا النوع من الطلبات، وهكذا سيفترض الخادم القديم أن هذه الطلبات قادمة من مصدر مخوّل بذلك، لأنه يتوقع أنّ صفحة الويب غير قادرة على إرسال هذه الطلبات.
</p>

<p>
	عندما نرسل طلبًا غير آمن، فسيرسل المتصفح طلبًا تمهيديَا preflight، سيسأل الخادم فيه إن كان سيوافق على طلبات ذات أصول مختلطةً أم لا، فإن لم يؤكد الخادم ذلك صراحةً من خلال الترويسات، فلن يُرسَل الطلب غير الآمن.
</p>

<h2>
	سياسة CROS للطلبات غير الآمنة
</h2>

<p>
	سيضيف المتصفح دائمًا الترويسة <code>Origin</code> إلى الطلب من الأصول المختلطة، فإذا طلبنا المورد <code><a data-ss1634991696="1" href="https://anywhere.com/request" ipsnoembed="true" rel="external nofollow">https://anywhere.com/request</a></code> من الموقع <code><a data-ss1634991696="1" href="https://javascript.info/page" ipsnoembed="true" rel="external nofollow">https://javascript.info/page</a></code> مثلًا؛ فستبدو الترويسات بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_27" style="">
<span class="pln">GET </span><span class="pun">/</span><span class="pln">request
</span><span class="typ">Host</span><span class="pun">:</span><span class="pln"> anywhere</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Origin</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//javascript.info</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	تحتوي الترويسة <code>origin</code> كما نرى الأصل (نطاق وبروتوكول ومنفذ) كما هو لكن دون مسار، ويمكن للخادم أن يتحقق من الترويسة <code>Origin</code>، فإن وافق على قبول هذا الطلب، فسيضيف ترويسةً خاصةً هي <code>Access-Control-Allow-Origin</code> إلى الاستجابة، وينبغي أن تحتوي الترويسة على الأصل المقبول (<code><a data-ss1634991696="1" href="https://javascript.info" ipsnoembed="true" rel="external nofollow">https://javascript.info</a></code> في حالتنا) أو رمز النجمة (*)، عندها سيكون الطلب ناجحًا وإلا فسيُعَد خاطئًا.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="74593" data-ss1634991696="1" href="https://academy.hsoub.com/uploads/monthly_2021_08/cors_unsafe_request_02.png.71a2e61b0352d71cb5b86ed86636381a.png" rel=""><img alt="cors_unsafe_request_02.png" class="ipsImage ipsImage_thumbnailed" data-fileid="74593" data-unique="hxt047rjp" src="https://academy.hsoub.com/uploads/monthly_2021_08/cors_unsafe_request_02.thumb.png.0c2d2a3c2980babc7b4c50e48be288f9.png"></a>
</p>

<p>
	يلعب المتصفح دور الوسيط الموثوق حيث:
</p>

<ol>
<li>
		يضمن إرسال الأصل الصحيح في الطلب ذي الأصول المختلطة.
	</li>
	<li>
		يتحقق من وجود السماحية <code>Access-Control-Allow-Origin</code> في الاستجابة، فإذا وُجدَت فسيسمح لشيفرة JavaScript بالوصول إلى الاستجابة وإلا ستخفق العملية وسيحدث خطأ.
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_29" style="">
<span class="lit">200</span><span class="pln"> OK
</span><span class="typ">Content</span><span class="pun">-</span><span class="typ">Type</span><span class="pun">:</span><span class="pln">text</span><span class="pun">/</span><span class="pln">html</span><span class="pun">;</span><span class="pln"> charset</span><span class="pun">=</span><span class="pln">UTF</span><span class="pun">-</span><span class="lit">8</span><span class="pln">
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Allow</span><span class="pun">-</span><span class="typ">Origin</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//javascript.info</span></pre>

<h2>
	ترويسات الاستجابة
</h2>

<p>
	افتراضيًا قد لا تتمكن JavaScript من الوصول إلا إلى الترويسات الآمنة للاستجابة عند إرسال طلبات ذات أصل مختلط، وهذه الترويسات هي:
</p>

<ul>
<li>
		<code>Cache-Control</code>.
	</li>
	<li>
		<code>Content-Language</code>.
	</li>
	<li>
		<code>Content-Type</code>.
	</li>
	<li>
		<code>Expires</code>.
	</li>
	<li>
		<code>Last-Modified</code>.
	</li>
	<li>
		<code>Pragma</code>.
	</li>
</ul>
<p>
	ويسبب الدخول إلى أي ترويسات أخرى خطأً.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		لاحظ أنه لا وجود للترويسة <code>Content-Length</code> في القائمة السابقة، والتي تحوي الحجم الكامل للاستجابة، فإذا كنا بصدد تنزيل شيء ما وأردنا تتبع النسبة المئوية لتقدم العملية، فلا بدّ من الحصول على سماحية أخرى للوصول إلى الترويسة.
	</p>
</blockquote>

<p>
	لمنح إمكانية الوصول إلى ترويسة الاستجابة، ينبغي أن يُرسل الخادم الترويسة <code>Access-Control-Expose-Headers</code>، والتي تتضمن قائمةً بأسماء الترويسات غير الآمنة التي يُفترض جعلها قابلةً للوصول، وتفصل بينها فاصلة، كالمثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_31" style="">
<span class="lit">200</span><span class="pln"> OK
</span><span class="typ">Content</span><span class="pun">-</span><span class="typ">Type</span><span class="pun">:</span><span class="pln">text</span><span class="pun">/</span><span class="pln">html</span><span class="pun">;</span><span class="pln"> charset</span><span class="pun">=</span><span class="pln">UTF</span><span class="pun">-</span><span class="lit">8</span><span class="pln">
</span><span class="typ">Content</span><span class="pun">-</span><span class="typ">Length</span><span class="pun">:</span><span class="pln"> </span><span class="lit">12345</span><span class="pln">
<abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">-</span><span class="typ">Key</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2c9de507f2c54aa1</span><span class="pln">
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Allow</span><span class="pun">-</span><span class="typ">Origin</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//javascript.info</span><span class="pln">
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Expose</span><span class="pun">-</span><span class="typ">Headers</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Content</span><span class="pun">-</span><span class="typ">Length</span><span class="pun">,</span><span class="pln"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">-</span><span class="typ">Key</span></pre>

<p>
	وبوجود ترويسة مثل <code>Access-Control-Expose-Headers</code> سيُسمح للسكربت بقراءة ترويستي الاستجابة <code>Content-Length</code> و<code><abbr title="Application Programming Interface | واجهة برمجية">API</abbr>-Key</code>.
</p>

<h2>
	الطلبات غير الآمنة
</h2>

<p>
	يمكن استخدام جميع طلبات HTTP مثل <code>PATCH</code> و<code>DELETE</code> وغيرها، وليس فقط <code>GET/POST</code>، ولم يتخيل أحد في السابق إمكانية تنفيذ صفحات الويب لهذه الطلبات، لذلك قد تجد بعض خدمات الويب التي تعامل الطلبات غير المعيارية مثل إشارة "بأنها طلبات مصدرها ليس المتصفح"، ويمكنها أن تأخذ هذا الأمر في الحسبان عندما تتحقق من حقوق الوصول، ولتفادي سوء الفهم، لن ينفِّذ المتصفح أي طلبات غير آمنة كانت قابلة للتنفيذ مباشرةً فيما مضى، وسيرسل طلبًا تمهيديًا preflight إلى الخادم لطلب الإذن، ويستخدم الطلب التمهيدي التابع <code>OPTIONS</code> دون جسم للطلب، وبوجود ترويستين:
</p>

<ul>
<li>
		الترويسة <code>Access-Control-Request-Method</code>: وتؤمن تابعًا للطلب غير الآمن.
	</li>
	<li>
		الترويسة <code>Access-Control-Request-Headers</code>: وتؤمن قائمةً بترويسات غير آمنة تفصل بينها فاصلة.
	</li>
</ul>
<p>
	إذا وافق الخادم على تنفيذ الطلبات، فسيرسل استجابةً بجسم فارغ ورمز الحالة 200، بالإضافة إلى الترويسات التالية:
</p>

<ul>
<li>
		<code>Access-Control-Allow-Origin</code>: ويجب أن تحمل القيمة (*) أو أصل الموقع الذي أرسل الطلب، مثل "https://javascript.info"، ليُسمح له بالوصول.
	</li>
	<li>
		<code>Access-Control-Allow-Methods</code>: ويجب أن تحتوي التوابع المسموحة.
	</li>
	<li>
		<code>Access-Control-Allow-Headers</code>: ويحب أن تضم قائمةً بالترويسات المسموحة.
	</li>
	<li>
		<code>Access-Control-Max-Age</code>: وهي ترويسة إضافية يمكنها تحديد الفترة الزمنية (ثوانٍ) للاحتفاظ بالإذن، لذا لن يكون على المتصفح إرسال طلبات تمهيدية للطلبات اللاحقة التي تحقق السماحيات الممنوحة سابقًا.
	</li>
</ul>
<p>
	لنلق نظرةً على آلية العمل خطوةً خطوةً، بمثال عن طلب <code>PATCH</code> ذي أصول مختلطة (والذي يُستخدَم غالبًا لتحديث البيانات):
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_34" style="">
<span class="pln">let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'https://site.com/service.json'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'PATCH'</span><span class="pun">,</span><span class="pln">
  headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">'Content-Type'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json'</span><span class="pun">,</span><span class="pln">
    </span><span class="str">'<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>-Key'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'secret'</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	لدينا ثلاثة أسباب لعدّ هذا الطلب غير آمن (ويكفي أحدها بالطبع):
</p>

<ul>
<li>
		الطلب هو <code>PATCH</code>.
	</li>
	<li>
		لاتحمل الترويسة <code>Content-Type</code> إحدى القيم: <code>application/x-www-form-urlencoded</code> أو <code>multipart/form-data</code> أو <code>text/plain</code>.
	</li>
	<li>
		وجود الترويسة <code><abbr title="Application Programming Interface | واجهة برمجية">API</abbr>-Key</code> غير الآمنة.
	</li>
</ul>
<h3>
	الخطوة 1: الطلب التمهيدي preflight
</h3>

<p>
	يرسل المتصفح بنفسه -قبل إرسال طلب غير آمنٍ كهذا- طلبًا تمهيديًا له الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_36" style="">
<span class="pln">OPTIONS </span><span class="pun">/</span><span class="pln">service</span><span class="pun">.</span><span class="pln">json
</span><span class="typ">Host</span><span class="pun">:</span><span class="pln"> site</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Origin</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//javascript.info</span><span class="pln">
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Request</span><span class="pun">-</span><span class="typ">Method</span><span class="pun">:</span><span class="pln"> PATCH
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Request</span><span class="pun">-</span><span class="typ">Headers</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Content</span><span class="pun">-</span><span class="typ">Type</span><span class="pun">,</span><span class="pln"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">-</span><span class="typ">Key</span></pre>

<ul>
<li>
		<code>OPTIONS</code>: تابع الطلب التمهيدي.
	</li>
	<li>
		‎<code>/service.json</code>: المسار، ويطابق مسار الطلب الرئيسي تمامًا.
	</li>
	<li>
		الترويسات الخاصة بالطلب ذي الأصل المختلط:
	</li>
	<li>
		<code>Origin</code>: أصل مُرسل الطلب.
	</li>
	<li>
		<code>Access-Control-Request-Method</code>: نوع الطلب
	</li>
	<li>
		<code>Access-Control-Request-Headers</code>:قائمة بترويسات غير آمنة تفصل بينها فاصلة.
	</li>
</ul>
<h3>
	الخطوة 2: الاستجابة للطلب التمهيدي
</h3>

<p>
	ينبغي أن يستجيب الخادم برمز الحالة 200 والترويسات التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1438_8" style="">
<span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Allow</span><span class="pun">-</span><span class="typ">Origin</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//javascript.info</span><span class="pln">
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Allow</span><span class="pun">-</span><span class="typ">Methods</span><span class="pun">:</span><span class="pln"> PATCH
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Allow</span><span class="pun">-</span><span class="typ">Headers</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Content</span><span class="pun">-</span><span class="typ">Type</span><span class="pun">,</span><span class="pln"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">-</span><span class="typ">Key</span><span class="pun">.</span></pre>

<p>
	تسمح هذه الاستجابة بالتواصل المستقبلي مع الخادم وإلا فسيقع خطأ.
</p>

<p>
	إذا كنت سترسل طلبات أو ترويسات من أنواع أخرى مستقبلًا، فمن المنطقي أن تطلب الإذن مسبقًا، وذلك بإضافتهم إلى القائمة أثناء الطلب التمهيدي، يوضح المثال التالي استجابةً يُسمح فيها باستخدام <code>PUT</code> و<code>DELETE</code> بالإضافة إلى ترويسات أخرى:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_38" style="">
<span class="lit">200</span><span class="pln"> OK
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Allow</span><span class="pun">-</span><span class="typ">Origin</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//javascript.info</span><span class="pln">
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Allow</span><span class="pun">-</span><span class="typ">Methods</span><span class="pun">:</span><span class="pln"> PUT</span><span class="pun">,</span><span class="pln">PATCH</span><span class="pun">,</span><span class="pln">DELETE
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Allow</span><span class="pun">-</span><span class="typ">Headers</span><span class="pun">:</span><span class="pln"> <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">-</span><span class="typ">Key</span><span class="pun">,</span><span class="typ">Content</span><span class="pun">-</span><span class="typ">Type</span><span class="pun">,</span><span class="typ">If</span><span class="pun">-</span><span class="typ">Modified</span><span class="pun">-</span><span class="typ">Since</span><span class="pun">,</span><span class="typ">Cache</span><span class="pun">-</span><span class="typ">Control</span><span class="pln">
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Max</span><span class="pun">-</span><span class="typ">Age</span><span class="pun">:</span><span class="pln"> </span><span class="lit">86400</span></pre>

<p>
	سيرى المتصفح الآن الطلب <code>PATCH</code> في قائمة الطلبات المسموحة <code>Access-Control-Allow-Methods</code>، كما سيرى الترويسة <code>Content-Type,<abbr title="Application Programming Interface | واجهة برمجية">API</abbr>-Key</code> في قائمة الترويسات المسموحة، لذا لن يتردد بإرسال الطلب الرئيسي المبني عليهما.
</p>

<p>
	إذا رأى المتصفح الترويسة <code>Access-Control-Max-Age</code> وقد أسندت إليها قيمة بالثواني، فسيحتفظ بالسماحيات التي مُنحت للطلب التمهيدي خلال هذه المدة الزمنية، أي سيُحتفظ بالسماحيات في مثالنا السابق فترة 86400 ثانية (أي يوم كامل)، ولن تحتاج الطلبات اللاحقة إلى نفس الخادم طلبات تمهيديةً أخرى، بفرض أنها تتلاءم مع السماحيات الممنوحة، وستُرسل مباشرةً.
</p>

<h3>
	الخطوة 3: الطلب الفعلي
</h3>

<p>
	يرسِل المتصفح الطلب الفعلي عندما ينجح الطلب التمهيدي، وتُنفَّذ العملية يطريقة مماثلة لإرسال طلب آمن.
</p>

<p>
	سيتضمن الطلب الرئيسي الترويسة <code>Origin</code> (لأنه طلب ذو أصل مختلط):
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_40" style="">
<span class="pln">PATCH </span><span class="pun">/</span><span class="pln">service</span><span class="pun">.</span><span class="pln">json
</span><span class="typ">Host</span><span class="pun">:</span><span class="pln"> site</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Content</span><span class="pun">-</span><span class="typ">Type</span><span class="pun">:</span><span class="pln"> application</span><span class="pun">/</span><span class="pln">json
<abbr title="Application Programming Interface | واجهة برمجية">API</abbr></span><span class="pun">-</span><span class="typ">Key</span><span class="pun">:</span><span class="pln"> secret
</span><span class="typ">Origin</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//javascript.info</span></pre>

<h3>
	الخطوة 4: الاستجابة الفعلية
</h3>

<p>
	لا بدّ للخادم من إضافة الترويسة <code>Access-Control-Allow-Origin</code> إلى الاستجابة الرئيسية، ولن يُعفيه الطلب التمهيدي الناجح من هذه المهمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_43" style="">
<span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Allow</span><span class="pun">-</span><span class="typ">Origin</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//javascript.info</span></pre>

<p>
	تستطيع بعد ذلك قراءة استجابة الخادم الفعلية.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		لاحظ أن الطلب التمهيدي يجري "خلف الستار"، ولن تراه JavaScript، وكل ما ستحصل عليه هو الاستجابة إلى الطلب الرئيسي، أو الخطأ إذا لم يأذن الخادم بإرسال الطلب.
	</p>
</blockquote>

<p>
	يمكن لشيفرة JavaScript الآن قراءة الاستجابة على الطلب الفعلي.
</p>

<h2>
	الثبوتيات Credentials
</h2>

<p>
	لا تحضر الطلبات ذات الأصل المختلط التي تنتج عن شيفرة JavaScript أية ثبوتيات (ملفات تعريف الارتباط cookies أو استيثاق HTTP)، وهذا ليس أمرًا شائعًا في طلبات HTTP، فعند إرسال طلب HTTP إلى الموقع <code><a data-ss1634991696="1" href="http://site.com" ipsnoembed="true" rel="external nofollow">http://site.com</a></code> مثلَا، فسيحمل الطلب جميع ملفات تعريف الارتباط الموجودة في نطاق المُرسِل، لكن الطلبات ذات الأصل المختلط الناتجة عن JavaScript تُمثّل استثناءً، حيث لن يُرسل الأمر <code>(fetch(http://another.com</code> أي ملفات تعريف ارتباط حتى تلك التي تنتمي إلى النطاق <code>another.com</code>.
</p>

<p>
	لكن لماذا؟ لأنّ الطلبات التي تُزوَّد بثبوتيات أقوى بكثير، إذ يمكن لشيفرة JavaScript -إن سُمح لها- أن تعمل بكامل إمكانياتها بالنيابة عن المستخدم، وأن تصل إلى معلومات حساسة بالاستفادة من هذه الثبوتيات.
</p>

<p>
	لكن هل يثق الخادم بسكربت ما إلى هذا الحد؟ إن كان الأمر كذلك، فلا بدّ من السماح صراحةً بالطلبات التي تحمل ثبوتيات من خلال ترويسة إضافية، حيث سنحتاج إلى إضافة الخيار <code>credentials: "include"</code> عند إرسال الثبوتيات مع <code>fetch</code>بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_45" style="">
<span class="pln">fetch</span><span class="pun">(</span><span class="str">'http://another.com'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  credentials</span><span class="pun">:</span><span class="pln"> </span><span class="str">"include"</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يمكن الآن إرسال ملفات تعريف الارتباط التي تنتمي إلى <code>another.com</code> عبر الطلب <code>fetch</code> إلى الموقع الهدف، وينبغي على الخادم إذا وافق على قبول الثبوتيات، إضافة الترويسة <code>Access-Control-Allow-Credentials: true</code> إلى استجابته بالإضافة إلى الترويسة <code>Access-Control-Allow-Origin</code>، مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2411_48" style="">
<span class="lit">200</span><span class="pln"> OK
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Allow</span><span class="pun">-</span><span class="typ">Origin</span><span class="pun">:</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//javascript.info</span><span class="pln">
</span><span class="typ">Access</span><span class="pun">-</span><span class="typ">Control</span><span class="pun">-</span><span class="typ">Allow</span><span class="pun">-</span><span class="typ">Credentials</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span></pre>

<p>
	لاحظ أنه يُمنع استخدام النجمة (*) كقيمة للترويسة <code>Access-Control-Allow-Origin</code> في الطلبات التي تحمل ثبوتيات، إذ لا بدّ -كما نرى في المثال السابق- من تحديد الأصل بدقة، وهذا معيار إضافي للتأكد من أنّ الخادم يعرف تمامًا الجهة التي يثق بها لتنفيذ طلبات مثل هذه.
</p>

<h2>
	خلاصة
</h2>

<p>
	هنالك نوعان من الطلبات ذات الأصول المختلطة من وجهة نظر المتصفح: آمنة وغير آمنة.
</p>

<p>
	لا بدّ للطلبات الآمنة من تحقيق الشرطين التاليين:
</p>

<ol>
<li>
		أن تستخدم <a data-ss1634991696="1" href="https://fetch.spec.whatwg.org/#cors-safelisted-method" rel="external nofollow">نوعًا آمنًا</a> مثل: GET أو POST أو HEAD.
	</li>
	<li>
		أن تستخدم الترويسات الآمنة التالية:
		<ul>
<li>
				<code>Accept</code>
			</li>
			<li>
				<code>Accept-Language</code>
			</li>
			<li>
				<code>Content-Language</code>
			</li>
			<li>
				<code>Content-Type</code>: وتحمل إحدى القيم <code>application/x-www-form-urlencoded</code> أو <code>multipart/form-data</code> أو <code>text/plain</code>.
			</li>
		</ul>
</li>
</ol>
<p>
	الفرق الجوهري هو أن الطلبات الآمنة ومنذ وقت طويل، تُنفَّذ باستخدام معرِّف النموذج <code>&lt;form&gt;</code> أو معرِّف السكربت <code>&lt;script&gt;</code>، بينما مُنعت المتصفحات من تنفيذ الطلبات غير الآمنة لفترة طويلة.
</p>

<p>
	يظهر هذا الفرق عمليًا في إمكانية إرسال الطلبات الآمنة مع الترويسة <code>Origin</code> مباشرةً، بينما يحتاج المتصفح إلى إرسال طلب تمهيدي preflight عند إرسال الطلبات غير الآمنة، يطلب فيها إذنًا من الخادم.
</p>

<p>
	<strong>للطلبات الآمنة</strong>:
</p>

<ul>
<li>
		يرسل المتصفح الترويسة مع الأصل <code>Origin</code>.
	</li>
	<li>
		بالنسبة للطلبات التي لا تحمل ثبوتيات (لا ترسَل الثبوتيات بشكل افتراضي) لا بدّ أن يضبط الخادم ما يلي:
	</li>
	<li>
		<code>Access-Control-Allow-Origin</code>: على القيمة (*) أو نفس قيمة الأصل <code>Origin</code>.
	</li>
	<li>
		بالنسبة للطلبات التي تحمل ثبوتيات لا بدّ أن يضبط الخادم:
	</li>
	<li>
		<code>Access-Control-Allow-Origin</code>: على نفس قيمة الأصل <code>Origin</code>.
	</li>
	<li>
		<code>Access-Control-Allow-Credentials</code>: على القيمة "true".
	</li>
</ul>
<p>
	ولمنح JavaScript الوصول إلى ترويسات الاستجابة عدا <code>Cache-Control</code> و <code>Content-Language</code> و<code>Content-Type</code> و<code>Expires</code> و<code>Last-Modified</code> و<code>Pragma</code>، فلا بدّ أن يضع الخادم الترويسة التي يُسمح بالوصول إليها ضمن الترويسة <code>Access-Control-Expose-Headers</code>.
</p>

<p>
	<strong>يُرسل المتصفح طلبًا تمهيديًا قبل الطلب الفعلي عند إرسال طلبات غير آمنة:</strong>
</p>

<ul>
<li>
		يرسل المتصفح الطلب <code>OPTIONS</code> إلى نفس العنوان الذي سيرسِل إليه الطلب الفعلي مزوّدًا بالترويسات التالية:
	</li>
	<li>
		<code>Access-Control-Request-Method</code>: ويحمل نوع الطلب.
	</li>
	<li>
		<code>Access-Control-Request-Headers</code>: ويحمل قائمةً بترويسات غير آمنة يُطلب الإذن باستخدامها.
	</li>
	<li>
		يستجيب الخادم برمز الحالة 200، وبالترويسات التالية:
	</li>
	<li>
		<code>Access-Control-Allow-Methods</code>: تضم قائمةً بأنواع الطلبات المسموحة.
	</li>
	<li>
		<code>Access-Control-Allow-Headers</code>: تضم قائمةً بالترويسات المسموحة.
	</li>
	<li>
		<code>Access-Control-Max-Age</code>: وتحتوي على قيمة تمثل الفترة الزمنية (مقدرةً بالثواني) التي يُحتفظ فيها بالسماحيات.
	</li>
	<li>
		يُرسَل الطلب الفعلي بعد ذلك، وتُطبق خطوات إرسال الطلب الآمن.
	</li>
</ul>
<p>
	ترجمة -وبتصرف- للفصل <a data-ss1634991696="1" href="https://javascript.info/fetch-crossorigin" rel="external nofollow">Fetch: Cross-origin Requests</a> من سلسلة <a href="https://javascript.info/" rel="external nofollow">The Modern JavaScript Tutorial</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a data-ss1634991696="1" href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AA%D8%A8%D8%B9-%D8%AA%D9%82%D8%AF%D9%85-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%86%D8%B2%D9%8A%D9%84-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-fetch-%D9%88%D8%A5%D9%84%D8%BA%D8%A7%D8%A1-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A9-fetch-r1295/" rel="">تتبع تقدم عملية التنزيل باستخدام Fetch وإلغاء العملية Fetch </a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1296</guid><pubDate>Sat, 21 Aug 2021 15:06:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629; &#x63A;&#x64A;&#x631; &#x627;&#x644;&#x645;&#x62A;&#x632;&#x627;&#x645;&#x646;&#x629; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1304/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/6129c82e8d5fe_--.png.3baaab311decf0d8d833e66a0618fe7f.png" /></p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		قد يدرك المتأني بعض حاجته، وقد يكون مع المستعجل الزلل.
	</p>
</blockquote>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="75594" href="https://academy.hsoub.com/uploads/monthly_2021_08/chapter_picture_11.jpg.02db73b8fbd9e3a3634b9d602d53112f.jpg" rel=""><img alt="chapter_picture_11.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="75594" data-unique="o1bfg9c89" src="https://academy.hsoub.com/uploads/monthly_2021_08/chapter_picture_11.jpg.02db73b8fbd9e3a3634b9d602d53112f.jpg"></a>
</p>

<p>
	معالج الحاسوب هو تلك الرقاقة المركزية بين دارات الحاسوب المنفِّذة للخطوات الصغيرة التي تتشكل منها البرامج، وما كانت البرامج التي رأيناها حتى الآن إلا أشياء تبقيه مشغولًا إلى أن تنهي مهامها، كما تعتمد سرعة تنفيذ تعليمة برمجية مثل حلقة تكرارية تعالج بيانات على سرعة المعالج نفسه.
</p>

<p>
	تتواجد مع هذا عدة برامج تتفاعل مع أشياء خارج المعالج، فقد تتواصل عبر شبكة حاسوبية، أو تطلب بيانات من قرص صلب مثلًا، والذي قد يكون أبطأ من الحصول على المعلومة نفسها من القرص الصلب، وحين يحدث مثل ذلك، فعلينا معرفة ما إذا كان يليق بنا ترك المعالج يقبع ساكنًا دون عمل، ولهذا لا بد من وجود عمل يمكنه تنفيذه في ذلك الوقت، كما يعالج نظام تشغيلك ذلك جزئيًا، حيث سيجعل المعالج يتنقل بين عدة برامج عاملة، لكن هذا لا ينفع حين نريد لبرنامج واحد العمل بينما ينتظر ردًا من الشبكة.
</p>

<h2>
	عدم التزامن Asynchronicity
</h2>

<p>
	تحدث الأشياء في نموذج البرمجة المتزامنة synchronous واحدةً تلو الأخرى، فلا تعيد دالة تنفِّذ إجراءً يعمل على المدى الطويل إلا إذا وقع الإجراء وصار بإمكانه إعادة النتيجة، وبالتالي سيُوقِف هذا برنامجك إلى حين تنفيذ ذلك الإجراء؛ أما النموذج غير المتزامن asynchronous فسيسمح بحدوث عدة أشياء في الوقت نفسه، حيث سيستمر برنامجك بالعمل إذا بدأت بإجراء ما، كما سيُبلَّغ البرنامج حين ينتهي الإجراء ويحصل على وصول access إلى النتيجة مثل قراءة البيانات من القرص.
</p>

<p>
	نستطيع موازنة البرمجة المتزامنة وغير المتزامنة باستخدام مثال بسيط، وهو برنامج يجلب مَوردَين من الشبكة ثم يوازن النتيجة، بحيث تُعيد دالة الطلب في البيئة المتزامنة بعد إنهاء عملها. وأسهل طريقة لتنفيذ ذلك الإجراء هي جعل الطلبات واحدًا تلو الآخر، وهذا له تبعته، إذ لن يبدأ الطلب الثاني إلا حين ينتهي الطلب الأول، كما سيكون الوقت الكلي المستغرق مساويًا وقت رد الطلبَين معًا.
</p>

<p>
	يكون حل تلك المشكلة في النظام المتزامن ببدء خيوط تحكم control threads إضافية، وقد يكون الخيط هنا برنامجًا آخرًا عاملًا قد يتزامن تنفيذه مع برامج أخرى بواسطة نظام التشغيل، وبما أنّ أغلب الحواسيب الحديثة تحتوي على عِدة معالجات، فقد تعمل الخيوط المتعددة في الوقت نفسه، بل قد تعمل على معالجات مختلفة، فيبدأ خيط جديد الطلب الثاني، ثم ينتظر الخَيطان عودة نتائجهما، وبعد ذلك يُعيدا المزامنة من أجل دمج النتائج.
</p>

<p>
	تمثِّل الخطوط السميكة thick lines في المخطط التالي الوقت الذي ينفقه البرنامج في العمل العادي، في حين تمثِّل الخطوط الرفيعة thin lines وقت انتظار الشبكة، كما يكون الوقت الذي تأخذه الشبكة في النموذج المتزامن جزءًا من الخط الزمني timeline لخط تحكم ما؛ أما في النموذج غير المتزامن، فيتسبب بدء إجراء شبكة في حدوث انقسام split في الخط الزمني، كما يستمر البرنامج الذي ابتدأ الحدث بالعمل، ويقع الحدث معه أيضًا، ثم ينبه البرنامج حين ينتهي.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="75593" href="https://academy.hsoub.com/uploads/monthly_2021_08/control-io.png.239e5552b9f06b2c34daf5273f948d6f.png" rel=""><img alt="control-io.png" class="ipsImage ipsImage_thumbnailed" data-fileid="75593" data-unique="bb78un684" src="https://academy.hsoub.com/uploads/monthly_2021_08/control-io.png.239e5552b9f06b2c34daf5273f948d6f.png"></a>
</p>

<p>
	هناك طريقة أخرى تصف الفرق بأنّ انتظار انتهاء الأحداث في النموذج المتزامن أمر ضمني implicit، بينما يكون صريحًا explicit وتحت تحكمنا في النموذج غير المتزامن.
</p>

<p>
	تتقاطع اللاتزامنية مع كلا الطريقتين، فهي تسهل التعبير عن البرامج التي لا تتوافق مع النموذج الخطي للتحكم، في حين قد تصعِّب التعبير عن البرامج التي تتبع النموذج الخطي، وسنرى بعض الطرق للنظر في تلك الغرابة في هذا المقال لاحقًا.
</p>

<p>
	تجعل المنصَّتان الهامتان لجافاسكربت -أي المتصفحات وNode.js- العمليات التي تستغرق وقتًا غير متزامنة، بدلًا من الاعتماد على الخيوط، وذلك جيد بما أنّ البرمجة بالخيوط صعبة جدًا بسبب صعوبة فهم مهمة البرنامج حين ينفِّذ أمورًا كثيرةً في وقت واحد.
</p>

<h2>
	تقنية الغراب
</h2>

<p>
	تستطيع الغربان استخدام الأدوات والتخطيط وتذكر الأشياء، وإيصال هذه الأشياء فيما بينهم، إذ أن شأنها في ذلك شأن غيرها من المخلوقات.
</p>

<p>
	لكن ما لا يعلمه الكثير عنها أنها قادرة على أمور أكثر من ذلك، إذ تُنشئ العديد من أسراب الغربان أجهزةً حاسوبيةً حيويةً مثلًا، فهي ليست إلكترونية مثل التي نصنعها، لكنها تتكون من حشرات دقيقة أشبه بالنمل الأبيض، إذ تُطوِّر معها علاقةً تكافليةً بحيث تزودها الغربان بالطعام مقابل أن تبني الحشرات تلك المستعمرات المعقدة وتشغلها، لتُجري حسابات لاحقًا بمساعدة كائنات حية داخلها.
</p>

<p>
	تقع مثل تلك المستعمرات عادةً في أعشاش كبيرة، حيث تعمل الطيور والحشرات معًا فيها لبناء هياكل طينية بصلية الشكل مخفية داخل أغصان الأعشاش تعيش فيها الحشرات وتعمل، وتَستخدِم تلك الآلات إشارات ضوئيةً للتواصل مع الأجهزة الأخرى، إذ تُدخِل الغربان قطعًا عاكسةً للضوء في سيقان خاصة بالتواصل، ثم توجه الحشرات تلك السيقان لعكس الضوء للأعشاش الأخرى، وهذا يعني أنّ الأعشاش التي بينها مجال بصري مفتوح هي التي تستطيع التواصل فقط.
</p>

<p>
	رسم أحد الخبراء خارطةً لشبكة أعشاش الغربان في قرية إييغ-سوغ-امبي Hières-sur-Amby الفرنسية، بحيث توضِّح أماكن الأعشاش واتصالاتها، كما في الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="75592" href="https://academy.hsoub.com/uploads/monthly_2021_08/Hieres-sur-Amby.png.739434e06d205909f44348f1a66f9f9c.png" rel=""><img alt="Hieres-sur-Amby.png" class="ipsImage ipsImage_thumbnailed" data-fileid="75592" data-unique="1thto5ctd" src="https://academy.hsoub.com/uploads/monthly_2021_08/Hieres-sur-Amby.png.739434e06d205909f44348f1a66f9f9c.png"></a>
</p>

<p>
	سنكتب في هذا المقال بعض الدوال الشبكية البسيطة لمساعدة الغربان على التواصل.
</p>

<h2>
	ردود النداء Callbacks
</h2>

<p>
	يمكن النظر إلى البرمجة غير المتزامنة على أنها تجعل الدوال التي تنفِّذ إجراءً بطيئًا تأخذ وسيطًا إضافيًا يكون دالة رد نداء callback function، حيث يبدأ الإجراء، ثم تُستدعى دالة رد النداء بالنتيجة حين ينتهي.
</p>

<p>
	لدينا مثلًا دالة <code>setTimeout</code> المتاحة في المتصفحات وNode.js على سواء، إذ تنتظر زمنًا بعينه يقاس بالميلي ثانية، ثم تَستدعي الدالة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_15" style="">
<span class="pln">setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Tick"</span><span class="pun">),</span><span class="pln"> </span><span class="lit">500</span><span class="pun">);</span></pre>

<p>
	لا يُعَدالانتظار عملًا مهمًا، لكن قد يكون مفيدًا حين نفعل أمرًا مثل تحديث رسم تحريكي أو التحقق من استغراق شيء وقتًا أطول من الوقت المسموح به، ويعني تنفيذ عدة أحداث غير متزامنة متتالية باستخدام ردود النداء؛ أنه علينا الاستمرار بتمرير دوال جديدة لمعالجة استمرارية الحوسبة بعد الحدث.
</p>

<p>
	تحتوي أغلب حواسيب أعشاش الغربان على حوصلة bulb تخزين بيانات طويل الأمد، حيث تُحفَر أجزاء المعلومات في الأغصان كي تُستخدَم لاحقًا، ويستغرق ذلك الحفر أو إيجاد جزء من البيانات وقتًا، لذا فإن واجهة التخزين طويل الأمد غير متزامنة، كما تَستخدِم دوال رد نداء.
</p>

<p>
	تخزِّن حواصل التخزين أجزاءً من بيانات JSON القابلة للتشفير تحت أسماء، وقد يخزِّن الغراب معلومات عن المكان الذي فيه الطعام المخبأ تحت اسم <code>"food caches"</code>، والذي قد يحمل مصفوفةً من الأسماء التي تشير إلى أجزاء أخرى من البيانات التي تصف الذاكرة المؤقتة الحقيقية، كما سيشغل الغراب مثل الشيفرة التالية للبحث عن مخزون طعام في حواصل التخزين storage bulbs في عش "Big Oak":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_17" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">bigOak</span><span class="pun">}</span><span class="pln"> from </span><span class="str">"./crow-tech"</span><span class="pun">;</span><span class="pln">

bigOak</span><span class="pun">.</span><span class="pln">readStorage</span><span class="pun">(</span><span class="str">"food caches"</span><span class="pun">,</span><span class="pln"> caches </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let firstCache </span><span class="pun">=</span><span class="pln"> caches</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
  bigOak</span><span class="pun">.</span><span class="pln">readStorage</span><span class="pun">(</span><span class="pln">firstCache</span><span class="pun">,</span><span class="pln"> info </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">info</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يُعَدّ هذا النسق من البرمجة قابلًا للتطبيق، لكنه يزيد من مستوى الإزاحة مع كل حدث غير متزامن، وذلك لأننا سنصل إلى دالة أخرى تفعل أمورًا أعقد من التي بين أيدينا لا محالة، مثل تنفيذ أحداث متعددة في الوقت نفسه، إذ سيكون الأمر غير مألوف.
</p>

<p>
	تُبنى حواسيب الغربان العشيَّة لتتواصل فيما بينها باستخدام أزواج من طلب-رد request-response، وهذا يعني أنّ أحد الأعشاش سيرسل رسالةً إلى عش آخر، ثم يرد الثاني مباشرةً مؤكدًا استلام الرسالة، وربما يرد أيضًا على الرسالة في الوقت نفسه، كما تُوسَم كل رسالة بنوع يحدد كيفية معالجتها، إذ تستطيع شيفرتنا تعريف المعالِجات handlers لكل نوع من أنواع الطلبات، ويُستدعى المعالج متى ما دخل ذلك الطلب لإنتاج رد.
</p>

<p>
	توفِّر الواجهة التي صدَّرتها وحدة <code>‎"./crow-tech"‎</code> دوالًا مبنيةً على ردود النداء من أجل التواصل، كما تحتوي الأعشاش على التابع <code>send</code> الذي يرسل الطلب، إذ يتوقع اسم العش الهدف ونوع الطلب ومحتواه على أساس أول ثلاثة وسائط، ثم يتوقع دالةً لتُستدعى حين يأتي الرد على أساس وسيط رابع وأخير.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_19" style="">
<span class="pln">bigOak</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="str">"Cow Pasture"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"note"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Let's caw loudly at 7PM"</span><span class="pun">,</span><span class="pln">
            </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Note delivered."</span><span class="pun">));</span></pre>

<p>
	لكن يجب تعريف نوع طلب أولًا باسم <code>"note"</code> لتمكين الأعشاش من استقبال ذلك الطلب، كما يجب أن تعمل الشيفرة التي تعالج الطلبات على جميع الأعشاش التي تستطيع استقبال رسالة من هذا النوع وليس على حاسوب العش فقط، حيث سنفترض أنّ الغراب سيطير هنا ليثبِّت شيفرة المعالج تلك على جميع الأعشاش.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_21" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">defineRequestType</span><span class="pun">}</span><span class="pln"> from </span><span class="str">"./crow-tech"</span><span class="pun">;</span><span class="pln">

defineRequestType</span><span class="pun">(</span><span class="str">"note"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">,</span><span class="pln"> source</span><span class="pun">,</span><span class="pln"> done</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">nest</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln"> received note</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">content</span><span class="pun">}`);</span><span class="pln">
  done</span><span class="pun">();</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تُعرِّف الدالة <code>defineRequestType</code> نوعًا جديدًا من الطلبات، كما يضيف المثال الدعم لطلبات <code>"note"</code> التي ترسِل تذكرة note إلى العش المعطى، ويَستدعي تطبيقنا <code>console.log</code> كي نستطيع توكيد وصول الطلب، كما تحتوي الأعشاش على الخاصية <code>name</code> التي تحمل أسماءها؛ أما الوسيط الرابع المعطى للمعالج فهو <code>done</code>، وهي دالة رد نداء يجب أن تستدعي عند انتهاء الطلب، فإذا استخدمنا قيمة الإعادة للمعالج على أساس قيمة رد، فهذا يعني عدم استطاعة معالج الطلب تنفيذ إجراءات غير متزامنة بنفسه، فالدالة التي تنفذ مهامًا غير متزامنة تُعيد قبل انتهاء العمل، كما تكون قد جهزت رد نداء عند انتهاءها، لذا فنحن نحتاج إلى آلية غير متزامنة، وهي دالة رد نداء أخرى في حالتنا لترسل إشعارًا حين يتاح ردًا.
</p>

<p>
	قد يكون اللاتزامن معديًا contagious نوعًا ما، فأيّ دالة تستدعي دالةً أخرى تعمل بأسلوب غير متزامن يجب أن تكون هي نفسها غير متزامنة، ومستخدمةً لرد نداء أو آليةً مشابهةً لتسليم نتيجتها؛ أما استدعاء رد النداء ففيه تفصيل أكثر من مجرد إعادة قيمة ما، كما يكون عرضةً للأخطاء، لذا فإنّ الحاجة إلى هيكلة أجزاء كبيرة من برنامجنا بهذه الطريقة ليست الأسلوب الأمثل.
</p>

<h2>
	الوعود
</h2>

<p>
	يسهل العمل مع المفاهيم المجردة حين يمكن تمثيلها بقيم، وفي حالة الإجراءات غير المتزامنة سنستطيع إعادة كائن يمثل الحدث المستقبلي، بدلًا من إعداد دالة لتُستدعى في نقطة ما في المستقبل، وهذه هي وظيفة الصنف <code>Promise</code>، فالوعد هو إجراء غير متزامن قد يحدث عند نقطة ما ويعطينا قيمة، ويستطيع إبلاغ أيّ أحد يريد إشعاره حين تكون القيمة متاحةً.
</p>

<p>
	يُعَدّ استدعاء <code>Promise.resolve</code> أسهل طريقة لإنشاء وعد، وهي دالة تضمن أنّ القيمة التي تعطيها إياها ستغلَّف داخل وعد، فإذا كان هي نفسها وعدًا، فستعاد ببساطة، وإلا فستحصل على وعد ينتهي مباشرةً بقيمتك على أساس نتيجة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_23" style="">
<span class="pln">let fifteen </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="lit">15</span><span class="pun">);</span><span class="pln">
fifteen</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">value </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Got</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">value</span><span class="pun">}`));</span><span class="pln">
</span><span class="com">// → Got 15</span></pre>

<p>
	نستطيع استخدام التابع <code>then</code> للحصول على وعد، إذ يسجل دالة رد نداء لتُستدعى حين ينتهي الوعد وينتج قيمةً، كما نستطيع إضافة عدة ردود نداء إلى وعد واحد، إذ ستُستدعى جميعًا حتى لو أضفتها بعد انتهاء الوعد، ومع هذا فلا زال التابع <code>then</code> يحتوي مزيدًا من الخصائص، إذ يُعيد وعدًا آخرًا ينتهي بالقيمة التي تعيدها دالة المعالِج، أو إذا أعاد ذلك وعدًا، فسينتظر ذلك الوعد وينتهي مُخرجًا نتيجته.
</p>

<p>
	يُعَدّ النظر للوعود على أنها جهاز لنقل القيم إلى بيئة غير متزامنة مفيدًا، فالقيمة العادية موجودة والقيمة الموعودة promised value قد تكون موجودة، كما قد تظهر في نقطة ما في المستقبل، كما تتصرف الحسابات المعرَّفة بالوعود على مثل هذه القيم المغلَّفة wrapped values وتنفَّذ تنفيذًا غير متزامن عندما تكون القيم متاحة.
</p>

<p>
	نستطيع استخدام <code>Promise</code> على أساس باني لكي ننشئ وعدًا، لكن هذا سيكون له واجهة غريبة نوعًا ما، إذ يتوقع الباني دالةً على أساس وسيط ويستدعيها مباشرةً، ثم يمرر إليها دالةً تستطيع استخدامها لحل الوعد وإنهائه، وسبب عملها بهذا الأسلوب بدلًا من استخدام التابع <code>resolve</code> مثلًا، هو اقتصار حل الوعد على الشيفرة التي أنشأته.
</p>

<p>
	يوضِّح المثال التالي كيفية إنشاء واجهة مبنية على وعد لدالة <code>readStorage</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_26" style="">
<span class="kwd">function</span><span class="pln"> storage</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    nest</span><span class="pun">.</span><span class="pln">readStorage</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> result </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="pln">result</span><span class="pun">));</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

storage</span><span class="pun">(</span><span class="pln">bigOak</span><span class="pun">,</span><span class="pln"> </span><span class="str">"enemies"</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">value </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Got"</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">));</span></pre>

<p>
	تُعيد هذه الدالة غير المتزامنة قيمةً ذات معنى، وهذه هي الميزة الأساسية للوعود، إذ تبسِّط استخدام الدوال غير المتزامنة، فبدلًا من الحاجة إلى تمرير ردود النداء كما تبدو الدوال المبنية على الوعود أشبه بالدوال العادية؛ فهي تأخذ الدخل على أساس وسائط وتُعيد مخرجاتها، ولا تختلف إلا أنّ الخرج قد يكون غير متاح بعد.
</p>

<h2>
	الفشل Failure
</h2>

<p>
	يمكن أن تفشل حسابات جافاسكربت العادية برفع استثناء exception، إذ تحتاج الحوسبة غير المتزامنة إلى ذلك عادةً، فقد يفشل طلب الشبكة أو شيفرة ترفع استثناءً وتكون جزءًا من الحوسبة غير المتزامنة.
</p>

<p>
	إحدى المشاكل المؤرقة في نمط رد النداء للبرمجة غير المتزامنة أنها تصعِّب ضمان الإبلاغ الصحيح لردود النداء بشأن حالات الفشل، وأحد الحلول المستخدَمة على نطاق واسع هو استخدام وسيط رد النداء الأول لتوضيح فشل الإجراء، بعدها سيحتوي الوسيط الثاني على القيمة التي أنتجها الإجراء حين كان ناجحًا.
</p>

<p>
	يجب أن تتحقق دوال ردود النداء هذه إذا كانت قد استقبلت استثناءً أم لا، كما عليها التأكد من أنّ أيّ مشكلة تسببها بما فيها الاستثناءات المرفوعة من قِبَل الدوال التي استدعتها، قد عُلم بها وسُلِّمت إلى الدالة المناسبة؛ أما الوعود فتيسِّر من هذه العملية كثيرًا، فهي إما محلولة -أي انتهى الإجراء بنجاح-، أو مرفوضة -أي فشلت-، إذ لا تُستدعى معالِجات الإنهاء resolve handlers التي سُجلت في <code>then</code> إلا عند نجاح الإجراء؛ أما عمليات الرفض فتُنقَل إلى الوعد الجديد الذي تُعيده <code>then</code>. وحين يرفع معالج استثناءً، فسيجعل هذا الوعد الذي أنتجه الاستدعاء إلى <code>then</code> مرفوضًا.
</p>

<p>
	لهذا إذا فشل أيّ عنصر في سلسلة إجراءات غير متزامنة، فسيحدَّد الناتج الكلي للسلسلة على أنه مرفوض، ولا تُستدعى معالِجات نجاح أبعد من النقطة التي فشلت فيها.
</p>

<p>
	كذلك فإنّ رَفْض وعد ما ينتج قيمةً تمامًا مثل التي ينتجها حل الوعد، كما يُطلق على تلك القيمة سبب الرفض، فإذا تسبب استثناء في دالة معالِجة بالرفض، فستُستخدِم قيمة الاستثناء على أساس سبب، وبالمثل، إذا أعاد معالج وعدًا مرفوضًا، فسينتقل ذلك الرفض إلى الوعد التالي، حيث لدينا دالة <code>Promise.reject</code> التي تنشئ وعدًا جديدًا مرفوضًا فورًا.
</p>

<p>
	تحتوي الوعود على التابع <code>catch</code> لمعالجة عمليات الرفض تلك صراحةً، إذ يسجِّل معالجًا ليُستدعى حين يُرفض الوعد مثلما تعالج معالجات <code>then</code> الحل العادي للوعود، كما تشبه <code>then</code> في أنها تُعيد وعدًا جديدًا، إذ يحل إلى قيمة الوعد الأصلية إذا حل حلًا عاديًا، وإلى نتيجة معالج <code>catch</code> في غير ذلك.
</p>

<p>
	يُرفَض الوعد الجديد كذلك إذا رفع معالج <code>catch</code> خطأً ما، وللاختصار، تقبل <code>then</code> معالج الرفض على أساس وسيط ثاني، لذا نستطيع تثبيت كلا النوعين من المعالجات في استدعاء تابع وحيد، كما تستقبل الدالة الممرَّرة إلى الباني <code>Promise</code> وسيطًا ثانيًا مع دالة حل resolve function لتستخدمها في رفض الوعد الجديد.
</p>

<p>
	يمكن النظر إلى سلاسل قيم الوعود المنشأة باستدعاءات إلى <code>then</code> و<code>catch</code> على أساس خط أنابيب تتحرك فيه عمليات الفشل أو القيم غير المتزامنة، وبما أنّ تلك السلاسل قد أُنشئت بتسجيل معالجات، فسيكون لكل رابط معالج نجاح أو معالج فشل أو كليهما؛ حيث تُهمَل المعالِجات التي لا تطابق نوع الخرج -بنجاح أو فشل-؛ أما المعالجات التي تطابق فستُستدعى ويحدِّد خرجها نوع القيمة التي ستأتي تاليًا، بحيث تكوّن نجاحًا حين تُعيد قيمةً ليست وعدًا، وفشلًا حين ترفع استثناءً، وخرْج الوعد حين تُعيد أحد هذين.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_29" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">_</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Fail"</span><span class="pun">)))</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">value </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Handler 1"</span><span class="pun">))</span><span class="pln">
  </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">reason </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Caught failure "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> reason</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"nothing"</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">value </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Handler 2"</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → Caught failure Error: Fail</span><span class="pln">
</span><span class="com">// → Handler 2 nothing</span></pre>

<p>
	تُعالَج الاستثناءات غير الملتقَطة بواسطة البيئة، إذ تستطيع بيئات جافاسكربت استشعار إذا لم يعالَج رفض الوعد، وستُبلِّغ هذا في صورة خطأ.
</p>

<h2>
	صعوبة الشبكات
</h2>

<p>
	قد تأتي أيام لا يكون فيها ضوء كافي لنظام المرايا الذي يستخدمه الغربان من أجل نقل إشارة، أو قد يحجب شيء ما مسار الإشارة، كذلك قد تُرسَل إشارة ولا تُستقبَل من الطرف الآخر، حيث سيتسبب ذلك في عدم استدعاء رد النداء المعطى إلى <code>send</code>، وهو الأمر الذي سيجعل البرنامج يتوقف دون أي ملاحظة حتى لوجود مشكلة، فلو بلَّغ الطلب بالفشل إذا مر زمن محدد دون رد، لكان أفضل.
</p>

<p>
	تكون حالات فشل الإرسال في العادة حوادث عرضية مثل تداخل الأضواء القادمة من مصابيح سيارة مع الإشارات الضوئية، وهنا سينجح الطلب بمجرد إعادة المحاولة، لذا سنجعل دالة الطلب تحاول تلقائيًا إعادة إرسال الطلب عدة مرات قبل أن تستسلم.
</p>

<p>
	وبما أننا عرفنا الآن أن الوعود أمر جيد، فسنجعل دالة الطلب تُعيد وعدًا، إذ يتشابه رد النداء والوعد من حيث ما يستطيعان التعبير عنه، كما يمكن تغليف الدوال المبنية على ردود النداء لكشف واجهة مبنية على وعد، والعكس صحيح.
</p>

<p>
	قد يشير الرد إلى فشل عند تسليم الطلب ورده بنجاح إذا حاول الطلب استخدام نوع طلب غير معرَّف أو رفع المعالج خطأ مثلًا.
</p>

<p>
	لدعم هذا يتَّبع كل من <code>send</code> و<code>defineRequestType</code> الأسلوب المذكور أعلاه، حيث يكون الوسيط الأول الممرَّر إلى ردود النداء هو سبب الفشل إذا وُجد، في حين يكون الثاني هو النتيجة الحقيقية، ويمكن ترجمة هذا إلى حل الوعد ورفضه باستخدام المغلِّف الخاص بنا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_31" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Timeout</span><span class="pln"> extends </span><span class="typ">Error</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> request</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> target</span><span class="pun">,</span><span class="pln"> type</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let done </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">function</span><span class="pln"> attempt</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      nest</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> type</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">failed</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        done </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">failed</span><span class="pun">)</span><span class="pln"> reject</span><span class="pun">(</span><span class="pln">failed</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln"> resolve</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">});</span><span class="pln">
      setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">done</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln"> attempt</span><span class="pun">(</span><span class="pln">n </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">else</span><span class="pln"> reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Timeout</span><span class="pun">(</span><span class="str">"Timed out"</span><span class="pun">));</span><span class="pln">
      </span><span class="pun">},</span><span class="pln"> </span><span class="lit">250</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    attempt</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سينجح ما فعلناه أعلاه لأنّ الوعود لا يمكن حلها أو رفضها إلا مرةً واحدة، إذ تُحدِّد المرة الأولى التي يُستدعى فيها <code>resolve</code> أو <code>reject</code> خرج الوعد، كما تُتجاهل الاستدعاءات التالية التي سببها طلب عائد بعد انتهاء طلب آخر.
</p>

<p>
	سنحتاج إلى استخدام دالة تعاودية لبناء حلقة غير متزامنة من أجل إعادة المحاولة التي ذكرناها، حيث لا تسمح الحلقة العادية بالتوقف وانتظار الإجراء غير المتزامن، وتنفِّذ الدالة <code>attempt</code> محاولةً واحدةً لإرسال الطلب وتضبط زمن مهلة محدد، فإذا لم تحصل على استجابة أو رد بعد ربع ثانية، فستبدأ المحاولة الثانية أو ترفض الوعد إذا كانت هذه هي المحاولة الثالثة، ويكون السبب هنا هو نسخة من <code>Timeout</code>.
</p>

<p>
	تُعَدّ المحاولة كل ربع ثانية وتركها إذا لم نتلق ردًا بعد ثلاثة أرباع ثانية نظامًا عشوائيًا نوعًا ما، فمن المحتمل إذا جاء الطلب لكن المعالج استغرق وقتًا أطول قليلًا أن تُسلَّم الطلبات أكثر من مرة، وسنكتب معالجاتنا واضعين تلك المشكلة في حساباتنا، فلا يجب أن تمثل الرسائل المكررة مشكلة.
</p>

<p>
	لن نبني شبكة عالية المستوى هنا، فسقف توقعات الغربان ليس عاليًا على أي حال حين يتعلق الأمر بالحوسبة، ولكي نعزل أنفسنا من ردود النداء كلها، فسنعرِّف مغلِّفًا لـ <code>defineRequestType</code> يسمح لدالة المعالِج أن تُعيد وعدًا أو قيمةً مجردةً plain value ويوصل ذلك لرد النداء من أجلنا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_33" style="">
<span class="kwd">function</span><span class="pln"> requestType</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> handler</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  defineRequestType</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">,</span><span class="pln"> source</span><span class="pun">,</span><span class="pln">
                           callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="pln">handler</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">,</span><span class="pln"> source</span><span class="pun">))</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> callback</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> response</span><span class="pun">),</span><span class="pln">
              failure </span><span class="pun">=&gt;</span><span class="pln"> callback</span><span class="pun">(</span><span class="pln">failure</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">exception</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      callback</span><span class="pun">(</span><span class="pln">exception</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نستخدِم <code>Promise.resolve</code> لتحويل القيمة التي يُعيدها <code>handler</code> إلى وعد إذا لم تُحوَّل فعليًا.
</p>

<p>
	لاحظ أنه يجب تغليف الاستدعاء إلى <code>handler</code> في كتلة <code>try</code> لضمان توصيل أي استثناءات يرفعها مباشرةً إلى رد النداء، حيث يوضح هذا صعوبة معالجة الأخطاء التي فيها ردود نداء معالجة صحيحة، ومن السهل نسيان كيفية توجيه استثناءات مثل هذه، وإذا لم نفعل ذلك فلن يُبلّغ بحالات الفشل إلى رد نداء مناسب؛ أما الوعود فستجعل هذا آليًا تقريبًا ومن ثم يكون أقل عرضةً للأخطاء.
</p>

<h2>
	تجميعات الوعود
</h2>

<p>
	يحتفظ حاسوب العش بمصفوفة من الأعشاش الأخرى التي في نطاق الاتصال في الخاصية <code>neighbors</code>.
</p>

<p>
	سنكتب دالةً تحاول إرسال طلب <code>"ping"</code> إلى كل حاسوب لنرى أيها ترد علينا، وهو طلب يسأل ردًا ببساطة، وذلك لنرى إن كان مكن الوصول إلى أيّ من تلك الأعشاش.
</p>

<p>
	تُعَدّ الدالة <code>Promise.all</code> مفيدةً عند العمل مع تجميعات من الوعود التي تعمل في نفس الوقت، إذ تُعيد وعدًا ينتظر حل جميع الوعود التي في المصفوفة، ثم يُحل هو إلى مصفوفة من القيم التي أنتجتها تلك الوعود بترتيب المصفوفة الأصلية نفسها، وإذا رُفض أي وعد فستُرفَض نتيجة <code>Promise.all</code> كذلك.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_37" style="">
<span class="pln">requestType</span><span class="pun">(</span><span class="str">"ping"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">"pong"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> availableNeighbors</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let requests </span><span class="pun">=</span><span class="pln"> nest</span><span class="pun">.</span><span class="pln">neighbors</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">neighbor </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> request</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> neighbor</span><span class="pun">,</span><span class="pln"> </span><span class="str">"ping"</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">(</span><span class="pln">requests</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">result </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> nest</span><span class="pun">.</span><span class="pln">neighbors</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">((</span><span class="pln">_</span><span class="pun">,</span><span class="pln"> i</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> result</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لا نريد فشل الوعد المجمَّع إذا كان أحد الجيران غير متاح ونحن ما زلنا لا نعرف باقي البيانات، لذا تُلحِق الدالة التي رُبطت بمجموعة من الجيران لتحولهم إلى وعود طلبات، حيث أن المعالجات تصنع طلبات ناجحة تنتج <code>true</code> وطلبات فاشلة تنتج <code>false</code>.
</p>

<p>
	يُستخدَم <code>filter</code> في معالج الوعد المجمَّع لحذف العناصر من مصفوفة <code>neighbors</code> التي تكون قيمتها الموافقة هي <code>false</code>، إذ يستفيد هذا من كون <code>filter</code> يمرر فهرس مصفوفة العنصر الحالي على أساس وسيط ثاني إلى دالة الترشيح، كما يفعل <code>map</code> و<code>some</code> وتوابع المصفوفات العليا المشابهة.
</p>

<h2>
	إغراق الشبكة Network flooding
</h2>

<p>
	يحد اقتصار تكلم الأعشاش لجيرانها كثيرًا من فائدة هذه الشبكة، وعلى ذلك فربما نحاول إعداد نوع من الطلبات يُعيد توجيه الطلبات تلقائيًا إلى الجيران، وذلك على أساس حل لنشر المعلومات إلى الشبكة كلها، ثم يُعيد هؤلاء الجيران توجيهها إلى جيرانهم، وهكذا حتى تستقبل الشبكة كلها تلك الرسالة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_39" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">everywhere</span><span class="pun">}</span><span class="pln"> from </span><span class="str">"./crow-tech"</span><span class="pun">;</span><span class="pln">

everywhere</span><span class="pun">(</span><span class="pln">nest </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  nest</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">gossip </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> sendGossip</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">,</span><span class="pln"> exceptFor </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  nest</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">gossip</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let neighbor of nest</span><span class="pun">.</span><span class="pln">neighbors</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">neighbor </span><span class="pun">==</span><span class="pln"> exceptFor</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">continue</span><span class="pun">;</span><span class="pln">
    request</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> neighbor</span><span class="pun">,</span><span class="pln"> </span><span class="str">"gossip"</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

requestType</span><span class="pun">(</span><span class="str">"gossip"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">,</span><span class="pln"> source</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">nest</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">gossip</span><span class="pun">.</span><span class="pln">includes</span><span class="pun">(</span><span class="pln">message</span><span class="pun">))</span><span class="pln"> </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">nest</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln"> received gossip </span><span class="str">'</span><span class="pln">$</span><span class="pun">{</span><span class="pln">
               message</span><span class="pun">}</span><span class="str">'</span><span class="pln"> from $</span><span class="pun">{</span><span class="pln">source</span><span class="pun">}`);</span><span class="pln">
  sendGossip</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">,</span><span class="pln"> source</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يحتفظ كل عش بمصفوفة من سلاسل gossip النصية التي رآها فعليُا، وذلك لتجنب تكرار إرسال الرسالة عبر الشبكة للأبد، كما نستخدِم دالة <code>everywhere</code> لتعريف تلك المصفوفة، إذ تُشغِّل هذه الدالة الشيفرة على كل عش من أجل إضافة خاصية إلى كائن <code>state</code> الخاص بالعش، وهو المكان الذي سنحفظ فيه حالة العش المحلية.
</p>

<p>
	يتجاهل العش رسالة gossip مكرَّرة مرسَلة إليه، الأمر الذي يحدث عادةً حين يعيد الناس إرسالها دون وعي، لكن حين يستلم رسالةً جديدةً، فسيخبر جميع جيرانه عدا ذلك الذي أرسلها.
</p>

<p>
	سينشر هذا جزءًا جديدًا من gossip في الشبكة كما تنتشر بقعة الحبر في الماء، فحتى إذا لم تكن بعض الاتصالات عاملةً وقتها، فستصل gossip العش المعطى إذا كان ثمة طريق بديلة إليه.
</p>

<p>
	يسمى ذلك الأسلوب في التواصل الشبكي باسم الإغراق أو الغمر flooding، فهو يغرق الشبكة بجزء من المعلومات إلى أن تكون لدى جميع العقد.
</p>

<p>
	نستطيع استدعاء <code>sendGossip</code> لنرى تدفق الرسائل خلال القرية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_41" style="">
<span class="pln">sendGossip</span><span class="pun">(</span><span class="pln">bigOak</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Kids with airgun in the park"</span><span class="pun">);</span></pre>

<h2>
	توجيه الرسائل
</h2>

<p>
	إذا أرادت عقدة ما التواصل مع عقدة أخرى فلن يكون أسلوب الإغراق هنا هو الأمثل، خاصةً حين تكون الشبكة كبيرة، فقد يؤدي هذا إلى كثير من نقل البيانات غير الضروري، والحل هنا هو إعداد طريقة تنتقل بها الرسائل من عقدة إلى عقدة حتى تصل إلى وجهتها.
</p>

<p>
	تكون الصعوبة في ذلك أنها ستحتاج إلى معرفة بتخطيط الشبكة، حيث من الضروري لإرسال طلب في اتجاه عش بعيد، معرفة أيّ الأعشاش الجارة قادرة على إيصاله بصورة أسرع إلى وجهته، حيث سنهدر كثيرًا من الموارد دون جدوى إذا أرسلناه في الاتجاه الخاطئ.
</p>

<p>
	لا يملك العش المعلومات التي يحتاجها لحساب الطريق لأنه لا يعرف إلا جيرانه المباشَرِين، وعليه يجب نشر معلومات الاتصال لكل الأعشاش بطريقة تسمح لها بالتغير مع مرور الوقت كلما هُجِر عش أو بُني آخر.
</p>

<p>
	نستطيع استخدام الإغراق هنا مرةً أخرى، لكننا سننظر هل تطابق مجموعة الجيران الجديدة لعش ما المجموعة الحالية التي لدينا، أم لا بدلًا من التحقق هل تم استلام الرسالة أم لا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_43" style="">
<span class="pln">requestType</span><span class="pun">(</span><span class="str">"connections"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> neighbors</span><span class="pun">},</span><span class="pln">
                            source</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let connections </span><span class="pun">=</span><span class="pln"> nest</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">connections</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">connections</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">name</span><span class="pun">))</span><span class="pln"> </span><span class="pun">==</span><span class="pln">
      JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">neighbors</span><span class="pun">))</span><span class="pln"> </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  connections</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> neighbors</span><span class="pun">);</span><span class="pln">
  broadcastConnections</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> source</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> broadcastConnections</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> exceptFor </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let neighbor of nest</span><span class="pun">.</span><span class="pln">neighbors</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">neighbor </span><span class="pun">==</span><span class="pln"> exceptFor</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">continue</span><span class="pun">;</span><span class="pln">
    request</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> neighbor</span><span class="pun">,</span><span class="pln"> </span><span class="str">"connections"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      name</span><span class="pun">,</span><span class="pln">
      neighbors</span><span class="pun">:</span><span class="pln"> nest</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">connections</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

everywhere</span><span class="pun">(</span><span class="pln">nest </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  nest</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">connections </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Map</span><span class="pun">();</span><span class="pln">
  nest</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">connections</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">.</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> nest</span><span class="pun">.</span><span class="pln">neighbors</span><span class="pun">);</span><span class="pln">
  broadcastConnections</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> nest</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تستخدِم الموازنة <code>JSON.stringify</code> لأنّ <code>==</code> لن يُعيد القيمة <code>true</code> إلا إذا كان الاثنان نفس القيمة بالضبط سواءً كان على كائنات أو مصفوفات، وليس هذا ما نريده هنا، وبالتالي فقد تكون موازنة سلاسل JSON النصية بدائيةً قليلًا لكنها فعالة لموازنة محتواها.
</p>

<p>
	تبدأ العقد بنشر اتصالاتها مباشرةً، مما يعطي كل عش خريطةً بمخطط الشبكة الحالي، ما لم يوجد عش منها يستحيل الوصول إليه، كما نستطيع باستخدام ذلك المخطط إيجاد الطرق فيه كما رأينا في مقال <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%8A-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%B1%D8%AC%D9%84-%D8%A2%D9%84%D9%8A-%D8%B1%D9%88%D8%A8%D9%88%D8%AA-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1244/" rel="">مشروع تطبيقي لبناء رجل آلي (روبوت) عبر جافاسكريبت</a>، فإذا كان لدينا طريق نحو وجهة رسالة ما، فسنستطيع معرفة أي اتجاه نرسلها فيه.
</p>

<p>
	تبحث الدالة <code>findRoute</code> -التي تشابه <code>findRoute</code> من مقال مشروع تطبيقي لبناء رجل آلي (روبوت) عبر جافاسكريبت- عن طريقة للوصول إلى عقدة معطاة في الشبكة، لكن بدلًا من إعادة الطريق كله، فهي تعيد الخطوة التالية فقط، وسيقرِّر العش التالي مستخدِمًا المعلومات المتوفرة لديه عن الشبكة أين سيرسل الرسالة تاليًا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_45" style="">
<span class="kwd">function</span><span class="pln"> findRoute</span><span class="pun">(</span><span class="pln">from</span><span class="pun">,</span><span class="pln"> to</span><span class="pun">,</span><span class="pln"> connections</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let work </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[{</span><span class="pln">at</span><span class="pun">:</span><span class="pln"> from</span><span class="pun">,</span><span class="pln"> via</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">}];</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> work</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let </span><span class="pun">{</span><span class="pln">at</span><span class="pun">,</span><span class="pln"> via</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> work</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let next of connections</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">at</span><span class="pun">)</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> </span><span class="pun">[])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">next </span><span class="pun">==</span><span class="pln"> to</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> via</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">work</span><span class="pun">.</span><span class="pln">some</span><span class="pun">(</span><span class="pln">w </span><span class="pun">=&gt;</span><span class="pln"> w</span><span class="pun">.</span><span class="pln">at </span><span class="pun">==</span><span class="pln"> next</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        work</span><span class="pun">.</span><span class="pln">push</span><span class="pun">({</span><span class="pln">at</span><span class="pun">:</span><span class="pln"> next</span><span class="pun">,</span><span class="pln"> via</span><span class="pun">:</span><span class="pln"> via </span><span class="pun">||</span><span class="pln"> next</span><span class="pun">});</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نستطيع الآن بناء دالة يمكنها إرسال رسائل بعيدة المدى، فإذا كانت مرسَلةً إلى جار مباشر، فستسلَّم مثل العادة؛ أما إذا كان إلى جار بعيد، فستُحزَّم في كائن وترسَل إلى جار قريب من الهدف باستخدام نوع الطلب <code>"route"</code> الذي سيجعل ذلك الجار يكرِّر السلوك نفسه.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_47" style="">
<span class="kwd">function</span><span class="pln"> routeRequest</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> target</span><span class="pun">,</span><span class="pln"> type</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">nest</span><span class="pun">.</span><span class="pln">neighbors</span><span class="pun">.</span><span class="pln">includes</span><span class="pun">(</span><span class="pln">target</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> request</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> target</span><span class="pun">,</span><span class="pln"> type</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let via </span><span class="pun">=</span><span class="pln"> findRoute</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">.</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> target</span><span class="pun">,</span><span class="pln">
                        nest</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">connections</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">via</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(`</span><span class="typ">No</span><span class="pln"> route to $</span><span class="pun">{</span><span class="pln">target</span><span class="pun">}`);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> request</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> via</span><span class="pun">,</span><span class="pln"> </span><span class="str">"route"</span><span class="pun">,</span><span class="pln">
                   </span><span class="pun">{</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> type</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">});</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

requestType</span><span class="pun">(</span><span class="str">"route"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">target</span><span class="pun">,</span><span class="pln"> type</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> routeRequest</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> target</span><span class="pun">,</span><span class="pln"> type</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نستطيع الآن إرسال الرسالة إلى العش الذي في برج الكنيسة، حيث سنكون قد وفرنا أربع محطات من الطريق في الشبكة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_49" style="">
<span class="pln">routeRequest</span><span class="pun">(</span><span class="pln">bigOak</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Church Tower"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"note"</span><span class="pun">,</span><span class="pln">
             </span><span class="str">"Incoming jackdaws!"</span><span class="pun">);</span></pre>

<p>
	لهذا نكون قد بنينا عدة طبقات من الوظائف فوق نظام اتصالات بدائي من أجل تسهيل استخدامه، وهذا نموذج جميل لكيفية عمل شبكات الحواسيب الحقيقية رغم بساطته أو سطحيته، لكن الخاصية التي تميز شبكات الحواسيب أنها لا يُعتمد عليها، فرغم استطاعتنا على بناء تجريدات abstractions فوقها لتساعدنا، إلا أنه لا يمكننا منع فشل الشبكة، لذا تدور برمجة الشبكات حول توقُّع حالات الفشل والتعامل معها.
</p>

<h2>
	دوال async
</h2>

<p>
	تنسخ الغربان المعلومات المهمة في الأعشاش من أجل الحفاظ عليها إذا هجم صقر ما على أحدها ودمره، فإذا أرادت استرجاع معلومات ليست موجودة في حوصلة التخزين الخاصة بها، فسيسأل حاسوب العش بقية الأعشاش في الشبكة حتى يجد واحدًا منها لديه تلك المعلومات.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_51" style="">
<span class="pln">requestType</span><span class="pun">(</span><span class="str">"storage"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> storage</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">));</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> findInStorage</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> storage</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">found </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">found </span><span class="pun">!=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> found</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> findInRemoteStorage</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> network</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Array</span><span class="pun">.</span><span class="pln">from</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">connections</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> findInRemoteStorage</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let sources </span><span class="pun">=</span><span class="pln"> network</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">).</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">n </span><span class="pun">=&gt;</span><span class="pln"> n </span><span class="pun">!=</span><span class="pln"> nest</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">function</span><span class="pln"> next</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">sources</span><span class="pun">.</span><span class="pln">length </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">reject</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Not found"</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      let source </span><span class="pun">=</span><span class="pln"> sources</span><span class="pun">[</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln">
                                      sources</span><span class="pun">.</span><span class="pln">length</span><span class="pun">)];</span><span class="pln">
      sources </span><span class="pun">=</span><span class="pln"> sources</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">n </span><span class="pun">=&gt;</span><span class="pln"> n </span><span class="pun">!=</span><span class="pln"> source</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> routeRequest</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> source</span><span class="pun">,</span><span class="pln"> </span><span class="str">"storage"</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">value </span><span class="pun">=&gt;</span><span class="pln"> value </span><span class="pun">!=</span><span class="pln"> </span><span class="kwd">null</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> value </span><span class="pun">:</span><span class="pln"> next</span><span class="pun">(),</span><span class="pln">
              next</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> next</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ولأن <code>connections</code> هي خارطة <code>Map</code>، فلن تعمل <code>Object.keys</code> عليها، لكن لديها التابع <code>keys</code> الذي يعيد مكرِّرًا iterator وليس مصفوفةً، كما يمكن تحويله إلى مصفوفة باستخدام الدالة <code>Array.from</code>، وهي شيفرة غير مألوفة حتى مع الوعود، إذ تُسلسَل عدة إجراءات غير متزامنة معًا بطرق غير واضحة، وسنحتاج إلى الدالة التعاودية <code>next</code> لوضع نموذج المرور الحلَقي على الأعشاش؛ أما ما تفعله الشيفرة على الحقيقة فهو سلوك خطي، إذ تنتظر تمام الإجراء السابق قبل بدء الذي يليه، وبالتالي ستكون أسهل في التعبير عنها في نموذج برمجة متزامنة.
</p>

<p>
	الجميل في الأمر هو سماح جافاسكربت بكتابة شيفرة وهمية متزامنة pseudo-synchronous لوصف الحوسبة غير المتزامنة، إذ تُعيد الدالة <code>async</code> -ضمنيًا- وعدًا وتنتظر <code>await</code> في متنها وعودًا أخرى بطريقة <strong>تبدو</strong> تزامنية.
</p>

<p>
	نُعيد كتابة <code>findInStorage</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_53" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> findInStorage</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let local </span><span class="pun">=</span><span class="pln"> await storage</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">local </span><span class="pun">!=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> local</span><span class="pun">;</span><span class="pln">

  let sources </span><span class="pun">=</span><span class="pln"> network</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">).</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">n </span><span class="pun">=&gt;</span><span class="pln"> n </span><span class="pun">!=</span><span class="pln"> nest</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">sources</span><span class="pun">.</span><span class="pln">length </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let source </span><span class="pun">=</span><span class="pln"> sources</span><span class="pun">[</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln">
                                    sources</span><span class="pun">.</span><span class="pln">length</span><span class="pun">)];</span><span class="pln">
    sources </span><span class="pun">=</span><span class="pln"> sources</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">n </span><span class="pun">=&gt;</span><span class="pln"> n </span><span class="pun">!=</span><span class="pln"> source</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      let found </span><span class="pun">=</span><span class="pln"> await routeRequest</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> source</span><span class="pun">,</span><span class="pln"> </span><span class="str">"storage"</span><span class="pun">,</span><span class="pln">
                                     name</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">found </span><span class="pun">!=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> found</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">_</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Not found"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُحدَّد الدالة <code>async</code> بكلمة <code>async</code> قبل كلمة <code>function</code> المفتاحية، كما يمكن جعل التوابع غير تزامنية بكتابة <code>async</code> قبل أسمائها. وإذا استدعيت دالةً مثل تلك؛ فستُعيد وعدًا، وكذلك الحال بالنسبة للتوابع، ويُحل الوعد عندما تُعيد الدالة شيئًا؛ أما إذا رفعت الدالة استثناءً فسيُرفض الوعد.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_55" style="">
<span class="pln">findInStorage</span><span class="pun">(</span><span class="pln">bigOak</span><span class="pun">,</span><span class="pln"> </span><span class="str">"events on 2017-12-21"</span><span class="pun">)</span><span class="pln">  
</span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">);</span></pre>

<p>
	يمكن وضع كلمة <code>await</code> المفتاحية أمام تعبير ما داخل دالة <code>async</code> لجعله ينتظر حل وعد ما، ولا يتابع تنفيذ الدالة إلا بعد حل ذلك الوعد.
</p>

<p>
	لم تَعُد مثل تلك الدوال تعمل من البداية للنهاية على مرة واحدة، شأنها شأن أيّ دالة جافاسكربت عادية، إذ يمكن تجميدها frozen في أي نقطة توجد فيها <code>await</code>، وبعدها ستستعيد العمل في وقت لاحق؛ أما بالنسبة للشيفرات غير المتزامنة المهمة، فتلك الصيغة أسهل من استخدام الوعود مباشرةً، حتى لو احتجنا إلى فعل شيء لا يناسب النموذج المتزامن مثل تنفيذ عدة إجراءات في الوقت نفسه، إذ من السهل دمج <code>await</code> مع الاستخدام المباشر للوعود.
</p>

<h2>
	المولدات Generators
</h2>

<p>
	إن قدرة الدالة على التوقف ثم المتابعة مرةً أخرى ليست حكرًا على دوال <code>async</code> وحدها، حيث تحتوي جافاسكربت على خاصية اسمها دوال المولِّد generator functions التي تشبهها لكن مع استثناء الوعود، فحين نعرِّف دالةً باستخدام <code>‎function*‎</code> -أي أننا سنضع محرف النجمة بعد الكلمة <code>function</code> لستصير مولِّدًا، ويُعيد مكررًا عند استدعائه كما رأينا في مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AD%D9%8A%D8%A7%D8%A9-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%A9-%D9%84%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1243/" rel="">الحياة السرية للكائنات في جافاسكريبت</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_57" style="">
<span class="kwd">function</span><span class="pun">*</span><span class="pln"> powers</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let current </span><span class="pun">=</span><span class="pln"> n</span><span class="pun">;;</span><span class="pln"> current </span><span class="pun">*=</span><span class="pln"> n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    yield current</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let power of powers</span><span class="pun">(</span><span class="lit">3</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">power </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">50</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">power</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// → 3</span><span class="pln">
</span><span class="com">// → 9</span><span class="pln">
</span><span class="com">// → 27</span></pre>

<p>
	تُجمَّد الدالة في بدايتها حين نستدعي <code>powers</code>، وتعمل في كل مرة تستدعي <code>next</code> على المكرِّر حتى تقابل تعبير <code>yield</code> الذي يوقفها ويجعل القيمة المحصَّلة هي القيمة التي ينتجها المكرِّر تاليًا، وحين تُعيد الدالة -علمًا أنّ دالة مثالنا لا تعيد- فسيكون المكرِّر قد انتهى.
</p>

<p>
	تُعَدّ كتابة المكرِّرات أسهل كثيرًا من استخدام الدوال المولِّدة، حيث يمكن كتابة مكرِّر الصنف <code>Group</code> من التدريب في مقال الحياة السرية للكائنات في جافاسكريبت بهذا المولِّد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_59" style="">
<span class="typ">Group</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">[</span><span class="typ">Symbol</span><span class="pun">.</span><span class="pln">iterator</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">*()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">members</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    yield </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">members</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	لم نعد في حاجة إلى إنشاء كائن ليحفظ حالة المكرِّر، إذ تحفظ المولِّدات حالتها المحلية تلقائيًا في كل مرة تُحصِّل فيها، وقد لا تقع تعبيرات مثل <code>yield</code> تلك إلا مباشرةً في دالة المولِّد نفسها وليس في دالة داخلية معرَّفة داخلها، والحالة التي يحفظها المولِّد عند التحصيل هي بيئته المحلية فقط والموضع الذي حَصَّل فيه؛ أما دالة <code>async</code> فهي نوع خاص من المولِّدات، فهي تُنتِج وعدًا عند استدعائها، كما يُحَل عند إعادتها -أي انتهائها-، في حين يُرفَض إذا رفعت استثناءً، ونحصل على ناتج الوعد سواء كان قيمة أو استثناءً مرفوعًا باستعمال التعبير <code>awaits</code> مع الوعد الذي تعيده الدالة.
</p>

<h2>
	حلقة الحدث التكرارية
</h2>

<p>
	تُنفَّذ البرامج غير المتزامنة جزءًا جزءًا، وقد يبدأ كل جزء ببعض الإجراءات ويجدول شيفرات لتنفيذها عند انتهاء الإجراء أو فشله؛ أما بين تلك الأجزاء فسيكون البرنامج ساكنًا منتظِرًا الإجراء التالي، وبالتالي لا تُستدعى ردود النداء من قِبَل الشيفرة التي جدولَتها، فإذا استدعينا <code>setTimeout</code> من داخل دالة، فستكون تلك الدالة قد أعادت بالوقت الذي تُستدعى فيه دالة رد النداء، وحين يُعيد رد النداء، فلن يعود التحكم إلى الدالة التي جدولته.
</p>

<p>
	يحدث السلوك غير المتزامن في مكدس استدعاء الدوال الفارغ الخاص به، وهذا أحد الأسباب التي تصعب معها إدارة الاستثناءات في الشيفرات غير المتزامنة في غياب الوعود، إذ لن تكون معالجات <code>catch</code> الخاصة بنا في المكدس حين ترفع استثناءً بما أنّ كل رد نداء يبدأ بمكدس شبه فارغ.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_61" style="">
<span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Woosh"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">},</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">_</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// لن يعمل هذا</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Caught!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	مهما كانت أوقات وقوع الأحداث -مثل طلبات timeouts أو incoming- متقاربة، فلن تشغّل بيئة جافاسكربت إلا برنامجًا واحدًا في كل مرة.
</p>

<p>
	يمكن النظر إلى هذا السلوك على أنه يشغِّل حلقةً تكراريةً كبيرةً على برنامجنا يُطلق عليها حلقة الحدث event loop، فإذا لم يكن ثمة شيء لفعله فستتوقف الحلقة؛ أما إذا جاءت الأحداث، فستضاف إلى طابور queue وتنفَّذ شيفراتها واحدةً تلو الأخرى، ولأنه لا يمكن تنفيذ شيئين في الوقت نفسه، فستؤخر الشيفرة البطيئة التنفيذ معالجة الأحداث الأخرى.
</p>

<p>
	يضبط المثال أدناه زمن إنهاء timeout، لكن يتلكأ بعد ذلك إلى ما بعد النقطة الزمنية المحدَّدة له مسببًا تأخر زمن الإنهاء.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_63" style="">
<span class="pln">let start </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">();</span><span class="pln">
setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Timeout ran at"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> start</span><span class="pun">);</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> </span><span class="lit">20</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> start </span><span class="pun">+</span><span class="pln"> </span><span class="lit">50</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Wasted time until"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> start</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → Wasted time until 50</span><span class="pln">
</span><span class="com">// → Timeout ran at 55</span></pre>

<p>
	تُحل الوعود دائمًا أو تُرفض على أساس حدث جديد، وحتى لو حُل الوعد، فسسيتسبب انتظاره في تشغيل رد النداء الخاص بك بعد انتهاء السكربت الحالية بدلًا من تشغيله مباشرةً.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_66" style="">
<span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">(</span><span class="str">"Done"</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Me first!"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → Me first!</span><span class="pln">
</span><span class="com">// → Done</span></pre>

<p>
	سنرى في الفصول اللاحقة عدة أنواع من الأحداث التي تعمل على حلقة الحدث.
</p>

<h2>
	زلات البرامج غير المتزامنة
</h2>

<p>
	لا توجد تغيرات حادثة في الحالة إذا كان البرنامج يعمل بتزامن على مرة واحدة عدا تلك التي يصنعها البرنامج نفسه؛ أما بالنسبة للبرامج غير المتزامنة فالأمر مختلف، إذ قد تحتوي على فجوات gaps في تنفيذها يمكن لشيفرات أخرى العمل خلالها.
</p>

<p>
	لننظر إلى المثال التالي، إذ يهوى أحد الغربان عدّ الفراخ التي تفقس في القرية كل عام، حيث تخزِّن الأعشاش هذا العدد في حواصل التخزين الخاصة بها، وتحاول الشيفرة التالية حساب العدد من جميع الأعشاش لأحد الأعوام.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_68" style="">
<span class="kwd">function</span><span class="pln"> anyStorage</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> source</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">source </span><span class="pun">==</span><span class="pln"> nest</span><span class="pun">.</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> storage</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> routeRequest</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> source</span><span class="pun">,</span><span class="pln"> </span><span class="str">"storage"</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

async </span><span class="kwd">function</span><span class="pln"> chicks</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> year</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let list </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">
  await </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">(</span><span class="pln">network</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">).</span><span class="pln">map</span><span class="pun">(</span><span class="pln">async name </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    list </span><span class="pun">+=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">name</span><span class="pun">}:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">
      await anyStorage</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> </span><span class="pun">`</span><span class="pln">chicks in $</span><span class="pun">{</span><span class="pln">year</span><span class="pun">}`)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">\n</span><span class="pun">`;</span><span class="pln">
  </span><span class="pun">}));</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> list</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يُظهر الجزء <code>async name‎ =&gt;‎</code> أنّ الدوال السهمية يمكن أن تكون غير متزامنة إذا وضعنا كلمة <code>async</code> أمامها. لن يبدو أيّ شيء غريبًا على الشيفرة لأول وهلة، فهي توجه الدوال السهمية غير المتزامنة <code>async</code> خلال مجموعة من الأعشاش لتنشئ مصفوفةً من الوعود، ثم ستستخدِم <code>Promise.all</code> لتنتظر حل كل تلك الوعود قبل إعادتها القائمة التي جهزتها، غير أنّ هذا السلوك معيب، إذ يُعيد سطرًا واحدًا يحتوي دائمًا على العش الأبطأ -الذي يصل رده متأخرًا- في الإجابة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_70" style="">
<span class="pln">chicks</span><span class="pun">(</span><span class="pln">bigOak</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2017</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">);</span></pre>

<p>
	تكمن المشكلة في العامِل <code>‎+=‎</code> الذي يأخذ قيمة <code>list</code> الحالية وقت بدء تنفيذ التعليمة، ثم يضبط رابطة <code>list</code> بعد انتهاء <code>await</code> لتكون تلك القيمة إضافةً إلى السلسلة النصية المضافة؛ أما في الوقت الذي يكون بين بدء تنفيذ التعليمة والوقت الذي تنتهي فيه، سيكون لدينا فجوة غير متزامنة.
</p>

<p>
	يعمل التعبير <code>map</code> قبل أي شيء أضيف إلى القائمة، لذا يبدأ كل عامل من العوامل <code>‎+=‎</code> بسلسلة فارغة، وينتهي حين تنتهي عملية استعادة التخزين بضبط قائمة <code>list</code> من سطر واحد، حيث تكون نتيجة إضافة السطر إلى السلسلة الفارغة.
</p>

<p>
	كان يمكن تجنب ذلك بسهولة بإعادة الأسطر من الوعود الموجَّهة واستدعاء <code>join</code> على نتيجة <code>Promise.all</code> بدلًا من بناء القائمة بتغيير رابطة ما، كما سيكون حساب قيمة جديدة أقل عرضةً للخطأ من تغيير القيم الموجودة سلفًا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_72" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> chicks</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> year</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let lines </span><span class="pun">=</span><span class="pln"> network</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">).</span><span class="pln">map</span><span class="pun">(</span><span class="pln">async name </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> name </span><span class="pun">+</span><span class="pln"> </span><span class="str">": "</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
      await anyStorage</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> </span><span class="pun">`</span><span class="pln">chicks in $</span><span class="pun">{</span><span class="pln">year</span><span class="pun">}`);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">await </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">(</span><span class="pln">lines</span><span class="pun">)).</span><span class="pln">join</span><span class="pun">(</span><span class="str">"\n"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تحدث أخطاء مثل هذه بسهولة خاصةً عند استخدام <code>await</code>، كما يجب أن نكون على علم بأماكن حدوث الفجوات في شيفراتنا، وإحدى مزايا اللاتزامن الصريح في جافاسكربت، هي أنّ العثور على تلك الفجوات سهل نسبيًا، إذ سيكون إما باستخدام ردود النداء أو الوعود أو <code>await</code>.
</p>

<h2>
	خاتمة
</h2>

<p>
	تمكننا البرمجة غير التزامنية من التعبير عن الانتظار للإجراءات actions التي تعمل لوقت طويل دون تجميد البرنامج أثناء تلك الإجراءات، وتنفّذ بيئات جافاسكربت ذلك النمط من البرمجة باستخدام ردود النداء، وهي الدوال التي تُستدعى عند تمام الإجراءات.
</p>

<p>
	تُجدوِل حلقة الحدث ردود النداء لتُستدعى في الوقت المناسب واحدًا تلو الآخر كي لا يتداخل تنفيذها، كما صارت البرمجة غير التزامنية سهلةً بفضل الوعود التي هي كائنات تمثّل إجراءات قد تكتمل في المستقبل، ودوال <code>async</code> التي تسمح لنا بكتابة برنامج غير متزامن كما لو كان تزامنيًا.
</p>

<h2>
	تدريبات
</h2>

<h3>
	تتبع المشرط
</h3>

<p>
	تملك غربان القرية مشرطًا قديمًا تستخدِمه أحيانًا في المهام الخاصة مثل التحزيم أو قطع الأسلاك، كما تريد الحفاظ عليه لئلا يضيع، لذلك، فكلما نُقل المشرط إلى عش جديد أُضيف مُدخل جديد في ذاكرة العش القديم والجديد معًا تحت اسم مشرط <code>"scalpel"</code>، حيث تكون قيمته هي موقعه الجديد، ويعني هذا أنّ إيجاد المشرط مسألة اتباع أثر مدخلات الذاكرة حتى تجد عشًا يشير إلى نفسه.
</p>

<p>
	اكتب دالة <code>async</code> اسمها <code>locateScalpel</code> تفعل ذلك، بحيث تبدأ من العش الذي تشغّله، كما يمكنك استخدام الدالة <code>anyStorage</code> التي عرَّفناها من قبل للوصول إلى الذاكرة في أعشاش عشوائية، وافترض أن المشرط قد دار لمدة تكفي لكي يحتوي كل عش على مُدخل <code>"scalpel"</code> في ذاكرة بياناته، ثم اكتب الدالة نفسها مرةً أخرى دون استخدام <code>async</code> و<code>await</code>.
</p>

<p>
	هل تظهر طلبات الفشل مثل رفض للوعود المعادة في كلا النسختين من الدالة؟ وكيف؟
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_74" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> locateScalpel</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// شيفرتك هنا</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> locateScalpel2</span><span class="pun">(</span><span class="pln">nest</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// شيفرتك هنا</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

locateScalpel</span><span class="pun">(</span><span class="pln">bigOak</span><span class="pun">).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → Butcher Shop</span></pre>

<h4>
	إرشادات للحل
</h4>

<p>
	يمكن تنفيذ هذا التدريب بحلقة واحدة تبحث في الأعشاش وتنتقل من العش لغيره حين تجد قيمةً لا تطابق اسم العش الحالي، وتعيد اسم العش حين تجد قيمة مطابِقة، كما يمكن استخدام حلقة <code>while</code> أو <code>for</code> عادية في دالة <code>async</code>.
</p>

<p>
	عليك بناء حلقتك باستخدام دالة تعاودية لتنفيذ الشيء نفسه في الدالة المجردة plain function، وأسهل طريقة لذلك هي جعل تلك الدالة تعيد وعدًا باستدعاء <code>then</code> على الوعد الذي يجلب قيمة الذاكرة.
</p>

<p>
	سيُعيد المعالج تلك القيمة أو وعدًا مستقبليًا يُنشأ باستدعاء الدالة الحلقية مرةً أخرى بناءً على مطابقة القيمة لاسم العش الحالي.
</p>

<p>
	لا تنسى بدء الحلقة باستدعاء الدالة التعاودية مرة واحدة من الدالة الأساسية.
</p>

<p>
	تحوِّل <code>await</code> الوعود المرفوضة في دالة <code>async</code> إلى استثناءات، وحين ترفع دالة <code>async</code> استثناءً سيُرفض وعدها، وعليه تكون هذه الطريقة ناجحةً.
</p>

<p>
	إذا استخدمت دالة ليست <code>async</code> كما وضحنا سابقًا، فستتسبب الطريقة التي تعمل بها <code>then</code> تلقائيًا في وجود الفشل في الوعد المعاد، وإذا فشل الطلب فلن يُستدعى المعالِج الممرَّر إلى <code>then</code>، ويُرفض الوعد الذي يعيده للسبب نفسه.
</p>

<h3>
	بناء Promise.all
</h3>

<p>
	إذا كانت لدينا مصفوفة من الوعود، فإن <code>Promise.all</code> تعيد وعدًا ينتظر جميع الوعود الأخرى في المصفوفة حتى انتهائها، حيث سينجح ذلك الوعد ويُحصِّل مصفوفةً من القيم الناتجة، فإذا فشل وعد في المصفوفة، فسيفشل الوعد المعاد من <code>all</code> أيضًا، ويكون سبب الفشل هو الوعد الفاشل.
</p>

<p>
	استخدِم شيئًا مثل ذلك بنفسك في صورة دالة عادية اسمها <code>Promise_all</code>، وتذكَّر أنّ الوعد لا يمكن أن ينجح أو يفشل بعد نجاحه أو فشله أول مرة، وأنّ الاستدعاءات إلى الدوال التي تحله تُتَجاهل، إذ يُبسِّط ذلك الطريقة التي تعالج بها فشل وعدك.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4526_13" style="">
<span class="kwd">function</span><span class="pln"> </span><span class="typ">Promise_all</span><span class="pun">(</span><span class="pln">promises</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// شيفرتك هنا.</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// شيفرة الاختبار.</span><span class="pln">
</span><span class="typ">Promise_all</span><span class="pun">([]).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">array </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"This should be []:"</span><span class="pun">,</span><span class="pln"> array</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> soon</span><span class="pun">(</span><span class="pln">val</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> resolve</span><span class="pun">(</span><span class="pln">val</span><span class="pun">),</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">500</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">Promise_all</span><span class="pun">([</span><span class="pln">soon</span><span class="pun">(</span><span class="lit">1</span><span class="pun">),</span><span class="pln"> soon</span><span class="pun">(</span><span class="lit">2</span><span class="pun">),</span><span class="pln"> soon</span><span class="pun">(</span><span class="lit">3</span><span class="pun">)]).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">array </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"This should be [1, 2, 3]:"</span><span class="pun">,</span><span class="pln"> array</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
</span><span class="typ">Promise_all</span><span class="pun">([</span><span class="pln">soon</span><span class="pun">(</span><span class="lit">1</span><span class="pun">),</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">reject</span><span class="pun">(</span><span class="str">"X"</span><span class="pun">),</span><span class="pln"> soon</span><span class="pun">(</span><span class="lit">3</span><span class="pun">)])</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">array </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"We should not get here"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
  </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">error </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error </span><span class="pun">!=</span><span class="pln"> </span><span class="str">"X"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Unexpected failure:"</span><span class="pun">,</span><span class="pln"> error</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span></pre>

<h4>
	إرشادات للحل
</h4>

<p>
	ستُستدعى <code>then</code> من قِبَل الدالة التي تمرَّر إلى باني <code>Promise</code> وذلك على كل وعد في المصفوفة المعطاة، وعند نجاح أحد تلك الوعود، فيجب تخزين القيمة الناتجة في الموضع الصحيح في المصفوفة الناتجة، كما يجب التحقق هل كان ذلك هو الوعد الأخير المنتظِر أم لا، وننهي وعدنا الخاص إن كان. ويمكن تنفيذ ذلك التحقق الأخير بعدَّاد يبدأ بطول مصفوفة الدخل، ونطرح منه 1 في كل مرة ينجح فيها أحد الوعود، ونكون قد انتهينا عند وصوله إلى الصفر.
</p>

<p>
	تأكد أن تضع في حسابك موقفًا تكون فيه مصفوفة الدخل فارغة من الوعود، فكيف تُحل الوعود وهي غير موجودة؟ تتطلب معالجة حالات الفشل بعض التأني، لكنها بسيطة في الوقت نفسه، إذ ما عليك سوى تمرير دالة <code>reject</code> للوعد المغلِّف إلى كل الوعود الموجودة في المصفوفة على أساس معالِج <code>catch</code>، أو على أساس وسيط ثاني إلى <code>then</code>، فإذا حدث فشل في أحدها، فسيطلق رفضًا للوعد المغلِّف كله.
</p>

<p>
	ترجمة -بتصرف- <a href="https://eloquentjavascript.net/11_async.html" rel="external nofollow">للفصل الحادي عشر من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1286/" rel="">الوحدات Modules في جافاسكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D9%83%D8%B1%D8%B1%D8%A7%D8%AA-iterators-%D9%88%D8%A7%D9%84%D9%85%D9%88%D9%84%D8%AF%D8%A7%D8%AA-generators-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r923/" rel="">المكررات iterators والمولدات generators غير المتزامنة في جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1304</guid><pubDate>Thu, 19 Aug 2021 15:00:00 +0000</pubDate></item><item><title>&#x62A;&#x62A;&#x628;&#x639; &#x62A;&#x642;&#x62F;&#x645; &#x639;&#x645;&#x644;&#x64A;&#x629; &#x62A;&#x646;&#x632;&#x64A;&#x644; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x639;&#x628;&#x631; Fetch &#x648;&#x645;&#x642;&#x627;&#x637;&#x639;&#x62A;&#x647;&#x627; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AA%D8%A8%D8%B9-%D8%AA%D9%82%D8%AF%D9%85-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D9%86%D8%B2%D9%8A%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%B9%D8%A8%D8%B1-fetch-%D9%88%D9%85%D9%82%D8%A7%D8%B7%D8%B9%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1295/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/611930c0b66d6_-----Fetch---Fetch.png.ef409eef19f1b9c3ca9ebc480ff55054.png" /></p>

<p>
	تتيح الدالة <code>fetch</code> تتبع عملية التنزيل download. لاحظ أنه لا توجد حاليًا طريقة تسمح للدالة <code>fetch</code> بتتبع عملية الرفع upload، نستخدم لهذه الغاية الكائن <a data-ss1634990087="1" data-ss1634991347="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86-xmlhttprequest-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1299" rel="">XMLHttpRequest</a> الذي سنغطيه لاحقًا.
</p>

<p>
	تُستخدم الخاصية <code>response.body</code> لتتبع تقدم التنزيل، وتمثل هذه الخاصية كائن <code>ReadableStream</code>، وهو كائن خاص يزودنا عند وصوله بجسم الطلب كتلةً بكتلة chunk-by-chunk، ستجد وصفًا لمجاري التدفق القابلة للقراءة Readable streams في توصيف الواجهة <a data-ss1634990087="1" data-ss1634991347="1" href="https://streams.spec.whatwg.org/#rs-class" rel="external nofollow">Streams <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a>، وتمنح الخاصية <code>response.body</code> تحكمًا كاملًا بعملية القراءة على خلاف التابعين <code>()response.text</code> و<code>()response.json</code> وغيرهما. كما تمنح إمكانية تقدير الوقت المستغرق في أية لحظة. إليك مثالُا عن شيفرة تقرأ الاستجابة من <code>response.body</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2238_8" style="">
<span class="com">// والطرق الأخرى  response.json() بدلا من </span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> reader </span><span class="pun">=</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">getReader</span><span class="pun">();</span><span class="pln">

</span><span class="com">// حلقة لا نهائية حتي يكتمل التنزيل</span><span class="pln">
</span><span class="kwd">while</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// عند آخر جزء true القيمة done ستحمل </span><span class="pln">
  </span><span class="com">//لبايتات كل جزء Unit8Array هو value </span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">done</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> await reader</span><span class="pun">.</span><span class="pln">read</span><span class="pun">();</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">done</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Received</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">value</span><span class="pun">.</span><span class="pln">length</span><span class="pun">}</span><span class="pln"> bytes</span><span class="pun">`)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستكون نتيجة الاستدعاء <code>()await reader.read</code> كائنًا له الخاصيتان التاليتان:
</p>

<ul>
<li>
		<code>done</code> : تأخذ القيمة <code>true</code> عندم اكتمال عملية القراءة، وإلا فستكون قيمتها <code>false</code>.
	</li>
	<li>
		<code>value</code> : مصفوفة من النوع <code>Uint8Array</code>.
	</li>
</ul>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		تصف الواجهة البرمجية المرور iteration غير المتزامن الذي يمكن تطبيقه على <code>ReadableStream</code> باستخدام الحلقة <code>for await..of</code>، لكنه لا يُدعم بشكل واسع (راجع <a data-ss1634990087="1" data-ss1634991347="1" href="https://github.com/whatwg/streams/issues/778#issuecomment-461341033" rel="external nofollow">browser issues</a>)، لذلك نستخدم الحلقة <code>while</code>، حيث سنحصل على أجزاء متتالية من الاستجابة عبرها حتى انتهاء التحميل، أي حتى تحمل الخاصية <code>done</code> القيمة <code>true</code>، وللحصول على سجل العملية سنحتاج إلى كل جزء <code>value</code> استقبلناه، وذلك لإضافة حجمه إلى عداد الحلقة، إليك المثال الكامل الذي يحصل على الاستجابة، ويصل إلى سجلات تقدم عملية التنزيل عبر الطرفية:
	</p>
</blockquote>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2238_10" style="">
<span class="com">//واحصل على قارئ للبيانات Fetch الخطوة1: إبدأ تنفيذ الدالة </span><span class="pln">
let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits?per_page=100'</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> reader </span><span class="pun">=</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">getReader</span><span class="pun">();</span><span class="pln">

</span><span class="com">// الخطوة2: احصل على الحجم الكلي</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> contentLength </span><span class="pun">=</span><span class="pln"> </span><span class="pun">+</span><span class="pln">response</span><span class="pun">.</span><span class="pln">headers</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'Content-Length'</span><span class="pun">);</span><span class="pln">

</span><span class="com">// الخطوة3 : إقرأ البيانات</span><span class="pln">
let receivedLength </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> </span><span class="com">// حجم البايتات المستقبلة حتى اللحظة</span><span class="pln">
let chunks </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln"> </span><span class="com">// مصفوفة الأجزاء المستلمة التي تمثل جسم الاستجابة</span><span class="pln">
</span><span class="kwd">while</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">done</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> await reader</span><span class="pun">.</span><span class="pln">read</span><span class="pun">();</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">done</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  chunks</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
  receivedLength </span><span class="pun">+=</span><span class="pln"> value</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln">

  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Received</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">receivedLength</span><span class="pun">}</span><span class="pln"> of $</span><span class="pun">{</span><span class="pln">contentLength</span><span class="pun">}`)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// الخطوة 4: ضم الأجزاء في مصفوفة واحدة</span><span class="pln">
let chunksAll </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint8Array</span><span class="pun">(</span><span class="pln">receivedLength</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (4.1)</span><span class="pln">
let position </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let chunk of chunks</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  chunksAll</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">,</span><span class="pln"> position</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (4.2)</span><span class="pln">
  position </span><span class="pun">+=</span><span class="pln"> chunk</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// الخطوة5: الترميز في سلسلة نصية</span><span class="pln">
let result </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TextDecoder</span><span class="pun">(</span><span class="str">"utf-8"</span><span class="pun">).</span><span class="pln">decode</span><span class="pun">(</span><span class="pln">chunksAll</span><span class="pun">);</span><span class="pln">

</span><span class="com">// النهاية</span><span class="pln">
let commits </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">commits</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">author</span><span class="pun">.</span><span class="pln">login</span><span class="pun">);</span></pre>

<p>
	المخرجات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2147_7" style="">
<span class="str">"Received 258566 of 0"</span><span class="pln">
</span><span class="str">"Received 444982 of 0"</span></pre>

<p>
	لنشرح الشيفرة السابقة:
</p>

<ol>
<li>
		لقد نفّذنا الدالة <code>ftech</code>، لكننا استخلصنا مجرى التدفق <code>()reader response.body.getReader</code> بدلًا من استدعاء التابع <code>()response.json</code>، ولايمكن استخدام الطريقتين معًا لقراءة الاستجابة، استخدم إحداهما للحصول على النتيجة.
	</li>
	<li>
		يمكننا قبل الشروع في قراءة الاستجابة تحديد الحجم الكلي لها عن طريق الترويسة <code>Content-Length</code>، وقد لا تكون الترويسة موجودةً في الطلبات ذات الأصل المختلط Cross-origin، لكن لن يُعدَّها الخادم عمليًا، وستبقى في مكانها.
	</li>
	<li>
		نستدعي التابع <code>()await reader.read</code> حتى ينهي عمله، ونُجمِّع أجزاء الاستجابة في المصفوفة <code>chunks</code>، وهذا الأمر ضروري لأن الاستجابة ستختفي ولن نتمكن من إعادة قراءتها باستخدام <code>()response.json</code> ولا بأي طريقة أخرى، وستحصل على خطأ إذا حاولت ذلك.
	</li>
	<li>
		سنحصل في النهاية على <code>chunks</code> وهي مصفوفة من الأجزاء لها النوع <code>Uint8Array</code>، وعلينا تجميعها ضمن نتيجة واحدة، ولسوء الحظ لا يوجد تابع لضمها، لهذا علينا كتابة الشيفرة التي ستنجز العملية:
	</li>
	<li>
		إنشاء المصفوفة <code>(chunksAll = new Uint8Array(receivedLength،</code> وهي مصفوفة من النوع <code>Uint8Array</code> لها حجم جميع الأجزاء.
	</li>
	<li>
		استخدام التابع <code>(set(chunk, position.</code> لنسخ كل جزء بدوره إليها.
	</li>
	<li>
		سنحصل على النتيجة ضمن المصفوفة <code>chunksAll</code>، وهي مصفوفة من البايتات وليست نصًا، ولتحويلها إلى نص لا بد من تفسير هذه البيانات عن طريق الكائن <a data-ss1634990087="1" data-ss1634991347="1" href="https://javascript.info/text-decoder" rel="external nofollow">TextDecoder</a>، ثم استخدام <code>JSON.parse</code> إن استدعت الحاجة.
	</li>
</ol>
<p>
	لكن ماذا لو احتجنا إلى محتوىً ثنائي بدل النص؟ سيكون الأمر أبسط، علينا فقط استبدال الخطوتين 4 و5 بسطر وحيد يُنشئ كائن بيانات ثنائية <code>Blob</code> يضم كل الأجزاء.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2238_13" style="">
<span class="pln">   let blob </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Blob</span><span class="pun">(</span><span class="pln">chunks</span><span class="pun">);</span></pre>

<p>
	وهكذا سنحصل على النتيجة بالصيغة التي نريد مع إمكانية تتبع تقدم العملية، مرةً أخرى يجب الانتباه إلى أن العملية غير صالحة لتتبع تقدم عملية الرفع، أي لا يمكن استخدام <code>Fetch</code>، بل فقط للتنزيل.
</p>

<p>
	ولا بد من التحقق من حجم البيانات الواصلة <code>receivedLength</code> في كل لحظة ضمن الحلقة وإنهائها بمجرد وصولها إلى حد معين، إذا لم يكن حجم البيانات التي سنستقبلها معروفًا، وبالتالي لن تستهلك المصفوفة <code>chunks</code> الذاكرة.
</p>

<h2>
	الكائن AbortController: مقاطعة العمليات غير المتزامنة
</h2>

<p>
	تعيد <code>fetch</code> كما نعرف وعدًا promise، ولكننا نعلم أنّ JavaScript لا تقبل إلغاء الوعود عمومًا، فكيف سنلغي عملية <code>fetch</code> أثناء تنفيذها؟
</p>

<p>
	هنالك كائن خاص مدمج لهذا الغرض هو <code>AbortController</code>، يمكن استخدامه لإلغاء <code>fetch</code> وغيرها من المهام غير المتزامنة، ويطبّق مباشرةً.
</p>

<p>
	لننشئ متحكمًا بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2238_15" style="">
<span class="pln">let controller </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AbortController</span><span class="pun">();</span></pre>

<p>
	والمتحكم هو كائن شديد البساطة، له:
</p>

<ul>
<li>
		تابع وحيد هو <code>()abort</code>.
	</li>
	<li>
		وخاصية واحدة هي <code>signal</code> تسمح بإعداد مستمع حدث event listener له.
	</li>
</ul>
<p>
	أما عند استدعاء التابع <code>()abort</code> فسيحدث الآتي:
</p>

<ul>
<li>
		تحرّض الخاصية <code>controller.signal</code> وقوع الحدث <code>abort</code>.
	</li>
	<li>
		تأخذ الخاصية <code>controller.signal.aborted</code> القيمة <code>true</code>.
	</li>
</ul>
<p>
	تكون للعملية في العادة مرحلتان:
</p>

<ol>
<li>
		المرحلة التي تُنفِّذ عمليةً قابلة للإلغاء، وتهيئ مستمع حدث للخاصية <code>controller.signal</code>.
	</li>
	<li>
		المرحلة التي تلغي: وتُنفَّذ باستدعاء التابع <code>()controller.abort</code> عندما يتطلب الأمر.
	</li>
</ol>
<p>
	إليك مثالًا كاملًا دون <code>Fetch</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2238_17" style="">
<span class="pln">let controller </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AbortController</span><span class="pun">();</span><span class="pln">
let signal </span><span class="pun">=</span><span class="pln"> controller</span><span class="pun">.</span><span class="pln">signal</span><span class="pun">;</span><span class="pln">

</span><span class="com">// القيام بعملية قابلة للإلغاء</span><span class="pln">
</span><span class="com">// "signal" الحصول على الكائن </span><span class="pln">
</span><span class="com">// controller.abort() ضبط إطلاق المستمع عند استدعاء </span><span class="pln">
signal</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'abort'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">"abort!"</span><span class="pun">));</span><span class="pln">

</span><span class="com">// القيام بالإلغاء</span><span class="pln">
controller</span><span class="pun">.</span><span class="pln">abort</span><span class="pun">();</span><span class="pln"> </span><span class="com">// abort!</span><span class="pln">

</span><span class="com">// true القيمة  signal.aborted إطلاق الحدث ويصبح لــ </span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">signal</span><span class="pun">.</span><span class="pln">aborted</span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span></pre>

<p>
	لاحظ أنّ الكائن <code>AbortController</code> هو مجرد أداة لتمرير الحدث <code>abort</code> عند استدعاء التابع <code>()abort</code>، ومن الواضح أنه بالإمكان تنفيذ مستمع حدث كهذا باستخدام شيفرتنا الخاصة دون الحاجة إلى <code>AbortController</code>، لكن أهميته ستظهر عندما نعلم أن <code>fetch</code> تعرف تمامًا كيفية التعامل معه، فهو متكامل معها.
</p>

<h2>
	مقاطعة العملية Fetch
</h2>

<p>
	لإلغاء العملية <code>fetch</code> علينا تمرير قيمة الخاصية <code>signal</code> العائدة للكائن <code>AbortController</code> مثل خيار لها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2238_19" style="">
<span class="pln">let controller </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AbortController</span><span class="pun">();</span><span class="pln">
fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  signal</span><span class="pun">:</span><span class="pln"> controller</span><span class="pun">.</span><span class="pln">signal
</span><span class="pun">});</span></pre>

<p>
	تعلم <code>fetch</code> تمامًا كيفية التعامل مع <code>AbortController</code>، وستستمع إلى الحدث <code>abort</code> الذي تحرّض الخاصية <code>signal</code> وقوعه.
</p>

<p>
	لإلغاء العملية سنستدعي التابع:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2238_22" style="">
<span class="pln">controller</span><span class="pun">.</span><span class="pln">abort</span><span class="pun">();</span></pre>

<p>
	وهكذا تلغى العملية، حيث تحصل <code>fetch</code> على الحدث <code>abort</code> من الخاصية <code>signal</code> وتلغي الطلب، عند إلغاء <code>fetch</code> سيُرفض الوعد الذي تعيده وسيُرمى الخطأ <code>AbortError</code>، وينبغي التعامل معه من خلال حلقة <code>try..catch</code> مثلًا.
</p>

<p>
	إليك مثالًا كاملًا مع استخدام <code>fetch</code>، حيث تلغى العملية بعد ثانية واحدة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2238_25" style="">
<span class="com">// الإلغاء خلال ثانية واحدة </span><span class="pln">
let controller </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AbortController</span><span class="pun">();</span><span class="pln">
setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> controller</span><span class="pun">.</span><span class="pln">abort</span><span class="pun">(),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'/article/fetch-abort/demo/hang'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    signal</span><span class="pun">:</span><span class="pln"> controller</span><span class="pun">.</span><span class="pln">signal
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">.</span><span class="pln">name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'AbortError'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// handle abort()</span><span class="pln">
    alert</span><span class="pun">(</span><span class="str">"Aborted!"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> err</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	كائن قابل للتوسع
</h2>

<p>
	يسمح الكائن<code>AbortController</code> بإلغاء عدة عمليات معًا، إليك الشيفرة التمثيلية التالية التي تحضر عدة موارد على التوازي، ثم تستخدم كائن متحكم وحيدًا لإلغائها جميعًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2238_27" style="">
<span class="pln">let urls </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[...];</span><span class="pln"> </span><span class="com">//قائمة بالموارد التي ينبغي إحضارها</span><span class="pln">

let controller </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AbortController</span><span class="pun">();</span><span class="pln">

</span><span class="com">// fetch مصفوفة من الوعود التي ستعيدها عمليات </span><span class="pln">
let fetchJobs </span><span class="pun">=</span><span class="pln"> urls</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">url </span><span class="pun">=&gt;</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  signal</span><span class="pun">:</span><span class="pln"> controller</span><span class="pun">.</span><span class="pln">signal
</span><span class="pun">}));</span><span class="pln">

let results </span><span class="pun">=</span><span class="pln"> await </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">(</span><span class="pln">fetchJobs</span><span class="pun">);</span><span class="pln">

</span><span class="com">// بمجرد استدعاء حدث الإلغاء ستلغى جميع عمليات الإحضار</span></pre>

<p>
	وسنتمكن أيضًا من إلغاء أي عمليات أخرى غير متزامنة مع عمليات <code>fetch</code> باستخدام كائن <code>AbortController</code> وحيد، بمجرد الاستماع إلى الحدث <code>abort</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2238_29" style="">
<span class="pln">let urls </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[...];</span><span class="pln">
let controller </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AbortController</span><span class="pun">();</span><span class="pln">

let ourJob </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">((</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// المهمة المطلوب إلغاءها</span><span class="pln">
  </span><span class="pun">...</span><span class="pln">
  controller</span><span class="pun">.</span><span class="pln">signal</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'abort'</span><span class="pun">,</span><span class="pln"> reject</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

let fetchJobs </span><span class="pun">=</span><span class="pln"> urls</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">url </span><span class="pun">=&gt;</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// عمليات الإحضار</span><span class="pln">
  signal</span><span class="pun">:</span><span class="pln"> controller</span><span class="pun">.</span><span class="pln">signal
</span><span class="pun">}));</span><span class="pln">

</span><span class="com">// انتظار إنجاز جميع العمليات</span><span class="pln">
let results </span><span class="pun">=</span><span class="pln"> await </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">([...</span><span class="pln">fetchJobs</span><span class="pun">,</span><span class="pln"> ourJob</span><span class="pun">]);</span><span class="pln">

</span><span class="com">// بمجرد استدعاء حدث الإلغاء ستلغى جميع عمليات الإحضار</span><span class="pln">
</span><span class="com">// بالإضافة إلى بقية المهام</span></pre>

<h2>
	خلاصة
</h2>

<p>
	بهذا نكون قد تعرفنا على كيفية تتبع عملية التنزيل باستخدام Fetch، وذلك بالاعتماد على عدة خاصيات، كما تعرفنا على كيفية مقاطعة العملية Fetch، وذلك بالاعتماد على الكائنات الآتية:
</p>

<ul>
<li>
		<code>AbortController</code>: هو كائن بسيط يولّد الحدث <code>abort</code> على الخاصية <code>signal</code> عند استدعاء التابع <code>()abort</code>، الذي يعطي الخاصية <code>signal.aborted</code> القيمة "true" أيضًا.
	</li>
	<li>
		تتكامل <code>fetch</code> مع هذا الكائن، حيث تُمرر الخاصية <code>signal</code> كخيار لتستمع إليه، وبالتالي يصبح إلغاؤها ممكنًا.
	</li>
	<li>
		يمكن استخدام <code>AbortController</code> في شيفرتنا، حيث يستمع التابع <code>()abort</code> إلى الحدث <code>abort</code> بعملية بسيطة تطبق في أي مكان، كما يمكن استخدامها دون استخدام <code>fetch</code>.
	</li>
</ul>
<p>
	ترجمة -وبتصرف- للفصلين <a data-ss1634990087="1" data-ss1634991347="1" href="https://javascript.info/fetch-progress" rel="external nofollow">Fetch: Download Progress</a> و<a data-ss1634990087="1" data-ss1634991347="1" href="https://javascript.info/fetch-abort" rel="external nofollow">Fetch: Abort</a> من سلسلة <a href="https://javascript.info/" rel="external nofollow">The Modern JavaScript Tutorial</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a data-ss1634990087="1" data-ss1634991347="1" href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1283/" rel="">ترميز النصوص والتعامل مع كائنات الملفات في جافاسكريبت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1295</guid><pubDate>Tue, 17 Aug 2021 15:08:00 +0000</pubDate></item><item><title>&#x625;&#x631;&#x633;&#x627;&#x644; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x648;&#x627;&#x633;&#x62A;&#x644;&#x627;&#x645;&#x647;&#x627; &#x639;&#x628;&#x631; &#x627;&#x644;&#x634;&#x628;&#x643;&#x629; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A5%D8%B1%D8%B3%D8%A7%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D8%B3%D8%AA%D9%84%D8%A7%D9%85%D9%87%D8%A7-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1294/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/611920ada8f20_-Fetch----formdata.png.02227ca2ed7dd5652a95a5b2de57ef56.png" /></p>

<p>
	يمكن للغة جافاسكربت JavaScript إرسال طلبات شبكة إلى الخادم وتحميل معلومات جديدة عندما يتطلب الأمر ذلك، إذ يمكننا على سبيل المثال استخدام طلبات الشبكة في الحالات التالية:
</p>

<ul>
<li>
		إرسال طلب.
	</li>
	<li>
		بتحميل معلومات مستخدم.
	</li>
	<li>
		الحصول على آخر التحديثات من الخادم.
	</li>
</ul>
<p>
	ويجري كل ذلك دون إعادة تحميل الصفحة.
</p>

<p>
	تنضوي طلبات الشبكة التي تنفذها لغة JavaScript تحت المظلة AJAX، وهي اختصار للعبارة <strong>A</strong>synchronous <strong>J</strong>avaScript <strong>A</strong>nd <strong>X</strong>ML، ورغم ذلك لا نحتاج إلى استخدام XML، فقد وضعت العبارة السابقة منذ فترة طويلة لذلك وجدت هذه الكلمة ضمنها، وقد تكون سمعت بهذه العبارة الآن أيضًا.
</p>

<p>
	هنالك طرق عديدة لإرسال طلبات عبر الشبكة والحصول على معلومات من الخادم، وسنبدأ بالطريقة الأحدث <code>()fetch</code>، علمًا أنه لا تدعم المتصفحات القديمة هذه الدالة (ويمكن الاستعاضة عنها بشيفرة بديلة)، لكنها مدعومة جيدًا في المتصفحات الحديثة، وإليك صيغتها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_7" style="">
<span class="pln">let promise </span><span class="pun">=</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">options</span><span class="pun">])</span></pre>

<p>
	حيث:
</p>

<ul>
<li>
		<code>url</code>: عنوان المورد الذي ستصل إليه الدالة.
	</li>
	<li>
		<code>options</code>: المعاملات الاختيارية من توابع وترويسات وغيرها.
	</li>
</ul>
<p>
	تتحول الدالة إلى طلب GET بسيط لتنزيل محتوى العنوان <code>url</code> إن لم تكن هناك معاملات اختيارية <code>options</code>، ويبدأ المتصفح الطلب مباشرةً ويعيد وعدًا promise ستستخدمه الشيفرة التي تستدعي الطلب للحصول على النتيجة، وتكون الاستجابة عادةً عمليةً بمرحلتين:
</p>

<p>
	<strong>الأولى</strong>: يُحلَّل الوعد الذي تعيده <code>fetch</code> عبر كائن من الصنف <a data-ss1634989018="1" data-ss1634989618="1" href="https://fetch.spec.whatwg.org/#response-class" rel="external nofollow">Respo-nse</a> حالما يستجيب الخادم بالترويسات المناسبة، ويمكن التحقق من نجاح الطلب أو عدم نجاحه، والتحقق أيضًا من الترويسات، لكن لن يصل جسم الطلب في هذه المرحلة، ويُرفَض الوعد إن لم تكن <code>fetch</code> قادرةً على إنجاز طلب HTTP لمشاكل في الشبكة مثلًا، أو لعدم وجود موقع على العنوان المُعطى، ولن تسبب حالات HTTP غير العادية مثل 404 أو 500 أخطاءً.
</p>

<p>
	يمكن معرفة حالة طلب من خصائص الاستجابة:
</p>

<ul>
<li>
		<code>status</code>: رمز الحالة status code لطلب HTTP مثل الرمز 200.
	</li>
	<li>
		<code>ok</code>: قيمة منطقية "true" عندما يكون رمز الحالة بين 200 و299.
	</li>
</ul>
<p>
	إليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_11" style="">
<span class="pln">let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">ok</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// إن كان رمز الحالة بين 200-299</span><span class="pln">
  </span><span class="com">// الحصول على جسم الطلب</span><span class="pln">
  let json </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">"HTTP-Error: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">status</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<strong>الثانية</strong>: استخدام استدعاء إضافي للحصول على جسم الطلب، ويؤمن الكائن <code>Response</code> عدة توابع مبنية على الوعد للوصول إلى جسم الطلب وبتنسيقات مختلفة:
</p>

<ul>
<li>
		<code>()response.text</code>: لقراءة الاستجابة وإعادة نص.
	</li>
	<li>
		<code>()response.json</code>: يفسِّر النص وفق تنسيق JSON.
	</li>
	<li>
		<code>()response.formData</code>: يعيد الاستجابة على شكل كائن <code>FormData</code> سنشرحه في الفقرة التالية.
	</li>
	<li>
		<code>()response.blob</code>: يعيد الاستجابة على شكل كائن البيانات الثنائية <a data-ss1634989018="1" data-ss1634989618="1" href="https://academy.hsoub.com/programming/javascript/%D9%83%D8%A7%D8%A6%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-blob-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1285/" rel="">Blob</a>.
	</li>
	<li>
		<code>()response.arrayBuffer</code>: يعيد الاستجابة على شكل كائن <a data-ss1634989018="1" data-ss1634989618="1" href="https://javascript.info/arraybuffer-binary-arrays" rel="external nofollow">ArrayBuffer</a> وهو تمثيل منخفض المستوى للبيانات الثنائية.
	</li>
	<li>
		الكائن <code>response.body</code> وهو كائن من الصنف <a data-ss1634989018="1" data-ss1634989618="1" href="https://streams.spec.whatwg.org/#rs-class" rel="external nofollow">ReadableStream</a> يسمح بقراءة جسم الطلب كتلةً كتلةً، وسنعرض مثالًا عن ذلك لاحقًا.
	</li>
</ul>
<p>
	لنحاول على سبيل المثال الحصول على كائن JSON من آخر نسخة معتمدة لموقع الدورة التعليمية هذه على GitHub:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_13" style="">
<span class="pln">let url </span><span class="pun">=</span><span class="pln"> </span><span class="str">'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'</span><span class="pun">;</span><span class="pln">
let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">);</span><span class="pln">

let commits </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln"> </span><span class="com">// Json قراءة الاستجابة على شكل شيفرة</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">commits</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">author</span><span class="pun">.</span><span class="pln">login</span><span class="pun">);</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634989018="1" data-ss1634989618="1" frameborder="no" height="300" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/rNmgJdd?default-tab=html" style="width: 100%;" title="JS-P3-Fetch-ex02">See the Pen JS-P3-Fetch-ex02 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	كما يمكن فعل ذلك من خلال الوعود الصرفة دون استخدام <code>await</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_15" style="">
<span class="pln">fetch</span><span class="pun">(</span><span class="str">'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">commits </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">commits</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">author</span><span class="pun">.</span><span class="pln">login</span><span class="pun">));</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634989018="1" data-ss1634989618="1" frameborder="no" height="233" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/oNWREyV?default-tab=html" style="width: 100%;" title="JS-P3-Fetch-ex03">See the Pen JS-P3-Fetch-ex03 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	استخدم <code>()await response.text</code> للحصول على نص الطلب بدلًا من <code>()json.</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_17" style="">
<span class="pln">let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'</span><span class="pun">);</span><span class="pln">

let text </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">text</span><span class="pun">();</span><span class="pln"> </span><span class="com">// قراءة جسم الاستجابة على شكل نص</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">text</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">80</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'...'</span><span class="pun">);</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634989018="1" data-ss1634989618="1" frameborder="no" height="300" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/xxdNYJQ?default-tab=html" style="width: 100%;" title="JS-P3-Fetch-ex04">See the Pen JS-P3-Fetch-ex04 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لنستعرض مثالًا عن قراءة بيانات بالصيغة الثنائية، ونحضر صورةً ما ونظهرها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5459_8" style="">
<span class="pln">let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'https://javascript.info/article/fetch/logo-fetch.svg'</span><span class="pun">);</span><span class="pln">

let blob </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">blob</span><span class="pun">();</span><span class="pln"> </span><span class="com">// Blob تنزيل على شكل </span><span class="pln">

</span><span class="com">// &lt;img&gt;  إنشاء عنصر </span><span class="pln">
let img </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'img'</span><span class="pun">);</span><span class="pln">
img</span><span class="pun">.</span><span class="pln">style </span><span class="pun">=</span><span class="pln"> </span><span class="str">'position:fixed;top:10px;left:10px;width:100px'</span><span class="pun">;</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">img</span><span class="pun">);</span><span class="pln">

</span><span class="com">// إظهاره</span><span class="pln">
img</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> URL</span><span class="pun">.</span><span class="pln">createObjectURL</span><span class="pun">(</span><span class="pln">blob</span><span class="pun">);</span><span class="pln">

setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// إخفاءه بعد ثلاث ثوان</span><span class="pln">
  img</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">();</span><span class="pln">
  URL</span><span class="pun">.</span><span class="pln">revokeObjectURL</span><span class="pun">(</span><span class="pln">img</span><span class="pun">.</span><span class="pln">src</span><span class="pun">);</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">);</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634989018="1" data-ss1634989618="1" frameborder="no" height="300" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/OJmYQoN?default-tab=html" style="width: 100%;" title="JS-P3-Fetch-ex05">See the Pen JS-P3-Fetch-ex05 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>أمر هام:</strong> يمكن اختيار طريقة واحدة لقراءة جسم الطلب، فلو تلقينا الاستجابة باستخدام التابع <code>()response.text</code>؛ فلن يعمل بعدها التابع <code>()response.json</code> لأنّ معالجة جسم الطلب قد أجريت وانتهت.
	</p>
</blockquote>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_21" style="">
<span class="pln">let text </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">text</span><span class="pun">();</span><span class="pln"> </span><span class="com">// انتهاء معالجة جسم الطلب</span><span class="pln">
let parsed </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln"> </span><span class="com">// سيخفق، فقد جرت المعالجة وانتهت</span></pre>

<h2>
	ترويسات الاستجابة
</h2>

<p>
	يمكن الحصول على ترويسات الاستجابة على شكل كائن ترويسات شبيه بالترابط Map من خلال الأمر <code>response.headers</code>، ولا يُعَد الكائن ترابطًا تمامًا، لكنه يمتلك توابع مماثلةً للحصول على ترويسات من خلال اسمها أو بالمرور عليها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_23" style="">
<span class="pln">let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'</span><span class="pun">);</span><span class="pln">

</span><span class="com">// الحصول على ترويسة واحدة</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">headers</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'Content-Type'</span><span class="pun">));</span><span class="pln"> </span><span class="com">// application/json; charset=utf-8</span><span class="pln">

</span><span class="com">// المرور على الترويسات كلها </span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let </span><span class="pun">[</span><span class="pln">key</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">]</span><span class="pln"> of response</span><span class="pun">.</span><span class="pln">headers</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">key</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">value</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634989018="1" data-ss1634989618="1" frameborder="no" height="300" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/JjNqpaw?default-tab=html" style="width: 100%;" title="JS-P3-Fetch-ex06">See the Pen JS-P3-Fetch-ex06 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h2>
	ترويسات الطلب
</h2>

<p>
	يمكن استخدام خيار الترويسات <code>headers</code> لإعداد ترويسة الطلب في الدالة <code>featch</code>، إذ تمتلك كائنًا يضم الترويسات المُرسَلة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_25" style="">
<span class="pln">let response </span><span class="pun">=</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">protectedUrl</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">Authentication</span><span class="pun">:</span><span class="pln"> </span><span class="str">'secret'</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	لكن هناك قائمة من <a data-ss1634989018="1" data-ss1634989618="1" href="https://fetch.spec.whatwg.org/#forbidden-header-name" rel="external nofollow">ترويسات HTTP المنوعة</a> التي لا يمكن ضبطها:
</p>

<ul>
<li>
		<code>Accept-Charset</code> و<code>Accept-Encoding</code>
	</li>
	<li>
		<code>Access-Control-Request-Headers</code>
	</li>
	<li>
		<code>Access-Control-Request-Method</code>
	</li>
	<li>
		<code>Connection</code>
	</li>
	<li>
		<code>Content-Length</code>
	</li>
	<li>
		<code>Cookie</code> و<code>Cookie2</code>
	</li>
	<li>
		<code>Date</code>
	</li>
	<li>
		<code>DNT</code>
	</li>
	<li>
		<code>Expect</code>
	</li>
	<li>
		<code>Host</code>
	</li>
	<li>
		<code>Keep-Alive</code>
	</li>
	<li>
		<code>Origin</code>
	</li>
	<li>
		<code>Referer</code>
	</li>
	<li>
		<code>TE</code>
	</li>
	<li>
		<code>Trailer</code>
	</li>
	<li>
		<code>Transfer-Encoding</code>
	</li>
	<li>
		<code>Upgrade</code>
	</li>
	<li>
		<code>Via</code>
	</li>
	<li>
		<code>Proxy-*‎</code>
	</li>
	<li>
		<code>Sec-*‎</code>
	</li>
</ul>
<p>
	تضمن هذه الترويسات ملاءمة طلبات HTTP وأمانها، لذلك يتحكم فيها المتصفح حصرًا.
</p>

<h2>
	طلبات الكتابة POST
</h2>

<p>
	لإرسال طلب POST أو طلب من أي نوع لا بدّ من استخدام خيارات <code>fetch</code>:
</p>

<ul>
<li>
		<code>method</code>: نوع طلب HTTP مثل HTTP-POST.
	</li>
	<li>
		<code>body</code>: ويمثل جسم الطلب وقد يكون:
	</li>
	<li>
		نصًا: بتنسيق JSON مثلًا.
	</li>
	<li>
		كائن <code>FormData</code> لإرسال بيانات على شكل <code>form/multipart</code>.
	</li>
	<li>
		كائن <code>Blob</code> أو <code>BufferSource</code>لإرسال بيانات ثنائية.
	</li>
	<li>
		<a data-ss1634989018="1" data-ss1634989618="1" href="https://javascript.info/url" rel="external nofollow">URLSearchParams</a> لإرسال البيانات بتشفير <code>x-www-form-urlencoded</code>، وهو نادر الاستخدام.
	</li>
</ul>
<p>
	يُستخدم تنسيق JSON غالبًا، حيث تُرسل الشيفرة التالية الكائن <code>user</code> وفق تنسيق JSON مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5459_10" style="">
<span class="pln">let user </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'John'</span><span class="pun">,</span><span class="pln">
  surname</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Smith'</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'https://javascript.info/article/fetch/post/user'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
  headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">'Content-Type'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'application/json;charset=utf-8'</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  body</span><span class="pun">:</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">user</span><span class="pun">)</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

let result </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span></pre>

<p>
	لاحظ ضبط الترويسة <code>Content-Type</code> افتراضيًا على القيمتين <code>text/plain;charset=UTF-8</code> إذا كان جسم الطلب على شكل نص، لكن طالما أننا سنرسل البيانات بصيغة JSON، فسنستخدم الخيار <code>headers</code> لإرسال الترويسة <code>application/json</code> بدلًا عن <code>text/plain</code> كونها تمثل المحتوى الصحيح للبيانات.
</p>

<h2>
	إرسال صورة
</h2>

<p>
	يمكن إرسال بيانات ثنائية عبر الدالة <code>fetch</code> باستخدام الكائنات <code>Blob</code> أو <code>BufferSource</code>، سنجد في المثال التالي معرّف لوحة رسم <code>&lt;canvas&gt;</code> التي يمكننا الرسم ضمنها بتحريك الفأرة، ومن ثم إرسال الصورة الناتجة إلى الخادم عند النقر على الزر "submit":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_29" style="">
<span class="pun">&lt;</span><span class="pln">body style</span><span class="pun">=</span><span class="str">"margin:0"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">canvas id</span><span class="pun">=</span><span class="str">"canvasElem"</span><span class="pln"> width</span><span class="pun">=</span><span class="str">"100"</span><span class="pln"> height</span><span class="pun">=</span><span class="str">"80"</span><span class="pln"> style</span><span class="pun">=</span><span class="str">"border:1px solid"</span><span class="pun">&gt;&lt;/</span><span class="pln">canvas</span><span class="pun">&gt;</span><span class="pln">

  </span><span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> value</span><span class="pun">=</span><span class="str">"Submit"</span><span class="pln"> onclick</span><span class="pun">=</span><span class="str">"submit()"</span><span class="pun">&gt;</span><span class="pln">

  </span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
    canvasElem</span><span class="pun">.</span><span class="pln">onmousemove </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      let ctx </span><span class="pun">=</span><span class="pln"> canvasElem</span><span class="pun">.</span><span class="pln">getContext</span><span class="pun">(</span><span class="str">'2d'</span><span class="pun">);</span><span class="pln">
      ctx</span><span class="pun">.</span><span class="pln">lineTo</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">clientX</span><span class="pun">,</span><span class="pln"> e</span><span class="pun">.</span><span class="pln">clientY</span><span class="pun">);</span><span class="pln">
      ctx</span><span class="pun">.</span><span class="pln">stroke</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">

    async </span><span class="kwd">function</span><span class="pln"> submit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      let blob </span><span class="pun">=</span><span class="pln"> await </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> canvasElem</span><span class="pun">.</span><span class="pln">toBlob</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> </span><span class="str">'image/png'</span><span class="pun">));</span><span class="pln">
      let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'/article/fetch/post/image'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
        body</span><span class="pun">:</span><span class="pln"> blob
      </span><span class="pun">});</span><span class="pln">

      </span><span class="com">// يستجيب الخادم بتأكيد وصول البيانات وبحجم الصورة</span><span class="pln">
      let result </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
      alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

  </span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">body</span><span class="pun">&gt;</span></pre>

<p>
	ستظهر النتيجة كالتالي:
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634989018="1" data-ss1634989618="1" frameborder="no" height="181" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/bGWyLmB?default-tab=result" style="width: 100%;" title="JS-P3-Fetch-ex07">See the Pen JS-P3-Fetch-ex07 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لاحظ أننا لم نضبط هنا قيمة الترويسة <code>Content-Type</code> يدويًا، لأنّ الكائن <code>Blob</code> له نوع مضمَّن (هو <code>image/png</code> في حالتنا، كما ولّده التابع <code>toBlob</code>)، وسيمثّل هذا النوع قيمة الترويسة <code>Content-Type</code> في كائنات <code>Blob</code>.
</p>

<p>
	يمكن كتابة الدالة <code>()submit</code> دون استخدام الصيغة <code>async/await</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5459_12" style="">
<span class="kwd">function</span><span class="pln"> submit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  canvasElem</span><span class="pun">.</span><span class="pln">toBlob</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">blob</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fetch</span><span class="pun">(</span><span class="str">'https://javascript.info/article/fetch/post/image'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
      body</span><span class="pun">:</span><span class="pln"> blob
    </span><span class="pun">})</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">result </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="pln">JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">result</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)))</span><span class="pln">
  </span><span class="pun">},</span><span class="pln"> </span><span class="str">'image/png'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	استخدام الكائن FormData لإرسال النماذج
</h2>

<p>
	يمكننا الاستفادة من الكائن <a data-ss1634989018="1" data-ss1634989618="1" href="https://xhr.spec.whatwg.org/#interface-formdata" rel="external nofollow">FormData</a> لإرسال نماذج HTML مع ملفات أو بدونها، بالإضافة إلى حقول إضافية. وكما قد تخمِّن؛ يمثل هذا الكائن بيانات نماذج HTML، وإليك صيغة الدالة البانية له:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_33" style="">
<span class="pln">let formData </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">FormData</span><span class="pun">([</span><span class="pln">form</span><span class="pun">]);</span></pre>

<p>
	سيتحكم الكائن <a data-ss1634989018="1" data-ss1634989618="1" href="https://xhr.spec.whatwg.org/#interface-formdata" rel="external nofollow">FormData</a> تلقائيًا بحقول العنصر <code>form</code> إذا استُخدم في مستند HTML، وما يميز الكائن <code>FormData</code> هو أنّ توابع إرسال الطلبات واستقبالها عبر الشبكة مثل <code>Fetch</code> ستقبله مثل جسم للطلب، إذ يُشفَّر ويُرسَل بترويسة قيمتها <code>Content-Type: multipart/form-data</code>، وتبدو العملية بالنسبة إلى الخادم مثل إرسال عادي لنموذج.
</p>

<h3>
	إرسال نموذج بسيط
</h3>

<p>
	لنرسل أولًا نموذجًا بسيطًا، وسيظهر في مثالنا هذا في نموذج من سطر واحد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5459_14" style="">
<span class="pun">&lt;</span><span class="pln">form id</span><span class="pun">=</span><span class="str">"formElem"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"name"</span><span class="pln"> value</span><span class="pun">=</span><span class="str">"John"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"surname"</span><span class="pln"> value</span><span class="pun">=</span><span class="str">"Smith"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"submit"</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  formElem</span><span class="pun">.</span><span class="pln">onsubmit </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    e</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">

    let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'https://javascript.info/article/formdata/post/user'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
      body</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">FormData</span><span class="pun">(</span><span class="pln">formElem</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    let result </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">

    alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	وستكون النتيجة كالتالي:
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634989018="1" data-ss1634989618="1" frameborder="no" height="140" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/wvdbyOo?default-tab=result" style="width: 100%;" title="JS-P3-FormData-ex01">See the Pen JS-P3-FormData-ex01 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لا توجد شيفرة خاصة بالخادم في هذا المثال، لأنها خارج نطاق هذه الدورة التعليمية، حيث سيقبل الخادم الطلب HTTP-POST ويستجيب بالرسالة "User saved" أي "خُزّن المستخدم".
</p>

<h3>
	توابع الكائن
</h3>

<p>
	نستخدم عددًا من التوابع لتعديل الحقول في الكائن <code>FormData</code>:
</p>

<ul>
<li>
		<code>(formData.append(name, value</code>: يُضيف حقلًا بالاسم <code>name</code> قيمته هي <code>value</code>.
	</li>
	<li>
		<code>(formData.append(name, blob, fileName</code>: يضيف حقلًا كما لو أنه العنصر <code>&lt;"input type="file&gt;</code>، حيث يحدد الوسيط الثالث للتابع <code>fileName</code> اسم الملف -وليس اسم الحقل- كما لو أنه اسم لملف في منظومة ملفات الجهاز.
	</li>
	<li>
		<code>(formData.delete(name</code>: يزيل حقلًا محددًا بالاسم <code>name</code>.
	</li>
	<li>
		<code>(formData.get(name</code>: يعطي قيمة الحقل المحدد بالاسم<code>name</code>.
	</li>
	<li>
		<code>(formData.has(name</code>: إذا وجد حقل بالاسم <code>name</code> فسيعيد القيمة <code>true</code> وإلا <code>false</code>.
	</li>
</ul>
<p>
	يمكن أن يحوي النموذج العديد من الحقول التي لها نفس الاسم، لذلك سينتج عن الاستدعاءات المختلفة لضم <code>append</code> الحقول حقولًا لها نفس الاسم. وسنجد التابع <code>set</code> الذي له صيغة <code>append</code> نفسها، لكنه يزيل جميع الحقول التي لها اسم محدد <code>name</code>، ثم يضيف الحقل الجديد، وبالتالي سنضمن وجود حقل وحيد بالاسم <code>name</code>، تشبه باقي التفاصيل التابع <code>append</code>.
</p>

<ul>
<li>
		<code>(formData.set(name, value</code>
	</li>
	<li>
		<code>(formData.set(name, blob, fileName</code>.
	</li>
</ul>
<p>
	يمكن أيضًا إجراء تعداد على عناصر الكائن <code>FormData</code> باستخدام الحلقة <code>for..of</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_37" style="">
<span class="pln">let formData </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">FormData</span><span class="pun">();</span><span class="pln">
formData</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="str">'key1'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'value1'</span><span class="pun">);</span><span class="pln">
formData</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="str">'key2'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'value2'</span><span class="pun">);</span><span class="pln">

</span><span class="com">// قائمة من الأزواج مفتاح/قيمة</span><span class="pln">
</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let </span><span class="pun">[</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">]</span><span class="pln"> of formData</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">name</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">value</span><span class="pun">}`);</span><span class="pln"> </span><span class="com">// key1 = value1, then key2 = value2</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634989018="1" data-ss1634989618="1" frameborder="no" height="300" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/WNjBMWO?default-tab=html" style="width: 100%;" title="JS-P3-FormData-ex02">See the Pen JS-P3-FormData-ex02 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h3>
	إرسال نموذج مع ملف
</h3>

<p>
	يُرسَل النموذج دائمًا بحيث تكون ترويسة المحتوى مثل التالي <code>Content-Type: multipart/form-data</code>، وتسمح هذه الطريقة في الترميز بإرسال الملفات، أي ستُرسَل الملفات التي يحددها العنصر <code>&lt;"input type="file&gt;</code> أيضًا بشكل مشابه للإرسال الاعتيادي للنماذج، إليك مثالًا عن ذلك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5459_16" style="">
<span class="pun">&lt;</span><span class="pln">form id</span><span class="pun">=</span><span class="str">"formElem"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"firstName"</span><span class="pln"> value</span><span class="pun">=</span><span class="str">"John"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="typ">Picture</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"file"</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"picture"</span><span class="pln"> accept</span><span class="pun">=</span><span class="str">"image/*"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"submit"</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  formElem</span><span class="pun">.</span><span class="pln">onsubmit </span><span class="pun">=</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    e</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">

    let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'https://javascript.info/article/formdata/post/user-avatar'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
      body</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">FormData</span><span class="pun">(</span><span class="pln">formElem</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    let result </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">

    alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	ستظهر النتيجة كالتالي:
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634989018="1" data-ss1634989618="1" frameborder="no" height="135" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/YzVbebz?default-tab=result" style="width: 100%;" title="JS-P3-FormData-ex03">See the Pen JS-P3-FormData-ex03 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h3>
	إرسال ملف يحتوي على كائن بيانات ثنائية
</h3>

<p>
	يمكن أن نرسل بيانات ثنائيةً مولّدةً تلقائيًا، مثل الصور على شكل كائن بيانات <code>Blob</code>، وبالتالي يمكن تمريره مباشرةً مثل المعامل <code>body</code> للدالة <a data-ss1634989018="1" data-ss1634989618="1" href="https://javascript.info/fetch" rel="external nofollow">Fetch</a> كما رأينا في الفقرة السابقة، ومن الأنسب عمليًا إرسال صورة لتكون جزءًا من نموذج له حقول وبيانات وصفية Metadata وليس بشكل منفصل، إذ يُلائم الخوادم عادةً استقبال نماذج مشفرة مكونة من أجزاء متعددة أكثر من بيانات ثنائية خام.
</p>

<p>
	يُرسل المثال التالي صورةً مرسومةً ضمن العنصر <code>&lt;canvas&gt;</code>، بالإضافة إلى بعض الحقول على شكل نموذج باستخدام الكائن <code>FormData</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5459_20" style="">
<span class="pun">&lt;</span><span class="pln">body style</span><span class="pun">=</span><span class="str">"margin:0"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">canvas id</span><span class="pun">=</span><span class="str">"canvasElem"</span><span class="pln"> width</span><span class="pun">=</span><span class="str">"100"</span><span class="pln"> height</span><span class="pun">=</span><span class="str">"80"</span><span class="pln"> style</span><span class="pun">=</span><span class="str">"border:1px solid"</span><span class="pun">&gt;&lt;/</span><span class="pln">canvas</span><span class="pun">&gt;</span><span class="pln">

  </span><span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"button"</span><span class="pln"> value</span><span class="pun">=</span><span class="str">"Submit"</span><span class="pln"> onclick</span><span class="pun">=</span><span class="str">"submit()"</span><span class="pun">&gt;</span><span class="pln">

  </span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
    canvasElem</span><span class="pun">.</span><span class="pln">onmousemove </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      let ctx </span><span class="pun">=</span><span class="pln"> canvasElem</span><span class="pun">.</span><span class="pln">getContext</span><span class="pun">(</span><span class="str">'2d'</span><span class="pun">);</span><span class="pln">
      ctx</span><span class="pun">.</span><span class="pln">lineTo</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">clientX</span><span class="pun">,</span><span class="pln"> e</span><span class="pun">.</span><span class="pln">clientY</span><span class="pun">);</span><span class="pln">
      ctx</span><span class="pun">.</span><span class="pln">stroke</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">

    async </span><span class="kwd">function</span><span class="pln"> submit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      let imageBlob </span><span class="pun">=</span><span class="pln"> await </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> canvasElem</span><span class="pun">.</span><span class="pln">toBlob</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> </span><span class="str">'image/png'</span><span class="pun">));</span><span class="pln">

      let formData </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">FormData</span><span class="pun">();</span><span class="pln">
      formData</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="str">"firstName"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"John"</span><span class="pun">);</span><span class="pln">
      formData</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="str">"image"</span><span class="pun">,</span><span class="pln"> imageBlob</span><span class="pun">,</span><span class="pln"> </span><span class="str">"image.png"</span><span class="pun">);</span><span class="pln">

      let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="str">'https://javascript.info/article/formdata/post/image-form'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        method</span><span class="pun">:</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">,</span><span class="pln">
        body</span><span class="pun">:</span><span class="pln"> formData
      </span><span class="pun">});</span><span class="pln">
      let result </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
      alert</span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">message</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

  </span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">body</span><span class="pun">&gt;</span></pre>

<p>
	وتظهر النتيجة كالتالي:
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634989018="1" data-ss1634989618="1" frameborder="no" height="201" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/MWmdQMV?default-tab=result" style="width: 100%;" title="JS-P3-FormData-ex04">See the Pen JS-P3-FormData-ex04 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لاحظ كيف يُضاف الكائن <code>Blob</code> الذي يمثل الصورة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_43" style="">
<span class="pln">formData</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="str">"image"</span><span class="pun">,</span><span class="pln"> imageBlob</span><span class="pun">,</span><span class="pln"> </span><span class="str">"image.png"</span><span class="pun">);</span></pre>

<p>
	وهذا الأسلوب مشابه لاستخدام العنصر ضمن نموذج، حيث يرسل الزائر الملف الذي يحمل الاسم "image.png" (الوسيط الثالث) والذي يحمل البيانات التي يحددها <code>imageBlob</code> (الوسيط الثاني) انطلاقًا من منظومة الملفات، ويقرأ الخادم بيانات النموذج وكذلك الملف كما لو أنها عملية إرسال نموذج اعتيادية.
</p>

<h2>
	خلاصة
</h2>

<ul>
<li>
		يتكون طلب إحضار بيانات تقليدي من استدعاءين باستخدام الصيغة <code>await</code>:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_45" style="">
<span class="pln">let response </span><span class="pun">=</span><span class="pln"> await fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> options</span><span class="pun">);</span><span class="pln"> </span><span class="com">// يُنفَّذ مع ترويسة الاستجابة headers</span><span class="pln">
let result </span><span class="pun">=</span><span class="pln"> await response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln"> </span><span class="com">//  JSON قراءة جسم الطلب بتنسيق </span></pre>

<p>
	أو دون الصيغة <code>await</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_47" style="">
<span class="pln">fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> options</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">result </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="com">/* process result */</span><span class="pun">)</span></pre>

<p>
	وتتمثل خصائص الاستجابة في الآتي:
</p>

<ul>
<li>
		<code>response.status</code>: رمز حالة HTTP للاستجابة.
	</li>
	<li>
		<code>response.ok</code>: يأخذ القيمة "true" إذا كانت قيمة رمز الحالة بين 200-299.
	</li>
	<li>
		<code>response.headers</code>: تعيد كائنًا شبيهًا بالترابط Map يضم ترويسات HTTP.
	</li>
</ul>
<p>
	توابع الحصول على جسم الاستجابة:
</p>

<ul>
<li>
		<code>()response.text</code>: لقراءة الاستجابة وإعادة نص.
	</li>
	<li>
		<code>()response.json</code>: يفسر النص وفق تنسيق JSON.
	</li>
	<li>
		<code>()response.formData</code>: يعيد الاستجابة على شكل كائن <code>FormData</code>.
	</li>
	<li>
		<code>()response.blob</code>: تعيد الاستجابة على شكل كائن بيانات ثنائية <a data-ss1634989018="1" data-ss1634989618="1" href="https://academy.hsoub.com/programming/javascript/%D9%83%D8%A7%D8%A6%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-blob-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1285/" rel="">Blob</a>.
	</li>
	<li>
		<code>()response.arrayBuffer</code>: يعيد الاستجابة على شكل كائن <a data-ss1634989018="1" data-ss1634989618="1" href="https://javascript.info/arraybuffer-binary-arrays" rel="external nofollow">ArrayBuffer</a> وهو تمثيل منخفض المستوى للبيانات الثنائية.
	</li>
</ul>
<p>
	خيارات <code>Fetch</code> التي تعرفنا عليها حتى الآن:
</p>

<ul>
<li>
		<code>method</code>: نوع طلب HTTP.
	</li>
	<li>
		<code>headers</code>: كائن يضم ترويسات الطلب، ويجب الانتباه إلى الترويسات التي يُمنع استخدامها.
	</li>
	<li>
		<code>body</code>: البيانات التي ستُرسل (جسم الطلب) على شكل <code>string</code> أو <code>FormData</code> أو <code>BufferSource</code> أو <code>Blob</code> أو <code>UrlSearchParams</code>.
	</li>
</ul>
<p>
	وسنتعرف على خيارات أخرى في الفصل التالي.
</p>

<ul>
<li>
		تُستخدم الكائنات <a data-ss1634989018="1" data-ss1634989618="1" href="https://xhr.spec.whatwg.org/#interface-formdata" rel="external nofollow">FormData</a> للتحكم بنماذج وإرسالها باستخدام <code>fetch</code> أو أي دوال لإرسال الطلبات عبر الشبكة، ويمكن إنشاؤها بالأمر <code>(new FormData(form</code> انطلاقًا من نموذج HTML موجود، أو إنشاؤها دون نموذج ثم نضيف إليه الحقول باستخدام التوابع:
	</li>
	<li>
		<code>(formData.append(name, value</code>
	</li>
	<li>
		<code>(formData.append(name, blob, fileName</code>
	</li>
	<li>
		<code>(formData.set(name, value</code>
	</li>
	<li>
		<code>(formData.set(name, blob, fileName</code>
	</li>
	<li>
		لاحظ هاتين الميزتين:
	</li>
</ul>
<ol>
<li>
		يزيل التابع <code>set</code> الحقول التي لها نفس الاسم، بينما لا يفعل ذلك التابع <code>append</code>، وهذا هو الاختلاف الوحيد بينهما.
	</li>
	<li>
		لا بدّ من استخدام صيغة تضم ثلاثة وسطاء لإرسال الملف، آخرها اسم الملف والذي يؤخذ عادةً من منظومة ملفات المستخدم من خلال العنصر <code>&lt;"input type="file&gt;</code>.
	</li>
</ol>
<ul>
<li>
		من التوابع الأخرى:
	</li>
	<li>
		<code>(formData.delete(name</code>
	</li>
	<li>
		<code>(formData.get(name</code>
	</li>
	<li>
		<code>(formData.has(name</code>
	</li>
</ul>
<h2>
	تمارين
</h2>

<h3>
	إحضار بيانات مستخدمين من GitHub
</h3>

<p>
	أنشئ دالةً غير متزامنة "async" باسم <code>(getUsers(names</code> للحصول على مصفوفة من سجلات الدخول Logins على GitHub، وتحضر المستخدمين أيضًا، ثم تعيد مصفوفةً بأسماء المستخدمين على GitHub.
</p>

<p>
	سيكون العنوان الذي يحوي معلومات مستخدم معين له اسم مستخدم محدد <code>USERNAME</code> هو:
</p>

<p>
	<code>api.github.com/users/USERNAME</code>.
</p>

<p>
	ستجد مثالًا تجريبيًا وضعناه في نمط الحماية sandbox يوضح ذلك.
</p>

<p>
	تفاصيل مهمة:
</p>

<ol>
<li>
		ينبغي أن يكون هناك طلب إحضار واحد لكل مستخدم.
	</li>
	<li>
		لا ينبغي أن ينتظر أي طلب انتهاء طلب آخر، لكي تصل البيانات بالسرعة الممكنة.
	</li>
	<li>
		في حال إخفاق أي طلب، أو عدم وجود مستخدم بالاسم المُعطى، فينبغي أن تعيد الدالة القيمة "null" في المصفوفة الناتجة.
	</li>
</ol>
<p>
	<a data-ss1634989018="1" data-ss1634989618="1" href="https://plnkr.co/edit/joLEhbz23gKnOfNb?p=preview" rel="external nofollow">افتح التمرين في بيئة تجريبية</a>
</p>

<h4>
	الحل
</h4>

<p>
	إليك حل التمرين:
</p>

<ul>
<li>
		نفِّذ التعليمة التالية لإحضار مستخدم:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_50" style="">
<span class="pln">fetch</span><span class="pun">(</span><span class="str">'https://api.github.com/users/USERNAME'</span><span class="pun">)</span></pre>

<ul>
<li>
		استدع التابع <code>()json.</code> لقراءة الكائن JS، إن كان رمز الحالة المرافق لاستجابة الخادم هو <code>200</code>.
	</li>
	<li>
		في الحالة التي تخفق فيها تعليمة الإحضار <code>fetch</code> أو لم يكن رمز الحالة <code>200</code>، أعد القيمة <code>null</code> في المصفوفة الناتجة.
	</li>
</ul>
<p>
	إليك الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_52" style="">
<span class="pln">async </span><span class="kwd">function</span><span class="pln"> getUsers</span><span class="pun">(</span><span class="pln">names</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let jobs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">

  </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let name of names</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let job </span><span class="pun">=</span><span class="pln"> fetch</span><span class="pun">(`</span><span class="pln">https</span><span class="pun">:</span><span class="com">//api.github.com/users/${name}`).then(</span><span class="pln">
      successResponse </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">successResponse</span><span class="pun">.</span><span class="pln">status </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> successResponse</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      failResponse </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">);</span><span class="pln">
    jobs</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">job</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  let results </span><span class="pun">=</span><span class="pln"> await </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">(</span><span class="pln">jobs</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> results</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<strong>ملاحظة</strong>: يرتبط استدعاء التابع <code>then.</code> بمباشرة بالدالة <code>fetch</code>، وبالتالي لا تنتظر عمليات إحضار أخرى لتنتهي عندما تصلك الاستجابة على أحدها بل إبدأ بقراءة الاستجابة مستخدمًا <code>()json.</code>.
</p>

<p>
	إن استخدمت الشيفرة التالية :
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9546_54" style="">
<span class="pln">await </span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">(</span><span class="pln">names</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">name </span><span class="pun">=&gt;</span><span class="pln"> fetch</span><span class="pun">(...)))</span></pre>

<p>
	ثم استدعيت <code>()json.</code> لقراءة النتائج، فقد يكون عليك الانتظار لتنتهي جميع عمليات الإحضار. ستضمن قراءة نتيجة كل عملية إحضار بمفردها إن استخدمت مباشرة <code>()json.</code> مع <code>fetch</code>.
</p>

<p>
	فما عرضناه كان مثالًا عن فائدة واجهة الوعود البرمجية منخفضة المستوى low-level Promise <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> حتى لو استخدمنا <code>async/await</code>.
</p>

<p>
	<a data-ss1634989018="1" data-ss1634989618="1" href="https://plnkr.co/edit/iecdfMfnlmOOspS6?p=preview" rel="external nofollow">إليك الحل في بيئة تجريبية مع الاختبارات</a>
</p>

<p>
	ترجمة -وبتصرف- للفصلين <a data-ss1634989018="1" data-ss1634989618="1" href="https://javascript.info/fetch" rel="external nofollow">popups and window methods</a> و <a data-ss1634989018="1" data-ss1634989618="1" href="https://javascript.info/formdata" rel="external nofollow">FormData</a> من سلسلة <a href="https://javascript.info/" rel="external nofollow">The Modern JavaScript Tutorial</a>
</p>
]]></description><guid isPermaLink="false">1294</guid><pubDate>Wed, 11 Aug 2021 11:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x648;&#x62D;&#x62F;&#x627;&#x62A; Modules &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1286/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/610e2deb3e38f_-Modules--.png.a503bf0349b769b9daccffb43405f1a1.png" /></p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		لا تكتب شيفرةً يسهل توسيعها فيما بعد، بل اكتبها بحيث يسهل محوها.
	</p>

	<p>
		ـــ توماس فِج Thomas Figg، كتاب "البرمجة سيئة" Programming is Terrible.
	</p>
</blockquote>

<p style="text-align: center;">
	<img alt="chapter_picture_10.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="73569" data-unique="ggqhm4t7s" src="https://academy.hsoub.com/uploads/monthly_2021_08/chapter_picture_10.jpg.399b97148c58417a01f477dd10d26a0d.jpg"></p>

<p>
	إذا أردنا وصف البرنامج المثالي، فسيكون ذلك الذي له بنية واضحة مثل الكريستال، ويسهل وصف طريقة عمله، كما يملك كل جزء فيه دورًا محدَّدًا ومعروفًا.
</p>

<p>
	لكن هذا لا يكون في الواقع، بل تكون لدينا برامج تنمو نموًا عضويًا، بحيث تضاف إليها المزايا كلما دعت الحاجة إلى ذلك؛ أما الهيكلة البنائية لها والحفاظ على تلك الهيكلية فهي أمر آخر، إذ لا تُرى ثمرتها إلا في المستقبل حين يأتي شخص آخر ليعمل على البرنامج، وعلى ذلك فمن المغري للمبرمج إهمالها الآن لتصبح أجزاء البرنامج متشعبةً ومتداخلةً إلى حد فظيع.
</p>

<p>
	يتسبب هذا في مشكلتين حقيقيتَين، أولاهما أنّ فهم مثل هذا النظام صعب جدًا، إذ سيكون من العسير النظر لأيّ جزء فيه نظرة مستقِلة إذا كان كل شيء فيه يتصل بشيء آخر، حيث سنُجبَر على دراسة البرنامج كله من أجل فهم جزء واحد فقط؛ أما الثانية فهي إذا أردنا استخدام أيّ وظيفة من مثل هذا البرنامج في حالة أخرى، فستكون إعادة كتابتها من الصفر أسهل من استخراجها مستقِلةً من شيفرتها لتعمل في مكان آخر.
</p>

<p>
	يستخدم مصطلح "كرة الوحل" لمثل تلك البرامج التي لا هيكل لها، والتي يلتصق كل شيء فيها ببعضه، فإذا حاولت استخراج جزء، فستنهار الكرة كلها، ولا ينالك إلا تلطيخ يدك.
</p>

<h2>
	الوحدات Modules
</h2>

<p>
	تُعَدّ الوحدات محاولةً لتفادي مثل تلك المشاكل التي وصفناها، فالوحدة هي جزء من برنامج ما يحدِّد بوضوح ما هي الأجزاء الأخرى التي يعتمد عليها، وما هي الوظيفة -أي الواجهة الخاصة به- التي سيوفرها للوحدات الأخرى لتستخدمها.
</p>

<p>
	تشترك واجهات الوحدات مع واجهات الكائنات في أمور كثيرة كما رأينا في مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AD%D9%8A%D8%A7%D8%A9-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%A9-%D9%84%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1243/" rel="">الحياة السرية للكائنات في جافاسكريبت</a>، إذ تجعل جزءًا من الوحدة متوفرًا للعالم الخارجي وتحافظ على خصوصية الباقي، كما يصبح النظام أشبه بقِطع الليجو LEGO في تقييد الطرق التي تتفاعل الوحدات بها مع بعضها البعض، فتتفاعل الأجزاء المختلفة من خلال وصلات connectors معرَّفة جيدًا، على عكس الوحل الذي يختلط فيه كل شيء.
</p>

<p>
	تسمى العلاقات التي بين الوحدات بالاعتماديات dependencies، فإذا احتاجت وحدة إلى جزء من وحدة أخرى، فسيقال أنها تعتمد على تلك الوحدة، وحين توصف تلك الحقيقة بوضوح داخل الوحدة نفسها، فيمكن استخدامها لمعرفة الوحدات الأخرى التي يجب أن تكون موجودةً من أجل استخدام وحدة ما، ولتحميل الاعتماديات تلقائيًا، كما ستحتاج كل واحدة منها إلى مجال خاص private scope إذا أردنا فصل الوحدات بهذه الطريقة.
</p>

<p>
	لا يكفي وضع شيفرة جافاسكربت في ملفات مختلفة لتلبية تلك المتطلبات، فلا زالت الملفات تتشارك فضاء الاسم العام global namespace نفسه، كما قد تتداخل عن قصد أو غير قصد مع رابطات بعضها bindings، وهكذا تظل هيكلية الاعتماديات غير واضحة، وسنرى بعد قليل كيف نعالج ذلك.
</p>

<p>
	قد يكون التفكير في تصميم وحدة ذات هيكل مناسب لبرنامج ما صعبًا بما أننا في مرحلة البحث عن المشكلة واستكشاف حلول لها، وذلك لنرى أيها يعمل، كما لا نريد شغل بالنا بتنظيم أحد الحلول إلا حين يثبت نجاحه.
</p>

<h2>
	الحزم
</h2>

<p>
	نستطيع استخدام نفس الجزء في برامج أخرى مختلفة، وهي إحدى المزايا التي نحصل عليها عند بناء برنامج من أجزاء منفصلة تستطيع العمل مستقِلة عن بعضها البعض.
</p>

<p>
	لكن كيف نُعِدّ هذا؟ لنقل أننا نريد استخدام الدالة <code>parseINI</code> من <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regular-expressions-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1278/" rel="">المقال السابق</a> في برنامج آخر، فإذا كان ما تعتمد عليه الدالة واضحًا -لا تعتمد الدالة على شيء في هذه الحالة-، فلن نفعل أكثر من نسخ شيفرتها إلى المشروع الجديد واستخدامها؛ أما إذا رأينا خطأً في تلك الشيفرة، فسنصلحه في ذلك المشروع الجديد وننسى إصلاحه في البرنامج الأول الذي أخذنا منه الشيفرة، وبهذا نهدر الوقت والجهد في نسخ الشيفرات من هنا إلى هناك والحفاظ عليها محدَّثة.
</p>

<p>
	وهنا يأتي دور الحِزم packages، فهي قطعة من شيفرة يمكن نشرها وتوزيعها -أي نسخها وتثبيتها-، وقد تحتوي على وحدة واحدة أو أكثر، كما فيها معلومات عن الحِزم الأخرى التي تعتمد عليها، وتأتي مع توثيق يشرح وظيفتها كي يستطيع الناس استخدامها، فلا تكون مقتصرةً على من كتبها فقط.
</p>

<p>
	تُحدَّث حزمة ما إذا وُجدت مشكلة فيها أو أضيفت إليها ميزة جديدة، وعليه تكون البرامج التي تعتمد عليها -والتي قد تكون حِزمًا أيضًا- يمكنها الترقية إلى تلك النسخة الجديدة.
</p>

<p>
	يتطلب العمل بهذه الطريقة بنيةً تحتيةً، وذلك لحاجتنا إلى مكان لتخزين تلك الحِزم والبحث فيها، وإلى طريقة سهلة لتثبيتها وترقيتها كذلك، كما تُوفَّر هذه البنية التحتية في عالم جافاسكربت من قِبَل <a href="https://npmjs.org" rel="external nofollow">NPM</a> وهي اختصار لـ Node Package Manager، التي تعني مدير الحِزم.
</p>

<p>
	يتكون مدير الحِزم NPM من شيئين، أولهما تقدِّم خدمة تنزيل ورفع الحزم عبر الإنترنت -أي أونلاين online-، وبرنامج -مضمَّن مع Node.js- يساعدك على تثبيت تلك الحزم وإدارتها، كما توجد أكثر من نصف مليون حزمة متاحة على NPM وقت كتابة هذه الكلمات (بنسختها الأجنبية)، وأكثرها لا فائدة منه، لكن الحزم المفيدة المتاحة للعامة موجودة هناك أيضًا.
</p>

<p>
	سنجد محلل ملفات INI مثلًا الذي يشبه ما بنيناه في المقال السابق في هيئة حزمة اسمها <code>ini</code>، كما سنرى في مقال لاحق كيف نُثبِّت مثل تلك الحزم محليًا باستخدام أمر <code>npm</code> في الطرفية.
</p>

<p>
	إتاحية مثل تلك الحزم عالية الجودة للتحميل مفيد جدًا، فهو يعني استطاعتنا تجنب إعادة اختراع برنامج كتبه مائة إنسان قبلنا، كما نتجاوز ذلك إلى استخدام حزمة مجرَّبة ومختبَرة جيدًا ببضع ضغطات على لوحة المفاتيح، ومن بداهة القول أنّ البرامج لا تكلف شيئًا في نَسخها، لكن كتابة البرامج أول مرة هي العمل الذي يكلف الجهد والوقت والمهارة، كما يماثلها الاستجابة لمن يجد مشاكل في الشيفرة، أو الاستجابة لمن يريد إدخال ميزات جديدة في البرنامج.
</p>

<p>
	مَن يكتب البرنامج يمتلك حقوقه افتراضيًا، ولا يستطيع أحد استخدامه إلا برخصة من المبرمج، لكن بما أنّ بعض البشر ذوو قلوب طيبة، ولأنّ نشر البرامج الجيدة سيبني لك سُمعة وسط المبرمجين، فستسمح صراحةً كثير من الحِزم تحت رخصة ما باستخدامها من أيّ كان.
</p>

<p>
	ترخَّص أغلب الشيفرات الموجودة في NPM بتلك الطريقة، في حين قد تشترط بعض الرخص نشر الشيفرة التي تبنيها على قمة الحزمة التي استخدمتها تحت رخصة الحزمة نفسها، وأخرى لا تريد أكثر من استخدام رخصة الحزمة نفسها حين تنشر برنامجك، وهي الرخصة التي يستخدمها أغلب مجتمع جافاسكربت، وبالتالي تأكد حين تستخدِم حِزمًا كتبها غيرك من قراءة الرخصة التي تأتي بها الحزمة.
</p>

<h2>
	الوحدات المرتجلة Improvised modules
</h2>

<p>
	لم يكن في جافاسكربت نظام وحدات مضمَّن حتى عام 2015، إذ لم يمنع ذلك الناس من بناء نظمًا كبيرة في جافاسكربت طيلة أكثر من عشر سنين، كما كانوا في أمسّ الحاجة إلى وحدات لهذا، وعليه فقد صمموا نظم وحداتهم الخاصة بهم فوق اللغة نفسها، حيث نستطيع استخدام دوال جافاسكربت لإنشاء نطاقات محلية local scopes وكائنات لتمثل واجهات الوحدات.
</p>

<p>
	لدينا فيما يلي وحدةً للانتقال بين أسماء الأيام وأرقامها -وذلك من إعادة التابع <code>getDay</code> الخاص بـ <code>Date</code>-، إذ تتكون واجهتها من <code>weekDay.name</code> و<code>weekDay.number</code>، كما تخفي رابطتها <code>names</code> المحلية داخل نطاق تعبير الدالة الذي يُستدعى فورًا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_6" style="">
<span class="kwd">const</span><span class="pln"> weekDay </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> names </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">"Sunday"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Monday"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Tuesday"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Wednesday"</span><span class="pun">,</span><span class="pln">
                 </span><span class="str">"Thursday"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Friday"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Saturday"</span><span class="pun">];</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    name</span><span class="pun">(</span><span class="pln">number</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> names</span><span class="pun">[</span><span class="pln">number</span><span class="pun">];</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
    number</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> names</span><span class="pun">.</span><span class="pln">indexOf</span><span class="pun">(</span><span class="pln">name</span><span class="pun">);</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}();</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">weekDay</span><span class="pun">.</span><span class="pln">name</span><span class="pun">(</span><span class="pln">weekDay</span><span class="pun">.</span><span class="pln">number</span><span class="pun">(</span><span class="str">"Sunday"</span><span class="pun">)));</span><span class="pln">
</span><span class="com">// → Sunday</span></pre>

<p>
	يوفِّر هذا النسق من الوحدات عزلًا إلى حد ما، لكنه لا يصرح عن الاعتماديات، كما يضع واجهته في النطاق العام global scope، ويَتوقع اعتمادياتها إذا وُجدت أن تحذو حذوه، في حين كان ذلك هو الأسلوب المتَّبع في برمجة الويب لمدة طويلة، لكنه صار مهجورًا الآن وقلّما يُستخدَم.
</p>

<p>
	إذا أردنا جعل علاقات الاعتماديات جزءًا من الشيفرة، فيجب التحكم في تحميل الاعتماديات، ويتطلب ذلك أن نستطيع تنفيذ السلاسل النصية على أساس شيفرات، إذ تستطيع جافاسكربت فعل ذلك لحسن الحظ.
</p>

<h2>
	تقييم البيانات على أساس شيفرات
</h2>

<p>
	هناك عدة طرق لأخذ البيانات -أي سلسلة نصية من شيفرات برمجية- وتشغيلها على أساس جزء من البرنامج الحالي، وأبسط طريقة هي العامل الخاص <code>eval</code> الذي سينفِّذ السلسلة النصية في النطاق الحالي current scope، لكن هذه فكرة سيئة لا يُنصح بها، إذ تعطِّل بعض الخصائص التي تكون عادةً للمجالات مثل توقعها للرابطة التي يشير إليها اسم ما بسهولة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_8" style="">
<span class="kwd">const</span><span class="pln"> x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> evalAndReturnX</span><span class="pun">(</span><span class="pln">code</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">eval</span><span class="pun">(</span><span class="pln">code</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">evalAndReturnX</span><span class="pun">(</span><span class="str">"var x = 2"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 2</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">x</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 1</span></pre>

<p>
	يمكن تفسير البيانات على أساس شيفرة بطريقة أبسط باستخدام الباني <code>Function</code> الذي يأخذ وسيطين هما سلسلة نصية تحتوي قائمة من أسماء الوسائط مفصول بينها بفاصلة أجنبية، وسلسلة نصية تحتوي على الدالة نفسها.
</p>

<p>
	يغلف الباني الشيفرة داخل قيمة دالة كي تحصل على نطاقها الخاص، ولا تفعل أمورًا غريبة مع النطاقات الأخرى.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_10" style="">
<span class="pln">let plusOne </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Function</span><span class="pun">(</span><span class="str">"n"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"return n + 1;"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">plusOne</span><span class="pun">(</span><span class="lit">4</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 5</span></pre>

<p>
	هذا هو المطلوب تحديدًا وما نحتاج إليه في نظام الوحدات، إذ نستطيع تغليف شيفرة الوحدة في دالة، ونستخدم نطاق تلك الدالة على أساس نطاق الوحدة.
</p>

<h2>
	CommonJS
</h2>

<p>
	لعل أكثر منظور لوحدات جافاسكربت المضافة إليها هو نظام وحدات جافاسكربت المشتركة common Javascript والذي يدعى بالاسم CommonJS، إذ تستخدِمه Node.js الذي هو النظام المستخدَم في أغلب الحِزم على NPM.
</p>

<p>
	تدور الفكرة العامة لوحدات CommonJS حول دالة اسمها <code>require</code>، فحين نستدعيها مع اسم وحدة الاعتمادية، فستضمن أن تحميل الوحدة وستعيد واجهتها.
</p>

<p>
	تحصل الوحدات على نطاقها المحلي الخاص بها تلقائيًا لأنّ المحمّل يغلف شيفرة الوحدة في دالة، وما عليها إلا استدعاء <code>require</code> كي تصل إلى اعتمادياتها، وتضع واجهاتها في الكائن المقيَّد بـ <code>exports</code>.
</p>

<p>
	توفِّر الوحدة المثال التالية دالةً لتنسيق التاريخ، إذ تستخدِم حزمتين من NPM هما <code>ordinal</code> لتحويل الأعداد إلى سلاسل نصية مثل <code>"1st"</code> و<code>"2nd"</code>، و<code>date-names</code> للحصول على الأسماء الإنجليزية للشهور وأيام الأسبوع، ثم تصدِّر دالةً وحيدةً هي <code>formatDate</code> تأخذ كائن <code>Date</code> وسلسلة قالب template string.
</p>

<p>
	قد تحتوي سلسلة القالب على شيفرات توجه التنسيق مثل <code>YYYY</code> للسنة كاملة و<code>Do</code> لترتيب اليوم في الشهر، ومن الممكن إعطاؤها سلسلةً نصيةً مثل <code>"MMMM Do YYYY"</code> للحصول على خرج مثل "November 22nd 2017".
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_12" style="">
<span class="kwd">const</span><span class="pln"> ordinal </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"ordinal"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">days</span><span class="pun">,</span><span class="pln"> months</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"date-names"</span><span class="pun">);</span><span class="pln">

exports</span><span class="pun">.</span><span class="pln">formatDate </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">date</span><span class="pun">,</span><span class="pln"> format</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> format</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/YYYY|M(MMM)?|Do?|dddd/</span><span class="pln">g</span><span class="pun">,</span><span class="pln"> tag </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">tag </span><span class="pun">==</span><span class="pln"> </span><span class="str">"YYYY"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> date</span><span class="pun">.</span><span class="pln">getFullYear</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">tag </span><span class="pun">==</span><span class="pln"> </span><span class="str">"M"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> date</span><span class="pun">.</span><span class="pln">getMonth</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">tag </span><span class="pun">==</span><span class="pln"> </span><span class="str">"MMMM"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> months</span><span class="pun">[</span><span class="pln">date</span><span class="pun">.</span><span class="pln">getMonth</span><span class="pun">()];</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">tag </span><span class="pun">==</span><span class="pln"> </span><span class="str">"D"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> date</span><span class="pun">.</span><span class="pln">getDate</span><span class="pun">();</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">tag </span><span class="pun">==</span><span class="pln"> </span><span class="str">"Do"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> ordinal</span><span class="pun">(</span><span class="pln">date</span><span class="pun">.</span><span class="pln">getDate</span><span class="pun">());</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">tag </span><span class="pun">==</span><span class="pln"> </span><span class="str">"dddd"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> days</span><span class="pun">[</span><span class="pln">date</span><span class="pun">.</span><span class="pln">getDay</span><span class="pun">()];</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">};</span><span class="pln"> </span></pre>

<p>
	تتكون واجهة <code>ordinal</code> من دالة واحدة، بينما تصدِّر <code>date-names</code> كائنًا يحتوي على عدة أشياء، إذ تُعَدّ <code>days</code> و<code>months</code> مصفوفات من الأسماء، كما تُعَدّ عملية فك البنية الهيكلية سهلةً جدًا عند إنشاء رابطات للواجهات المستوردة.
</p>

<p>
	تضيف الوحدة كذلك دالة واجهتها إلى <code>exports</code> كي تصل إليها الوحدات التي تعتمد عليها، إذ نستطيع استخدام الوحدة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_14" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">formatDate</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"./format-date"</span><span class="pun">);</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">formatDate</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">(</span><span class="lit">2017</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13</span><span class="pun">),</span><span class="pln">
                       </span><span class="str">"dddd the Do"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → Friday the 13th</span></pre>

<p>
	نستطيع تعريف <code>require</code> في أبسط صورها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_16" style="">
<span class="pln">require</span><span class="pun">.</span><span class="pln">cache </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">create</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> require</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!(</span><span class="pln">name in require</span><span class="pun">.</span><span class="pln">cache</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let code </span><span class="pun">=</span><span class="pln"> readFile</span><span class="pun">(</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
    let module </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">exports</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{}};</span><span class="pln">
    require</span><span class="pun">.</span><span class="pln">cache</span><span class="pun">[</span><span class="pln">name</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> module</span><span class="pun">;</span><span class="pln">
    let wrapper </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Function</span><span class="pun">(</span><span class="str">"require, exports, module"</span><span class="pun">,</span><span class="pln"> code</span><span class="pun">);</span><span class="pln">
    wrapper</span><span class="pun">(</span><span class="pln">require</span><span class="pun">,</span><span class="pln"> module</span><span class="pun">.</span><span class="pln">exports</span><span class="pun">,</span><span class="pln"> module</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> require</span><span class="pun">.</span><span class="pln">cache</span><span class="pun">[</span><span class="pln">name</span><span class="pun">].</span><span class="pln">exports</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تَقرأ الدالة المختلَقة <code>readFile</code> في المثال أعلاه ملفًا وتعيد محتوياته في سلسلة نصية؛ أما جافاسكربت فلا توفِّر مثل تلك الخاصية، لكن توفِّر بيئات جافاسكربت المختلفة مثل المتصفحات وNode.js طرقها الخاصة للوصول إلى الملفات، وقد كان المثال يفترض وجود دالة اسمها <code>readFile</code>.
</p>

<p>
	تحتفظ دالة <code>require</code> بذاكرة مؤقتة cache من الوحدات المحمَّلة بالفعل، وذلك لتجنب تحميل الوحدة نفسها عدة مرات، فإذا استُدعيت، فستنظر أولًا إن كانت الوحدة المطلوبة محمَّلة من قبل أم لا، ثم تُحمِّلها إن لم تكن محمَّلة، حيث يتطلب ذلك قراءة شيفرة الوحدة وتغليفها في دالة واستدعاءها.
</p>

<p>
	لم تكن واجهة حِزمة <code>ordinal</code> التي رأيناها من قبل كائنًا وإنما دالةً، ومن مزايا CommonJS أنه رغم إنشاء نظام الوحدات لكائن واجهة فارغ مقيَّد بـ <code>exports</code>، يمكننا استبدال أيّ قيمة بذلك الكائن عبر إعادة كتابة <code>module.exports</code>، حيث يتم ذلك بعدة وحدات لتصدير قيمة واحدة بدلًا من كائن واجهة؛ وإذا عرَّفنا <code>require</code> و<code>exports</code> و<code>module</code> على أساس معامِلات لدالة التغليف المولَّدة وتمرير القيم المناسبة عند استدعائها، فسيضمن المحمِّل إتاحية تلك الرابطات في نطاق الوحدة.
</p>

<p>
	تختلف الطريقة التي تُرجِمت بها السلسلة النصية التي أُعطيت إلى دالة <code>require</code> إلى اسم ملف حقيقي أو عنوان ويب باختلاف الأنظمة، فإذا بدأت بـ <code>‎"./"‎</code> أو <code>‎"../"‎</code>، فستُفسَّر تفسيرًا مرتبطًا باسم ملف الوحدة، وبالتالي سيكون <code>‎"./format-date"‎</code> هو الملف المسمى <code>format-date.js</code> في المجلد نفسه؛ أما إذا لم يكن الاسم مرتبطًا بالوحدة، فستبحث Node.js عن حِزمة مثبَّتة تحمل الاسم نفسه، وسنفسر مثل تلك الأسماء التي في شيفرات أمثلة هذا المقال على أنها تشير إلى حِزم NPM، كما سننظر بالتفصيل في كيفية تثبيت واستخدام وحدات NPM لاحقًا في هذه السلسلة.
</p>

<p>
	نستطيع استخدام واحد من NPM الآن بدلًا من كتابة محلل ملف INI الخاص بنا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_18" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">parse</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"ini"</span><span class="pun">);</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">parse</span><span class="pun">(</span><span class="str">"x = 10\ny = 20"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → {x: "10", y: "20"}</span></pre>

<h2>
	وحدات ESCMAScript
</h2>

<p>
	تظل وحدات CommonJS حلًا غير دائم وغير احترافي رغم عملها بكفاءة وسماحها لمجتمع جافاسكربت -مع NPM- بتشارك الشيفرات على نطاق واسع، وتُعَدّ صيغتها غريبةً قليلًا، إذ لا تكون الأشياء التي نضيفها إلى <code>exports</code> متاحةً في النطاق المحلي مثلًا، وبما أنّ <code>require</code> هي استدعاء دالة عادية تأخذ أيّ نوع من الوسطاء وليس القيم مصنَّفة النوع فقط، فمن الصعب تحديد اعتماديات الوحدة دون تشغيل شيفرتها.
</p>

<p>
	لهذا يُصدِر معيار جافاسكربت نظام وحداته الخاص منذ 2015، كما يطلق عليه غالبًا ES modules، حيث تشير ES إلى ECMAScript، ورغم أنّ المفاهيم الأساسية للاعتماديات والواجهات لا زالت كما هي، إلا أنه يختلف في التفاصيل، فصارت الصيغة الآن مدمجةً في اللغة، كما نستطيع استخدام الكلمة المفتاحية الخاصة <code>import</code> بدلًا من استدعاء دالة للوصول إلى اعتمادية ما.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_20" style="">
<span class="kwd">import</span><span class="pln"> ordinal from </span><span class="str">"ordinal"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">days</span><span class="pun">,</span><span class="pln"> months</span><span class="pun">}</span><span class="pln"> from </span><span class="str">"date-names"</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> formatDate</span><span class="pun">(</span><span class="pln">date</span><span class="pun">,</span><span class="pln"> format</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">/* ... */</span><span class="pln"> </span><span class="pun">}</span></pre>

<p>
	تُستخدم بالمثل كلمة <code>export</code> المفتاحية لتصدير الأشياء، وقد تأتي قبل تعريف دالة أو صنف أو رابطة (<code>let</code> أو <code>const</code> أو <code>var</code>).
</p>

<p>
	لا تكون واجهة وحدة ES قيمةً واحدةً، بل مجموعة من الرابطات المسماة، كما تربط الوحدة السابقة <code>formatDate</code> بدالة، فإذا استوردنا من وحدة أخرى، فسنستورد الرابطة وليس القيمة، مما يعني أن الوحدة المصدِّرة قد تغير قيمة الرابطة في أي وقت، وسترى الوحدات المستوردة القيمة الجديدة.
</p>

<p>
	إذا وُجدت رابطة اسمها <code>default</code> فستُعامَل على أنها القيمة المصدَّرة الأساسية للوحدة، فإذا استوردنا وحدةً مثل <code>ordinal</code> التي في المثال دون الأقواس التي حول اسم الرابطة، فسنحصل على رابطة <code>default</code>، كما تستطيع مثل تلك الوحدات تصدير رابطات أخرى تحت أسماء مختلفة مع تصدير <code>default</code> الخاص بها.
</p>

<p>
	إذا أردنا إنشاء تصدير افتراضي، فسنكتب <code>export default</code> قبل التعبير أو تصريح الدالة أو تصريح الصنف.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_22" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="pun">[</span><span class="str">"Winter"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Spring"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Summer"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Autumn"</span><span class="pun">];</span></pre>

<p>
	من الممكن إعادة تسمية الرابطات المستورَدة باستخدام الكلمة <code>as</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_24" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">days as dayNames</span><span class="pun">}</span><span class="pln"> from </span><span class="str">"date-names"</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">dayNames</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 7</span></pre>

<p>
	من الفروقات المهمة كذلك هو حدوث تصديرات وحدات ES قبل بدء تشغيل سكربت الوحدة، وبالتالي قد لا تظهر تصريحات <code>import</code> داخل الدوال أو الكتل، ويجب أن تكون أسماء الاعتماديات سلاسل نصية مقتَبسة وليست تعبيرات عشوائية.
</p>

<p>
	يعكف مجتمع جافاسكربت أثناء كتابة هذه الكلمات على اعتماد وتبني أسلوب الوحدات هذا، لكن يبدو أنها عملية طويلة، فقد استغرقنا بضع سنين بعد الاستقرار على الصياغة كي تدعمها المتصفحات وNode.js، ورغم أنها صارت مدعومةً إلى حد كبير إلا أنّ ذلك الدعم به مشاكل، ولا زال الجدل قائمًا حول الكيفية التي يجب توزيع تلك الوحدات بها في NPM.
</p>

<p>
	تُكتب مشاريع عديدة باستخدام وحدات ES، ثم تُحوَّل تلقائيًا إلى صيغة أخرى عند نشرها، فنحن في مرحلة انتقالية يُستخدم فيها نظامَي وحدات جنبًا إلى جنب، ومن المفيد أننا نستطيع قراءة وكتابة شيفرات بكليهما.
</p>

<h2>
	البناء والتجميع
</h2>

<p>
	إذا نظرنا إلى مشاريع جافاسكربت فسنجد العديد منها لا يُكتب بجافاسكربت من الأساس -تقنيًا-، فثَمة امتدادات مستَخدَمة على نطاق واسع مثل ذلك الذي تعرضنا له في مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B2%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1245/" rel="">الزلات البرمجية والأخطاء في جافاسكريبت</a> الذي يتحقق من اللهجة، وقد بدأت الناس باستخدام الامتدادات الجاهزة في اللغة قبل إضافتها إلى المنصات التي تشغِّل جافاسكربت بزمن طويل أصلًا، ولكي يستطيع المبرمج فعل ذلك فإنه يصرِّف compile شيفرته، ثم يترجمها من لهجة جافاسكربت التي استخدمها إلى جافاسكربت عادية، أو إلى نسخة أقدم من جافاسكربت كي تستطيع المتصفحات الأقدم تشغيلها.
</p>

<p>
	لكن إدخال برنامج يتكون من وحدات ومن 200 ملف مختلف إلى صفحة ويب له مشاكله، فإذا كان جلب الملف عبر الشبكة يستغرق 50 ميلي ثانية، فسيستغرق تحميل البرنامج كله 10 ثواني، أو نصف تلك المدة إن كان يحمِّل عدة ملفات في الوقت نفسه، وهذا وقت ضائع.
</p>

<p>
	بدأ مبرمجو الويب باستخدام أدوات تجمع برامجهم التي قضوا الساعات المضنية في تقسيمها إلى وحدات، ليكون البرنامج ملفًا واحدًا كبيرًا، ثم ينشرونه على الويب، ذلك أن جلب ملف واحد وإن كان كبيرًا عبر الويب سيكون أسرع من جلب الكثير من الملفات الصغيرة، ويطلق على مثل تلك الأدوات اسم المجمِّعات bundlers.
</p>

<p>
	يتحكم حجم الملفات في سرعة نقلها عبر الشبكة كذلك، فليس العدد وحده هو المعيار، وعليه فقد اخترع مجتمع جافاسكربت مصغِّرات minifiers، وهي أدوات تأخذ برنامج جافاسكربت وتجعله أصغر عبر حذف التعليقات والمسافات تلقائيًا، كما تعيد تسمية الرابطات bindings، وتستبدل شيفرات أصغر بالشيفرات التي تأخذ حجمًا كبيرًا؛ وهكذا فليس من الغريب إيجاد شيفرة في حزمة NPM أو شيفرة تعمل في صفحة ويب، وتكون قد مرت بعدة مراحل من التحويل من جافاسكربت الحديثة إلى نسخة أقدم، ومن صيغة وحدات ES إلى CommonJS، ثم جُمِّعت وصغِّرت كذلك، كما لن نخوض في تفاصيل تلك الأدوات في هذه السلسلة لأنها مملة وتتغير بسرعة، لكن من المهم معرفة أنّ شيفرة جافاسكربت التي تشغِّلها لا تكون بهيئتها التي كُتبت بها أول مرة.
</p>

<h2>
	تصميم الوحدة
</h2>

<p>
	تُعَدّ هيكلة البرامج واحدةً من أدق الأمور في البرمجة، إذ يمكن نمذجة أيّ وظيفة غريبة بعدة طرق، كما يُعَدّ القول بجودة تصميم برنامج ما أمرًا نسبيًا، فهناك حلول وسط دومًا، كما تتدخل التفضيلات الشخصية في الحكم على التصميم، وأفضل ما يمكن فعله لتعلُّم قيمة التصميم الجيد هو القراءة والعمل على برامج كثيرة، وملاحظة ما ينجح وما يفشل، وإذا رأيت شيفرات فوضوية فلا تركن إلى قولك أن هذا هو الواقع ولا مجال لتغييره، بل هناك مساحة لتطوير هيكل أيّ شيء تقريبًا بإعمال الفكر فيه.
</p>

<p>
	تُعَدّ سهولة الاستخدام أحد أركان تصميم الوحدات، فإذا كنت تصمم شيئًا ليستخدمه أشخاص عدة -أو حتى نفسك فقط-، فيُستحسن أن تكون واجهتك بسيطةً وسهلة الفهم والتوقع حين تنظر إليها بعد ثلاثة أشهر حين تنسى تفاصيل ما كتبته وما عملته، وقد يعني ذلك اتباع سلوكيات موجودة سلفًا، كما تُعَدّ حزمة <code>ini</code> مثالًا على ذلك، إذ تحاكي كائن <code>JSON</code> القياسي من خلال توفير دوال <code>parse</code> و<code>stringify</code> -لكتابة ملف INI-، كما تحوِّل بين السلاسل النصية والكائنات المجردة مثل <code>JSON</code>، وهكذا فإنّ الواجهة صغيرة ومألوفة، وستذكر كيف استخدمتها لمجرد عملك معها مرةً واحدةً.
</p>

<p>
	حتى لو لم تكن ثمة دالة قياسية أو حِزمة مستخدَمة على نطاق كبير لمحاكاتها، فستستطيع إبقاء وحداتك مألوفةً وسهلة التوقع باستخدام هياكل بيانات بسيطة تفعل أمرًا واحدًا فقط، كما توجد على NPM مثلًا وحدات عديدة لتحليل ملف-INI، إذ تحتوي على دالة تقرأ مثل هذا الملف من القرص الصلب مباشرة وتُحلله، ويجعل ذلك من المستحيل استخدام مثل تلك الوحدات في المتصفح، حيث لا يكون لنا وصولًا مباشرًا إلى نظام الملفات، كما يضيف تعقيدًا كان يمكن معالجته بأسلوب أفضل عبر تزويد composing الوحدة بدالة قارئة للملفات file-reading.
</p>

<p>
	يشير ذلك إلى منظور آخر مفيد في تصميم الوحدات، وهو سهولة تطعيم شيء ما بشيفرة خارجية، إذ تكون الوحدات المركزة التي تحسب قيمًا قابلةً للتطبيق في طيف واسع من البرامج، على عكس الوحدات الأكبر التي تنفِّذ إجراءات معقدة لها آثار جانبية، وقارئ ملف INI الذي يركز على قراءة الملف من القرص الصلب ليس له فائدة في سيناريو يكون فيه محتوى الملف من مصدر خارجي، وبالمثل فإنّ الكائنات الحالة stateful objects مفيدة أحيانًا، بل قد تكون ضرورية؛ لكن إذا كان يمكن تنفيذ أمر ما بدالة فمن الأفضل استخدام الدالة.
</p>

<p>
	توفِّر العديد من قارئات ملفات INI على NPM نمط واجهة interface style، إذ يحتاج منك إنشاء كائن أولً، ثم تحمِّل الملف إلى الكائن، وبعد ذلك تستخدِم توابع مخصصة للحصول على النتائج، وذلك الأمر شائع في البرمجة كائنية التوجه، كما هو أمر مرهق وخاطئ.
</p>

<p>
	علينا تنفيذ طقوس نقل الكائن خلال عدة مراحل بدلًا من تنفيذ استدعاء واحد لدالة، وبما أن البيانات مغلفة الآن في نوع كائن مخصص، فيجب على كل الشيفرات التي تتفاعل معها معرفة ذلك النوع، مما يجعل لدينا اعتماديات غير ضرورية.
</p>

<p>
	قد تكون لدينا حالات لا يمكن تجنب تعريف هياكل بيانات جديدة فيها، حيث لا توفر اللغة إلا بعض الهياكل البسيطة، كما ستكون العديد من أنواع البيانات معقدةً أكثر من مجرد مصفوفة أو خارطة map، لكن إذا كانت المصفوفة تكفي، فسنستخدِم المصفوفة.
</p>

<p>
	يمكن النظر إلى مثال المخطط الذي تعرضنا له في مقال <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%8A-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%B1%D8%AC%D9%84-%D8%A2%D9%84%D9%8A-%D8%B1%D9%88%D8%A8%D9%88%D8%AA-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1244/" rel="">مشروع تطبيقي لبناء رجل آلي (روبوت) عبر جافاسكريبت</a> على أساس مثال على هيكل بيانات معقد، إذ لا توجد طريقة واضحة لتمثيل المخطط في جافاسكربت، وقد استخدمنا في ذلك المقال كائنًا تحمل خصائصه مصفوفات من السلاسل النصية، وهي العقد الأخرى التي يمكن الوصول إليها من تلك العقدة.
</p>

<p>
	توجد العديد من حِزم إيجاد المسار في NPM، لكن لا تستخدِم أيّ منها صيغة المخطط تلك، فهي تسمح عادةً لحدود المخطط أن يكون لها وزن يكون التكلفة أو المسافة المرتبطة به، ولم يكن ذلك ممكنًا في تمثيلنا.
</p>

<p>
	هناك حِزمة <code>dijkstrajs</code> مثلًا التي تستخدِم منظورًا شائعًا جدًا لإيجاد المسارات والذي يُسمى بخوارزمية ديكسترا Dijkstra's algorithm، إذ سُمي باسم إدزجر ديكسترا Edsger Dijkstra الذي كتبه، وهو مشابه لدالة <code>findRoute</code> الخاصة بنا، ومن الشائع أن تضاف اللاحقة js إلى اسم الحِزمة لتوضيح أنها مكتوبة بجافاسكربت.
</p>

<p>
	تستخدِم حزمة <code>dijkstrajs</code> صيغة مخطط تشبه صيغتنا، لكنها تستخدم كائنات تكون قيم خصائصها أعدادًا -أي أوزان الحدود- بدلًا من المصفوفات التي استخدمناها، فإذا أردنا استخدام تلك الحزمة، فسيكون علينا التأكد من تخزين المخطط بالصيغة التي تتوقعها الحزمة، كما يجب أن تحصل جميع الحدود على الوزن نفسه بما أنّ نموذجنا المبسط يعامِل كل طريق على أساس امتلاكه التكلفة نفسها -أي منعطف واحد-.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_26" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">find_path</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"dijkstrajs"</span><span class="pun">);</span><span class="pln">

let graph </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let node of </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">(</span><span class="pln">roadGraph</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let edges </span><span class="pun">=</span><span class="pln"> graph</span><span class="pun">[</span><span class="pln">node</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let dest of roadGraph</span><span class="pun">[</span><span class="pln">node</span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    edges</span><span class="pun">[</span><span class="pln">dest</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">find_path</span><span class="pun">(</span><span class="pln">graph</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Post Office"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Cabin"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → ["Post Office", "Salma’s House", "Cabin"]</span></pre>

<p>
	قد يكون ذلك حاجزًا معيقًا عن التركيب compositing، حيث تستخدِم حِزم عدة هياكل بيانات مختلفة لوصف أشياء متشابهة، فيكون جمعها معًا صعبًا، وبالتالي إذا أردنا تصميم شيء ليكون قابلًا للتركيب، فيجب علينا معرفة هياكل البيانات التي يستخدِمها الأشخاص أولًا، ثم نحذو حذوهم كلما تيسر.
</p>

<h2>
	خاتمة
</h2>

<p>
	توفر الوحدات هيكلًا لبرامج أكبر من خلال فصل الشيفرة إلى أجزاء صغيرة لها واجهات واضحة واعتماديات، كما تُعَدّ الواجهة الجزء المرئي من الوحدة للوحدات الأخرى، والاعتماديات هي الوحدات الأخرى التي تستخدِمها. ولأنّ جافاسكربت لم توفر نظامًا للوحدات في البداية، فقد بُني نظام CommonJS عليها، ثم حصلت جافاسكربت بعد مدة على نظام وحدات خاص بها، وهو الآن موجود إلى جانب CommonJS.
</p>

<p>
	تُعَدّ الحزمة مجموعة شيفرات يمكن توزيعها مستقِلة بذاتها، كما يُعَدّ NPM مستودعًا لحِزم جافاسكربت، ونستطيع تحميل شتى الحِزم منه سواء مفيدة أو غير مفيدة.
</p>

<h2>
	تدريبات
</h2>

<h3>
	الروبوت التركيبي
</h3>

<p>
	فيما يلي الرابطات التي ينشئها المشروع الذي في مقال مشروع تطبيقي لبناء رجل آلي (روبوت) عبر جافاسكريبت الذي أشرنا إليه بالأعلى.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_28" style="">
<span class="pln">roads
buildGraph
roadGraph
</span><span class="typ">VillageState</span><span class="pln">
runRobot
randomPick
randomRobot
mailRoute
routeRobot
findRoute
goalOrientedRobot</span></pre>

<p>
	إذا أردت كتابة هذا المشروع على أساس برنامج تركيبي modular، فما الوحدات التي ستنشئها؟ وما الوحدات التي ستعتمد عليها كل وحدة؟ وكيف ستبدو واجهاتها؟ وأيّ الأجزاء ستكون متاحةً ومكتوبةً مسبقًا على NPM؟ وهل ستفضِّل استخدام حِزمة NPM أم تكتبها بنفسك؟
</p>

<h4>
	إرشادات للحل
</h4>

<p>
	إليك ما كنا لنفعله بأنفسنا، لكن مرةً أخرى ليست هناك طريقة صحيحة وحيدة لتصميم وحدة معطاة:
</p>

<p>
	توجد الشيفرة المستخدَمة لبناء مخطط الطريق في وحدة <code>graph</code>، ولأننا نُفضِّل استخدام <code>dijkstrajs</code> من NPM بدلًا من شيفرة إيجاد المسار التي كتبناها، فسنجعل هذا المخطط قريبًا من الذي تتوقعه <code>dijkstrajs</code>.
</p>

<p>
	تصدِّر هذه الوحدة دالةً واحدةً هي <code>buildGraph</code>، كما سنجعل هذه الدالة تقبل مصفوفةً من مصفوفات ثنائية العناصر بدلًا من سلاسل نصية تحتوي على شخطات <code>-</code>، وذلك من أجل تقليل اعتماد الوحدة على صيغة الإدخال.
</p>

<p>
	تحتوي وحدة <code>roads</code> على بيانات الطريق الخام -أي مصفوفة <code>roads</code>- ورابطة <code>roadGraph</code>، كما تعتمد تلك الوحدة على <code>‎./graph</code> وتصدر مخطط الطريق.
</p>

<p>
	يوجد صنف <code>VillageState</code> في وحدة <code>state</code>، حيث يعتمد على وحدة <code>‎./roads</code> لأنه يحتاج إلى أن يكون قادرًا على التحقق من وجود طريق موجود فعليًا، كما يحتاج إلى <code>randomPick</code>، وبما أنّ هذه دالة من ثلاثة أسطر، فسنضعها في وحدة <code>state</code> على أساس دالة مساعدة داخلية، لكن <code>randomRobot</code> يحتاج إليها كذلك، لذا يجب تكرارها أو وضعها في وحدتها الخاصة، وبما أنّ تلك الدالة موجودة في NPM في حِزمة <code>random-item</code>، فمن الأفضل جعل كلا الوحدتين تعتمدان عليها، كما نستطيع إضافة دالة <code>runRobot</code> إلى تلك الوحدة أيضًا، بما أنها صغيرة ومرتبطة ارتباطًا وثيقًا بإدارة الحالة، في حين تصدِّر الوحدة كلا من الصنف <code>VillageState</code> ودالة <code>runRobot</code>.
</p>

<p>
	أخيرًا، يمكن دخول الروبوتات والقيم التي تعتمد عليها مثل <code>mailRoute</code> في وحدة <code>example-robots</code> التي تعتمد على <code>‎./roads</code> وتصدِّر دوال الروبوت، ولكي يستطيع <code>goalOrientedRobot</code> البحث عن المسار، فستعتمد الوحدة على <code>dijkstrajs</code> أيضًا.
</p>

<p>
	صارت الشيفرة أصغر قليلًا من خلال نقل بعض المهام إلى وحدات NPM، حيث تنفِّذ كل وحدة مستقِلة شيئًا بسيطًا ويمكن قراءتها بنفسها، ويقترح تقسيم الشيفرات إلى وحدات على أساس تحسينات أكثر لتصميم البرنامج.
</p>

<p>
	يُعد اعتماد الروبوتات و<code>VillageState</code> على مخطط طريق بعينه غريبًا بعض الشيء، فربما يكون من الأفضل جعل المخطط وسيطًا لباني الحالة، ونجعل الروبوتات تقرؤه من كائن الحالة، فنقلل الاعتماديات -وهو أمر جيد-، ونجعل من الممكن إجراء عمليات محاكاة على خرائط مختلفة، وذلك خير وأفضل.
</p>

<p>
	هل من الجيد استخدام وحدات NPM لأمور كان يمكن كتابتها بأنفسنا؟ نظريًا، نعم، فمن المرجح أنك ستقع في أخطاء في الأمور المعقَّدة مثل دالة إيجاد المسار وتضيع وقتك في كتابتها بنفسك؛ أما بالنسبة للدوال الصغيرة مثل <code>random-item</code> فتكون كتابتها أمرًا يسيرًا، لكن إضافتها في أيّ مكان تحتاج إليها فيه سيعكر وحداتك.
</p>

<p>
	لكن بأي حال، لا تقلل من شأن الجهد المطلوب لإيجاد حِزمة NPM مناسبة، فحتى لو وجدتها فقد لا تعمل جيدًا أو تكون مفتقدةً إلى بعض المزايا التي تحتاجها، وفوق هذا يعني الاعتماد على حِزم NPM التأكد من أنها مثبَّتة، كما ستنشرها مع برنامجك، بل ربما يكون عليك ترقيتها دوريًا، فهذه قد تكون إحدى التبعات التي عليك تحملها، وعلى أيّ حال تستطيع اتخاذ القرار الذي يناسبك وفقًا لمقدار العون الذي تقدمه تلك الحزم لك.
</p>

<h3>
	وحدة الطرق
</h3>

<p>
	اكتب وحدة CommonJS بناءً على المثال الذي في مقال مشروع تطبيقي لبناء رجل آلي (روبوت) عبر جافاسكريبت، بحيث تحتوي على مصفوفة من الطرق وتصدِّر هيكل بيانات المخطط الذي يمثلها على أساس <code>roadgraph</code>، كما يجب أن تعتمد على وحدة <code>‎./graph</code> التي تصدِّر الدالة <code>buildGraph</code> المستخدَمة لبناء المخطط، وتتوقع هذه الدالة مصفوفةً من مصفوفات ثنائية العناصر -أي نقاط البداية والنهاية للطرق-.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1442_32" style="">
<span class="com">// أضف اعتماديات وتصديرات.</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> roads </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="str">"Salma's House-Omar's House"</span><span class="pun">,</span><span class="pln">   </span><span class="str">"Salma's House-Cabin"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Salma's House-Post Office"</span><span class="pun">,</span><span class="pln">   </span><span class="str">"Omar's House-Town Hall"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Sara's House-Mostafa's House"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Sara's House-Town Hall"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Mostafa's House-Sama's House"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Sama's House-Farm"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Sama's House-Shop"</span><span class="pun">,</span><span class="pln">          </span><span class="str">"Marketplace-Farm"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Marketplace-Post Office"</span><span class="pun">,</span><span class="pln">     </span><span class="str">"Marketplace-Shop"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Marketplace-Town Hall"</span><span class="pun">,</span><span class="pln">       </span><span class="str">"Shop-Town Hall"</span><span class="pln">
</span><span class="pun">];</span></pre>

<h4>
	إرشادات للحل
</h4>

<p>
	بما أنّ هذه وحدة CommonJS، فعليك استخدام <code>require</code> لاستيراد وحدة المخطط، وقد وُصف ذلك على أساس تصدير دالة <code>buildGraph</code>، إذ تستطيع استخراجه من كائن واجهته مع تصريح فك الهيكلة <code>const</code>.
</p>

<p>
	أضف خاصيةً إلى كائن <code>exports</code> من أجل تصدير <code>roadGraph</code>، كما يجب أن يكون فصل سلاسل الطريق النصية داخل وحدتك لأن <code>buildGraph</code> تأخذ هيكل بيانات لا يطابق <code>roads</code> بالضبط.
</p>

<h3>
	الاعتماد المتبادل بين الوحدات
</h3>

<p>
	تُعَدّ حالة الاعتماد المتبادل بين وحدتين ويدعى circular dependency حالةً تعتمد فيها الوحدة A على الوحدة B -أي يكون الاعتماد على شكل حلقة تبادلية بين وحدتين ولذلك سمي باللغة الإنجليزية circular dependency-، والعكس صحيح مباشرةً أو غير مباشرة، كما تمنع العديد من أنظمة الوحدات ذلك بسبب استطاعتك التأكد من تحميل اعتماديات وحدة قبل تشغيلها، بعض النظر عن الترتيب الذي تختاره لتحميل مثل تلك الوحدات.
</p>

<p>
	تسمح وحدات CommonJS بصورة محدودة من الاعتماديات الدورية cyclic dependencies تلك، طالما أنّ الوحدات لا تستبدل كائن <code>exports</code> الافتراضي الخاص بها، ولا يكون لها وصول إلى واجهة غيرها حتى تنتهي من التحميل.
</p>

<p>
	تدعم دالة <code>require</code> التي تعرضنا لها سابقًا في هذا المقال مثل ذلك النوع من تبادلية الاعتماديات dependency cycle، فهل يمكنك رؤية كيف تعالج هذه الحالة؟ وما الذي قد يحدث إذا استبدلت وحدة في تعتمد وحدة أخرى عليها (أي داخلة في دورة الاعتمادية)دورة، كائن <code>exports</code> الافتراضي الخاص بها؟
</p>

<h4>
	إرشادات للحل
</h4>

<p>
	تدور الفكرة هنا حول إضافة <code>require</code> وحدات إلى ذاكرتها المؤقتة قبل بدء تحميل الوحدة، وهكذا فإذا حاول أيّ استدعاء <code>require</code> تحميلها أثناء تشغيلها، فسيكون معروفًا وتُعاد الواجهة الحالية بدلًا من تحميل الوحدة مرةً أخرى، وذلك يتسبب في طفحان المكدس؛ أما إذا أعادت وحدة كتابة قيمة <code>module.exports</code> الخاصة بها، فستحصل أيّ وحدة أخرى كانت قد استقبلت قيمة واجهتها قبل أن تنهي التحميل، على كائن الواجهة الافتراضي -الذي سيكون فارغًا على الأرجح- بدلًا من قيمة الواجهة المقصودة.
</p>

<p>
	ترجمة -بتصرف- <a href="https://eloquentjavascript.net/10_modules.html" rel="external nofollow">للفصل العاشر من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r926/" rel="">مقدمة إلى الوحدات Modules في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%88%D8%A7%D9%84%D8%AD%D8%B2%D9%85-packages-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r329/" rel="">الوحدات Modules والحزم Packages في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/angularjs/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-angularjs-r196/" rel="">الوحدات Modules في AngularJS</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1286</guid><pubDate>Sat, 07 Aug 2021 07:14:30 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x643;&#x627;&#x626;&#x646; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x627;&#x644;&#x62B;&#x646;&#x627;&#x626;&#x64A;&#x629; Blob &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%83%D8%A7%D8%A6%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-blob-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1285/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/Blob.png.fdbb140e96563189bf403573656a9410.png" /></p>

<p>
	يمثل <code>ArrayBuffer</code> جزءًا من معيار "ECMA"، وهو جزء من جافاسكريبت JavaScript، لكن توجد كائنات عالية المستوى ضمن المتصفح وُصِفت في <a data-ss1634984872="1" data-ss1634985247="1" href="https://www.w3.org/TR/FileAPI/" rel="external nofollow">الواجهة البرمجية الخاصة بالملفات File <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> وبالتحديد الكائن <code>Blob</code>، والذي يتألف من نص افتراضي هو <code>type</code> (من النوع متعدد الوسائط MIME عادةً)، بالإضافة إلى الوسيط <code>blobParts</code> وهو سلسلة من كائنات <code>Blob</code> أخرى ونصوص ومصدر للبيانات الثنائية <code>BufferSource</code>.
</p>

<p style="text-align: center;">
	<img alt="blob_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="73491" data-unique="74goxrs64" src="https://academy.hsoub.com/uploads/monthly_2021_08/blob_01.png.29aaaff8ea25166d45ea7756532ad3e7.png"></p>

<p>
	تأخذ الدالة البانية الصيغة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1417_9" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">Blob</span><span class="pun">(</span><span class="pln">blobParts</span><span class="pun">,</span><span class="pln"> options</span><span class="pun">);</span></pre>

<p>
	حيث:
</p>

<ul>
<li>
		<code>blobParts</code>: هو مصفوفة قيمها كائنات <code>Blob</code> و<code>BufferSource</code> و<code>String</code>.
	</li>
	<li>
		<code>options</code>: ويتضمن كائنات اختياريةً هي:
	</li>
	<li>
		<code>type</code>: يمثل نوع الكائن <code>Blob</code>، وهو عادةً من النوع متعدد الوسائط MIME مثل: "image/png".
	</li>
	<li>
		<code>endings</code>: ويحدد إن كنا سنحوّل محرف نهاية السطر للكائن <code>Blob</code> بما يناسب نظام التشغيل الحالي (<code>n\</code> أو <code>\r\n\</code>)، وسيأخذ افتراضيًا القيمة "transparent" أي لا تفعل شيئًا، وقد يأخذ القيمة "native" أي أَجرِ التحويل.
	</li>
</ul>
<p>
	إليك المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1417_12" style="">
<span class="com">//  (blob) إنشاء كائن بيانات ثنائية من نص</span><span class="pln">
let blob </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Blob</span><span class="pun">([</span><span class="str">"&lt;html&gt;…&lt;/html&gt;"</span><span class="pun">],</span><span class="pln"> </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">'text/html'</span><span class="pun">});</span><span class="pln">
</span><span class="com">// لاحظ أن الوسيط الأول هو مصفوفة</span><span class="pln">
</span><span class="com">// إنشاء كائن بيانات ثنائية من نص ومصفوفة</span><span class="pln">
let hello </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint8Array</span><span class="pun">([</span><span class="lit">72</span><span class="pun">,</span><span class="pln"> </span><span class="lit">101</span><span class="pun">,</span><span class="pln"> </span><span class="lit">108</span><span class="pun">,</span><span class="pln"> </span><span class="lit">108</span><span class="pun">,</span><span class="pln"> </span><span class="lit">111</span><span class="pun">]);</span><span class="pln"> </span><span class="com">// "Hello" بالصيغة الثنائية</span><span class="pln">

let blob </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Blob</span><span class="pun">([</span><span class="pln">hello</span><span class="pun">,</span><span class="pln"> </span><span class="str">' '</span><span class="pun">,</span><span class="pln"> </span><span class="str">'world'</span><span class="pun">],</span><span class="pln"> </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">'text/plain'</span><span class="pun">});</span></pre>

<p>
	يمكن استخراج الشرائح المكونة للكائن <code>Blob</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1417_14" style="">
<span class="pln">blob</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">([</span><span class="pln">byteStart</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="pln">byteEnd</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="pln">contentType</span><span class="pun">]);</span></pre>

<p>
	حيث:
</p>

<ul>
<li>
		<code>byteStart</code>: بايت البداية وافتراضيًا هو البايت 0.
	</li>
	<li>
		<code>byteEnd</code>: البايت الأخير (ضمنًا، وافتراضيًا حتى آخر المخزن).
	</li>
	<li>
		<code>contentType</code>: نوع كائن <code>blob</code> الجديد، وسيكون افتراضيًا نفس نوع مصدر البيانات.
	</li>
</ul>
<p>
	حيث تشابه هذه الوسائط مقابلاتها في التابع <code>array.slice</code>، ويُسمح باستخدام القيم السالبة.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<h4>
		الكائنات <code>Blob</code>هي كائنات ثابتة
	</h4>

	<p>
		لا يمكن تغيير بيانات <code>Blob</code> مباشرةً، لكن يمكن اجتزاء شرائح منها أو إنشاء كائن <code>Blob</code> جديد أو خلط البيانات في كائن جديد وهكذا، ويشابه هذا السلوك سلوك الكائنات النصية في JavaScript، حيث لا يمكن تغيير حرف في النص، لكن بالإمكان إنشاء نص جديد مصحح.
	</p>
</blockquote>

<h2>
	الكائن Blob كعنوان لمورد URL
</h2>

<p>
	يمكن استخدام الكائن <code>Blob</code> مثل عناوين للرابط التشعبي <code>&lt;a&gt;</code> ولمعرّف الصورة <code>&lt;img&gt;</code> لإظهار محتوياتهما بفضل الوسيط <code>type</code>، كما يمكن رفع أو تنزيل الكائنات <code>Blob</code>، وسيتحول النوع <code>type</code> بالطبع إلى <code>Content-Type</code> في طلبات الشبكة network requests.
</p>

<p>
	لنبدأ بالمثال البسيط التالي، فبالنقر على الرابط سينزل كائن <code>Blob</code> مولَّد آليًا يحتوي على النص "Hello world" ضمن ملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1417_21" style="">
<span class="pun">&lt;!--</span><span class="pln"> download attribute forces the browser to download instead of navigating </span><span class="pun">--&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">a download</span><span class="pun">=</span><span class="str">"hello.txt"</span><span class="pln"> href</span><span class="pun">=</span><span class="str">'#'</span><span class="pln"> id</span><span class="pun">=</span><span class="str">"link"</span><span class="pun">&gt;</span><span class="typ">Download</span><span class="pun">&lt;/</span><span class="pln">a</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
let blob </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Blob</span><span class="pun">([</span><span class="str">"Hello, world!"</span><span class="pun">],</span><span class="pln"> </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">'text/plain'</span><span class="pun">});</span><span class="pln">

link</span><span class="pun">.</span><span class="pln">href </span><span class="pun">=</span><span class="pln"> URL</span><span class="pun">.</span><span class="pln">createObjectURL</span><span class="pun">(</span><span class="pln">blob</span><span class="pun">);</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634984872="1" data-ss1634985247="1" frameborder="no" height="132" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/yLogJMq?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-Blob-ex1">See the Pen JS-P3-01-Blob-ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	بالإمكان أيضًا إنشاء رابط آليًا في JavaScript، ومن ثم محاكاة عملية النقر بزر الفأرة <code>()link.click</code> وسيبدأ بعدها التنزيل آليًا، إليك الشيفرة التالية التي تسمح للمستخدم بتنزيل كائن <code>Blob</code> المولَّد آليًا دون أي شيفرة HTML:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1417_23" style="">
<span class="pln">let link </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'a'</span><span class="pun">);</span><span class="pln">
link</span><span class="pun">.</span><span class="pln">download </span><span class="pun">=</span><span class="pln"> </span><span class="str">'hello.txt'</span><span class="pun">;</span><span class="pln">

let blob </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Blob</span><span class="pun">([</span><span class="str">'Hello, world!'</span><span class="pun">],</span><span class="pln"> </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">'text/plain'</span><span class="pun">});</span><span class="pln">

link</span><span class="pun">.</span><span class="pln">href </span><span class="pun">=</span><span class="pln"> URL</span><span class="pun">.</span><span class="pln">createObjectURL</span><span class="pun">(</span><span class="pln">blob</span><span class="pun">);</span><span class="pln">

link</span><span class="pun">.</span><span class="pln">click</span><span class="pun">();</span><span class="pln">

URL</span><span class="pun">.</span><span class="pln">revokeObjectURL</span><span class="pun">(</span><span class="pln">link</span><span class="pun">.</span><span class="pln">href</span><span class="pun">);</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634984872="1" data-ss1634985247="1" frameborder="no" height="180" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/GRvrqmN?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-Blob-ex2">See the Pen JS-P3-01-Blob-ex2 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يأخذ التابع <code>URL.createObjectURL</code> الكائن <code>Blob</code> مثل وسيط وينشئ عنوانًا له على الشكل <code>&lt;blob:&lt;origin&gt;/&lt;uuid</code>.
</p>

<p>
	ستبدو قيمة الخاصية <code>link.href</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1417_25" style="">
<span class="pln">blob</span><span class="pun">:</span><span class="pln">https</span><span class="pun">:</span><span class="com">//javascript.info/1e67e00e-860d-40a5-89ae-6ab0cbee6273</span></pre>

<p>
	يُخزِّن المتصفح كل عنوان يولّده التابع <code>URL.createObjectURL</code> على شكل ارتباط map داخلي من الشكل "URL&gt;Blob"، لذا ستكون هذه العناوين قصيرةً، لكنها تسمح بالوصول إلى الكائن <code>Blob</code>، وسيكون العنوان المولَّد -وبالتالي الرابط المتعلق به- صالحًا ضمن المستند الحالي طالما كان مفتوحًا، كما سيسمح بتحديد مرجع للكائن في كل من <code>&lt;img&gt;</code> و<code>&lt;a&gt;</code> وغيرهما من الكائنات التي تحتاج إلى عنوان URL، ومع ذلك هنالك أثر جانبي، فعلى الرغم من ارتباط <code>Blob</code> بالعنوان فهذا الكائن مقيم في الذاكرة، ولا يمكن للمتصفح تحرير الذاكرة المتعلقة به، وسيُزال الارتباط كليًا عند إنهاء المستند، وبالتالي ستتحرر الذاكرة المرتبطة بالكائنات <code>Blob</code>، لكن إن استمر التطبيق لفترة طويلة فلن تحدث هذه العملية خلال فترة وجيزة.
</p>

<p>
	<strong>إذا أنشأنا عنوانًا فسيبقى الكائن <code>Blob</code> مقيمًا في الذاكرة حتى لو لم تَعُد هناك حاجة له</strong>.
</p>

<p>
	يزيل التابع <code>(URL.revokeObjectURL(url</code> المرجع من علاقة الارتباط الداخلي بين الكائن <code>Blob</code> والعنوان متيحًا المجال لإزالته (إن لم يرتبط بمراجع أخرى) وتحرير الذاكرة، وقد حرصنا في المثال الأخير على استخدام <code>Blob</code> مرةً واحدةً للتنزيل الفوري، لذلك سنستدعي مباشرةً الآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2644_9" style="">
<span class="pln"> URL</span><span class="pun">.</span><span class="pln">revokeObjectURL</span><span class="pun">(</span><span class="pln">link</span><span class="pun">.</span><span class="pln">href</span><span class="pun">)</span></pre>

<p>
	لم نستدع التابع <code>(URL.revokeObjectURL(link.href</code> في مثالنا السابق الذي تضمن رابط HTML قابلًا للنقر، مما سيجعل الكائن <code>Blob</code> غير صالح، ولن يعمل العنوان بعد إلغاء الارتباط المرجعي.
</p>

<h2>
	التحويل بين Blob ونص مشفر بطريقة base64
</h2>

<p>
	يمكن أن نحوّل الكائن <code>Blob</code> إلى سلسلة نصية بتشفير base64 لتكون يمثابة وسيلة بديلة لاستخدام التابع <code>URL.createObjectURL</code>، يمثل التشفير السابق البيانات الثنائية مثل نص يتكون من محارف ASCII من 0 حتى 64 قابلة للقراءة، ويتمتع بحيز أمان كبير جدًا، والأهم من ذلك إمكانية استخدام هذا التشفير مع <a data-ss1634984872="1" data-ss1634985247="1" href="https://developer.mozilla.org/en-US/docs/Web/http/Data_URIs" rel="external nofollow">عناوين موارد البيانات data urls</a>، والتي لها الشكل:<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1417_42" style="">
<span class="pun">&lt;</span><span class="pln">data</span><span class="pun">:[&lt;</span><span class="pln">mediatype</span><span class="pun">&gt;][;</span><span class="pln">base64</span><span class="pun">],&lt;</span><span class="pln">data
</span></pre>

<p>
	 يمكن استخدام هذه العناوين في أي مكان مثل العناوين النظامية. إليك طريقة تمثيل بيانات تعطي وجهًا تعبيريًا Smiley:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5239_7" style="">
<span class="pun">&lt;</span><span class="pln">img src</span><span class="pun">=</span><span class="str">"data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7"</span><span class="pun">&gt;</span></pre>

<p>
	حيث سيفكك المتصفح شيفرة البيانات ويعطي كنتيجة الوجه التعبيري التالي: <img alt="emoji.png" class="ipsImage ipsImage_thumbnailed" data-fileid="73492" data-unique="eshc2b3lw" src="https://academy.hsoub.com/uploads/monthly_2021_08/emoji.png.5ee318e1e734ca9b5aba3eaafe957203.png"></p>

<p>
	نستخدم الكائن <code>FileReader</code> المدمج لتحويل <code>Blob</code> إلى base64، حيث يستطيع قراءة البيانات من <code>Blob</code> بصيغ مختلفة، وسنتعمق في هذا الموضوع أكثر في الفصل القادم.
</p>

<p>
	إليك نموذجًا عن تنزيل <code>Blob</code> من خلال التشفير base64 هذه المرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1417_29" style="">
<span class="pln">let link </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'a'</span><span class="pun">);</span><span class="pln">
link</span><span class="pun">.</span><span class="pln">download </span><span class="pun">=</span><span class="pln"> </span><span class="str">'hello.txt'</span><span class="pun">;</span><span class="pln">

let blob </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Blob</span><span class="pun">([</span><span class="str">'Hello, world!'</span><span class="pun">],</span><span class="pln"> </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">'text/plain'</span><span class="pun">});</span><span class="pln">

let reader </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">FileReader</span><span class="pun">();</span><span class="pln">
reader</span><span class="pun">.</span><span class="pln">readAsDataURL</span><span class="pun">(</span><span class="pln">blob</span><span class="pun">);</span><span class="pln"> </span><span class="com">// استدعاء التحويل</span><span class="pln">

reader</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  link</span><span class="pun">.</span><span class="pln">href </span><span class="pun">=</span><span class="pln"> reader</span><span class="pun">.</span><span class="pln">result</span><span class="pun">;</span><span class="pln"> </span><span class="com">// عنوان بيانات</span><span class="pln">
  link</span><span class="pun">.</span><span class="pln">click</span><span class="pun">();</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634984872="1" data-ss1634985247="1" frameborder="no" height="178" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/MWvJemP?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-Blob-ex3">See the Pen JS-P3-01-Blob-ex3 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يمكن استخدام إحدى الطريقتين السابقتين لتحويل كائن <code>Blob</code> إلى عنوان، لكن تكون عادةً الطريقة <code>(URL.createObjectURL(blob</code> أبسط وأسرع.
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th style="text-align:center">
				Blob to data url
			</th>
			<th style="text-align:center">
				(URL.createObjectURL(blob
			</th>
		</tr></thead>
<tbody>
<tr>
<td style="text-align:center">
				لا حاجة لإزالة أي شيء
			</td>
			<td style="text-align:center">
				لابد من إزالتها إذا كنا نهتم بمقدار الذاكرة المحجوزة
			</td>
		</tr>
<tr>
<td style="text-align:center">
				خسارة في الأداء والذاكرة لكائنات <code>Blob</code> الضخمة عند الترميز
			</td>
			<td style="text-align:center">
				وصول مباشر إلى الكائن <code>Blob</code>، لا حاجة للترميز وفك الترميز
			</td>
		</tr>
</tbody>
</table>
<h2>
	تحويل الصورة إلى كائن Blob
</h2>

<p>
	يمكن تحويل صورة أو جزء من صورة أو لقطة شاشة إلى كائن بيانات ثنائية <code>Blob</code>، وهذا مفيد عند تحميل هذه الصور إلى مكان ما، وتُنفّذ العمليات على الصور باستخدام العنصر <code>&lt;canvas&gt;</code>:
</p>

<ol>
<li>
		ارسم صورةً أو جزءًا منها ضمن لوحة الرسم Canvas باستخدام التابع <a data-ss1634984872="1" data-ss1634985247="1" href="https://developer.mozilla.org/en-US/docs/Web/api/CanvasRenderingContext2D/drawImage" rel="external nofollow">canvas.drawImage</a>.
	</li>
	<li>
		استدع التابع <a data-ss1634984872="1" data-ss1634985247="1" href="https://developer.mozilla.org/en-US/docs/Web/api/HTMLCanvasElement/toBlob" rel="external nofollow">(toBlob(callback, format, quality</a> الذي يُنشئ وينفّذ استدعاءً محددًا عندما يكتمل.
	</li>
</ol>
<p>
	ستجد في المثال التالي صورةً نُسخت للتو، لكن يمكن اقتطاع جزء منها أو نقلها إلى لوحة رسم قبل إنشاء <code>Blob</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1417_31" style="">
<span class="com">// خذ أية صورة</span><span class="pln">
let img </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'img'</span><span class="pun">);</span><span class="pln">

</span><span class="com">// شكّل لوحة رسم بنفس الحجم</span><span class="pln">
let canvas </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'canvas'</span><span class="pun">);</span><span class="pln">
canvas</span><span class="pun">.</span><span class="pln">width </span><span class="pun">=</span><span class="pln"> img</span><span class="pun">.</span><span class="pln">clientWidth</span><span class="pun">;</span><span class="pln">
canvas</span><span class="pun">.</span><span class="pln">height </span><span class="pun">=</span><span class="pln"> img</span><span class="pun">.</span><span class="pln">clientHeight</span><span class="pun">;</span><span class="pln">

let context </span><span class="pun">=</span><span class="pln"> canvas</span><span class="pun">.</span><span class="pln">getContext</span><span class="pun">(</span><span class="str">'2d'</span><span class="pun">);</span><span class="pln">

</span><span class="com">// انسخ الصورة إلى اللوحة</span><span class="pln">
context</span><span class="pun">.</span><span class="pln">drawImage</span><span class="pun">(</span><span class="pln">img</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">
</span><span class="com">// يمكن أن نعدل اللوحة كما نشاء</span><span class="pln">

</span><span class="com">// عملية التحويل إلى كائن ثنائي غير متزامنة</span><span class="pln">
canvas</span><span class="pun">.</span><span class="pln">toBlob</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">blob</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">//الكائن جاهز سننزله</span><span class="pln">
  let link </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'a'</span><span class="pun">);</span><span class="pln">
  link</span><span class="pun">.</span><span class="pln">download </span><span class="pun">=</span><span class="pln"> </span><span class="str">'example.png'</span><span class="pun">;</span><span class="pln">

  link</span><span class="pun">.</span><span class="pln">href </span><span class="pun">=</span><span class="pln"> URL</span><span class="pun">.</span><span class="pln">createObjectURL</span><span class="pun">(</span><span class="pln">blob</span><span class="pun">);</span><span class="pln">
  link</span><span class="pun">.</span><span class="pln">click</span><span class="pun">();</span><span class="pln">

  </span><span class="com">// امسح المرجع الداخلي للكائن حتى يتمكن Canvas المتصفح من إزالته </span><span class="pln">
  URL</span><span class="pun">.</span><span class="pln">revokeObjectURL</span><span class="pun">(</span><span class="pln">link</span><span class="pun">.</span><span class="pln">href</span><span class="pun">);</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> </span><span class="str">'image/png'</span><span class="pun">);</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634984872="1" data-ss1634985247="1" frameborder="no" height="192" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/wvqgWer?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-The-clickjacking-attack-ex7">See the Pen JS-P3-01-The-clickjacking-attack-ex7 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يمكن استخدام الصيغة <code>async/await</code> بدلًا من دوال الاستدعاء:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1417_33" style="">
<span class="pln">let blob </span><span class="pun">=</span><span class="pln"> await </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><span class="pln">resolve </span><span class="pun">=&gt;</span><span class="pln"> canvasElem</span><span class="pun">.</span><span class="pln">toBlob</span><span class="pun">(</span><span class="pln">resolve</span><span class="pun">,</span><span class="pln"> </span><span class="str">'image/png'</span><span class="pun">));</span></pre>

<p>
	يمكن استخدام <a data-ss1634984872="1" data-ss1634985247="1" href="https://github.com/niklasvh/html2canvas" rel="external nofollow">مكتبات خاصة</a> لالتقاط صورة للشاشة، وما عليك إلا الانتقال ضمن الصفحة ورسمها ضمن لوحة <code>&lt;canvas&gt;</code>، ثم يمكن نقلها بعد ذلك إلى <code>Blob</code> بنفس الأسلوب السابق.
</p>

<h2>
	التحويل من الكائن Blob إلى الكائن arrayBuffer
</h2>

<p>
	تسمح الدالة البانية للكائن <code>Blob</code> بإنشاء هذا الكائن من أي شيء تقريبًا، بما في ذلك أية كائنات <code>BufferSource</code>، لكن لو أردنا إنجاز عملية معالجة منخفضة المستوى، فيمكننا الحصول على كائن <code>ArrayBuffer</code> ذو مستوى أدنى مستخدمين <code>FileReader</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1417_36" style="">
<span class="com">// get arrayBuffer from blob</span><span class="pln">
let fileReader </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">FileReader</span><span class="pun">();</span><span class="pln">

fileReader</span><span class="pun">.</span><span class="pln">readAsArrayBuffer</span><span class="pun">(</span><span class="pln">blob</span><span class="pun">);</span><span class="pln">

fileReader</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let arrayBuffer </span><span class="pun">=</span><span class="pln"> fileReader</span><span class="pun">.</span><span class="pln">result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<h2>
	خلاصة
</h2>

<ul>
<li>
		تمثل الكائنات <code>ArrayBuffer</code> و<code>Uint8Array</code> وغيرها من الكائنات التي تنضوي تحت المصطلح <code>BufferSource</code> بيانات ثنائيةً، بينما يمثل <a data-ss1634984872="1" data-ss1634985247="1" href="https://www.w3.org/TR/FileAPI/#dfn-Blob" rel="external nofollow">Blob</a> بيانات ثنائيةً لها نوع، مما يجعل الكائن <code>Blob</code> مناسبًا لعمليات الرفع والتنزيل التي تستخدم بكثرة من المتصفح.
	</li>
	<li>
		يمكن للتوابع التي تُنفِّذ طلبات الويب web-requests مثل: <a data-ss1634984872="1" data-ss1634985247="1" href="https://javascript.info/xmlhttprequest" rel="external nofollow">XMLHttpRequest</a> و<a data-ss1634984872="1" data-ss1634985247="1" href="https://javascript.info/fetch" rel="external nofollow">fetch</a> وغيرها؛ أن تعمل مع <code>Blob</code> كما تعمل مع غيره من أنواع البيانات الثنائية.
	</li>
	<li>
		يمكن التحويل بسهولة بين <code>Blob</code> وكائنات البيانات الثنائية منخفضة المستوى:
	</li>
	<li>
		يمكن التحويل بين <code>Blob</code> ومصفوفة النوع باستخدام الدالة البانية <code>(...)new Blob</code>
	</li>
	<li>
		يمكن الحصول على الكائن <code>ArrayBuffer</code> من الكائن <code>Blob</code> باستخدام التعليمة <code>FileReader</code>، ثم إنشاء كائن استعراض بناءً على هذا الأخير، وذلك لمعالجة البيانات الثنائية في مستويات عمل منخفضة.
	</li>
</ul>
<p>
	ترجمة -وبتصرف- للفصل <a data-ss1634984872="1" data-ss1634985247="1" href="https://javascript.info/blob" rel="external nofollow">Blob</a> من سلسلة <a href="https://javascript.info/" rel="external nofollow">The Modern JavaScript Tutorial</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a data-ss1634984872="1" data-ss1634985247="1" href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1283/" rel="">ترميز النصوص والتعامل مع كائنات الملفات في جافاسكريبت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1285</guid><pubDate>Fri, 06 Aug 2021 15:03:00 +0000</pubDate></item><item><title>&#x62A;&#x631;&#x645;&#x64A;&#x632; &#x627;&#x644;&#x646;&#x635;&#x648;&#x635; &#x648;&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x643;&#x627;&#x626;&#x646;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x644;&#x641;&#x627;&#x62A; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1283/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/610c3cb139182_-----.png.7cdaf0d4087cca1017656ae753db0c1c.png" /></p>

<p>
	ماذا لو كانت البيانات الثنائية معلومات نصيةً؟ أي ماذا لو تلقينا ملفًا يحتوي على نص؟ سيسمح لنا الكائن <a data-ss1634984545="1" href="https://encoding.spec.whatwg.org/#interface-textdecoder" rel="external nofollow">TextDecoder</a> المدمج ضمن JavaScript بقراءة البيانات وتحويلها إلى نص فعلي، بعد تزويده بالمخزن المؤقت buffer الذي يحتوي البيانات الثنائية وطريقة فك الترميز، حيث علينا أوّلًا إنشاء مفكك الترميز Decoder:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_304_8" style="">
<span class="pln">let decoder </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TextDecoder</span><span class="pun">([</span><span class="pln">label</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="pln">options</span><span class="pun">]);</span></pre>

<p>
	حيث:
</p>

<ul>
<li>
		<code>label</code>: يمثل طريقة الترميز، والتي ستكون <code>utf-8</code> افتراضيًا، كما تدعم كلًا من الطريقتين <code>big5</code> و<code>windows-1251</code> وغيرها.
	</li>
	<li>
		<code>options</code>: وتضم كائنات اختياريةً هي:
		<ul>
<li>
				<code>fatal</code>: قيمة منطقية boolean، حيث عندما تأخذ القيمة "true"، فسترمي استثناءً عند وجود محارف غير صالحة أي لا يمكن فك ترميزها، وإلا -وهي الحالة الافتراضية- فستستبدلها بالمحرف "uFFFD\".
			</li>
			<li>
				<code>ignoreBOM</code>: وهي قيمة منطقية ستتجاهل BOM (وهي علامة Unicode خاصة بترتيب البايتات) التي تُستخدم نادرًا، ويحدث ذلك عندما تأخذ القيمة "true".
			</li>
		</ul>
</li>
</ul>
<p>
	ومن ثم عملية فك الترميز Decoding:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_304_10" style="">
<span class="pln">let str </span><span class="pun">=</span><span class="pln"> decoder</span><span class="pun">.</span><span class="pln">decode</span><span class="pun">([</span><span class="pln">input</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="pln">options</span><span class="pun">]);</span></pre>

<ul>
<li>
		<code>input</code>: ويمثل كائن <code>BufferSource</code> الذي يحتوي البيانات الثنائية.
	</li>
	<li>
		<code>options</code>: وتضم كائنًا اختياريًا هو:
		<ul>
<li>
				<code>stream</code>: وتأخذ القيمة "true" عندما نريد فك ترميز مجرى تدفق دخل، وذلك عند استدعاء مفكك الترميز باستمرار عن طريق مجموعات البيانات القادمة، وفي هذه الحالة قد يوضع محرف من عدة بايتات multi-byte charecter ليفصل بين هذه المجموعات، ويخبر هذا الخيار مفكك الترميز بتذكر المحارف غير المرمزة، وأن يفك ترميزها عندما تصل المجموعة الجديدة من البيانات. فمثلًا:
			</li>
		</ul>
</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_304_13" style="">
<span class="pln">let uint8Array </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint8Array</span><span class="pun">([</span><span class="lit">72</span><span class="pun">,</span><span class="pln"> </span><span class="lit">101</span><span class="pun">,</span><span class="pln"> </span><span class="lit">108</span><span class="pun">,</span><span class="pln"> </span><span class="lit">108</span><span class="pun">,</span><span class="pln"> </span><span class="lit">111</span><span class="pun">]);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TextDecoder</span><span class="pun">().</span><span class="pln">decode</span><span class="pun">(</span><span class="pln">uint8Array</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// Hello</span></pre>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_304_15" style="">
<span class="pln">let uint8Array </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint8Array</span><span class="pun">([</span><span class="lit">228</span><span class="pun">,</span><span class="pln"> </span><span class="lit">189</span><span class="pun">,</span><span class="pln"> </span><span class="lit">160</span><span class="pun">,</span><span class="pln"> </span><span class="lit">229</span><span class="pun">,</span><span class="pln"> </span><span class="lit">165</span><span class="pun">,</span><span class="pln"> </span><span class="lit">189</span><span class="pun">]);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TextDecoder</span><span class="pun">().</span><span class="pln">decode</span><span class="pun">(</span><span class="pln">uint8Array</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 你好</span></pre>

<p>
	يمكن فك ترميز جزء من المخزن المؤقت بإنشاء مصفوفة ثانوية subarray بالطريقة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_304_18" style="">
<span class="pln">let uint8Array </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint8Array</span><span class="pun">([</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">72</span><span class="pun">,</span><span class="pln"> </span><span class="lit">101</span><span class="pun">,</span><span class="pln"> </span><span class="lit">108</span><span class="pun">,</span><span class="pln"> </span><span class="lit">108</span><span class="pun">,</span><span class="pln"> </span><span class="lit">111</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">]);</span><span class="pln">

</span><span class="com">// النص في الوسط</span><span class="pln">
</span><span class="com">// أنشئ تمثيلًا جديدًا دون نسخ أي شيء</span><span class="pln">
let binaryString </span><span class="pun">=</span><span class="pln"> uint8Array</span><span class="pun">.</span><span class="pln">subarray</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">);</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TextDecoder</span><span class="pun">().</span><span class="pln">decode</span><span class="pun">(</span><span class="pln">binaryString</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// Hello</span></pre>

<h2>
	مرمز النصوص TextEncoder
</h2>

<p>
	مهمته معاكسة لمهمة مفكك الترميز، إذ يحوّل <a data-ss1634984545="1" href="https://encoding.spec.whatwg.org/#interface-textencoder" rel="external nofollow">المُرمِّز TextEncoder</a> النص إلى بايتات.
</p>

<p>
	ويُستخدم كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_304_20" style="">
<span class="pln">let encoder </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TextEncoder</span><span class="pun">();</span></pre>

<p>
	كما يدعم طريقة الترميز "utf-8" فقط. وله تابعان هما:
</p>

<ul>
<li>
		<code>(encode(str</code>: ويعيد كائنًا من النوع <code>Uint8Array</code> انطلاقًا من نص.
	</li>
	<li>
		<code>(encodeInto(str, destination</code>: يُرمز <code>str</code> إلى <code>destination</code> والتي ينبغي أن تكون من النوع <code>Uint8Array</code>.
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_304_22" style="">
<span class="pln">let encoder </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TextEncoder</span><span class="pun">();</span><span class="pln">

let uint8Array </span><span class="pun">=</span><span class="pln"> encoder</span><span class="pun">.</span><span class="pln">encode</span><span class="pun">(</span><span class="str">"Hello"</span><span class="pun">);</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">uint8Array</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 72,101,108,108,111</span></pre>

<h2>
	الكائنان File وFileReader
</h2>

<p>
	يرث الكائن <a data-ss1634984545="1" href="https://www.w3.org/TR/FileAPI/#dfn-file" rel="external nofollow">File</a> الكائن <code>Blob</code> ويُوسَّع بإمكانيات تتعلق بنظام الملفات، وتوجد طريقتان لإنشائه:
</p>

<ol>
<li>
		باستخدام الدالة البانية بشكل مشابه للكائن <code>Blob</code>:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_304_24" style="">
<span class="pln">   </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">File</span><span class="pun">(</span><span class="pln">fileParts</span><span class="pun">,</span><span class="pln"> fileName</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">options</span><span class="pun">])</span></pre>

<p>
	حيث:
</p>

<ul>
<li>
		<code>fileparts</code>: يمثل مصفوفةً قد تكون قيمها نصية أو <code>blob</code> أو <code>BufferSource</code>.
	</li>
	<li>
		<code>fileName</code>: اسم الملف.
	</li>
	<li>
		<code>options</code>: وتضم الكائن الاختياري التالي:
		<ul>
<li>
				<code>lastModified</code>: تاريخ آخر تعديل (تاريخ من النوع الصحيح integer).
			</li>
		</ul>
</li>
</ul>
<ol start="2">
<li>
		الحصول على ملف باستخدام <code>&lt;"input type="file&gt;</code> أو أسلوب الجر والإفلات، أو غيرها من الواجهات التي يؤمنها المتصفح، حيث يأخذ الملف في هذه الحالات المعلومات السابقة التي استخدمَت بمثابة وسطاء من نظام التشغيل.
	</li>
</ol>
<p>
	بما أن الكائن <a data-ss1634984545="1" href="https://www.w3.org/TR/FileAPI/#dfn-file" rel="external nofollow">File</a> يرث الكائن <code>Blob</code>، فله خصائصه نفسها، بالإضافة إلى الآتي:
</p>

<ul>
<li>
		<code>name</code>: اسم الملف.
	</li>
	<li>
		<code>lastModified</code>: تاريخ آخر تعديل.
	</li>
</ul>
<p>
	إليك مثالًا يصف الحصول على الكائن <code>File</code> باستخدام <code>&lt;"input type="file&gt;</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_304_26" style="">
<span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"file"</span><span class="pln"> onchange</span><span class="pun">=</span><span class="str">"showFile(this)"</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> showFile</span><span class="pun">(</span><span class="pln">input</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let file </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">.</span><span class="pln">files</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">

  alert</span><span class="pun">(`</span><span class="typ">File</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">file</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}`);</span><span class="pln"> </span><span class="com">// e.g my.png</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Last</span><span class="pln"> modified</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">file</span><span class="pun">.</span><span class="pln">lastModified</span><span class="pun">}`);</span><span class="pln"> </span><span class="com">// e.g 1552830408824</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634984545="1" frameborder="no" height="139" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/GRvrqjj?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-02-File-and-FileReader-ex1">See the Pen JS-P3-02-File-and-FileReader-ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>ملاحظة</strong>:
	</p>

	<p>
		يمكن اختيار عدة ملفات، لذلك نستخدم الأمر <code>input.files</code> الذي يمثل كائنًا يشبه المصفوفة يضم هذه الملفات، وسنتعامل في مثالنا مع كائن واحد لذلك نرى الأمر السابق على الشكل <code>[input.files[0</code>.
	</p>
</blockquote>

<h2>
	الكائن FileReader
</h2>

<p>
	يمثل <a data-ss1634984545="1" href="https://www.w3.org/TR/FileAPI/#dfn-filereader" rel="external nofollow">FileReader</a> كائنًا يخدم غرضًا وحيدًا هو قراءة البيانات من الكائن <code>Blob</code> (وبالتالي من الكائن <code>File</code> أيضًا)، وينقل البيانات مستخدمًا الأحداث، لأن القراءة من القرص قد تستغرق وقتًا، وصيغة الدالة البانية له هي بالشكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_304_28" style="">
<span class="pln">let reader </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">FileReader</span><span class="pun">();</span><span class="pln"> </span><span class="com">// لا وسطاء</span></pre>

<p>
	لهذا الكائن عدة توابع، أهمها:
</p>

<ul>
<li>
		<code>(readAsArrayBuffer(blob</code>: يقرأ البيانات بالصيغة الثنائية على شكل <code>ArrayBuffer</code>.
	</li>
	<li>
		<code>([readAsText(blob, [encoding</code>: يقرأ البيانات مثل نص مستعمل للتشفير المحدد، وهو <code>utf-8</code> افتراضيًا.
	</li>
	<li>
		<code>(readAsDataURL(blob</code>: يقرأ البيانات الثنائية ويحولها إلى عنوان بيانات data url مشفر بطريقة base64.
	</li>
	<li>
		<code>()abort</code>: يلغي العملية.
	</li>
</ul>
<p>
	نحدد التابع الذي سيستعمَل في القراءة وفقًا لصيغة البيانات التي نفضلها، وكيف سنستخدمها.
</p>

<ul>
<li>
		<code>readAsArrayBuffer</code>: يُستخدم لقراءة الملفات الثنائية وتنفيذ عمليات ثنائية منخفضة المستوى، بينما نستدعي الكائن <code>File</code> مباشرةً دون قراءة عند تنفيذ عمليات عالية المستوى (ليست ثنائيةً) مثل اقتطاع شرائح من البيانات slicing.
	</li>
	<li>
		<code>readAsText</code>: يُستخدم عند قراءة ملفات نصية والحصول على قيم نصية.
	</li>
	<li>
		<code>readAsDataURL</code>: يُستخدم عندما نريد استخدام البيانات المقروءة في الخاصية <code>src</code> للمعرف <code>img</code> وغيره من المعرّفات، كما توجد طريقة أخرى لقراءة الملف تعرفنا عليها في مقال "<a data-ss1634984545="1" href="https://academy.hsoub.com/programming/javascript/%D9%83%D8%A7%D8%A6%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-blob-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1285/" rel="">كائن البيانات الثنائية Blob</a>"، وهي استخدام التابع <code>(URL.createObjectURL(file</code>
	</li>
</ul>
<p>
	يمكن الاستفادة من عدة أحداث تقع أثناء عملية القراءة:
</p>

<ul>
<li>
		<code>loadstart</code>: يقع عند بداية التحميل.
	</li>
	<li>
		<code>progress</code>: يقع خلال القراءة.
	</li>
	<li>
		<code>load</code>: يقع عند انتهاء القراءة دون أخطاء.
	</li>
	<li>
		<code>abort</code>: يقع عند استدعاء التابع <code>()abort</code>.
	</li>
	<li>
		<code>error</code>: يقع عند حدوث خطأ.
	</li>
	<li>
		<code>loadend</code>: يقع عند انتهاء القراءة بنجاح أو بفشل.
	</li>
</ul>
<p>
	يمكن الوصول إلى النتيجة عند انتهاء القراءة بالشكل التالي:
</p>

<ul>
<li>
		<code>reader.result</code>: للحصول على النتيجة عند نجاح العملية.
	</li>
	<li>
		<code>reader.error</code>: للحصول على الخطأ عند إخفاق القراءة.
	</li>
</ul>
<p>
	وأكثر الأحداث استخدامًا هما الحدثان <code>load</code> و<code>error</code>.
</p>

<p>
	إليك مثالًا عن قراءة ملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_304_30" style="">
<span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"file"</span><span class="pln"> onchange</span><span class="pun">=</span><span class="str">"readFile(this)"</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> readFile</span><span class="pun">(</span><span class="pln">input</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let file </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">.</span><span class="pln">files</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">

  let reader </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">FileReader</span><span class="pun">();</span><span class="pln">

  reader</span><span class="pun">.</span><span class="pln">readAsText</span><span class="pun">(</span><span class="pln">file</span><span class="pun">);</span><span class="pln">

  reader</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">reader</span><span class="pun">.</span><span class="pln">result</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

  reader</span><span class="pun">.</span><span class="pln">onerror </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">reader</span><span class="pun">.</span><span class="pln">error</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

</span><span class="pun">}</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634984545="1" frameborder="no" height="147" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/YzxNWpy?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-02-File-and-FileReader-ex2">See the Pen JS-P3-02-File-and-FileReader-ex2 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>استخدام الكائن FileReader مع الكائن blob</strong> أشرنا في "كائن البيانات الثنائية Blob" إلى إمكانية استخدام <code>FileReader</code> في قراءة كائنات <code>Blob</code>، وبالتالي تحويلها إلى صيغة أخرى، وهي:
	</p>

	<ul>
<li>
			<code>ArrayBuffer</code> باستخدام <code>(readAsArrayBuffer(blob</code>
		</li>
		<li>
			<code>TextDecoder</code> باستخدام <code>([readAsText(blob, [encoding</code>
		</li>
		<li>
			<code>عنوان بيانات data URL</code> بتشفير base64 باستخدام <code>(readAsDataURL(blob</code>
		</li>
	</ul>
<p>
		<strong>الكائن <code>FileReaderSync</code> متاح لعمال ويب Web Workers</strong> ويوجد بديل متزامن للكائن <code>FileReader</code> يستخدم مع عمال الويب Web Workers يُدعى <a data-ss1634984545="1" href="https://www.w3.org/TR/FileAPI/#FileReaderSync" rel="external nofollow">FileReaderSync</a>. حيث لا تولّد توابع القراءة الخاصة به <code>*read</code> أية أحداث، بل تعيد نتيجةً نظاميةً كما تفعل الدوال، ويجري ذلك فقط ضمن شيفرة عامل الويب، لأنّ التأخير في الاستدعاءات المتزامنة التي قد تحدث أثناء قراءة الملفات أقل أهميةً في عامل الويب، لذا لن تؤثر في الصفحة.
	</p>
</blockquote>

<h2>
	خلاصة
</h2>

<p>
	تعرفنا في هذا المقال على كيفية ترميز النصوص وفك ترميزها بجافاسكريبت، مركزين على مرمز النصوص TextEncoder، بعدها انتقلنا للحديث عن كيفية التعامل مع كائنات الملفات، حيث توصلنا إلى الآتي:
</p>

<ul>
<li>
		يرث الكائن <code>File</code> والكائن <code>Blob</code>.
	</li>
	<li>
		للكائن <code>File</code> الخاصيتين <code>name</code> و<code>lastModified</code> بالإضافة إلى خصائص الكائن <code>Blob</code> وتوابعه، مع إمكانية القراءة من نظام الملفات المرتبط بنظام التشغيل.
	</li>
	<li>
		يمكن الحصول على الكائنات <code>File</code> عن طريق مُدخلات المستخدم مثل <code>&lt;input&gt;</code> أو أحداث الجر والإفلات مثل <code>ondragend</code>.
	</li>
	<li>
		يمكن لكائن <code>FileReader</code> القراءة من ملف أو <code>Blob</code> بإحدى التنسيقات التالية:
	</li>
	<li>
		نص باستخدام التابع <code>readAsText</code>.
	</li>
	<li>
		<code>ArrayBuffer</code> باستخدام التابع <code>readAsArrayBuffer</code>.
	</li>
	<li>
		عنوان بيانات بتشفير base64 باستخدام التابع <code>readAsDataURL</code>.
	</li>
	<li>
		لا نحتاج في بعض الحالات إلى قراءة محتويات ملف، لذا وكما فعلنا مع الكائن <code>blob</code> سننشئ عنوانًا قصيرًا باستخدام الأمر <code>(URL.createObjectURL(file</code> ونسنده إلى المعرف <code>&lt;a&gt;</code> أو <code>&lt;img&gt;</code>، وبهذا يمكن تنزيل الملف أو عرضه كصورة أو كجزء من لوحة رسم <code>canvas</code>.
	</li>
	<li>
		سيكون الأمر بسيطًا إذا كنا سنرسل ملفًا عبر الشبكة، إذ يقبل الكائنان <code>XMLHttpRequest</code> و <code>fetch</code> الكائن <code>File</code> مباشرةً.
	</li>
</ul>
<p>
	ترجمة -وبتصرف- للفصلين <a data-ss1634984545="1" href="https://javascript.info/text-decoder" rel="external nofollow">text decoder and text encoder</a> و<a data-ss1634984545="1" href="https://javascript.info/file" rel="external nofollow">File and FileReader</a> من سلسلة <a href="https://javascript.info/" rel="external nofollow">The Modern JavaScript Tutorial</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a data-ss1634984545="1" href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A9-%D8%A7%D9%84%D9%85%D8%AE%D8%B2%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-arraybuffer-%D9%88%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D8%A7%D9%84%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-binary-arrays-r1277/" rel="">مصفوفة المخزن المؤقت ArrayBuffer والمصفوفات الثنائية binary arrays</a>
	</li>
	<li>
		<a data-ss1634984545="1" href="https://academy.hsoub.com/programming/javascript/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1234/" rel="">هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت</a>
	</li>
	<li>
		<a data-ss1634984545="1" href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B7%D9%88%D9%8A%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1271/" rel="">تطويع البيانات في جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1283</guid><pubDate>Tue, 03 Aug 2021 15:01:00 +0000</pubDate></item><item><title>&#x645;&#x635;&#x641;&#x648;&#x641;&#x629; &#x627;&#x644;&#x645;&#x62E;&#x632;&#x646; &#x627;&#x644;&#x645;&#x624;&#x642;&#x62A; ArrayBuffer &#x648;&#x627;&#x644;&#x645;&#x635;&#x641;&#x648;&#x641;&#x627;&#x62A; &#x627;&#x644;&#x62B;&#x646;&#x627;&#x626;&#x64A;&#x629; binary arrays &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A9-%D8%A7%D9%84%D9%85%D8%AE%D8%B2%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-arraybuffer-%D9%88%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D8%A7%D9%84%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-binary-arrays-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1277/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/610630dfe25d7_---ArrayBuffer---binary-arrays.png.916e46d2f9887cd1018488d5f2aaed59.png" /></p>

<p>
	نتعامل في تطوير الويب مع البيانات بالصيغة الثنائية لما نتعامل مع الملفات، سواءً عند إنشائها أو رفعها أو تنزيلها، كما يمكن استخدامها في عمليات معالجة الصور، ويمكن إنجاز كل ما سبق في JavaScript التي تتميز العمليات الثنائية فيها بأنها عالية الأداء على الرغم من وجود بعض الالتباسات، نظرًا لوجود العديد من الأصناف، منها <code>ArrayBuffer</code> و<code>Uint8Array</code> و<code>DataView</code> و<code>Blob</code> و<code>File</code>، وغيرها.
</p>

<p>
	لا توجد طريقة معيارية للتعامل مع البيانات الثنائية في JavaScript موازنةً بغيرها من اللغات، لكن عندما نصنف الأمور فسيبدو كل شيء بسيطًا نوعًا ما، إذ يمثل الكائن الثنائي الأساسي <code>ArrayBuffer</code> مثلًا مرجعًا إلى مساحة ذاكرة متجاورة وذات حجم محدد، ويمكن إنشاؤه كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7468_10" style="">
<span class="pln">let buffer </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayBuffer</span><span class="pun">(</span><span class="lit">16</span><span class="pun">);</span><span class="pln"> </span><span class="com">// إنشاء مخزن مؤقت حجمه 16</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">buffer</span><span class="pun">.</span><span class="pln">byteLength</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 16</span></pre>

<p>
	حيث تحجز الشيفرة السابقة حجمًا من الذاكرة مقداره 16 بايت، ويُملأ بالأصفار.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		لايمثل الكائن <code>ArrayBuffer</code> مصفوفةً من الأشياء.
	</p>

	<p>
		لتوضيح الأمور لا بدّ من الإشارة أن <code>ArrayBuffer</code> لا يشترك مع المصفوفات <code>Array</code> بأي شيء:
	</p>

	<ul>
<li>
			له حجم ثابت، لا يمكن زيادته أو إنقاصه.
		</li>
		<li>
			يحتل المقدار المحدد من الذاكرة تمامًا.
		</li>
		<li>
			نحتاج إلى كائن عرض view للوصول إلى بايت محدد، وليس <code>[buffer[index</code>.
		</li>
	</ul>
</blockquote>

<p>
	فالكائن <code>ArrayBuffer</code> هو مساحة من الذاكرة التي لا يمكن معرفة ما يُخزّن فيها، بل هي سلسلة خام من البايتات، وللتعامل مع <code>ArrayBuffer</code>، سنحتاج إلى كائن عرض لا يخزن أي بيانات بمفرده، بل تنحصر مهمته في تفسير البايتات المخزنة في <code>ArrayBuffer</code>، ومن بين هذه الكائنات الآتي:
</p>

<ul>
<li>
		"Uint8Array": يعامِل كل بايت من <code>ArrayBuffer</code> مثل عدد مستقل يأخذ قيمةً بين 0 و255، وتُمثِّل عددًا صحيحًا بلا إشارة مؤلفًا من 8-بت.
	</li>
	<li>
		"Uint16Array": يعامِل كل بايتين مثل عدد صحيح يأخذ قيمةً بين 0 و65535، وتمثِّل عددًا صحيحًا بلا إشارة مؤلفًا من 16-بت.
	</li>
	<li>
		"Uint32Array": يعامِل كل 4 بايتات مثل عدد صحيح يأخذ قيمةً بين 0 و4294967295، وتمثل عددًا صحيحًا بلا إشارة مؤلفًا من 16-بت.
	</li>
	<li>
		"Float64Array": يعامل كل 8 بايتات مثل عدد عشري ذي فاصلة عائمة floating-point، ويأخذ قيمةً بين <code>5.0x 10^-324</code> و<code>1.8x 10^308</code>.
	</li>
</ul>
<p>
	وبالتالي سيفسَّر الكائن المؤلف من 16 بايت -وفق الكائنات السابقة- إلى 16 عدد صغير، أو 8 أرقام أكبر (بايتان لكل عدد)، أو 4 أعداد أضخم (4 بايتات لكل عدد)، أو عددين بالفاصلة العائمة عاليي الدقة (8 بايتات لكل منهما).
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="72934" data-ss1634984028="1" href="https://academy.hsoub.com/uploads/monthly_2021_07/array_buffer_16bit_01.png.37c41b5a708b9e4012216dc4bfc7ac25.png" rel=""><img alt="array_buffer_16bit_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="72934" data-unique="8ad2b8cz4" src="https://academy.hsoub.com/uploads/monthly_2021_07/array_buffer_16bit_01.png.37c41b5a708b9e4012216dc4bfc7ac25.png"></a>
</p>

<p>
	فالكائن <code>ArrayBuffer</code> هو الكائن البنيوي وأساس البيانات الخام.
</p>

<p>
	ولا بدّ من استخدام كائن عرض عند محاولة الكتابة ضمنه أو المرور على قيمه وكذلك لأي عملية بالأساس، مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7468_13" style="">
<span class="pln">let buffer </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ArrayBuffer</span><span class="pun">(</span><span class="lit">16</span><span class="pun">);</span><span class="pln"> </span><span class="com">// إنشاء مخزن مؤقت من 16 بايت</span><span class="pln">

let view </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint32Array</span><span class="pun">(</span><span class="pln">buffer</span><span class="pun">);</span><span class="pln"> </span><span class="com">// تعامل مع المخزن كعدد صحيح من 32-بت</span><span class="pln">

alert</span><span class="pun">(</span><span class="typ">Uint32Array</span><span class="pun">.</span><span class="pln">BYTES_PER_ELEMENT</span><span class="pun">);</span><span class="pln"> </span><span class="com">//  أربعة بايت لكل عدد </span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">view</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 4</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">view</span><span class="pun">.</span><span class="pln">byteLength</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 16</span><span class="pln">

</span><span class="com">// كتابة قيمة</span><span class="pln">
view</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">123456</span><span class="pun">;</span><span class="pln">

</span><span class="com">// المرور على قيمه</span><span class="pln">
</span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let num of view</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">num</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 123456, then 0, 0, 0 (أربعة قيم ككل)</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	الكائن TypedArray
</h2>

<p>
	إنّ المصطلح الرئيسي لكائنات العرض السابقة هو <a data-ss1634984028="1" href="https://tc39.github.io/ecma262/#sec-typedarray-objects" rel="external nofollow">مصفوفات النوع TypedArray</a>، والتي تشترك جميعها بمجموعة التوابع والخصائص، مع ملاحظة عدم وجود دالة بانية اسمها <code>TypedArray</code>، فهو مجرد مصطلح يمثِّل كائنات العرض السابقة التي تطبق على <code>ArrayBuffer</code>، وهي <code>Int8Array</code> و<code>Uint8Array</code>، وغيرها من الكائنات، وسنتابع بقية القائمة لاحقًا. عندما نرى أمرًا وفق الصيغة <code>new TypedArray</code>، فقد يعني ذلك أيًا من <code>new Int8Array</code> أو <code>new Uint8Array</code> وغيرها، إذ تسلك مصفوفات النوع سلوك بقية المصفوفات، فلها فهارس indexes ويمكن المرور على عناصرها، كما يختلف سلوكها باختلاف نوع الوسائط التي تقبلها، وهناك 5 حالات ممكنة للوسائط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7468_16" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">TypedArray</span><span class="pun">(</span><span class="pln">buffer</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">byteOffset</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="pln">length</span><span class="pun">]);</span><span class="pln">
</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TypedArray</span><span class="pun">(</span><span class="pln">object</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TypedArray</span><span class="pun">(</span><span class="pln">typedArray</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TypedArray</span><span class="pun">(</span><span class="pln">length</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TypedArray</span><span class="pun">();</span></pre>

<ol>
<li>
		<p>
			إذا أضيف وسيط من النوع <code>ArrayBuffer</code> فسيتشكل كائن عرض موافق له، وقد استخدمنا هذه الصيغة سابقًا.
		</p>

		<p>
			يمكن أن نختار إزاحةً <code>byteOffset</code> لنبدأ منها (ولها القيمة 0 افتراضيًا)، وأن نحدد حجمًا محددًا <code>length</code> من المخزن (وسيكون حجم المخزن الكلي هو القيمة الافتراضية)، وبهذا سنغطي جزءًا من المخزن المؤقت <code>buffer</code> فقط.
		</p>
	</li>
	<li>
		<p>
			عندما نضع وسيطًا على شكل مصفوفة <code>Array</code> أو أي كائن شبيه بها، فستُنشِئ مصفوفةَ نوع لها نفس الحجم وتنسخ المحتويات إليها، ويمكن استخدام ذلك لملء المصفوفة مسبقًا بالبيانات:
		</p>
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7468_18" style="">
<span class="pln">   let arr </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint8Array</span><span class="pun">([</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">]);</span><span class="pln">
   alert</span><span class="pun">(</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">length </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 4, ينشئ مصفوفة ثنائية لها الحجم نفسه</span><span class="pln">
   alert</span><span class="pun">(</span><span class="pln"> arr</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1,لها القيم نفسها  (unsigned 8-bit integers)  يملؤها بـ 4 بايتات من النوع </span></pre>

<ol start="3">
<li>
		لو استخدمنا مصفوفة نوع <code>TypedArray</code> من نوع مختلف، فستُنشَأ مصفوفة نوع لها نفس الحجم وتنسخ المحتويات إليها، وستحوَّل القيم إلى النوع الجديد خلال العملية عند الحاجة لذلك، مثلًا:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7468_20" style="">
<span class="pln">   let arr16 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint16Array</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">]);</span><span class="pln">
   let arr8 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint8Array</span><span class="pun">(</span><span class="pln">arr16</span><span class="pun">);</span><span class="pln">
   alert</span><span class="pun">(</span><span class="pln"> arr8</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span><span class="pln">
   alert</span><span class="pun">(</span><span class="pln"> arr8</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 232, tried to copy 1000, but can't fit 1000 into 8 bits (explanations below)</span></pre>

<ol start="4">
<li>
		يُنشئ الوسيط العددي <code>length</code> مصفوفة نوع تحتوي عدد عناصر مساويًا لقيمته، أما طولها بالبايت فسيكون مساويًا للقيمة <code>length</code> مضروبةً بعدد البايتات اللازم لتمثيل أحد عناصرها <code>TypedArray.BYTES_PER_ELEMENT</code>، مثلًا:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7468_22" style="">
<span class="pln">   let arr </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint16Array</span><span class="pun">(</span><span class="lit">4</span><span class="pun">);</span><span class="pln"> </span><span class="com">// integer إنشاء مصفوفة نوع تحوي 4 أعداد من النوع</span><span class="pln">
   alert</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Uint16Array</span><span class="pun">.</span><span class="pln">BYTES_PER_ELEMENT </span><span class="pun">);</span><span class="pln"> </span><span class="com">// بايتان لكل عدد</span><span class="pln">
   alert</span><span class="pun">(</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">byteLength </span><span class="pun">);</span><span class="pln"> </span><span class="com">// حجمها 8 بايت</span></pre>

<ol start="5">
<li>
		عندما لا نستخدم أي وسائط، فستتشكل مصفوفة نوع طولها 0.
	</li>
</ol>
<p>
	يمكن إنشاء مصفوفة نوع مباشرةً دون الإشارة إلى الكائن <code>ArrayBuffer</code>، لكن كائن العرض لا يتشكل دون وجود الكائن الأساسي <code>ArrayBuffer</code>، وبالتالي سيتشكل كائن التخزين المؤقت أوتوماتيكيًا في الحالات السابقة كلها عدا الحالة الأولى (عندما ننشئه نحن).
</p>

<p>
	سنستخدم الخصائص التالية للوصول إلى الكائن <code>ArrayBuffer</code>:
</p>

<ul>
<li>
		<code>arr.buffer</code>: يعيد مرجعًا إلى الكائن <code>ArrayBuffer</code>.
	</li>
	<li>
		<code>arr.byteLength</code>: يعيد طول الكائن <code>ArrayBuffer</code>.
	</li>
</ul>
<p>
	وبالتالي يمكننا الانتقال من كائن عرض إلى آخر، مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7468_24" style="">
<span class="pln">let arr8 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint8Array</span><span class="pun">([</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">]);</span><span class="pln">

</span><span class="com">// كائن عرض آخر للمعطيات نفسها</span><span class="pln">
let arr16 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint16Array</span><span class="pun">(</span><span class="pln">arr8</span><span class="pun">.</span><span class="pln">buffer</span><span class="pun">);</span></pre>

<p>
	إليك قائمةً بمصفوفات النوع:
</p>

<ul>
<li>
		"‘Uint32Array" و"Uint16Array" و"Uint8Array": تستخدم للأعداد الصحيحة 8 و16 و32 بت.
	</li>
	<li>
		"Uint8ClampedArray": لتمثيل الأعداد الصحيحة ذات 8-بت، وتبقي القيمة 0 أو 255 إن كانت خارج المجال.
	</li>
	<li>
		"‘Int32Array" و"Int16Array" و"Int8Array": لتمثيل الأعداد الصحيحة السالبة أو الموجبة.
	</li>
	<li>
		"Float32Array" و"Float64Array": لتمثيل الأعداد العشرية ذات 32 و64 بت والتي تحمل إشارةً بطريقة الفاصلة العائمة.
	</li>
</ul>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>لايوجد النوع <code>int8</code> أو أي نوع أحادي القيمة single-valued مشابه</strong> على الرغم من وجود أسماء مثل <code>Int8Array</code>، فلا يوجد نوع أحادي القيمة مثل <code>int</code> أو <code>int8</code> في JavaScript، والأمر منطقي هنا، حيث لا يمثل الكائن <code>Int8Array</code> مصفوفةً من هذه القيم المفردة، بل هو عرض وتمثيل للكائن <code>ArrayBuffer</code>.
	</p>
</blockquote>

<h2>
	سلوك المصفوفات عند خروج القيم عن حدودها
</h2>

<p>
	عندما نحاول كتابة قيمة خارج حدود مصفوفة النوع فلن يظهر لنا خطأ، لكن ستُحذف البتات الزائدة.
</p>

<p>
	لنحاول مثلًا وضع القيمة 256 ضمن <code>Uint8Array</code>، فإذا حولنا 256 إلى الصيغة الثنائية، فستكون بالشكل "100000000" (9 بتات)، لكن النوع الذي استخدمناه يحوي 8 بتات فقط، أي قيمًا بين 0 و255، وفي حال أدخلنا رقمًا أكبر من النطاق السابق فستُخزن الخانات اليمينية الثمان (الأقل قيمة) فقط ويُحذف الباقي، وبالتالي سنحصل على القيمة 0 في هذا المثال.
</p>

<p>
	لنأخذ العدد 257 مثالًا آخر لذلك، أينما سنحصل ثنائيًا على تسع بتات "100000001"، ستُخزّن منها الخانات اليمينية الثمان الأولى، وسنحصل بالنتيجة على القيمة 1.
</p>

<p style="text-align: center;">
	<img alt="binary_representation_of_ 8bit_integer_2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="72935" data-unique="fqebxgwl0" src="https://academy.hsoub.com/uploads/monthly_2021_07/6103ad827d425_binary_representation_of_8bit_integer_2.png.e0f29996ba98e19cdf0d43dafcfed17b.png"></p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7468_26" style="">
<span class="pln">let uint8array </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint8Array</span><span class="pun">(</span><span class="lit">16</span><span class="pun">);</span><span class="pln">

let num </span><span class="pun">=</span><span class="pln"> </span><span class="lit">256</span><span class="pun">;</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">num</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln"> </span><span class="com">// 100000000 (التمثيل الثنائي)</span><span class="pln">

uint8array</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">256</span><span class="pun">;</span><span class="pln">
uint8array</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">257</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">uint8array</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln"> </span><span class="com">// 0</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln">uint8array</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]);</span><span class="pln"> </span><span class="com">// 1</span></pre>

<p>
	للنوع <code>Uint8ClampedArray</code> ميزة خاصة، فهو يعطي القيمة 255 لأي عدد أكبر من 255، والقيمة 0 لأي عدد سالب، وهذا مفيد في معالجة الصور.
</p>

<h2>
	توابع الصنف TypedArray
</h2>

<p>
	لهذا الصنف نفس توابع المصفوفات مع بعض الاستثناءات الواضحة، حيث يمكننا مسح عناصره، وتنفيذ التوابع <code>map</code> و<code>slice</code> و<code>find</code> و<code>reduce</code>؛ إلا أنه لا يمكن تنفيذ عدة أشياء، منها الآتي:
</p>

<ul>
<li>
		لا يمكن استخدام التابع <code>splice</code>: لا يمكن "حذف" قيمة ما، لأنّ مصفوفات النوع هي كائنات تعرض مساحات ثابتةً متجاورةً من الذاكرة مؤقتًا، وكل ما يمكننا فعله هو إسناد القيمة 0 لها.
	</li>
	<li>
		لا يمكن استخدام التابع <code>concat</code>.
	</li>
</ul>
<p>
	كما يوجد تابعان إضافيان:
</p>

<ul>
<li>
		<code>([arr.set(fromArr, [offset</code>: ينقل عناصر المصفوفة <code>fromArr</code> إلى <code>arr</code> ابتداءً من الموقع <code>offset</code>، والذي يأخذ القيمة 0 افتراضيًا.
	</li>
	<li>
		<code>([arr.subarray([begin, end</code>: يُنشئ كائن عرض جديد من نفس النوع بين القيمتين <code>begin</code> و<code>end</code> ضمنًا، وهذا يشبه التابع <code>slice</code> المدعوم أيضًا، لكنه لا ينسخ أي قيم، بل ينشئ كائن عرض جديد للتعامل مع جزء محدد من البيانات.
	</li>
</ul>
<p>
	تتيح لنا التوابع السابقة نسخ مصفوفات النوع ودمجها وإنشاء مصفوفات جديدة من مصفوفات موجودة مسبقًا وغيرها.
</p>

<h2>
	الكائن DataView
</h2>

<p>
	يمثل الكائن <a data-ss1634984028="1" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView" rel="external nofollow">DataView</a> كائن عرض عالي المرونة بلا نوع untyped للكائن <code>ArrayBuffer</code>، حيث يسمح بالوصول إلى البيانات ابتداءً من أي موضع وبأي تنسيق.
</p>

<ul>
<li>
		تحدِّد الدالة البانية تنسيق البيانات في مصفوفات النوع، ويُفترض أن تكون الدالة منتظمةً ككل، وأن نحصل على العنصر الذي ترتيبه "i" باستخدام التعليمة <code>[arr[i</code>.
	</li>
	<li>
		يمكن الوصول إلى البيانات عند استخدام <code>DataView</code> من خلال توابع مثل <code>(getUint8(i.</code> أو <code>(getUint16(i.</code>، ونختار التنسيق عند استدعاء التابع وليس عند إنشائه.
	</li>
</ul>
<p>
	نستخدم <code>DataView</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7468_28" style="">
<span class="kwd">new</span><span class="pln"> </span><span class="typ">DataView</span><span class="pun">(</span><span class="pln">buffer</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="pln">byteOffset</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="pln">byteLength</span><span class="pun">])</span></pre>

<ul>
<li>
		<code>buffer</code>: ويمثل الكائن <code>ArrayBuffer</code> الأساسي، ولا تنشئ <code>DataView</code> مخزنًا مؤقتًا خاصًا بها على عكس مصفوفات النوع، وإنما ينبغي أن يكون موجودًا مسبقًا.
	</li>
	<li>
		<code>byteOffset</code>: ويمثل بايت البداية لكائن العرض (القيمة الافتراضية 0).
	</li>
	<li>
		<code>byteLength</code>: عدد بايتات كائن العرض (إلى نهاية المخزن افتراضيًا).
	</li>
</ul>
<p>
	في المثال التالي نستخرج أعدادًا بتنسيقات مختلفة من المخزن ذاته:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7468_30" style="">
<span class="com">// مصفوفة ثنائية مكونة من 4 بايتات، وستكون أكبر قيمة 255</span><span class="pln">
let buffer </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint8Array</span><span class="pun">([</span><span class="lit">255</span><span class="pun">,</span><span class="pln"> </span><span class="lit">255</span><span class="pun">,</span><span class="pln"> </span><span class="lit">255</span><span class="pun">,</span><span class="pln"> </span><span class="lit">255</span><span class="pun">]).</span><span class="pln">buffer</span><span class="pun">;</span><span class="pln">

let dataView </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">DataView</span><span class="pun">(</span><span class="pln">buffer</span><span class="pun">);</span><span class="pln">

</span><span class="com">// الحصول على رقم 8 بت عند  الإزاحة 0</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> dataView</span><span class="pun">.</span><span class="pln">getUint8</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 255</span><span class="pln">

</span><span class="com">// الحصول على رقم 16 بت عند  الإزاحة 0</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> dataView</span><span class="pun">.</span><span class="pln">getUint16</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 65535 (biggest 16-bit unsigned int)</span><span class="pln">

</span><span class="com">// الحصول على رقم 32 بت عند  الإزاحة 0</span><span class="pln">
alert</span><span class="pun">(</span><span class="pln"> dataView</span><span class="pun">.</span><span class="pln">getUint32</span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln"> </span><span class="com">// 4294967295 (biggest 32-bit unsigned int)</span><span class="pln">

dataView</span><span class="pun">.</span><span class="pln">setUint32</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln"> </span><span class="com">// إعطاء القيمة 0 لرقم من 4 بايت، وبالتالي تصفير المخزن</span></pre>

<p>
	تَظهر قدرة <code>DataView</code> عند تخزين أرقام ذات تنسيق مختلف في المخزن المؤقت نفسه، فلو خزّنّا مثلًا سلسلةً من البيانات مؤلفةً من الأزواج (عدد صحيح 16 بت، عدد عشري عائم 32 بت)، فسيسمح لنا <code>DataView</code> بالوصول إليها بكل سهولة.
</p>

<h2>
	الخلاصة
</h2>

<ul>
<li>
		يمثل الكائن الأساسي <code>ArrayBuffer</code> مرجعًا إلى حجم ثابت ومتجاور في الذاكرة.
	</li>
	<li>
		نحتاج إلى كائن عرض لتنفيذ أي عملية على <code>ArrayBuffer</code>، ومن هذه الكائنات:
	</li>
	<li>
		مصفوفة النوع، والتي يمكن أن تكون:
		<ol>
<li>
				<code>Uint8Array</code> و<code>Uint16Array</code> و<code>Uint32Array</code>: وهي أعداد بلا إشارة من 8 أو 16 أو 32 بت.
			</li>
			<li>
				<code>Uint8ClampedArray</code>: أعداد صحيحة من 8 بت، مع اجتزاء البتات التي تزيد عن 8.
			</li>
			<li>
				<code>Int8Array</code> و<code>Int16Array</code> و<code>Int32Array</code>: أعداد صحيحة ذات إشارة.
			</li>
			<li>
				<code>Float32Array</code> و<code>Float64Array</code>: أعداد بفاصلة عائمة من 32 أو 64 بت، وذات إشارة.
			</li>
		</ol>
</li>
	<li>
		الكائن <code>DataView</code>: ويستخدم التوابع لتحديد تنسيق الأعداد في مخزن مؤقت للبيانات، مثل <code>(getUint8(offset</code>.
	</li>
</ul>
<p>
	في معظم الأحيان يمكن العمل مباشرةً على مصفوفات النوع محتفظين بالكائن <code>ArrayBuffer</code> خلف الستار، ويمكن الوصول إليه باستخدام الأمر <code>buffer.</code>، كما يمكن إنشاء كائن عرض جديد عند الحاجة.
</p>

<p>
	يوجد مصطلحان إضافيان يستخدَمان في وصف التوابع التي تتعامل مع البيانات الثنائية:
</p>

<ul>
<li>
		<code>ArrayBufferView</code>: وهو مصطلح يغطي كل أنواع كائنات العرض.
	</li>
	<li>
		<code>BufferSource</code>: مصطلح يغطي كلًا من <code>ArrayBuffer</code> أو <code>ArrayBufferView</code>، وسنرى هذه المصطلحات في الفصول القادمة، <code>BufferSource</code> من المصطلحات الشائعة، ويعني "أي نوع من البيانات الثنائية" سواءً كانت الكائن <code>ArrayBuffer</code> أو أي كائن عرض له.
	</li>
</ul>
<p style="text-align: center;">
	<img alt="buffre_source_3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="72936" data-unique="jvtpa5e22" src="https://academy.hsoub.com/uploads/monthly_2021_07/buffre_source_3.png.7873a04670953b17d168cf283382da76.png" style=""></p>

<h2>
	مهمات للإنجاز
</h2>

<h3>
	ضم مصفوفات النوع Concatenate typed arrays
</h3>

<p>
	لنفترض وجود مصفوفة من النوع <code>Uint8Array</code>، يمكن استخدام الدالة <code>(concat(arrays</code> لضمّ عدة مصفوفات في مصفوفة واحدة.
</p>

<h4>
	الحل
</h4>

<p>
	إليك الحل المتمثل بالشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7468_8" style="">
<span class="kwd">function</span><span class="pln"> concat</span><span class="pun">(</span><span class="pln">arrays</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// مجموع أطوال كل مصفوفة</span><span class="pln">
  let totalLength </span><span class="pun">=</span><span class="pln"> arrays</span><span class="pun">.</span><span class="pln">reduce</span><span class="pun">((</span><span class="pln">acc</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> acc </span><span class="pun">+</span><span class="pln"> value</span><span class="pun">.</span><span class="pln">length</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">arrays</span><span class="pun">.</span><span class="pln">length</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">

  let result </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Uint8Array</span><span class="pun">(</span><span class="pln">totalLength</span><span class="pun">);</span><span class="pln">

  </span><span class="com">// ‫انسخ محتوى كل مصفوفة إلى الناتج result</span><span class="pln">
  </span><span class="com">// يُنسخ محتوى المصفوفة اللاحقة على الجانب الأيمن للمصفوفة السابقة</span><span class="pln">
  let length </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let array of arrays</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    result</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">array</span><span class="pun">,</span><span class="pln"> length</span><span class="pun">);</span><span class="pln">
    length </span><span class="pun">+=</span><span class="pln"> array</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	افتح الحل في <a data-ss1634984028="1" href="https://plnkr.co/edit/sazJ16sytvM74sAH?p=preview" rel="external nofollow">تجربة حية</a>
</p>

<p>
	ترجمة -وبتصرف- للفصل <a data-ss1634984028="1" href="https://javascript.info/arraybuffer-binary-arrays" rel="external nofollow">Array buffer, Binary data</a> من سلسلة <a data-ss1634984028="1" href="https://javascript.info" rel="external nofollow">The Modern JavaScript Tutorial</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a data-ss1634984028="1" href="https://academy.hsoub.com/programming/javascript/%D9%87%D8%AC%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%B7%D8%A7%D9%81-%D8%A8%D8%A7%D9%84%D9%86%D9%82%D8%B1-clickjacking-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1254/" rel="">هجمات الاختطاف بالنقر Clickjacking في جافاسكريبت</a>
	</li>
	<li>
		<a data-ss1634984028="1" href="https://academy.hsoub.com/programming/javascript/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1234/" rel="">هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت</a>
	</li>
	<li>
		<a data-ss1634984028="1" href="https://academy.hsoub.com/programming/java/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-arrays-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1158/" rel="">معالجة المصفوفات Arrays في جافا</a>
	</li>
	<li>
		<a data-ss1634984028="1" href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-%D8%A7%D9%84%D8%A8%D8%B9%D8%AF-two-dimensional-arrays-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1161/" rel="">المصفوفات ثنائية البعد Two-dimensional Arrays في جافا</a>
	</li>
	<li>
		<a data-ss1634984028="1" href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D9%88%D8%A7%D9%84%D8%AA%D8%B1%D8%AA%D9%8A%D8%A8-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-array-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1160/" rel="">البحث والترتيب في المصفوفات Array في جافا</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1277</guid><pubDate>Sat, 31 Jul 2021 15:02:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x628;&#x64A;&#x631; &#x627;&#x644;&#x646;&#x645;&#x637;&#x64A;&#x629; Regular Expressions &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regular-expressions-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1278/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_08/6106311d563b2_--Regular-Expressions--.png.0ac4d7d48770aeaceea4cd86c5cbd8b6.png" /></p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		يبادر البعض إذا قابلَته مشكلة إلى استخدام التعابير النمطية، ولا يدرك المرء أنه بهذا قد جعل المشكلة اثنتين. ـــ جيمي زاوينيسكي Jamie Zawiniski
	</p>

	<p>
		إذا قطعت الخشب في عكس اتجاه أليافه فستحتاج إلى قوة أكبر، كذلك إذا سار البرنامج عكس اتجاه المشكلة، إذ ستحتاج شيفرات أكثر لحلها. ـــ يوان-ما Yuan-Ma، كتاب البرمجة The Book of Programming.
	</p>
</blockquote>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="72937" href="https://academy.hsoub.com/uploads/monthly_2021_07/chapter_picture_9.jpg.b33a2c463255b6a3345e76a92871570b.jpg" rel=""><img alt="chapter_picture_9.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="72937" data-unique="ydstdmuo5" src="https://academy.hsoub.com/uploads/monthly_2021_07/chapter_picture_9.jpg.b33a2c463255b6a3345e76a92871570b.jpg"></a>
</p>

<p>
	إذا نظرنا إلى التقنيات والأدوات البرمجية المنتشرة، فسنرى أنّ التقنية أو الأداة المشهورة هي التي تُثبت كفاءتها العملية في مجالها، أو التي تتكامل تكاملًا ممتازًا مع تقنية أخرى مستخدَمة بكثرة، وليس لأنها الأجمل أو الأذكى.إرشادات للحل
</p>

<p>
	سنناقش هنا إحدى تلك الأدوات التي تدعى بالتعابير النمطية Regular Expressions، وهي طريقة لوصف الأنماط patterns في بيانات السلاسل النصية، إذ تُكوِّن هذه التعابير لغةً صغيرةً مستقلةً بذاتها، لكنها رغم ذلك تدخل في لغات برمجية أخرى مثل جافاسكربت، كما تدخل في العديد من الأنظمة.
</p>

<p>
	قد تكون صيغة التعابير النمطية غريبةً للناظر إليها، كما أنّ الواجهة التي توفرها جافاسكربت للتعامل معها ليست بالمثلى، لكن رغم هذا، فتلك التعابير تُعَد أداةً قويةً لفحص ومعالجة السلاسل النصية، كما سيعينك فهمها على كتابة حلول أكفأ للمشاكل التي تواجهك.
</p>

<h2>
	إنشاء تعبير نمطي
</h2>

<p>
	التعبير النمطي هو نوع من أنواع الكائنات؛ فإما يُنشأ باستخدام الباني <code>RegExp</code>، أو يُكتب على أساس قيمة مصنَّفة النوع literal value بتغليف النمط بشرطتين مائلتين أماميتين من النوع <code>/</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_13" style="">
<span class="pln">let re1 </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RegExp</span><span class="pun">(</span><span class="str">"abc"</span><span class="pun">);</span><span class="pln">
let re2 </span><span class="pun">=</span><span class="pln"> </span><span class="str">/abc/</span><span class="pun">;</span></pre>

<p>
	يُمثِّل كائنا التعبيرات النمطية في المثال السابق النمط نفسه أي محرف a يتبعه b ثم c، ويُكتب النمط على أساس سلسلة نصية عادية عند استخدام الباني <code>RegExp</code>، لذا تطبَّق القواعد المعتادة للشرطات المائلة الخلفية <code>\</code>، على عكس الحالة الثانية التي نرى فيها النمط بين شرطتين مائلتين، إذ تعامَل الشرطات هنا تعاملًا مختلفًا.
</p>

<p>
	نحتاج إلى وضع شرطة خلفية قبل أي شرطة أمامية نريدها جزءًا من النمط نفسه، وذلك لأن الشرطة الأمامية تنهي النمط إذا وُجدت. كذلك فإن الشرطات الخلفية التي لا تكون جزءًا من ترميز خاص لمحرف -مثل <code>‎\n</code>- لن تُتَجاهل كما نفعل في السلاسل النصية، وعليه ستتسبب في تغيير معنى النمط.
</p>

<p>
	تملك بعض المحارف مثل علامات الاستفهام وإشارات الجمع مَعاني خاصة في التعابير النمطية، حيث سنحتاج إلى وضع شرطة مائلة خلفية قبلها إذا أردنا لها تمثيل المحرف نفسه وليس معناه في التعابير النمطية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_15" style="">
<span class="pln">let eighteenPlus </span><span class="pun">=</span><span class="pln"> </span><span class="str">/eighteen\+/</span><span class="pun">;</span></pre>

<h2>
	التحقق من المطابقات
</h2>

<p>
	تملك كائنات التعابير النمطية توابع عديدة، وأبسط تلك التوابع هو التابع <code>test</code> الذي إن مرَّرنا سلسلةً نصيةً إليه، فسيُعيد قيمةً بوليانيةً تخبرنا هل تحتوي السلسلة على تطابق للنمط الذي في التعبير أم لا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_17" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/abc/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"abcde"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/abc/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"abxde"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span></pre>

<p>
	يتكون التعبير النمطي من محارف عادية غير خاصة تمثِّل -ببساطة- ذلك التسلسل من المحارف، فإذا وُجد <code>abc</code> في أيّ مكان في السلسلة النصية التي نختبرها -ولا يُشترط وجودها في بدايتها-، فسيُعيد <code>test</code> القيمة <code>true</code>.
</p>

<h2>
	مجموعات المحارف
</h2>

<p>
	يمكن التحقق من وجود abc في سلسلة نصية باستدعاء التابع <code>indexof</code>.
</p>

<p>
	تسمح لنا التعابير النمطية بالتعبير عن أنماط أكثر تعقيدًا، فمثلًا كل ما علينا فعله لمطابقة عدد ما هو وضع مجموعة من المحارف بين قوسين مربعين لجعل ذلك الجزء من التعبير يطابق أيًا من المحارف الموجودة بين الأقواس.
</p>

<p>
	لننظر المثال التالي حيث يطابق التعبيران جميع السلاسل النصية التي تحتوي على رقم ما:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_19" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/[0123456789]/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"in 1992"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/[0-9]/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"in 1992"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	يمكن الإشارة إلى مجال من المحارف داخل القوسين المربعين باستخدام الشرطة <code>-</code> بين أول محرف فيه وآخر محرف، ويُحدَّد الترتيب في تلك المحارف برمز اليونيكود لكل محرف -كما ترى من المثال أعلاه الذي يشير إلى مجال الأرقام من 0 إلى 9-، وهذه الأرقام تحمل رمز 48 حتى 57 بالترتيب في اليونيكود، وعليه فإنّ المجال <code>[0-9]‎</code>‎ يشملها جميعًا، ويطابق أي رقم.
</p>

<p>
	تمتلك مجموعة محارف الأعداد اختصارات خاصة بها كما هو شأن العديد من مجموعات المحارف الشائعة، فإذا أردنا الإشارة إلى المجال من 0 حتى 9، فسنستخدِم الاختصار <code>‎\d</code>.
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th>
				الاختصار
			</th>
			<th>
				الدلالة
			</th>
		</tr></thead>
<tbody>
<tr>
<td style="text-align: center;">
				<code>‎\d</code>
			</td>
			<td style="text-align: center;">
				أيّ محرف رقمي
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎\w</code>
			</td>
			<td style="text-align: center;">
				محرف أبجدي أو رقمي، أي محرف الكلمة
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎\s</code>
			</td>
			<td style="text-align: center;">
				أيّ محرف مسافة بيضاء، مثل المسافات الفارغة والجداول والأسطر الجديدة وما شابهها
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎\D</code>
			</td>
			<td style="text-align: center;">
				محرف غير رقمي
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎\W</code>
			</td>
			<td style="text-align: center;">
				محرف غير أبجدي وغير رقمي
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎\S</code>
			</td>
			<td style="text-align: center;">
				محرف مغاير للمسافة البيضاء
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>.</code>
			</td>
			<td style="text-align: center;">
				أيّ محرف عدا السطر الجديد
			</td>
		</tr>
</tbody>
</table>
<p>
	نستطيع مطابقة تنسيق التاريخ والوقت -كما في 01-30-2003 15:20- باستخدام التعبير التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_21" style="">
<span class="pln">let dateTime </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\d\d-\d\d-\d\d\d\d \d\d:\d\d/</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">dateTime</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"01-30-2003 15:20"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">dateTime</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"30-jan-2003 15:20"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span></pre>

<p>
	تعيق الشرطات المائلة الموجودة في المثال أعلاه قراءة النمط الذي نعبر عنه، وسنرى لاحقًا نسخةً أفضل في هذا المقال.
</p>

<p>
	يمكن استخدام رموز الشرطات المائلة تلك داخل أقواس مربعة، إذ تعني <code>[‎\d.‎]</code> مثلًا أيّ رقم أو محرف النقطة <code>.</code>، لكن تفقِد النقطة نفسها معناها المميز لها إذا كانت داخل أقواس مربعة، وبالمثل في حالة إشارة الجمع <code>+</code>؛ أما إذا أردنا عكس مجموعة محارف، أي إذا أردنا التعبير عن رغبتنا في مطابقة أيّ محرف عدا تلك المحارف التي في المجموعة، فنستخدم رمز الإقحام <code>^</code> بعد قوس الافتتاح.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_23" style="">
<span class="pln">let notBinary </span><span class="pun">=</span><span class="pln"> </span><span class="str">/[^01]/</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">notBinary</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"1100100010100110"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">notBinary</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"1100100010200110"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span></pre>

<h2>
	تكرار أجزاء من النمط
</h2>

<p>
	لقد بتنا الآن نعلم كيف نطابق رقمًا واحدًا، لكن كيف سنفعل ذلك إذا أردنا مطابقة عدد يحتوي على أكثر من رقم؟ إذا وضعنا إشارة الجمع <code>+</code> بعد شيء ما في تعبير نمطي، فستشير إلى أنّ هذا العنصر قد يكرَّر أكثر من مرة، بالتالي تعني <code>‎/\d+/‎</code> مطابقة محرف رقم أو أكثر.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_26" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/'\d+'/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"'123'"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/'\d+'/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"''"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/'\d*'/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"'123'"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/'\d*'/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"''"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	تحمل إشارة النجمة <code>*</code> معنى قريبًا من ذلك، حيث تسمح للنمط بالمطابقة على أي حال، فإذا لحقت إشارة النجمة بشيء ما، فلن تمنع النمط من مطابقته، إذ ستطابق نُسخًا صفرية zero instances حتى لو لم تجد نصًا مناسبًا لمطابقته؛ أما إذا جاءت علامة الاستفهام بعد محرف في نمط، فستجعل ذلك المحرف اختياريًا optional، أي قد يحدث مرةً واحدةً أو لا يحدث.
</p>

<p>
	يُسمح للمحرف <code>u</code> في المثال التالي بالحدوث، ويحقق النمط المطابقة حتى لو لم يكن <code>u</code> موجودًا أيضًا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_28" style="">
<span class="pln">let neighbor </span><span class="pun">=</span><span class="pln"> </span><span class="str">/neighbou?r/</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">neighbor</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"neighbour"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">neighbor</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"neighbor"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	نستخدم الأقواس المعقوصة إذا أردنا حدوث النمط عددًا معينًا من المرات، فإذا وضعنا <code>{4}</code> بعد عنصر ما مثلًا، فسيجبره بالحدوث 4 مرات حصرًا، ومن الممكن تحديد المجال الذي يمكن للعنصر حدوثه فيه بكتابة <code>{‎2,4‎}</code> التي تشير إلى وجوب ظهور العنصر مرتين على الأقل، وأربع مرات على الأكثر.
</p>

<p>
	لدينا نسخة أخرى من نمط التاريخ والوقت، حيث تسمح بذكر الأيام برقم واحد -أو رقمين-، والأشهر، والساعات، إذ تُعَدّ أسهل قليلًا في قراءتها، وهي المثال الذي قلنا أننا سنعود إليه بنسخة أفضل.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_30" style="">
<span class="pln">let dateTime </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\d{1,2}-\d{1,2}-\d{4} \d{1,2}:\d{2}/</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">dateTime</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"1-30-2003 8:45"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	نستطيع تحديد مجالات مفتوحة عند استخدام الأقواس بإهمال الرقم الموجود بعد الفاصلة، وبالتالي، تعني <code>{‎5,‎}</code> خمس مرات على الأقل.
</p>

<h2>
	جمع التعبيرات الفرعية
</h2>

<p>
	إذا أردنا استخدام عامل مثل <code>*</code>، أو <code>+</code> على أكثر من عنصر في المرة الواحدة، فيجب استخدام الأقواس، وسترى العوامل الجزء الذي داخل الأقواس من التعبير النمطي عنصرًا واحدًا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_32" style="">
<span class="pln">let cartoonCrying </span><span class="pun">=</span><span class="pln"> </span><span class="str">/boo+(hoo+)+/</span><span class="pln">i</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">cartoonCrying</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"Boohoooohoohooo"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	تطبَّق إشارتا الجمع الأولى والثانية على o الثانية فقط في boo، وhoo على الترتيب؛ أما علامة الجمع الثالثة فتطبَّق على المجموعة كلها <code>(hoo+‎)</code> مطابِقةً تسلسلًا واحدًا أو أكثر بهذا.
</p>

<p>
	يجعل محرف <code>i</code> -الذي في نهاية التعبير- هذا التعبير النمطي غير حساس لحالة المحارف، إذ يسمح بمطابقة المحرف <code>B</code> في سلسلة الدخل النصية رغم تكوّن النمط من محارف صغيرة.
</p>

<h2>
	التطابقات والمجموعات
</h2>

<p>
	يُعَدّ التابع <code>test</code> أبسط طريقة لمطابقة تعبير نمطي، إذ لا يخبرك إلا بمطابقة التعبير النمطي من عدمها وفقط، كذلك تملك التعبيرات النمطية تابعًا اسمه <code>exec</code>، حيث يُعيد القيمة <code>null</code> إذا لم يجد مطابقة، كما يُعيد كائنًا مع معلومات عن المطابقة إذا وجد تطابق.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_34" style="">
<span class="pln">let match </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\d+/</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="str">"one two 100"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">match</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → ["100"]</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">match</span><span class="pun">.</span><span class="pln">index</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 8</span></pre>

<p>
	تكون للكائن المعاد من <code>exec</code> خاصية تدعى <code>index</code>، إذ تخبرنا أين تبدأ المطابقة الناجحة للسلسلة النصية؛ أما خلاف هذا فيبدو الكائن أشبه بمصفوفة من السلاسل النصية -وهو كذلك حقًا-، ويكون أول عنصر في تلك المصفوفة هو السلسلة المطابَقة، كما يكون ذلك هو تسلسل الأرقام الذي كنا نبحث عنه في المثال السابق.
</p>

<p>
	تحتوي قيم السلاسل النصية على التابع <code>match</code> الذي له سلوك مشابه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_36" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"one two 100"</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/\d+/</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → ["100"]</span></pre>

<p>
	حين يحتوي التعبير النمطي على تعبيرات فرعية مجمَّعة داخل أقواس، فسيظهر النص الذي يطابق تلك المجموعات في مصفوفة، ويكون العنصر الأول هو التطابق كله دومًا، في حين يكون العنصر التالي هو الجزء المطابَق بواسطة المجموعة الأولى التي يأتي قوس افتتاحها أولًا في التعبير، ثم المجموعة الثانية، وهكذا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_38" style="">
<span class="pln">let quotedText </span><span class="pun">=</span><span class="pln"> </span><span class="str">/'([^']*)'/</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">quotedText</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="str">"she said 'hello'"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → ["'hello'", "hello"]</span></pre>

<p>
	إذا لم تطابَق مجموعة ما مطلقًا -كأن تُتبَع بعلامة استفهام-، فسيكون موضعها في مصفوفة الخرج غير معرَّفًا <code>undefined</code>، وبالمثل، فإذا طابقت مجموعةً ما أكثر من مرة، فستكون المطابقة الأخيرة هي التي في المصفوفة فقط.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_40" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/bad(ly)?/</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="str">"bad"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → ["bad", undefined]</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/(\d)+/</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="str">"123"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → ["123", "3"]</span></pre>

<p>
	تفيدنا المجموعات في استخراج أجزاء من سلسلة نصية، فإذا أردنا التحقق من احتواء السلسلة النصية على تاريخ، ومن ثم استخراج ذلك التاريخ وبناء كائن يمثله؛ فيمكننا إحاطة الأنماط الرقمية بأقواس، وأخذ التاريخ مباشرةً من نتيجة <code>exec</code>، لكن نحتاج قبل ذلك إلى النظر سريعًا على الطريقة المضمَّنة لتمثيل قيم التاريخ والوقت في جافاسكربت.
</p>

<h2>
	صنف التاريخ
</h2>

<p>
	تحتوي جافاسكربت على صنف قياسي لتمثيل البيانات -أو النقاط- في الزمن، ويسمى ذلك الصنف <code>Date</code>، فإذا أنشأنا كائن تاريخ باستخدام <code>new</code>، فسنحصل على التاريخ والوقت الحاليين.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_42" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → Mon Nov 13 2017 16:19:11 GMT+0100 (CET)</span></pre>

<p>
	من الممكن إنشاء كائن لوقت محدد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_44" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">(</span><span class="lit">2009</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → Wed Dec 09 2009 00:00:00 GMT+0100 (CET)</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">(</span><span class="lit">2009</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="lit">59</span><span class="pun">,</span><span class="pln"> </span><span class="lit">59</span><span class="pun">,</span><span class="pln"> </span><span class="lit">999</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → Wed Dec 09 2009 12:59:59 GMT+0100 (CET)</span></pre>

<p>
	تستخدِم جافاسكربت تقليدًا تبدأ فيه أعداد الشهور بالصفر -وعليه يكون شهر ديسمبر هو عدد 11-، بينما تبدأ أرقام الأيام بالواحد، وذلك أمر محيِّر وسخيف كذلك لكنه واقع، وإنما ذكرناه للتنبيه.
</p>

<p>
	تُعَدّ آخر أربعة وسائط arguments -أي الساعات والدقائق والثواني والميلي ثانية- وسائط اختيارية، وإذا لم تحدد قيمة أيّ منهم فتكون صفرًا افتراضيًا.
</p>

<p>
	تُخزَّن العلامات الزمنية Timestamps بعدد من الميلي ثانية منذ العام 1970 في منطقة UTC الزمنية -أي التوقيت العالمي-، ويتبع هذا اصطلاحًا ضُبِط بواسطة توقيت يونكس Unix time الذي اختُرع في تلك الفترة أيضًا، كما يمكن استخدام الأرقام السالبة للتعبير عن الأعوام التي سبقت 1970.
</p>

<p>
	إذا استُخدم التابع <code>getTime</code> على كائن تاريخ، فسيُعيد ذلك العدد، وهو عدد كبير كما هو متوقع.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_46" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">(</span><span class="lit">2013</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="lit">19</span><span class="pun">).</span><span class="pln">getTime</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → 1387407600000</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">(</span><span class="lit">1387407600000</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → Thu Dec 19 2013 00:00:00 GMT+0100 (CET)</span></pre>

<p>
	إذا أعطينا الباني <code>Date</code> وسيطًا واحدًا، فسيعامَل الوسيط على أنه تعداد الميلي ثانية، ويمكن الحصول على القيمة الحالية لتعداد المللي ثانية بإنشاء كائن <code>Date</code> جديد، واستدعاء <code>getTime</code> عليه، أو استدعاء الدالة <code>Date.now</code>.
</p>

<p>
	توفِّر كائنات التاريخ توابعًا، مثل <code>getFullYear</code> و<code>getMonth</code> و<code>getDate</code> و<code>getHours</code> و<code>getMinutes</code> و<code>getSeconds</code>، من أجل استخراج مكوناتها، كما يعطينا التابع <code>getYear</code> السنة بعد طرح 1900 منها -<code>98</code>، أو <code>119</code>-، لكن لن نستخدِمه كثيرًا لعدم وجود فائدة حقيقية منه.
</p>

<p>
	نستطيع الآن إنشاء كائن تاريخ من سلسلة نصية بما أننا وضعنا أقواسًا حول أجزاء التعبير التي تهمنا، أي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_48" style="">
<span class="kwd">function</span><span class="pln"> getDate</span><span class="pun">(</span><span class="pln">string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let </span><span class="pun">[</span><span class="pln">_</span><span class="pun">,</span><span class="pln"> month</span><span class="pun">,</span><span class="pln"> day</span><span class="pun">,</span><span class="pln"> year</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln">
    </span><span class="str">/(\d{1,2})-(\d{1,2})-(\d{4})/</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">string</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">(</span><span class="pln">year</span><span class="pun">,</span><span class="pln"> month </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> day</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">getDate</span><span class="pun">(</span><span class="str">"1-30-2003"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → Thu Jan 30 2003 00:00:00 GMT+0100 (CET)</span></pre>

<p>
	تُهمَل رابطة الشرطة السفلية <code>_</code>، ولا تُستخدَم إلا لتجاوز عنصر المطابَقة التامة في المصفوفة التي يُعيدها التابع <code>exec</code>.
</p>

<h2>
	حدود الكلمة والسلسلة النصية
</h2>

<p>
	يستخرِج التابع <code>getDate</code> التاريخ 00-1-3000 من السلسلة النصية <code>"‎100-1-30000‎"</code>، وهو تاريخ غير منطقي لا شك، حيث تحدث المطابقة في أي موقع في السلسلة النصية، لذا تبدأ في حالتنا عند المحرف الثاني، وتنتهي عند المحرف الثاني من النهاية.
</p>

<p>
	نضيف العلامتين <code>^</code> و<code>$</code> لإجبار المطابقة على النظر في السلسلة كلها، إذ تطابِق علامة الإقحام بداية سلسلة الدخل، بينما تطابِق علامة الدولار نهايتها، إذ تطابق <code>‎/^\d+$/‎</code> مثلًا سلسلةً مكونةً من رقم واحد أو أكثر، وتطابق <code>‎/^!/‎</code> أي سلسلة تبدأ بعلامة تعجب، بينما لا تطابق <code>‎/x^/‎</code> أي سلسلة نصية، إذ لا يمكن وجود x قبل بداية السلسلة.
</p>

<p>
	نستخدم العلامة <code>‎\b‎</code> للتأكد من أنّ التاريخ يبدأ وينتهي عند حدود كلمة، وقد يكون حد الكلمة بداية السلسلة، أو نهايتها، أو أي نقطة فيها تملك محرف كلمة -كما في <code>‎\w</code> على أحد الجانبين، و محرف غير كلمي على الجانب الآخر.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_50" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/cat/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"concatenate"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/\bcat\b/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"concatenate"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span></pre>

<p>
	لاحظ أنّ علامة الحد لا تطابق محرفًا حقيقيًا، بل تضمن عدم مطابقة التعبير النمطي إلا عند حدوث حالة معينة في الموضع الذي يظهر فيه في النمط.
</p>

<h2>
	أنماط الاختيار
</h2>

<p>
	لنقل أننا نريد معرفة هل يحتوي جزء ما من النص على عدد متبوع بإحدى الكلمات التالية: horse، أو cow، أو chicken، أو أي صورة من صور الجمع لها، قد نكتب ثلاثة تعابير نمطية ونختبرها لكن ثَم طريقة أفضل، وذلك بوضع محرف الأنبوب <code>|</code> الذي يشير إلى خيار بين النمط الذي عن يمينه والنمط الذي عن يساره، وعليه نستطيع القول كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_52" style="">
<span class="pln">let animalCount </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\b\d+ (horse|cow|chicken)s?\b/</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">animalCount</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"15 horses"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">animalCount</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"15 horsechickens"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span></pre>

<p>
	يمكن استخدام الأقواس لتقييد جزء النمط الذي يطبَّق عليه عامل الأنبوب، ويمكن وضع عدة عوامل مثل هذا بجانب بعضها البعض للتعبير عن اختيار بين أكثر من بديلين اثنين.
</p>

<h2>
	آلية المطابقة
</h2>

<p>
	يبحث محرك التعبير نظريًا عند استخدام <code>exec</code> أو <code>test</code> عن تطابق في سلسلتنا النصية، وذلك بمحاولة مطابقة التعبير من بداية السلسلة أولًا، ثم من المحرف الثاني، وهكذا حتى يجد تطابقًا أو يصل إلى نهاية السلسلة، وعندئذ يُعيد أول تطابق وجده، أو يكون قد فشل في إيجاد تطابق أصلًا؛ أما في عملية المطابقة الفعلية، فيعامِل المحرك التعبير النمطي مثل مخطط تدفق flow diagram، وإذا استخدمنا مثالنا السابق عن الحيوانات، فسيبدو مخطط التعبير الخاص بها كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="72938" href="https://academy.hsoub.com/uploads/monthly_2021_07/re_horsechickens.png.f6833c028615092c897daa04402bb1c0.png" rel=""><img alt="re_horsechickens.png" class="ipsImage ipsImage_thumbnailed" data-fileid="72938" data-unique="g5e8a9i76" src="https://academy.hsoub.com/uploads/monthly_2021_07/re_horsechickens.thumb.png.5bac2a79beed0999e04c9b576d2101bd.png"></a>
</p>

<p>
	يكون تعبيرنا مطابقًا إذا استطعنا إيجاد مسار من جانب المخطط الأيسر إلى جانبه الأيمن، حيث نحفظ الموضع الحالي في السلسلة النصية، ونتأكد في كل حركة نتحركها خلال صندوق من أنّ جزء السلسلة التالي لموضعنا الحالي يطابق ذلك الصندوق.
</p>

<p>
	إذا كنا نحاول مطابقة <code>"the 3 horses"</code> من الموضع 4، فسيبدو مسار تقدمنا داخل المخطط كما يلي:
</p>

<ul>
<li>
		يمكننا تجاوز الصندوق الأول لوجود حد كلمي word boundary عند الموضع 4.
	</li>
	<li>
		لا زلنا في الموضع 4 ووجدنا رقمًا، لذا نستطيع تجاوز الصندوق الثاني.
	</li>
	<li>
		يتكرر أحد المسارات إلى ما قبل الصندوق الثاني (الرقم) عند الموضع 5، بينما يتحرك الآخر للأمام خلال الصندوق، ويحمل محرف مسافة واحد؛ لذا يجب أخذ المسار الثاني لوجود مسافة وليس رقمًا.
	</li>
	<li>
		نحن الآن في الموضع 6، أي بداية horses وعند التفرع الثلاثي في المخطط، إذ لا نرى cow، ولا chicken هنا، لكن نرى horse، لذا سنأخذ ذلك الفرع.
	</li>
	<li>
		يتخطى أحد المسارات صندوق s عند الموضع 9 بعد التفرع الثلاثي، ويذهب مباشرةً إلى حد الكلمة الأخير، بينما يطابق المسار الآخر s، كما سنمر خلال صندوق s لوجود المحرف s وليس حدًا لكلمة.
	</li>
	<li>
		نحن الآن عند الموضع 10 وهو نهاية السلسلة النصية، ونستطيع مطابقة حد الكلمة فقط، وتُحسب نهاية السلسلة النصية على أنها حد كلمي، لذا سنمر خلال الصندوق الأخير ونكون قد طابقنا تلك السلسلة النصية بنجاح.
	</li>
</ul>
<h2>
	التعقب الخلفي
</h2>

<p>
	يطابق التعبير النمطي <code>‎/\b([01]+b|[\da-f]+h|\d+)\b/‎</code> عددًا ثنائيًا متبوعًا بـ b، أو عددًا ست عشريًا hexadecimal -وهو نظام رقمي تمثل فيه الأعداد من 10 إلى 15 بالأحرف a حتى f- متبوعًا بـ h، أو عددًا عشريًا عاديًا ليس له محرف لاحق، وفيما يلي المخطط الذي يصف ذلك:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="72939" href="https://academy.hsoub.com/uploads/monthly_2021_07/re_number.png.25b690d1131cee620fb80262b7ebe856.png" rel=""><img alt="re_number.png" class="ipsImage ipsImage_thumbnailed" data-fileid="72939" data-unique="0eprewfmu" src="https://academy.hsoub.com/uploads/monthly_2021_07/re_number.thumb.png.ff40cc23c718bf2715d44481bddce1ee.png"></a>
</p>

<p>
	سيدخل الفرع العلوي الثنائي عند مطابقة ذلك التعبير حتى لو لم يحوي الدخل على عدد ثنائي، حيث سيصبح من الواضح عند المحرف 3 مثلًا أننا في الفرع الخاطئ عند مطابقة السلسلة <code>"103"</code>، فعلى الرغم تطابق السلسلة للتعبير، إلا أنها لا تطابق الفرع الذي نحن فيه.
</p>

<p>
	يبدأ هنا المطابِق matcher بالتعقب الخلفي، فيتذكر موضعه الحالي عند دخول فرع ما -وهو بداية السلسلة في هذه الحالة، بعد صندوق "حد الكلمة" الأول في المخطط-، وذلك ليستطيع العودة والنظر في فرع آخر إذا لم ينجح الفرع الحالي.
</p>

<p>
	سيجرب الفرع الخاص بالأرقام الست عشرية في حالة السلسلة النصية <code>"103"</code> إذا وصل إلى المحرف 3، لكنه سيفشل مجددًا لعدم وجود h بعد العدد، وهنا سيحاول في الفرع الخاص بالأعداد العشرية، وتنجح المطابقة، ويُبلَّغ بها.
</p>

<p>
	يتوقف المطابِق عندما يجد مطابقةً تامةً، وهذا يعني أنه حتى لو كان لدينا فروع متعددة يمكنها مطابقة سلسلة نصية ما، فهو لن يُستخدَم إلا الفرع الأول الذي ظهر وفقًا لترتيبه في التعبير النمطي.
</p>

<p>
	ويحدث التعقب الخلفي أيضًا لعوامل التكرار، مثل <code>+</code> و<code>*</code>، فإذا طابقنا <code>‎/^.*x/‎</code> مع <code>"abcxe"</code>، فسيحاول الجزء <code>‎.*‎</code> أخذ السلسلة كلها أولًا، لكن سيدرك المحرك أنه يحتاج إلى x كي يطابق النمط، وبما أنه لا توجد x بعد نهاية السلسلة، فسيحاول عامل النجمة المطابقة من غير المحرف الأخير، لكن لا يعثر المطابِق على x بعد <code>abcx</code>، فيعود أدراجه بالتعقب الخلفي ليطابق عامل النجمة مع <code>abc</code> فقط، وهنا يجد x حيث يحتاجها، ويبلِّغ بمطابقة ناجحة من الموضع 0 حتى 4.
</p>

<p>
	قد يحدث ونكتب تعبيرًا نمطيًا ينفذ الكثير من عمليات التعقب الخلفي، وهنا تحدث مشكلة حين يستطيع النمط مطابقة جزء من الدخل بطرق عديدة مختلفة، فإذا لم ننتبه عند كتابة تعبير نمطي لعدد ثنائي، فقد نكتب شيئًا مثل <code>‎/([01]+)+b/‎</code>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="72940" href="https://academy.hsoub.com/uploads/monthly_2021_07/re_slow.png.1c32c4dddd713c1c72f63b8e1267dcc4.png" rel=""><img alt="re_slow.png" class="ipsImage ipsImage_thumbnailed" data-fileid="72940" data-unique="zh5051jpv" src="https://academy.hsoub.com/uploads/monthly_2021_07/re_slow.png.1c32c4dddd713c1c72f63b8e1267dcc4.png"></a>
</p>

<p>
	عندما يحاول التعبير مطابقة سلسلة طويلة من الأصفار والآحاد التي ليس لها لاحقة b، فسيمر المطابِق على الحلقة الداخلية حتى تنتهي الأرقام، ثم يلاحظ عدم وجود المحرف b، فينفِّذ تعقبًا خلفيًا لموضع واحد فقط، ثم يمر على الحلقة الخارجية مرةً واحدةً قبل أن يستسلم ويعود أدراجه ليتعقب الحلقة الداخلية مرةً أخرى، كما سيظل يحاول جميع الطرق الممكنة عبر هاتين الحلقتين، وهذا يعني مضاعفة مقدار العمل مع كل محرف إضافي، فلو أضفنا بعض العشرات من المحارف، لاستغرقت عملية المطابقة إلى ما لا نهاية.
</p>

<h2>
	التابع replace
</h2>

<p>
	تحتوي قيم السلاسل النصية على التابع <code>replace</code> الذي يمكن استخدامه لاستبدال سلسلة نصية بجزء من سلسلة أخرى.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_54" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"papa"</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">"p"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"m"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → mapa</span></pre>

<p>
	يمكن أن يكون الوسيط الأول تعبيرًا نمطيًا، وعندئذ يُستبدل التطابق الأول للتعبير النمطي؛ أما إذا أضيف الخيار <code>g</code> -اختصارًا لـ global- إلى التعبير النمطي، فتُستبدل جميع التطابقات.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_56" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Borobudur"</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/[ou]/</span><span class="pun">,</span><span class="pln"> </span><span class="str">"a"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → Barobudur</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Borobudur"</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/[ou]/</span><span class="pln">g</span><span class="pun">,</span><span class="pln"> </span><span class="str">"a"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → Barabadar</span></pre>

<p>
	لو كان لدينا وسيط إضافي للتابع <code>replace</code> بحيث نختار منه استبدال تطابق واحد أو جميع التطابقات، لكان أفضل من الاعتماد على خاصية للتعبير النمطي، بل لو كان من خلال توفير تابع مختلف باسم <code>replaceAll</code> لكان أفضل.
</p>

<p>
	<strong>ملاحظة</strong>: أضيف دعم حديث للغة جافاسكربت يحل النقطة السابقة وأصبحت تدعم التابع <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll" rel="external nofollow">replaceAll</a></code> لتبديل كل التطابقات في النص.
</p>

<p>
	يأتي مكمن القوة في استخدام التعابير النمطية مع التابع <code>replace</code> من حقيقة استطاعتنا الإشارة إلى المجموعات المطابَقة في السلسلة النصية البديلة، فمثلًا، لدينا سلسلة كبيرة تحتوي على أسماء أشخاص، بحيث يحتوي كل سطر على اسم واحد، ويبدأ بالاسم الأخير، ثم الاسم الأول، أي بالصورة:<br>
	 
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_60" style="">
<span class="typ">Lastname</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Firstname</span><span class="pln">
</span></pre>

<p>
	إذا أردنا التبديل بين تلك الأسماء وحذف الفاصلة الأجنبية التي بين كل منها لنحصل على الصورة <code>Firstname Lastname</code>، فنستطيع استخدام الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_58" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">
  </span><span class="str">"Mohsin, Samira\nFady, Eslam\nSahl, Hasan"</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/(\w+), (\w+)/</span><span class="pln">g</span><span class="pun">,</span><span class="pln"> </span><span class="str">"$2 $1"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → Samira Mohsin</span><span class="pln">
</span><span class="com">//   Eslam Fady</span><span class="pln">
</span><span class="com">//   Hasan Sahl</span></pre>

<p>
	يشير كل من <code>‎$1</code>، و<code>‎$2</code> في السلسلة البديلة إلى المجموعات المحاطة بأقواس في النمط، ويحل النص الذي يطابق المجموعة الأولى محل <code>‎$1</code>، كما يحل النص الذي يطابق المجموعة الثانية محل <code>‎$2</code>، وهكذا حتى نصل إلى <code>‎$9</code>؛ أما التطابق كله فيمكن الإشارة إليه باستخدام <code>‎$&amp;‎</code>.
</p>

<p>
	من الممكن تمرير دالة بدلًا من سلسلة نصية على أساس وسيط ثاني إلى التابع <code>replace</code>، حيث تُستدعى الدالة لكل استبدال مع المجموعات المطابَقة والتطابق كله على أساس وسائط arguments، وتُدخَل قيمتها المعادة في السلسلة الجديدة، أي كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_63" style="">
<span class="pln">let s </span><span class="pun">=</span><span class="pln"> </span><span class="str">"the cia and fbi"</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">s</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/\b(fbi|cia)\b/</span><span class="pln">g</span><span class="pun">,</span><span class="pln">
            str </span><span class="pun">=&gt;</span><span class="pln"> str</span><span class="pun">.</span><span class="pln">toUpperCase</span><span class="pun">()));</span><span class="pln">
</span><span class="com">// → the CIA and FBI</span></pre>

<p>
	وهذا مثال آخر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_65" style="">
<span class="pln">let stock </span><span class="pun">=</span><span class="pln"> </span><span class="str">"1 lemon, 2 cabbages, and 101 eggs"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> minusOne</span><span class="pun">(</span><span class="pln">match</span><span class="pun">,</span><span class="pln"> amount</span><span class="pun">,</span><span class="pln"> unit</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  amount </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Number</span><span class="pun">(</span><span class="pln">amount</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">amount </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// only one left, remove the 's'</span><span class="pln">
    unit </span><span class="pun">=</span><span class="pln"> unit</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> unit</span><span class="pun">.</span><span class="pln">length </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">amount </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    amount </span><span class="pun">=</span><span class="pln"> </span><span class="str">"no"</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> amount </span><span class="pun">+</span><span class="pln"> </span><span class="str">" "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> unit</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">stock</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/(\d+) (\w+)/</span><span class="pln">g</span><span class="pun">,</span><span class="pln"> minusOne</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → no lemon, 1 cabbage, and 100 eggs</span></pre>

<p>
	يأخذ المثال أعلاه سلسلةً نصيةً، ويبحث عن حالات حدوث عدد متبوع بكلمة أبجدية رقمية، ويُعيد سلسلةً نصيةً، إذ تكون في كل حالة من تلك الحالات أُنقصت بمقدار 1.
</p>

<p>
	ستكون المجموعة <code>‎(\d+)‎</code> هي الوسيط <code>amount</code> للدالة، وتقيَّد المجموعة <code>‎(\w+)‎</code> بالوسيط <code>unit</code>، وتحوِّل الدالة الوسيط <code>amount</code> إلى عدد، وينجح ذلك بما أنه طابَق <code>‎\d+‎</code>، كما تُجري بعض التعديلات في حالة إذا كان المتبقي صفر أو واحد فقط.
</p>

<h2>
	الجشع Greed
</h2>

<p>
	من الممكن استخدام <code>replace</code> لكتابة دالة تحذف جميع التعليقات من شيفرة جافاسكربت، لننظر في محاولة أولية لها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_67" style="">
<span class="kwd">function</span><span class="pln"> stripComments</span><span class="pun">(</span><span class="pln">code</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> code</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/\/\/.*|\/\*[^]*\*\//</span><span class="pln">g</span><span class="pun">,</span><span class="pln"> </span><span class="str">""</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">stripComments</span><span class="pun">(</span><span class="str">"1 + /* 2 */3"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 1 + 3</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">stripComments</span><span class="pun">(</span><span class="str">"x = 10;// ten!"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → x = 10;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">stripComments</span><span class="pun">(</span><span class="str">"1 /* a */+/* b */ 1"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 1  1</span></pre>

<p>
	يطابق الجزء الذي يسبق العامل or محرفي شرطة مائلة متبوعتين بعدد من محارف لا تكون محارف سطر جديد؛ أما الجزء المتعلِّق بالتعليقات متعددة الأسطر فيملك بعض التفصيل، إذ نستخدم <code>[^]</code> -أيّ محرف ليس ضمن مجموعة المحارف الفارغة- على أساس طريقة لمطابقة أي محرف، غير أننا لا نستطيع استخدام محرف النقطة هنا لاحتواء التعليقات الكتلية على عدة أسطر، ولا يطابق محرف النقطة محارف السطر الجديد.
</p>

<p>
	لكن إذا نظرنا إلى خرج السطر الأخير فسنرى أنه خطأ نوعًا ما، وذلك أنّ الجزء <code>‎[^]*‎</code> من التعبير سيطابِق كل ما يستطيع مطابقته كما وضحنا في القسم الخاص بالتعقب الخلفي، فإذا تسبب ذلك في فشل الجزء التالي من التعبير، فسيعود المطابِق محرفًا واحدًا إلى الوراء، ثم يحاول مرةً أخرى من هناك، وهو في هذا المثال يحاول مطابقة بقية السلسلة، ثم يعود إلى الوراء من هناك.
</p>

<p>
	سيجد الحدث <code>‎*/‎</code> بعد العودة أربعة محارف إلى الوراء ويطابقها، وليس هذا ما أردنا، إذ كنا نريد مطابقة تعليق واحد، وليس العودة إلى نهاية الشيفرة لنجد نهاية آخر تعليق كتلي.
</p>

<p>
	نقول بسبب هذا السلوك أنّ عوامل التكرار مثل <code>+</code> و<code>*</code> و<code>?</code> و<code>{}</code> هي عوامل جشعة greedy، أي تطابق كل ما تستطيع مطابقته وتتعقب خلفيًا من هناك، لكن إذا وضعنا علامة استفهام بعد تلك العوامل لتصير هكذا <code>?+</code> و<code>?*</code> و<code>??</code> و<code>?{}</code>، فسننفي عنها صفة الجشع لتطابق أقل ما يمكن، ولا تطابِق أكثر إلا كان النمط الباقي لا يناسب تطابقًا أصغر.
</p>

<p>
	هذا هو عين ما نريده في هذه الحالة، فبجعل عامل النجمة يطابق أصغر امتداد من المحارف التي تقودنا إلى <code>‎*/‎</code>، فسنستهلك تعليقًا كتليًا واحدًا فقط.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_69" style="">
<span class="kwd">function</span><span class="pln"> stripComments</span><span class="pun">(</span><span class="pln">code</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="kwd">return</span><span class="pln"> code</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/\/\/.*|\/\*[^]*?\*\//</span><span class="pln">g</span><span class="pun">,</span><span class="pln"> </span><span class="str">""</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">stripComments</span><span class="pun">(</span><span class="str">"1 /* a */+/* b */ 1"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 1 + 1</span></pre>

<p>
	نستطيع على ذلك نسْب الكثير من الزلات البرمجية bugs في برامج التعابير النمطية إلى استخدام عامل جشِع من غير قصد، في حين أنّ استخدام عامل غير جشِع أفضل، لهذا يجب النظر في استخدام النسخة الغير جشعة من العامل أولًا عند استخدام أحد عوامل التكرار.
</p>

<h2>
	إنشاء كائنات RegExp ديناميكيا
</h2>

<p>
	ستكون لدينا حالات لا نعرف فيها النمط الذي يجب مطابقته عند كتابة الشيفرة، فمثلًا، نريد البحث عن اسم المستخدِم في جزء من النص، وإحاطته بمحرفي شرطة سفلية لإبرازه عما حوله، إذ لن نستطيع استخدام الترميز المبني على الشرطة المائلة لأننا لن نعرف الاسم إلا عند تشغيل البرنامج فعليًا، لكن نستطيع رغم ذلك بناء سلسلة نصية، واستخدام باني <code>RegExp</code> عليها، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_71" style="">
<span class="pln">let name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Saad"</span><span class="pun">;</span><span class="pln">
let text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Saad is a suspicious character."</span><span class="pun">;</span><span class="pln">
let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RegExp</span><span class="pun">(</span><span class="str">"\\b("</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> name </span><span class="pun">+</span><span class="pln"> </span><span class="str">")\\b"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"gi"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">text</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">,</span><span class="pln"> </span><span class="str">"_$1_"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → _Saad_ is a suspicious character.</span></pre>

<p>
	يجب استخدام شرطتين خلفيتين مائلتين عند إنشاء علامات الحدود <code>‎\b</code>، وذلك لعدم كتابتها في تعبير نمطي محاط بشرطات مائلة، وإنما في سلسلة نصية عادية، كذلك يحتوي الوسيط الثاني للباني <code>RegExp</code> على خيارات للتعبير النمطي، وهي <code>"gi"</code> في هذه الحالة لتشير إلى عمومها global وعدم حساسيتها لحالة المحارف case insensitive.
</p>

<p>
	إذا احتوى اسم المستخدِم على محارف غريبة مثل <code>"dea+hl[]rd"</code>، فسيتسبب هذا في تعبير نمطي غير منطقي، وسنحصل على اسم مستخدِم لا يطابق اسم المستخدِم الفعلي.
</p>

<p>
	سنضيف شرطات مائلة خلفية قبل أي محرف يملك معنىً خاص به لحل هذه المشكلة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_73" style="">
<span class="pln">let name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"dea+hl[]rd"</span><span class="pun">;</span><span class="pln">
let text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"This dea+hl[]rd guy is super annoying."</span><span class="pun">;</span><span class="pln">
let escaped </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/[\\[.+*?(){|^$]/</span><span class="pln">g</span><span class="pun">,</span><span class="pln"> </span><span class="str">"\\$&amp;"</span><span class="pun">);</span><span class="pln">
let regexp </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">RegExp</span><span class="pun">(</span><span class="str">"\\b"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> escaped </span><span class="pun">+</span><span class="pln"> </span><span class="str">"\\b"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"gi"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">text</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">,</span><span class="pln"> </span><span class="str">"_$&amp;_"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → This _dea+hl[]rd_ guy is super annoying.</span></pre>

<h2>
	التابع search
</h2>

<p>
	لا يمكن استخدام تعبير نمطي لاستدعاء التابع <code>indexOf</code> على سلسلة نصية، والحل هو استخدام التابع <code>search</code> الذي يتوقع تعبيرًا نمطيًا، حيث يُعيد أول فهرس يجده التعبير كما يفعل <code>indexOf</code>، أو يُعيد <code>‎-1</code> إذا لم يجده.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_75" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"  word"</span><span class="pun">.</span><span class="pln">search</span><span class="pun">(</span><span class="str">/\S/</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 2</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"    "</span><span class="pun">.</span><span class="pln">search</span><span class="pun">(</span><span class="str">/\S/</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → -1</span></pre>

<p>
	لا توجد طريقة في التابع <code>search</code> لاختيار بدء المطابقة عند إزاحة offset بعينها، على عكس <code>indexOf</code> الذي يملكها في الوسيط الثاني، إذ تُعَدّ مفيدةً في بعض الأحيان.
</p>

<h2>
	خاصية lastIndex
</h2>

<p>
	لا يوفر التابع <code>exec</code> طريقةً سهلةً لبدء البحث من موضع بعينه في السلسلة النصية، شأنه في ذلك شأن التابع <code>search</code>، والطريقة التي يوفرها لذلك موجودة، لكنها ليست سهلة.
</p>

<p>
	تحتوي كائنات التعبير النمطي على خصائص إحداها هي <code>source</code> التي تحتوي على السلسلة التي أُنشئ منها التعبير، وثمة خاصية أخرى هي <code>lastIndex</code> التي تتحكم في موضع بدء التطابق التالي، وإن كان في حالات محدودة، وحتى حينئذ يجب أن يكون كل من الخيار العام <code>g</code> وخيار <code>y</code> اللزج sticky مفعَّلين في التعبير النمطي، كما يجب وقوع التطابق من خلال التابع <code>exec</code>. كان من الممكن هنا السماح بتمرير وسيط إضافي إلى <code>exec</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_77" style="">
<span class="pln">let pattern </span><span class="pun">=</span><span class="pln"> </span><span class="str">/y/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">
pattern</span><span class="pun">.</span><span class="pln">lastIndex </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
let match </span><span class="pun">=</span><span class="pln"> pattern</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="str">"xyzzy"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">match</span><span class="pun">.</span><span class="pln">index</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 4</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">pattern</span><span class="pun">.</span><span class="pln">lastIndex</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 5</span></pre>

<p>
	إذا نجح التطابق، فسيحدِّث الاستدعاء إلى <code>exec</code> خاصية <code>lastIndex</code> إلى النقطة التي تلي التطابق تلقائيًا؛ أما إذا لم يُعثر على تطابق، فستُضبِط خاصية <code>lastIndex</code> على الصفر، حيث يكون هو القيمة في كائن التعبير النمطي الذي سيُبنى تاليًا.
</p>

<p>
	يكون الفرق بين الخيارين العام واللزج هو عدم نجاح التطابق حالة الخيار اللزج إلا عندما يبدأ من <code>lastIndex</code> مباشرةً؛ أما في حالة الخيار العام، فسيبحث عن موضع يمكن بدء التطابق عنده.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_79" style="">
<span class="pln">let global </span><span class="pun">=</span><span class="pln"> </span><span class="str">/abc/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">global</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="str">"xyz abc"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → ["abc"]</span><span class="pln">
let sticky </span><span class="pun">=</span><span class="pln"> </span><span class="str">/abc/</span><span class="pln">y</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">sticky</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="str">"xyz abc"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → null</span></pre>

<p>
	تتسبب تلك التحديثات التلقائية لخاصية <code>lastIndex</code> في مشاكل عند استخدام قيمة تعبير نمطي مشتركة لاستدعاءات <code>exec</code> متعددة، فقد يبدأ تعبيرنا النمطي عند فهرس من مخلَّفات استدعاء سابق.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_81" style="">
<span class="pln">let digit </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\d/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">digit</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="str">"here it is: 1"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → ["1"]</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">digit</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="str">"and now: 1"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → null</span></pre>

<p>
	كذلك من الآثار اللافتة للخيار العام أنه يغيِّر الطريقة التي يعمل بها التابع <code>match</code> على السلاسل النصية، إذ يبحث عن جميع تطابقات النمط في السلسلة النصية، ويُعيد مصفوفةً تحتوي على السلاسل المطابَقة عندما يُستدعى مع الخيار العام، بدلًا من إعادة مصفوفة تشبه التي يُعيدها <code>exec</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_83" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Banana"</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/an/</span><span class="pln">g</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → ["an", "an"]</span></pre>

<p>
	لهذا يجب الحذر عند التعامل مع التعابير النمطية العامة، واستخدامها في الحالات الضرورية فقط،، مثل الاستدعاءات إلى <code>replace</code> والأماكن التي تريد استخدام <code>lastIndex</code> فيها صراحةً.
</p>

<h3>
	التكرار على التطابقات
</h3>

<p>
	يُعَدّ البحث في جميع مرات حدوث النمط في سلسلة نصية بطريقة تعطينا وصولًا إلى كائن المطابقة في متن الحلقة loop body أمرًا شائعًا، ويمكن فعل ذلك باستخدام <code>lastIndex</code>، و<code>exec</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_86" style="">
<span class="pln">let input </span><span class="pun">=</span><span class="pln"> </span><span class="str">"A string with 3 numbers in it... 42 and 88."</span><span class="pun">;</span><span class="pln">
let number </span><span class="pun">=</span><span class="pln"> </span><span class="str">/\b\d+\b/</span><span class="pln">g</span><span class="pun">;</span><span class="pln">
let match</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">match </span><span class="pun">=</span><span class="pln"> number</span><span class="pun">.</span><span class="pln">exec</span><span class="pun">(</span><span class="pln">input</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Found"</span><span class="pun">,</span><span class="pln"> match</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln"> </span><span class="str">"at"</span><span class="pun">,</span><span class="pln"> match</span><span class="pun">.</span><span class="pln">index</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// → Found 3 at 14</span><span class="pln">
</span><span class="com">//   Found 42 at 33</span><span class="pln">
</span><span class="com">//   Found 88 at 40</span></pre>

<p>
	يستفيد هذا من كون قيمة تعبير الإسناد <code>=</code> هي القيمة المسندَة، لذا ننفذ التطابق عند بداية كل تكرار باستخدام <code>match = number.exec(input)‎</code> على أساس شرط في تعليمة <code>while</code>، ثم نحفظ النتيجة في رابطة binding، ونوقف التكرار إذا لم نجد تطابقات أخرى.
</p>

<h2>
	تحليل ملف INI
</h2>

<p>
	لنقل أننا نكتب برنامجًا يجمع بيانات عن أعدائنا على الإنترنت، رغم أننا لن نكتبه حقًا وإنما يهمنا الجزء الذي يقرأ ملف التهيئة، على أساس مثال على مشكلة تحتاج إلى التعابير النمطية، إذ سيبدو ملف التهيئة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_89" style="">
<span class="pln">searchengine</span><span class="pun">=</span><span class="pln">https</span><span class="pun">:</span><span class="com">//duckduckgo.com/?q=$1</span><span class="pln">
spitefulness</span><span class="pun">=</span><span class="lit">9.7</span><span class="pln">

</span><span class="pun">;</span><span class="pln"> </span><span class="pun">تُسبق</span><span class="pln"> </span><span class="pun">التعليقات</span><span class="pln"> </span><span class="pun">بفاصلة</span><span class="pln"> </span><span class="pun">منقوطة...</span><span class="pln">
</span><span class="pun">;</span><span class="pln"> </span><span class="pun">يختص</span><span class="pln"> </span><span class="pun">كل</span><span class="pln"> </span><span class="pun">قسم</span><span class="pln"> </span><span class="pun">بعدو</span><span class="pln"> </span><span class="pun">منفصل</span><span class="pln">
</span><span class="pun">[</span><span class="pln">larry</span><span class="pun">]</span><span class="pln">
fullname</span><span class="pun">=</span><span class="typ">Larry</span><span class="pln"> </span><span class="typ">Doe</span><span class="pln">
type</span><span class="pun">=</span><span class="pln">kindergarten bully
website</span><span class="pun">=</span><span class="pln">http</span><span class="pun">:</span><span class="com">//www.geocities.com/CapeCanaveral/11451</span><span class="pln">

</span><span class="pun">[</span><span class="pln">davaeorn</span><span class="pun">]</span><span class="pln">
fullname</span><span class="pun">=</span><span class="typ">Davaeorn</span><span class="pln">
type</span><span class="pun">=</span><span class="pln">evil wizard
outputdir</span><span class="pun">=</span><span class="str">/home/</span><span class="pln">marijn</span><span class="pun">/</span><span class="pln">enemies</span><span class="pun">/</span><span class="pln">davaeorn</span></pre>

<p>
	تكون القواعد الحاكمة لهذه الصيغة -وهي صيغة مستخدمة بكثرة، ويطلق عليها اسم INI- كما يلي:
</p>

<ul>
<li>
		تُتجاهل الأسطر الفارغة والأسطر البادئة بفاصلة منقوطة.
	</li>
	<li>
		تبدأ الأسطر المغلَّفة بالقوسين المعقوفين <code>[ ]</code> قسمًا جديدًا.
	</li>
	<li>
		تضيف الأسطر التي تحتوي على معرِّف أبجدي-رقمي متبوع بمحرف <code>=</code> إعدادًا setting إلى القسم الحالي.
	</li>
	<li>
		لا يُسمح بأي شيء غير ما سبق، ويُعَدّ ما سواه غير صالح.
	</li>
</ul>
<p>
	مهمتنا هنا هي تحويل سلسلة نصية مثل هذه إلى كائن تحمل خصائصه سلاسل نصية للإعدادات المكتوبة قبل ترويسة القسم الأول، وكائنات فرعية للأقسام، بحيث يحمل كل كائن فرعي إعدادات القسم الخاص به، وبما أنه يجب معالجة الصيغة سطرًا سطرًا، فمن الجيد تقسيم الملف إلى أسطر منفصلة، مستفيدين من التابع <code>split</code> الذي تعرضنا له في <a href="https://academy.hsoub.com/programming/javascript/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1234/" rel="">هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت</a>.
</p>

<p>
	لا تقتصر بعض أنظمة التشغيل على محرف السطر الجديد لفصل الأسطر، وإنما تستخدِم محرف الإرجاع carriage return متبوعًا بسطر جديد <code>‎"\r\n"‎</code>، وبما أنّ التابع <code>split</code> يسمح بالتعبير النمطي على أساس وسيط، فنستطيع استخدام تعبير نمطي مثل <code>‎/\r?\n/‎</code> للتقسيم بطريقة تسمح بوجود كل من <code>‎"\n"‎</code> و<code>‎"\r\n"‎</code> بين الأسطر.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_91" style="">
<span class="kwd">function</span><span class="pln"> parseINI</span><span class="pun">(</span><span class="pln">string</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ابدأ بكائن ليحمل حقول المستوى العلوي</span><span class="pln">
  let result </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
  let section </span><span class="pun">=</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
  string</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">/\r?\n/</span><span class="pun">).</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">line </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let match</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">match </span><span class="pun">=</span><span class="pln"> line</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/^(\w+)=(.*)$/</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      section</span><span class="pun">[</span><span class="pln">match</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> match</span><span class="pun">[</span><span class="lit">2</span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">match </span><span class="pun">=</span><span class="pln"> line</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="str">/^\[(.*)\]$/</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      section </span><span class="pun">=</span><span class="pln"> result</span><span class="pun">[</span><span class="pln">match</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="str">/^\s*(;.*)?$/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">line</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Line '"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> line </span><span class="pun">+</span><span class="pln"> </span><span class="str">"' is not valid."</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">parseINI</span><span class="pun">(`</span><span class="pln">
name</span><span class="pun">=</span><span class="typ">Vasilis</span><span class="pln">
</span><span class="pun">[</span><span class="pln">address</span><span class="pun">]</span><span class="pln">
city</span><span class="pun">=</span><span class="typ">Tessaloniki</span><span class="pun">`));</span><span class="pln">
</span><span class="com">// → {name: "Vasilis", address: {city: "Tessaloniki"}}</span></pre>

<p>
	تمر الشيفرة على أسطر الملف وتبني الكائن، كما تخزَّن الخصائص التي في القمة داخل الكائن مباشرةً، بينما تخزن الخصائص الموجودة في الأقسام داخل كائن قسم مستقل، كما تشير الرابطة <code>section</code> إلى كائن القسم الحالي.
</p>

<p>
	لدينا نوعان من الأسطر المميزة، وهما ترويسة الأقسام، أو أسطر الخصائص، فإذا كان السطر خاصيةً عاديةً، فسيخزَّن في الموضع الحالي؛ أما إذا كان ترويسةً لقسم، فسيُنشأ كائن قسم جديد وتُضبط <code>section</code> لتشير إليه.
</p>

<p>
	نضمن بالاستخدام المتكرر لمحرفي <code>^</code> و<code>$</code>، مطابقة التعبير للسطر كاملًا وليس جزءًا منه فقط، كما ستعمل الشيفرة عند إهمالهما، لكنها ستتصرف مع بعض المدخلات بغرابة، وهو الأمر الذي سيكون زلةً bug يصعب تعقبها وإصلاحها.
</p>

<p>
	يُعَدّ النمط <code>‎if (match = string.match(...))‎</code> شبيهًا بما سبق في شأن استخدام الإسناد على أساس شرط لتعليمة <code>while</code>، فلن نكون على يقين من نجاح استدعاء <code>match</code>، لذا لا نستطيع الوصول إلى الكائن الناتج إلا داخل تعليمة <code>if</code> تختبر ذلك، ولكي لا نقطع سلسلة الصيغ <code>else if</code>، فسنسند نتيجة التطابق إلى رابطة، وسنستخدم هذا التعيين على أساس اختبار لتعليمة <code>if</code> مباشرةً.
</p>

<p>
	تتحقق الدالة من السطر باستخدام التعبير <code>‎/^\s*(;.*)?$/‎</code> إذا لم يكن ترويسةً لقسم أو خاصيةً ما، إذ تتحقق من أنه تعليق أو سطر فارغ، حيث يطابق الجزء الذي بين الأقواس التعليقات، ثم تتأكد <code>?</code> أنه يطابق الأسطر التي تحتوي على مسافة بيضاء فقط، وإذا وُجد سطر لا يطابق أي صيغة من الصيغ المتوقعة، فسترفع الدالة اعتراضًا exception.
</p>

<h2>
	المحارف الدولية
</h2>

<p>
	كان اتجاه تصميم جافاسكربت في البداية نحو سهولة الاستخدام، وقد ترسخ ذلك الاتجاه مع الوقت إلى أن صار هو السمة الأساسية للغة ومعيارًا لتحديثاتها، لكن أتت هذه السهولة بعواقب لم تكن في الحسبان وقتها، إذ تُعَدّ تعابير جافاسكربت النمطية غبيةً لغويًا، فالمحرف الكلمي بالنسبة لها هو واحد من 26 محرفًا فقط، وهي المحارف الموجودة في الأبجدية اللاتينية بحالتيها الصغرى والكبرى، أو أرقامًا عشريةً، أو محرف الشرطة السفلية <code>_</code>؛ أما بالنسبة لأيّ شيء غير ذلك، مثل é أو β، فلن تطابق <code>‎\w</code> رغم أنها محارف كلمية، لكنها ستطابق الحالة الكبرى منها <code>‎\W</code> التي تشير إلى التصنيف غير الكلمي nonword category.
</p>

<p>
	تجدر الإشارة إلى ملاحظة غريبة في شأن محرف المسافة البيضاء العادية <code>‎\s</code>، إذ لا تعاني من هذه المشكلة، وتطابق جميع المحارف التي يَعُدّها معيار اليونيكود محارف مسافة بيضاء، بما في ذلك المسافة غير الفاصلة nonbreaking space، والفاصلة المتحركة المنغولية Mongolian vowel separator.
</p>

<p>
	أيضًا، من المشاكل التي سنواجهها مع التعابير النمطية في جافاسكربت أنها لا تعمل على المحارف الحقيقية وإنما تعمل على الأعداد البِتّية للمحارف code units كما ذكرنا في <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B9%D9%84%D9%8A%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1242/" rel="">الدوال العليا في جافاسكريبت</a>، وبالتالي سيكون سلوك المحارف المكونة من عددين بِتّيين غريبًا، وعلى خلاف ما نريد.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_93" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/?{3}/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"???"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/&lt;.&gt;/</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"&lt;?&gt;"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/&lt;.&gt;/</span><span class="pln">u</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"&lt;?&gt;"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	المشكلة أنّ ? التي في السطر الأول تعامَل على أنها عددين بِتّيين، ولا يطبَّق الجزء <code>{3}</code> إلا على العدد الثاني. وبالمثل، تطابق النقطة عددًا بِتَيًا واحدًا، وليس العددين اللذَين يكونان الرمز التعبيري للوردة، كما يجب إضافة خيار اليونيكود <code>u</code> للتعبير النمطي كي يعامَل مثل تلك المحارف على الوجه الذي ينبغي.
</p>

<p>
	سيظل السلوك الخاطئ للتعبير النمطي هو الافتراضي للأسف، لأنّ التغيير قد يتسبب في مشاكل للشيفرة الموجودة والتي تعتمد عليه، ومن الممكن استخدام <code>‎\p</code> في تعبير نمطي مفعّل فيه خيار اليونيكود لمطابقة جميع المحارف التي يسند اليونيكود إليها خاصية معطاة، رغم أنّ هذه الطريقة معتمدة حديثًا ولم تُستخدم كثيرًا بعد.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_95" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/\p{Script=Greek}/</span><span class="pln">u</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"α"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/\p{Script=Arabic}/</span><span class="pln">u</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"α"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/\p{Alphabetic}/</span><span class="pln">u</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"α"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">/\p{Alphabetic}/</span><span class="pln">u</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="str">"!"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span></pre>

<p>
	يعرِّف اليونيكود عددًا من الخصائص المفيدة رغم أنّ إيجاد الخاصية التي نحتاجها قد لا يكون أمرًا سهلًا في كل مرة، حيث يمكن استخدام الصيغة <code>‎\p{Property=Value}‎</code> لمطابقة أيّ محرف له قيمة معطاة لتلك الخاصية، وإذا أُهمل اسم الخاصية كما في <code>‎\p{Name}‎</code>، فسيُفترض الاسم إما خاصيةً بِتّيةً مثل <code>Alphabetic</code>، أو فئةً مثل <code>Number</code>.
</p>

<h2>
	خاتمة
</h2>

<p>
	التعبيرات النمطية هي كائنات تمثل أنماطًا في السلاسل النصية، وتستخدم لغتها الخاصة للتعبير عن تلك الأنماط.
</p>

<table>
<thead><tr>
<th>
				التعبير النمطي
			</th>
			<th>
				دلالته
			</th>
		</tr></thead>
<tbody>
<tr>
<td style="text-align: center;">
				<code>/abc/</code>
			</td>
			<td style="text-align: center;">
				تسلسل من المحارف
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>/[abc]/</code>
			</td>
			<td style="text-align: center;">
				أيّ محرف في مجموعة محارف
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎/[^abc]/‎</code>
			</td>
			<td style="text-align: center;">
				أيّ محرف ليس في مجموعة ما من المحارف
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>/[0-9]/</code>
			</td>
			<td style="text-align: center;">
				أيّ محرف من مجال ما من المحارف
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎/x+/‎</code>
			</td>
			<td style="text-align: center;">
				مرة حدوث واحدة أو أكثر للنمط <code>x</code>
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎/x+?/‎</code>
			</td>
			<td style="text-align: center;">
				مرة حدوث أو أكثر غير جشعة
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎/x*/‎</code>
			</td>
			<td style="text-align: center;">
				حدوث صفري أو أكثر.
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎/x?/‎</code>
			</td>
			<td style="text-align: center;">
				حدوث صفري أو حدوث لمرة واحدة
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎/x{2,4}/‎</code>
			</td>
			<td style="text-align: center;">
				حدوث لمرتَين إلى أربعة مرات
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>/(abc)/</code>
			</td>
			<td style="text-align: center;">
				مجموعة
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				`/a
			</td>
			<td style="text-align: center;">
				b
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				c/`
			</td>
			<td style="text-align: center;">
				أيّ نمط من بين أنماط متعددة
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎/\d/‎</code>
			</td>
			<td style="text-align: center;">
				أيّ محرف رقمي
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎/\w/‎</code>
			</td>
			<td style="text-align: center;">
				محرف أبجدي رقمي alphanumeric، أي محرف كلمة
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎/\s/‎</code>
			</td>
			<td style="text-align: center;">
				أيّ محرف مسافة بيضاء
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>/./</code>
			</td>
			<td style="text-align: center;">
				أيّ محرف عدا الأسطر الجديدة
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>‎/\b/‎</code>
			</td>
			<td style="text-align: center;">
				حد كلِمي word boundary
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>/^/</code>
			</td>
			<td style="text-align: center;">
				بداية الدخل
			</td>
		</tr>
<tr>
<td style="text-align: center;">
				<code>/$/</code>
			</td>
			<td style="text-align: center;">
				نهاية الدخل
			</td>
		</tr>
</tbody>
</table>
<p>
	يملك التعبير النمطي التابع <code>test</code> للتحقق هل السلسلة المعطاة مطابقة أم لا، كما يملك التابع <code>exec</code> الذي يُعيد مصفوفةً تحتوي على جميع المجموعات المطابِقة إذا وُجدت مطابقات، ويكون لتلك المصفوفة خاصية <code>index</code> التي توضِّح أين بدأت المطابقة.
</p>

<p>
	تملك السلاسل النصية التابع <code>match</code> الذي يطابقها مع تعبير نمطي، وتابع <code>search</code> الذي يبحث عن التعابير النمطية ثم يُعيد موضع بداية التطابق فقط، كما تملك السلاسل النصية تابعًا اسمه <code>replace</code>، حيث يستبدِل سلسلةً نصيةً أو دالةً بتطابقات النمط.
</p>

<p>
	يمكن امتلاك التعابير النمطية خيارات تُكتب بعد شرطة الإغلاق المائلة، إذ يجعل الخيار <code>i</code> التطابق حساسًا لحالة الأحرف، كما يجعل الخيار <code>g</code> التعبير عامًا global، ويمكِّن التابع <code>replace</code> من استبدال جميع النسخ بدلًا من النسخة الأولى فقط؛ أما الخيار <code>y</code> فيجعله لزجًا، أي لن يتجاوز جزءًا من السلسلة أثناء البحث عن تطابق، كذلك يفعِّل الخيار <code>u</code> وضع اليونيكود الذي يصلح لنا عددًا من المشاكل المتعلقة بمعالجة المحارف التي تأخذ أكثر من عددين بتيين.
</p>

<p>
	وهكذا فإنّ التعابير النمطية أشبه بسكين حاد لها مقبض غريب الشكل، فهي تيسِّر المهام التي ننفذها كثيرًا، لكن قد تصبح صعبة الإدارة حين نستخدمها في مشاكل معقدة، ومن الحكمة تجنب حشر الأشياء التي لا تستطيع التعبيرات النمطية التعبير عنها بسهولة.
</p>

<h2>
	تدريبات
</h2>

<p>
	ستجد نفسك لا محالةً أثناء العمل على هذه التدريبات أمام سلوكيات محيِّرة للتعابير النمطية، فمن المفيد عندئذ إدخال تعبيراتك النمطية في أداة مثل <a href="https://www.debuggex.com/" rel="external nofollow">https://debuggex.com</a> لترى إن كان تصورها المرئي يوافق السلوك الذي أردت أم لا، ولترى كيف تستجيب لسلاسل الدخل المختلفة.
</p>

<h3>
	Regexp golf
</h3>

<p>
	Code golf هو مصطلح مستخدم للعبة تحاول التعبير عن برنامج معيَّن بأقل عدد ممكن من المحارف، وبالمثل، يكون regexp golf عملية كتابة تعابير نمطية، بحيث تكون أصغر ما يمكن، وتطابق النمط المعطى فقط.
</p>

<p>
	اكتب تعبيرًا نمطيًا لكل عنصر مما يلي، بحيث يتحقق من حدوث أيّ سلسلة نصية فرعية داخل السلسلة النصية الأم، ويجب على التعبير النمطي مطابقة السلاسل المحتوية على إحدى السلاسل الفرعية التي ذكرناها.
</p>

<p>
	لا تشغل بالك بحدود الكلمات إلا إذا ذُكر ذلك صراحةً، وإذا نجح تعبيرك النمطي فانظر إن كنت تستطيع جعله أصغر.
</p>

<ol>
<li>
		Car وcat.
	</li>
	<li>
		Pop وprop.
	</li>
	<li>
		Ferret وferry وferrari.
	</li>
	<li>
		أيّ كلمة تنتهي بـ ious.
	</li>
	<li>
		محرف مسافة بيضاء متبوع بنقطة، أو فاصلة أجنبية، أو نقطتين رأسيتين، أو فاصلة منقوطة.
	</li>
	<li>
		كلمة أكبر من ستة أحرف.
	</li>
	<li>
		كلمة ليس فيها الحرف e أو E.
	</li>
</ol>
<p>
	استرشد بالجدول الذي في خاتمة المقال أعلاه، واختبر كل حل ببعض السلاسل النصية.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_97" style="">
<span class="com">// املأ التعابير النمطية التالية</span><span class="pln">

verify</span><span class="pun">(</span><span class="str">/.../</span><span class="pun">,</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"my car"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"bad cats"</span><span class="pun">],</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"camper"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"high art"</span><span class="pun">]);</span><span class="pln">

verify</span><span class="pun">(</span><span class="str">/.../</span><span class="pun">,</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"pop culture"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"mad props"</span><span class="pun">],</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"plop"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"prrrop"</span><span class="pun">]);</span><span class="pln">

verify</span><span class="pun">(</span><span class="str">/.../</span><span class="pun">,</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"ferret"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"ferry"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"ferrari"</span><span class="pun">],</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"ferrum"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"transfer A"</span><span class="pun">]);</span><span class="pln">

verify</span><span class="pun">(</span><span class="str">/.../</span><span class="pun">,</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"how delicious"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"spacious room"</span><span class="pun">],</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"ruinous"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"consciousness"</span><span class="pun">]);</span><span class="pln">

verify</span><span class="pun">(</span><span class="str">/.../</span><span class="pun">,</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"bad punctuation ."</span><span class="pun">],</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"escape the period"</span><span class="pun">]);</span><span class="pln">

verify</span><span class="pun">(</span><span class="str">/.../</span><span class="pun">,</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"Siebentausenddreihundertzweiundzwanzig"</span><span class="pun">],</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"no"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"three small words"</span><span class="pun">]);</span><span class="pln">

verify</span><span class="pun">(</span><span class="str">/.../</span><span class="pun">,</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"red platypus"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"wobbling nest"</span><span class="pun">],</span><span class="pln">
       </span><span class="pun">[</span><span class="str">"earth bed"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"learning ape"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"BEET"</span><span class="pun">]);</span><span class="pln">


</span><span class="kwd">function</span><span class="pln"> verify</span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">,</span><span class="pln"> yes</span><span class="pun">,</span><span class="pln"> no</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// تجاهل التدريبات غير المكتملة</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">.</span><span class="pln">source </span><span class="pun">==</span><span class="pln"> </span><span class="str">"..."</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let str of yes</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">regexp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">str</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Failure</span><span class="pln"> to match </span><span class="str">'${str}'</span><span class="pun">`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let str of no</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">regexp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">str</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Unexpected</span><span class="pln"> match </span><span class="kwd">for</span><span class="pln"> </span><span class="str">'${str}'</span><span class="pun">`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	أسلوب الاقتباس
</h3>

<p>
	تخيَّل أنك كتبت قصةً، واستخدمت علامات الاقتباس المفردة فيها لتحديد النصوص التي قالتها الشخصيات فيها، وتريد الآن استبدال علامات الاقتباس المزدوجة بكل تلك العلامات المفردة، لكن مع استثناء الكلمات التي تكون فيها العلامة المفردة لغرض مختلف مثل كلمة aren't.
</p>

<p>
	فكر في نمط يميز هذين النوعين من استخدامات الاقتباس، وصمم استدعاءً إلى التابع <code>replace</code> الذي ينفذ عملية الاستبدال المناسبة.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_99" style="">
<span class="pln">let text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"'أنا الطاهي،' he said, 'إنها وظيفتي.' "</span><span class="pun">;</span><span class="pln">
</span><span class="com">// غيِّر هذا الاستدعاء.</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">text</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">/A/</span><span class="pln">g</span><span class="pun">,</span><span class="pln"> </span><span class="str">"B"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → "أنا الطاهي،" he said, "إنها وظيفتي."</span></pre>

<h4>
	إرشادات للحل
</h4>

<p>
	يكون الحل البديهي هنا هو استبدال محرف غير كلمي nonword بعلامات الاقتباس على الأقل من جانب واحد، كما في <code>‎/\W'|'\W/‎</code>، لكن سيكون عليك أخذ بداية السطر ونهايته في حسابك.
</p>

<p>
	كذلك يجب ضمان أنّ الاستبدال سيشمل المحارف التي طابقها نمط <code>‎\W</code> كي لا تُنسى، ويمكن فعل ذلك بتغليفها في أقواس، ثم تضمين مجموعاتها في السلسلة النصية البديلة (<code>1$‎</code> و<code>2$‎</code>)، كما لا يُستبدل شيء بالمجموعات التي لم تطابَق.
</p>

<h3>
	الأعداد مرة أخرى
</h3>

<p>
	اكتب تعبيرًا لا يطابق إلا الأعداد التي لها نسق جافاسكربت، ويجب عليه دعم علامة + أو - قبل العدد، والعلامة العشرية، والصيغة الأسية -أي <code>5e-3</code>، أو <code>1E10</code>-، مع علامتي موجب أو سالب قبل الأس.
</p>

<p>
	لاحظ عدم اشتراط وجود أرقام قبل العلامة العشرية أو بعدها، لكن لا يمكن أن يكون العدد مكونًا من العلامة العشرية وحدها، أي يسمح بكل من <code>.5</code> و<code>5.</code> في جافاسكربت، لكن لا يُسمح بـ <code>.</code>.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_428_101" style="">
<span class="com">// املأ التعبير النمطي التالي.</span><span class="pln">
let number </span><span class="pun">=</span><span class="pln"> </span><span class="str">/^...$/</span><span class="pun">;</span><span class="pln">

</span><span class="com">// الاختبارات:</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let str of </span><span class="pun">[</span><span class="str">"1"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"-1"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"+15"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"1.55"</span><span class="pun">,</span><span class="pln"> </span><span class="str">".5"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"5."</span><span class="pun">,</span><span class="pln">
                 </span><span class="str">"1.3e2"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"1E-4"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"1e+12"</span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">number</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">str</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Failed</span><span class="pln"> to match </span><span class="str">'${str}'</span><span class="pun">`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let str of </span><span class="pun">[</span><span class="str">"1a"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"+-1"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"1.2.3"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"1+1"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"1e4.5"</span><span class="pun">,</span><span class="pln">
                 </span><span class="str">".5."</span><span class="pun">,</span><span class="pln"> </span><span class="str">"1f5"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"."</span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">number</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">str</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Incorrectly</span><span class="pln"> accepted </span><span class="str">'${str}'</span><span class="pun">`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h4>
	<strong>إرشادات للحل</strong>
</h4>

<ul>
<li>
		يجب عدم نسيان الشرطة المائلة الخلفية التي قبل النقطة.
	</li>
	<li>
		يمكن مطابقة العلامة الاختيارية التي قبل العدد وقبل الأس بواسطة <code>‎[+\-]?‎</code>، أو <code>‎(\+|-|)‎</code>، والتي تعني موجب، أو سالب، أو لا شيء.
	</li>
	<li>
		يبقى الجزء الأصعب من هذا التدريب مطابقة كل من <code>‎"5."‎</code> و<code>‎".5"‎</code> دون مطابقة <code>"."</code>، وأحد الحلول الممتازة هنا هو استخدام العامل <code>|</code> لفصل الحالتين؛ فإما رقم واحد أو أكثر متبوع اختياريًا بنقطة وصفر، أو أرقام أخرى، أو نقطة متبوعة برقم واحد أو أكثر.
	</li>
	<li>
		وأخيرًا، نريد جعل e حساسة لحالتها؛ فإما نضيف الخيار <code>i</code> للتعبير النمطي أو نستخدم <code>[eE]</code>.
	</li>
</ul>
<p>
	ترجمة -بتصرف- <a href="https://eloquentjavascript.net/09_regexp.html" rel="external nofollow">للفصل التاسع من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B2%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1245/" rel="">الزلات البرمجية والأخطاء في جافاسكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B7%D9%88%D9%8A%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1271/" rel="">تطويع البيانات في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D9%86%D9%88%D8%A7%D9%81%D8%B0-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1250/" rel="">التخاطب بين نوافذ المتصفح عبر جافاسكريبت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1278</guid><pubDate>Thu, 29 Jul 2021 15:00:00 +0000</pubDate></item><item><title>&#x643;&#x62A;&#x627;&#x628;&#x629; &#x634;&#x64A;&#x641;&#x631;&#x627;&#x62A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; &#x645;&#x642;&#x631;&#x648;&#x621;&#x629;: &#x642;&#x635;&#x629; &#x646;&#x648;&#x639;&#x64A;&#x646; &#x645;&#x646; &#x627;&#x644;&#x62E;&#x628;&#x631;&#x627;&#x621;</title><link>https://academy.hsoub.com/programming/javascript/%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D9%85%D9%82%D8%B1%D9%88%D8%A1%D8%A9-%D9%82%D8%B5%D8%A9-%D9%86%D9%88%D8%B9%D9%8A%D9%86-%D9%85%D9%86-%D8%A7%D9%84%D8%AE%D8%A8%D8%B1%D8%A7%D8%A1-r1272/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_07/60ffab4f6d982_-----.png.8f636a2cb039ad724656196ab8a9f53b.png" /></p>

<p>
	يرغب الجميع بأن يصبح خبيرًا، إذ يوجد على مر السنين نوعان من البشر يُدعون "الخبراء"؛ حيث خبراء النوع الأول هم ممن يعرفون جميع الأدوات المتوفرة في لغة البرمجة ويحرصون على استخدام كل جزئيةٍ منها سواءً كانت تُفيدهم أم لا؛ والنوع الثاني هم ممن يعرفون كل جزئيةٍ من لغة البرمجة ولكنهم انتقائيون بما يجب استخدامه لحل المشاكل البرمجية، حيث يأخذون بالحسبان مجموعةً من العوامل المرتبطة بالشيفرة البرمجية وأمورًا أخرى غير برمجية.
</p>

<p>
	يمكننا تخمين أي نوعٍ من هؤلاء الخبراء يرغب مدير الفريق العمل معهم، إذ يقع الاختيار غالبًا على النوع الثاني من الخبراء، فهم مطورون يركزون على تسليم سطورٍ برمجيةٍ مقروءة من جافاسكربت ومفهومةٍ أيضًا، كما يعملون على صيانتها، إضافةً إلى قدرتهم على جعل المعقد يبدو بسيطًا.
</p>

<p>
	هناك عدة عواملٍ يجب النظر إليها من قِبل الخبراء عند كتابة شيفرةٍ مقروءة، وتحديد الخيارات الواضحة الصحيحة منها والخاطئة.
</p>

<h2>
	الخيار الواضح
</h2>

<p>
	لقد أضاف TC39 الكثير من الميزات الجديدة إلى ECMAScript خلال السنوات الماضية بهدف تحسين تجربة المطوّر، حيث تتضمن هذه الميزات أنماطًا مُحسنةً ومستوحاةً من لغاتٍ أخرى، وأحد هذه الإضافات في ES2019 هي <code>Array.prototype.flat()‎</code>، حيث يُطلب مُدخلٌ واحدٌ وهو العمق أو <code>Infinity</code> لنشر مصفوفة، ويكون العمق الافتراضي عند عدم استخدام أي مدخلٍ هو 1، وبدون هذه الإضافة كان علينا استخدام الصيغة التالية لنشر المصفوفة في مستوٍ واحدٍ.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2834_11" style="">
<span class="pln">let arr </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">]];</span><span class="pln">
</span><span class="pun">[].</span><span class="pln">concat</span><span class="pun">.</span><span class="pln">apply</span><span class="pun">([],</span><span class="pln"> arr</span><span class="pun">);</span><span class="pln"> 
</span><span class="com">// [1, 2, 3, 4]</span></pre>

<p>
	أما عند إضافة <code>flat()‎</code> أصبح من المُمكن التعبير عن نفس الوظيفة باستخدام تعليمةٍ واحدةٍ وهي الدالّة التالية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2834_13" style="">
<span class="pln">arr</span><span class="pun">.</span><span class="pln">flat</span><span class="pun">();</span><span class="pln">
</span><span class="com">// [1, 2, 3, 4]</span></pre>

<p>
	يُعَد السطر الثاني من الشيفرة أكثر قابليةً للقراءة، وسوف يتفق على هذه النقطة كِلا النوعين من الخبراء.
</p>

<p>
	لا يعلم جميع المطورين بوجود الدالّة <code>flat()‎</code> وهم ليسوا بحاجةٍ لذلك، وعند ورودها فإن <code>flat</code> سيكون فعلًا يصف نفسه ويتضمن محتوى ما يفعله وهي أسهل للفهم من <code>concat.apply()‎</code>.
</p>

<p>
	من النادر وجود جوابٍ واضحٍ وصريح بأفضلية الصيغ الجديدة عن القديمة، وسوف يختار الخبير -لأي مجموعةٍ كان ينتمي- الخيار الثاني إن كان على علمٍ بكلا الخيارين لكتابة التعليمات، لأن الخبير يميل لاختيار الشيفرة الأقصر والأوضح والتي تسهل صيانتها وتتبع الخطأ فيها.
</p>

<h2>
	الفحص الداخلي
</h2>

<p>
	تتميز لغة جافاسكربت بأنها متعددة الاستخدام وواسعة الانتشار، فهي مُستخدمة في جميع أجزاء شبكة الويب، ويمكن كتابة الشيفرة نفسها بعدة طرقٍ ولكن لن تتمكن من تحديد الخيار الصحيح ما لم تفهم جيدًا الخيارات المتوفرة والقيود التي تُرافق كل منها.
</p>

<p>
	ستُستخدم البرمجة الوظيفية مع <code>map()‎</code> مثالًا لذلك وستُشرح جميع التكرارات التي تعطي نفس النتيجة، ويُعَد المثال التالي النسخة الأقصر من الأمثلة التي تستخدم <code>map()‎</code> حيث يتسع في سطرٍ واحدٍ.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2834_15" style="">
<span class="kwd">const</span><span class="pln"> arr </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">
 let multipliedByTwo </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">el </span><span class="pun">=&gt;</span><span class="pln"> el </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
 </span><span class="com">// multipliedByTwo is [2, 4, 6]</span></pre>

<p>
	سيُضاف مِحرفان فقط إلى المثال التالي وهما قوسان "()"، حيث توجد دائمًا خسارةٌ وكسب بعد أي تغيير، كما يوجد اختلافُ عند استخدام دالةٍ متعددة المتغيرات الأقواس دائمًا، ولا يوجد ضررٌ في إضافة هذين القوسين هنا فهما يحسّنان التناسق عند كتابة دالةٍ متعددة المتغيرات، وحقيقةً يجبرنا <a href="https://prettier.io/" rel="external nofollow">Prettier</a> على استخدام الأقواس، فهو لا يسمح بإنشاء دالةٍ سهميّةٍ دون استخدام الأقواس.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2834_17" style="">
<span class="pln">let multipliedByTwo </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">map</span><span class="pun">((</span><span class="pln">el</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> el </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span></pre>

<p>
	ستُرفع سوية المثال وتُضاف أقواس مجموعة وتعليمة <code>return</code> لتبدو هذه الدالّة أشبه بالدوال التقليدية. هذه الإضافات مطلوبة عندما تكون الدالّة أكثر من سطرٍ واحد، وبالتأكيد ستُكتب العديد من الدوال المتضمنة أكثر من سطر واحد ضمن الشيفرة البرمجية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2834_19" style="">
<span class="pln">let multipliedByTwo </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">map</span><span class="pun">((</span><span class="pln">el</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> el </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">});</span></pre>

<p>
	ستُزال الآن الدالّة السهمية وتُستخدم نفس صيغة المثال السابق لكن مع استخدام الكلمة المفتاحية <code>function</code>، وهذا مثير لأن هذه الصيغة سوف تعمل في جميع الحالات، ولن ينتج أي خطأ مهما كان عدد المتغيرات أو السطور، وذلك نظرًا لكونها مقروءةً ومألوفة أكثر من الصيغة المستخدمة في المثال الأول، ولكن هل هذا التغيير سيء؟ وكيف سيكون وقعه على مُبرمجٍ جديد أو شخصٍ خبير جدًا بلغةٍ مختلفةٍ عن جافاسكريبت؟ هل سوف تُشكل هذه الصيغة عبئًا على شخص يعرف جافاسكربت جيدًا بالموازنة مع المثال الأول؟
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2834_21" style="">
<span class="pln">let multipliedByTwo </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">el</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> el </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">});</span></pre>

<p>
	وصلنا الآن إلى الخيار الأخير وهو تمرير الدالّة فقط، حيث يمكن كتابة <code>timesTwo</code> بأي طريقةٍ تريدها ولا توجد مشكلة يُسببها تمرير اسم الدالّة في أية حالة، لكن لنتوقف للحظة ونفكر فيما إذا كانت هذه الصيغة تُسبب الحيرة أم لا، فإذا كنت جديدًا على طريقة كتابة هذه الشيفرة، فلن يكون واضحًا لك أن <code>timesTwo</code> هي دالّة وليست كائنًا، ولكن <code>map()‎</code> موجودة لتُعطيك تلميحًا، لذلك هناك العديد من الأمور الهامة الواجب الانتباه إليها وهي امكانية إغفال هذه الدالة، وسهولة العثور عليها، وتأثيرها على النتيجة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2834_25" style="">
<span class="kwd">const</span><span class="pln"> timesTwo </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">el</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> el </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
let multipliedByTwo </span><span class="pun">=</span><span class="pln"> arr</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">timesTwo</span><span class="pun">);</span></pre>

<p>
	لا توجد إجابةٌ واضحةٌ هنا، حيث أن اتخاذ الخيار الصحيح لطريقة كتابة الشيفرة يعني فهم جميع الخيارات المتوفرة ومحدوديتها، كما يجب إدراك أن التناسق يتطلب استخدام أقواس المجموعة والأقواس العادية والكلمة المفتاحية <code>return</code>.
</p>

<p>
	يجب أن تسأل نفسك مجموعةً من الأسئلة عند كتابة شيفرة برمجية، والأسئلة المرتبطة بالأداء هي الأكثر رواجًا، ولكن عندما تنظر إلى شيفرة برمجية متطابقة من الناحية الوظيفية، فيجب أن يكون اختيارك مبنيًا على الإنسان وكيفية فهم غيرك لهذه الشيفرة.
</p>

<h2>
	ليس بالضرورة أن يكون الأحدث هو الأفضل
</h2>

<p>
	طُرح حتى الآن مثالٌ يوّضح أي صيغةٍ برمجيةٍ جديدةٍ، بحيث يستخدم كل نوعٍ من الخبراء حتى وإن كانت غير معروفةٍ عالميًا، كما طُرح مثالٌ يدعو للكثير من التساؤلات دون إجاباتٍ كافية، وأصبح الوقت الآن مُناسبًا للتعمق أكثر في الشيفرة البرمجية التي كُتبت سابقًا ثم أُزيلت، وهي الشيفرة البرمجية التي استخدمت صيغًا غير معروفة على نطاقٍ واسع لحل مشكلة، إضافةً إلى قابلية صيانتها بالاعتماد على القواعد المعروفة لكتابة الشيفرة.
</p>

<p>
	يسمح لك <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment" rel="external nofollow">التعيين بالتفكيك Destructing assignment</a>؛ إمكانية تفريغ القيم من الكائنات (أو المصفوفات) وهو مماثل للشكل الآتي.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2834_28" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln">node</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> exampleObject</span><span class="pun">;</span></pre>

<p>
	وهو يُهيئ متغيرًا ويُسند له قيمةً في سطرٍ واحد ولكن هذا غير ضروري.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2834_30" style="">
<span class="pln">let node
</span><span class="pun">;({</span><span class="pln">node</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> exampleObject</span><span class="pun">)</span></pre>

<p>
	يُسنِد السطر الأخير من الشيفرة البرمجية قيمةً للمتغير باستخدام التفكيك، لكن التصريح عن المتغير يشغل سطرًا قبله، وهذا أمرٌ يرغب الكثير باستخدامه، إلا أن الغالبية لا تُدرك إمكانية ذلك.
</p>

<p>
	نلاحظ عند النظر إلى الشيفرة بتمعُّن؛ وجود الفاصلة المنقوطة غير المُستخدمة لإنهاء السطر كما جرت العادة، إضافةً إلى استخدام الأقواس للإحاطة بالتعليمة واستخدام أقواس مجموعة داخلها أيضًا، وبالتأكيد فإن وظيفة هذه التعليمة غير واضحة، فمن الصعب قراءتها، و يجب ألا تكون موجودة في أي شيفرةٍ مكتوبة من قِبل خبير.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2834_32" style="">
<span class="pln">let node
node </span><span class="pun">=</span><span class="pln"> exampleObject</span><span class="pun">.</span><span class="pln">node</span></pre>

<p>
	تحل هذه الشيفرة المشكلة فهي تعمل ووظيفتها واضحة، كما يسهل فهمها دون الحاجة للبحث عنها، وبالتالي فلا يجب استخدام الصيغة التفكيكية فقط لأننا نستطيع ذلك.
</p>

<h2>
	الشيفرة ليست كل شيء
</h2>

<p>
	كما هو ملاحظ فقد كان حل خبراء النوع الثاني واضحًا بالاعتماد على الشيفرة فقط، ومع ذلك لا يزال هناك فرقٌ واضحٌ بالنسبة للشيفرة التي يكتبها كل خبير، وذلك لأن الشيفرة موجهةٌ للآلة كي تقرأها وللإنسان كي يُفسرها، وبالتالي لا توجد عوامل مرتبطة بالشيفرة هنا لأخذها بالحسبان.
</p>

<p>
	تختلف صيغة لغة البرمجة التي تستخدمها من أجل فريقٍ من مطوّري جافاسكربت عن تلك التي تستخدمها من أجل فريقٍ متعدد اللغات لم يتعمق أفراده في تفاصيل اللغة، ونذكر على سبيل المثال الصيغة <code>spread</code> مقابل <code>concat()‎</code>، حيث أُضيفت <code>spread</code> إلى ECMAScript منذ عدة سنوات واعتُمدت على نحوٍ واسعٍ، فهي تُعَد ميزةً للصيغة تسمح لك بإنجاز العديد من الأمور وأحدها هو تعاقب تسلسل مجموعةٍ من المصفوفات.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2834_34" style="">
<span class="kwd">const</span><span class="pln"> arr1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> arr2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> nums </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[...</span><span class="pln">arr1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">arr2</span><span class="pun">];</span></pre>

<p>
	بالرغم من فعالية وقوة <code>spread</code> إلا أنها ليست رمزًا بديهيًا تستطيع التنبؤ بعمله، وبالتالي إن كنت لا تعلم وظيفته فلن يكون مفيدًا.
</p>

<p>
	بينما يعتقد العديد من الخبراء أن هذه الصيغة مألوفةً بالنسبة لفريقٍ من مُختصي جافاسكربت، يتساءل خبراء النوع الثاني عما إذا كان هذا الكلام صحيحًا بالنسبة لفريقٍ من المبرمجين متعددي اللغات، لذلك فمن المُحتمل أن يختار هؤلاء الخبراء استخدام <code>concat()‎</code> بدلًا من <code>spread</code> لأن اسمها يُعبّر عن عملها.
</p>

<p>
	ينتج عن الشيفرة البرمجية التالية نفس النتيجة التي تُقدمها <code>spread</code> في المثال السابق.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2834_36" style="">
<span class="kwd">const</span><span class="pln"> arr1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> arr2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="lit">13</span><span class="pun">];</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> nums </span><span class="pun">=</span><span class="pln"> arr1</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">arr2</span><span class="pun">);</span></pre>

<p>
	كان هذا أحد الأمثلة لتأثير العامل البشري على خيارات الشيفرة البرمجية، لذلك عند التعامل مع شيفرةٍ برمجيةٍ تعمل عليها فرقٌ مختلفة، فلا بد من الالتزام بمعاييرٍ صارمةٍ لا تراعي بالضرورة الصيغة الأحدث للغة البرمجة، وتنتقل بعد ذلك لعواملٍ أخرى مرتبطة بالأدوات المُستخدمة والتي تجعل العمل أكثر سلاسةً أو صعوبةً لأفراد الفريق.
</p>

<p>
	توجد شيفرات برمجية من الصعب اختبارها، وشيفرات من الصعب استخدامها على مجالٍ أوسع في المستقبل نظرًا لصعوبة إضافة وظائف إضافية لها، وشيفرات ذات أداءٍ ضعيف، وشيفرات لا تستطيع التعامل مع متصفحاتٍ مختلفة، وهذه جميعها عواملٌ يُفكر بها خبراء النوع الثاني، إضافةً إلى تأثير الأسماء المستخدمة. ولكن حتى هم لا يستطيعون اعتماد صيغةٍ صحيحةٍ معظم الوقت لهذا الأمر.
</p>

<h2>
	الخلاصة
</h2>

<p>
	لا يُثبت الخبير نفسه من خلال قدرته على استخدام كل جزئية من خواص لغة البرمجة، بل من خلال معرفة ما يكفي من هذه الخواص لاستخدامٍ حكيمٍ لصيغةٍ برمجية واتخاذ القرارات المناسبة، فهكذا يكون الخبراء، وبهذه الطريقة يساهمون في تخريج خبراء جُدد، وهذا يعني الكثير لمن يجد نفسه خبيرًا أو يطمح إلى أن يصبح خبيرًا، إذ عليه أن يسأل نفسه العديد من الأسئلة عند كتابة شيفرة برمجية والتفكير بمن يُتابعه من المطوّرين، حيث أن أفضل شيفرةٍ برمجية هي تلك التي تُنفِذ شيئًا معقدًا، ولكنها بنفس الوقت مفهومةٌ عند مراجعتها من قِبل فريق العمل، وهذا بالطبع ليس أمرًا سهل المنال ولا توجد طريقةٌ واضحةٌ للوصول له لكن لا بد من أخذه بالحسبان عند كتابة أي دالّة.
</p>

<p>
	ترجمة -وبتصرّف- للمقال <a href="https://alistapart.com/article/human-readable-javascript/" rel="external nofollow">Human-Readable JavaScript: A Tale of Two Experts</a> لصاحبه Laurie Barth.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B7%D9%88%D9%8A%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1271/" rel="">تطويع البيانات في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D9%86%D9%88%D8%A7%D9%81%D8%B0-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1250/" rel="">التخاطب بين نوافذ المتصفح عبر جافاسكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%8A-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%B1%D8%AC%D9%84-%D8%A2%D9%84%D9%8A-%D8%B1%D9%88%D8%A8%D9%88%D8%AA-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1244/" rel="">مشروع تطبيقي لبناء رجل آلي (روبوت) عبر جافاسكريبت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1272</guid><pubDate>Thu, 15 Jul 2021 15:00:00 +0000</pubDate></item><item><title>&#x62A;&#x637;&#x648;&#x64A;&#x639; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B7%D9%88%D9%8A%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1271/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_07/60f29555a397f_---.png.36eda553223dd1983acd5c400ed9a066.png" /></p>

<p>
	تُعَد البيانات وجافاسكربت للواجهة الأمامية من الناحية العملية أحداثًا متنافية، إذ يرى سوق العمل أن وظيفة الواجهة الخلفية معالجة البيانات وتجميعها، أما وظيفة جافاسكربت فهي عرض البيانات المُجمّعة مسبقًا، كما يُنظر إلى عرض النطاق الترددي وزمن المعالجة على أنهما عقبتان رئيسيتان لدى التعامل مع البيانات في الواجهة الأمامية وهذا صحيح إلى حدٍ كبيرٍ، ولكن توجد حالاتٌ من المجدي فيها معالجة البيانات ضمن المتصفح. إذًا فكيف يمكن النجاح بمعالجة البيانات في هذه الحالات؟
</p>

<h2>
	التفكير في البيانات
</h2>

<p>
	يتطلب العمل على البيانات باستخدام جافاسكربت امتلاكًا كاملًا للبيانات وفهمًا للأدوات المتوفرة دون اتصالٍ غير ضروريٍ بالخادم، حيث من المفيد التمييز بين البيانات التابعة لثلاثة أطرافٍ والبيانات المُلّخصة.
</p>

<p>
	تتألف البيانات ثلاثية الأطراف من بيانات معاملاتٍ أولية، وهي بيانات منخفضة المستوى يصعب تحليلها لوحدها؛ وتوجد من ناحيةٍ أخرى البيانات المُلخصة وهي البيانات الممكن عرضها بطريقةٍ مفيدةٍ ومنطقية، وتُدعى أيضًا بالبيانات المكوّنة. يهتم المطورون بهيكلية البيانات الكامنة بين تفاصيل المعاملات والبيانات المكوّنة، وهذه هي النقطة التي سنُركز عليها، فهذه البيانات مُجمّعة ولكنها تتضمن أكثر مما نحتاج للعرض النهائي، كما تمتلك بُعدين أو أكثر (متعددة الأبعاد)، وهذا يوفّر مرونةً في طريقة عرضها، فهي تسمح للمستخدمين النهائيين بتشكيل البيانات واستخراج المعلومات لمزيدٍ من التحليل، وبالرغم من أنها صغيرة وذات أداء جيدٍ، إلا أنها توفّر تفاصيلًا تسمح لنا وللمؤلف بالاطلاع على معلوماتٍ لم نكن نتوقعها.
</p>

<p>
	يجب أن لا يكون الهدف هو الوصول بالبيانات إلى هيئةٍ مثالية، وذلك لتجنب أي تلاعبٍ بها في الواجهة الأمامية، بل يجب تقليص البيانات إلى مجموعة بياناتٍ متعددة الأبعاد، كما يجب استخدام أبعادٍ متعددة يهتم بها العميل المُستهدف مثل الأشخاص والمنتجات والأماكن والوقت، وقياساتٍ متعددة مثل المجموع والتعداد والمتوسط وأصغر قيمة وأعظم قيمة، وبالتالي فعلينا في النهاية عرض هذه البيانات على صفحةٍ مع عناصر نموذج تسمح بتقسيم البيانات لتحليلها بصورةٍ أعمق.
</p>

<p>
	يُعَد إنشاء مجموعات البيانات عمليةً دقيقة جدًا، حيث لا بد من امتلاك بياناتٍ كافية ليكون تحليلنا ذو قيمة وبدون إجهاد جهاز العميل سواءً كان حاسوبًا أو هاتفًا ذكيًا أو غير ذلك، وهذا يعني الوصول لمتطلباتٍ واضحة وموجزة، وسوف نحتاج لاستخدام أبعاد متنوعة وقياسات مختلفة، وهذا يعتمد على مدى اتساع مجموعة البيانات التي نتعامل معها.
</p>

<p>
	سنذكر فيما يلي بعض الأمور الواجب أخذها بالحسبان:
</p>

<ul>
<li>
		هل غالبية المحتوى هو محتوى مميز أو سوف يُستخدم مرارًا؟ حيث نعتمد على قاعدة 80/20 التي تنص على أن 80% من المستخدمين عامةً يحتاجون إلى 20% مما هو متوفرٌ.
	</li>
	<li>
		هل كل الأبعاد منتهية؟ حيث يجب أن تمتلك الأبعاد مجموعةً من القيم المُحددة مسبقًا، فمثلًا مخزون منتج ما يزداد باستمرار سوف يتضخم بسرعة، لذلك فإن العمل على تصنيفات المنتج ربما سيكون أفضل.
	</li>
	<li>
		العمل على تجميع البيانات ما أمكن وبالأخص التواريخ، وإن كنت تستطيع الاكتفاء بعملية التجميع باستخدام الأعوام فافعل ذلك، وإن كنت بحاجة للتجميع بالاعتماد على الشهر فلا بأس بذلك، ولكن تجنب المجالات الأصغر من ذلك.
	</li>
	<li>
		اعتماد مبدأ "القلة أفضل"، فكلما كان عدد القيم ضمن البعد أقل، كان هذا أفضل من ناحية الأداء. لنأخذ مجموعة بيانات مؤلفةً من 200 صفٍ على سبيل المثال، فإذا أضفنا بعدًا آخر بأربع قيمٍ ممكنة، فسوف تتضخم مجموعة البيانات هذه إلى 200x4 = 800 صفٍ كحدٍ أعظمي؛ أما إذا أضفت بعدًا بخمسين قيمة، فعندها سوف تتضخم مجموعة البيانات إلى 200x50 = 10000 صفًا، وهذا سوف يتضاعف مع كل بعد نُضيفه.
	</li>
	<li>
		تجنُب القياسات المُلخصة في مجموعات البيانات متعددة الأبعاد لأنها تحتاج إعادة الحساب في كل مرةٍ تتغير فيها مجموعة البيانات، إذ يجب عليك مثلًا تضمين المجموع الكلي والعدد الكلي وحساب المتوسطات ديناميكيًا إذا كنت تخطط لعرض المتوسط، ونتمكن بهذه الطريقة من إعادة حساب المتوسطات باستخدام القيم المُلخصة عند تلخيص البيانات.
	</li>
</ul>
<p>
	تأكد من فهمك الجيد للبيانات التي تعمل عليها قبل البدء بتنفيذ أيٍ من المذكور سابقًا، فمن المحتمل أن تفترض افتراضاتٍ خاطئةٍ تقود إلى قراراتٍ مبنيةٍ على معلوماتٍ خاطئة، لذلك فإن جودة البيانات هي أولويةٌ هامة وهذا ينطبق على البيانات التي تطلبها أو تُنشئها.
</p>

<p>
	لا تأخذ أي مجموعة بيانات وتبني افتراضاتٍ عن بعد أو قياسٍ فيها، ولا تتردد بالسؤال دائمًا عن <a data-ss1628955989="1" data-ss1628956278="1" href="http://library.ucmerced.edu/node/10249" rel="external nofollow">فهرس البيانات</a> أو أي توثيقٍ خاصٍ بها يُمكن أن يساعد على فهم ما تتعامل معه، حيث لا يعتمد تحليل البيانات على الحدس أو التنبؤ، فربما طُبقت قواعد خاصة بعملٍ تجاريٍ أو حُذفت بيانات في خطوةٍ سابقة، وبالتالي فإن كنت لا تملك هذه المعلومات فسينتهي بك الأمر بإنتاج مجموعات بياناتٍ وتصوراتٍ لا معنى لها أو مضللة بأسوء الأحوال. <a data-ss1628955989="1" data-ss1628956278="1" href="https://github.com/ignoreintuition/tamingData" rel="external nofollow">سوف يساعد المثال التالي على فهم ما سبق</a>.
</p>

<h2>
	حالة الاستخدام المدروسة
</h2>

<p>
	سوف نستخدم <a data-ss1628955989="1" data-ss1628956278="1" href="https://github.com/BuzzFeedNews/2015-11-refugees-in-the-united-states" rel="external nofollow">مجموعة بيانات BuzzFeed</a> المختصة بتحليل البيانات المتعلقة بالأماكن التي يأتي منها اللاجئون إلى الولايات المتحدة والأماكن التي يذهبون إليها، حيث سنبني تطبيقًا صغيرًا يعرض لنا عدد اللاجئين الواصلين لولايةٍ محددة في عامٍ محدد، وسنعرض على وجه الخصوص واحدةً مما يلي بناءً على طلب المستخدم:
</p>

<ul>
<li>
		العدد الكلي للواصلين إلى ولايةٍ معينة في عامٍ معينٍ.
	</li>
	<li>
		العدد الكلي للواصلين خلال كل الأعوام لولايةٍ معينة.
	</li>
	<li>
		العدد الكلي للواصلين لكل الولايات في عام معيّن.
	</li>
</ul>
<p>
	سوف تكون واجهة المستخدم عبارة عن نموذجٍ بسيطٍ لاختيار الولاية والعام، وسوف تعمل الشيفرة على:
</p>

<ol>
<li>
		إرسال طلبٍ بالبيانات.
	</li>
	<li>
		تحويل النتيجة إلى JSON.
	</li>
	<li>
		معالجة البيانات.
	</li>
	<li>
		تسجيل أي خطأ في الطرفية.
	</li>
	<li>
		عرض النتائج للمستخدم.
	</li>
</ol>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>ملاحظة</strong>: لضمان عدم تنفيذ الخطوة 3 إلا بعد جلب كامل مجموعة البيانات، سوف نستخدم الطريقة <code>then</code> مع تطبيق كل عمليات معالجة البيانات ضمن هذه الكتلة.
	</p>
</blockquote>

<p>
	لن نمرّر مجموعات بياناتٍ ضخمة الحجم للمتصفح لسببين هما عرض الحزمة، وقدرات وحدة المعالجة المركزية CPU، لذلك ستُجمع البيانات في الخادم باستخدام Node.js.
</p>

<ul>
<li>
		بيانات المصدر
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9551_7" style="">
<span class="pun">[{</span><span class="str">"year"</span><span class="pun">:</span><span class="lit">2005</span><span class="pun">,</span><span class="str">"origin"</span><span class="pun">:</span><span class="str">"Afghanistan"</span><span class="pun">,</span><span class="str">"dest_state"</span><span class="pun">:</span><span class="str">"Alabama"</span><span class="pun">,</span><span class="str">"dest_city"</span><span class="pun">:</span><span class="str">"Mobile"</span><span class="pun">,</span><span class="str">"arrivals"</span><span class="pun">:</span><span class="lit">0</span><span class="pun">},</span><span class="pln">
</span><span class="pun">{</span><span class="str">"year"</span><span class="pun">:</span><span class="lit">2006</span><span class="pun">,</span><span class="str">"origin"</span><span class="pun">:</span><span class="str">"Afghanistan"</span><span class="pun">,</span><span class="str">"dest_state"</span><span class="pun">:</span><span class="str">"Alabama"</span><span class="pun">,</span><span class="str">"dest_city"</span><span class="pun">:</span><span class="str">"Mobile"</span><span class="pun">,</span><span class="str">"arrivals"</span><span class="pun">:</span><span class="lit">0</span><span class="pun">},</span><span class="pln">
</span><span class="pun">...</span><span class="pln"> </span><span class="pun">]</span></pre>

<ul>
<li>
		البيانات متعددة الأبعاد:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9551_9" style="">
<span class="pun">[{</span><span class="str">"year"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2005</span><span class="pun">,</span><span class="pln"> </span><span class="str">"state"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Alabama"</span><span class="pun">,</span><span class="str">"total"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1386</span><span class="pun">},</span><span class="pln"> 
 </span><span class="pun">{</span><span class="str">"year"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2005</span><span class="pun">,</span><span class="pln"> </span><span class="str">"state"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Alaska"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"total"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">989</span><span class="pun">},</span><span class="pln"> 
</span><span class="pun">...</span><span class="pln"> </span><span class="pun">]</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="72184" data-ss1628955989="1" data-ss1628956278="1" href="https://academy.hsoub.com/uploads/monthly_2021_07/002_javascript_taming_data_1.png.156c15173d5777e377affd687eea3164.png" rel=""><img alt="002_javascript_taming_data_1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="72184" data-unique="jecg935lq" src="https://academy.hsoub.com/uploads/monthly_2021_07/002_javascript_taming_data_1.thumb.png.4f430cb162e2e9803a1c5836fabe495a.png"></a>
</p>

<h2>
	كيفية ضبط هيكلية البيانات في المكان الصحيح
</h2>

<h3>
	واجهة AJAX وFetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>
</h3>

<p>
	توجد عدة طرق في جافاسكربت من أجل جلب البيانات من مصدرٍ خارجيٍ، وكان علينا قديمًا استخدام <a data-ss1628955989="1" data-ss1628956278="1" href="https://translate.google.com/translate?hl=en&amp;sl=ar&amp;u=https://academy.hsoub.com/programming/javascript/%25D8%25A7%25D9%2584%25D9%2588%25D8%25A7%25D8%25AC%25D9%2587%25D8%25A9-%25D8%25A7%25D9%2584%25D8%25A8%25D8%25B1%25D9%2585%25D8%25AC%25D9%258A%25D8%25A9-fetch-%25D9%2581%25D9%258A-javascript-r739/&amp;prev=search" rel="external nofollow">طلب XHR</a> لذلك، لأنه مدعوم على نطاقٍ واسعٍ ولكنه معقد ويتطلب استخدام عدة طرقٍ مختلفة، كما توجد مكتبات تُساعد على تخفيض التعقيد، مثل <a data-ss1628955989="1" data-ss1628956278="1" href="https://github.com/axios/axios" rel="external nofollow">Axios</a> أو <a data-ss1628955989="1" data-ss1628956278="1" href="https://academy.hsoub.com/programming/javascript/jquery/%D9%85%D9%8F%D9%82%D8%AF%D9%91%D9%85%D8%A9-%D8%A5%D9%84%D9%89-ajax-%D9%88%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%8F%D8%A4%D8%AC%D9%91%D9%84%D8%A9-deferred-objects-%D8%B9%D9%84%D9%89-jquery-r64//" rel="">jQuery's Ajax <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a>، كما أنها توفّر دعمًا عبر المتصفحات، ولهذا فهي خيارٌ متاحٌ إن كنت تستخدم أحدها، لكن لا بد من اختيار الحلول الأصيلة ما أمكن. توجد أيضًا طريقة <a data-ss1628955989="1" data-ss1628956278="1" href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" rel="external nofollow">Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr></a> وهي الأحدث، ولذلك فهي مدعومةٌ على نطاقٍ أضيق من سابقاتها، ولكنها أسهل وقابلة للتسلسل، وستُحوّل الشيفرة البرمجية إلى مكافئٍ مدعومٍ على نطاقٍ أوسع إن كنت تستخدم ناقلًا مثل <a data-ss1628955989="1" data-ss1628956278="1" href="https://babeljs.io/" rel="external nofollow">Babel</a>.
</p>

<p>
	ستُستخدم واجهة Fetch <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> في الحالة المدروسة لجلب البيانات للتطبيق.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9551_11" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">fetchData </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">fetchData </span><span class="pun">||</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
  fetch</span><span class="pun">(</span><span class="str">'./data/aggregate.json'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(</span><span class="pln">response </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// when the fetch executes we will convert the response</span><span class="pln">
      </span><span class="com">// to json format and pass it to .then()</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}).</span><span class="pln">then</span><span class="pun">(</span><span class="pln">jsonData </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// take the resulting dataset and assign to a global object</span><span class="pln">
      window</span><span class="pun">.</span><span class="pln">fetchData</span><span class="pun">.</span><span class="pln">jsonData </span><span class="pun">=</span><span class="pln"> jsonData</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}).</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Fetch process failed"</span><span class="pun">,</span><span class="pln"> err</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span></pre>

<p>
	الشيفرة البرمجية السابقة هي جزءٌ من ملف main.js الموجود في مستودع GitHub، حيث ترسل الطريقة <code>fetch()‎</code> طلبًا بالبيانات ثم نحوّل النتائج إلى JSON، وستُستخدم الطريقة <code>then()‎</code> من أجل ضمان عدم تنفيذ التعليمة التالية إلا بعد جلب كامل مجموعة البيانات، وستُنفذُ جميع عمليات معالجة البيانات ضمن هذه الكتلة، كما ستُسجل الأخطاء باستخدام <code>console.log()‎</code>.
</p>

<p>
	الهدف هنا هو تحديد الأبعاد الأساسية المطلوبة لعمل تقريرٍ بالعام والولاية قبل تجميع عدد الواصلين المرتبطين بهذه الأبعاد، وإزالة الدولة الأم والمدينة المتوجهين لها، ويمكنك الاطلاع على النص البرمجي Node.js في الملف <code>preprocess/index.js/</code> في مستودع GitHub لفهم كيفية إنجاز العمليات السابقة بصورةٍ أكبر، حيث يُنشئ هذا النص البرمجي ملف <code>aggregate.json</code> الذي يجلبه التابع <code>fetch()‎</code>.
</p>

<h3>
	البيانات متعددة الأبعاد
</h3>

<p>
	الهدف من الصيغة متعددة الأبعاد هو تحقيق المرونة بحيث تكون البيانات مُفصلةً جيدًا، حتى لا يُضطر المستخدم لطلب استعلام للخادم في كل مرةٍ يحتاج فيها إجابةً عن سؤالٍ مختلف، وتكون مُلخصةً بنفس الوقت بحيث لا يُضطر التطبيق للبحث في كامل مجموعة البيانات مع كل جزءٍ جديدٍ من البيانات. يجب علينا توقع الأسئلة وتقديم بيانات تُجيب عن هذه الأسئلة لأن المستخدم يحتاج إلى أن يكون قادرًا على إجراء بعض التحليلات دون أن يشعر بأنه مُقيدٌ أو مغمورٌ بكمٍ هائلٍ من المعلومات.
</p>

<p>
	ستُستخدم بيانات <a data-ss1628955989="1" data-ss1628956278="1" href="http://json.org/" rel="external nofollow">JSON</a> كما هو الحال مع معظم واجهات برمجة التطبيقات APIs، فهي معيارٌ مُستخدمٌ لإرسال البيانات للتطبيقات على أنها كائناتٌ تتألف من أزواج (اسم وقيمة). ألقِ نظرةً على العينة التالية من مجموعة بياناتٍ متعددة الأبعاد قبل العودة إلى الحالة المدروسة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9551_13" style="">
<span class="kwd">const</span><span class="pln"> ds </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[{</span><span class="pln">
  </span><span class="str">"year"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2005</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"state"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Alabama"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"total"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1386</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"priorYear"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1201</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"year"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2005</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"state"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Alaska"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"total"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">811</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"priorYear"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1541</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"year"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2006</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"state"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Alabama"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"total"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">989</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"priorYear"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1386</span><span class="pln">
</span><span class="pun">}];</span></pre>

<p>
	تستطيع استخدام جافاسكربت على نحوٍ أكبر لتحليل مجموعة البيانات عندما تكون مُجمّعةً بطريقة صحيحة. لنُلقِ نظرةً على بعض من توابع مصفوفات جافاسكربت المُستخدمة لتكوين البيانات.
</p>

<h2>
	كيفية العمل بفعالية مع البيانات من خلال جافاسكربت
</h2>

<h3>
	ترشيح المصفوفة
</h3>

<p>
	تأخذ طريقة <code>filter()‎</code> الخاصة بالنموذج الأولي للمصفوفة <code>Array.prototype.filter()‎</code>، وظيفة اختبار كل عنصرٍ ضمن المصفوفة، وتُعيد مصفوفةً أخرى تحتوي على القيم التي تجاوزت الاختبار، وهذا يسمح بإنشاء مجموعة بياناتٍ فرعيةٍ ذات معنى عند استخدام القائمة المنسدلة أو مرشحات النص، كما سيتمكن المستخدم من الاطلاع على المعلومات من خلال عرض أقسامٍ من البيانات، وهذا صحيحٌ أيضُا عند استخدام أبعادٍ ذات معنى ومنفصلة لمجموعة البيانات متعددة الأبعاد.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9551_15" style="">
<span class="pln">ds</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">d </span><span class="pun">=&gt;</span><span class="pln"> d</span><span class="pun">.</span><span class="pln">state </span><span class="pun">===</span><span class="pln"> </span><span class="str">"Alabama"</span><span class="pun">);</span><span class="pln">

</span><span class="com">// Result</span><span class="pln">
</span><span class="pun">[{</span><span class="pln">
  state</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Alabama"</span><span class="pun">,</span><span class="pln">
  total</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1386</span><span class="pun">,</span><span class="pln">
  year</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2005</span><span class="pun">,</span><span class="pln">
  priorYear</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1201</span><span class="pln">
</span><span class="pun">},{</span><span class="pln">
  state</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Alabama"</span><span class="pun">,</span><span class="pln">
  total</span><span class="pun">:</span><span class="pln"> </span><span class="lit">989</span><span class="pun">,</span><span class="pln">
  year</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2006</span><span class="pun">,</span><span class="pln">
  priorYear</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1386</span><span class="pln">
</span><span class="pun">}]</span></pre>

<h3>
	ربط بيانات المصفوفة
</h3>

<p>
	تأخذ طريقة <code>map()‎</code> الخاصة بالنموذج الأولي للمصفوفة <code>Array.prototype.map()‎</code> وظيفة تمرير كل عنصرٍ في المصفوفة وإعادة مصفوفةً جديدةً بنفس عدد العناصر القديم، حيث يسمح ربط البيانات بإنشاء مجموعات بياناتٍ مرتبطة، وأحد استخدامات هذه الطريقة هو ربط بياناتٍ غامضةٍ مع بياناتٍ ذات معنى وموصوفة، أو استخدامها لأخذ قياسات وإجراء حسابات على عناصر المصفوفة لتحقيق تحليلٍ أكثر عمقًا.
</p>

<h4>
	 1. ربط البيانات ببيانات ذات معنى
</h4>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9551_17" style="">
<span class="pln">ds</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">d </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">d</span><span class="pun">.</span><span class="pln">state</span><span class="pun">.</span><span class="pln">indexOf</span><span class="pun">(</span><span class="str">"Alaska"</span><span class="pun">))</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> </span><span class="str">"Contiguous US"</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">"Continental US"</span><span class="pun">);</span><span class="pln">

</span><span class="com">// Result</span><span class="pln">
</span><span class="pun">[</span><span class="pln">
  </span><span class="str">"Contiguous US"</span><span class="pun">,</span><span class="pln"> 
  </span><span class="str">"Continental US"</span><span class="pun">,</span><span class="pln"> 
  </span><span class="str">"Contiguous US"</span><span class="pln">
</span><span class="pun">]</span></pre>

<h4>
	 2. ربط البيانات مع النتائج المحسوبة
</h4>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9551_19" style="">
<span class="pln">ds</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">d </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">round</span><span class="pun">(((</span><span class="pln">d</span><span class="pun">.</span><span class="pln">priorYear </span><span class="pun">-</span><span class="pln"> d</span><span class="pun">.</span><span class="pln">total</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> d</span><span class="pun">.</span><span class="pln">total</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">100</span><span class="pun">));</span><span class="pln">

</span><span class="com">// Result</span><span class="pln">
</span><span class="pun">[-</span><span class="lit">13</span><span class="pun">,</span><span class="pln"> </span><span class="lit">56</span><span class="pun">,</span><span class="pln"> </span><span class="lit">40</span><span class="pun">]</span></pre>

<h3>
	تلخيص بيانات المصفوفة
</h3>

<p>
	تأخذ طريقة <code>reduce()‎</code>الخاصة بالنموذج الأولي للمصفوفة <code>Array.prototype.reduce()‎</code> وظيفة معالجة كل عنصرٍ ضمن المصفوفة وإعادة بياناتٍ مُجمعةٍ، وتُستخدم لإنجاز حساباتٍ رياضيةٍ مثل إضافة أو ضرب كل رقمٍ ضمن المصفوفة، ويُمكن استخدامها أيضًا لضم المحارف والعديد من الأمور الأخرى، ولا بد من تعلُّم هذه الدالة من خلال مثالٍ نظرًا لصعوبتها.
</p>

<p>
	عليك التأكد من تلخيص البيانات عند عرضها، بحيث تُقدم فكرةً مُلخصةً للمستخدمين، ولذلك يُمكن تجميع البيانات اعتمادًا على ما يحتاجه المُستخدم حتى عند تطبيق عمليات تلخيصٍ عامة على البيانات من جهة الخادم، وبالنسبة للتطبيق الذي ننشئه، فعليّنا إضافة المجموع الكلي لكل مُدخلٍ وعرض النتيجة المُجمّعة باستخدام طريقة <code>reduce()‎</code> للمرور على كل سجلٍ وإضافة القيمة الحالية إلى المجمّع، وبهذا تكون النتيجة النهائية هي المجموع الكلي لقيم كل مصفوفة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9551_21" style="">
<span class="pln">ds</span><span class="pun">.</span><span class="pln">reduce</span><span class="pun">((</span><span class="pln">accumulator</span><span class="pun">,</span><span class="pln"> currentValue</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> 
accumulator </span><span class="pun">+</span><span class="pln"> currentValue</span><span class="pun">.</span><span class="pln">total</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

</span><span class="com">// Result</span><span class="pln">
</span><span class="lit">3364</span></pre>

<h3>
	تطبيق هذه الدوال على الحالة المدروسة
</h3>

<p>
	حالما نحصل على البيانات سنعيّن حدثّا للزر "Get the Data" من أجل عرض المجموعة الفرعية من البيانات المناسبة، حيث توجد عدة مئاتٍ من العناصر ضمن بيانات JSON الخاصة بالتطبيق، وتتواجد الشيفرة المسؤولة عن دمج البيانات مع الزر في الملف main.js الخاص بنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3752_11" style="">
<span class="pln">document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"submitBtn"</span><span class="pun">).</span><span class="pln">onclick </span><span class="pun">=</span><span class="pln">
  </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">e</span><span class="pun">){</span><span class="pln">
      e</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">
      let state </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"stateInput"</span><span class="pun">).</span><span class="pln">value </span><span class="pun">||</span><span class="pln"> </span><span class="str">"All"</span><span class="pln">
      let year </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"yearInput"</span><span class="pun">).</span><span class="pln">value </span><span class="pun">||</span><span class="pln"> </span><span class="str">"All"</span><span class="pln">
      let subset </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">fetchData</span><span class="pun">.</span><span class="pln">filterData</span><span class="pun">(</span><span class="pln">year</span><span class="pun">,</span><span class="pln"> state</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">subset</span><span class="pun">.</span><span class="pln">length </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pln">  </span><span class="pun">)</span><span class="pln">
        subset</span><span class="pun">.</span><span class="pln">push</span><span class="pun">({</span><span class="str">'state'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'N/A'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'year'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'N/A'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'total'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'N/A'</span><span class="pun">})</span><span class="pln">
      document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"output"</span><span class="pun">).</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln">
      </span><span class="pun">`&lt;</span><span class="pln">table </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"table"</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">thead</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">tr</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">th scope</span><span class="pun">=</span><span class="str">"col"</span><span class="pun">&gt;</span><span class="typ">State</span><span class="pun">&lt;/</span><span class="pln">th</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">th scope</span><span class="pun">=</span><span class="str">"col"</span><span class="pun">&gt;</span><span class="typ">Year</span><span class="pun">&lt;/</span><span class="pln">th</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">th scope</span><span class="pun">=</span><span class="str">"col"</span><span class="pun">&gt;</span><span class="typ">Arrivals</span><span class="pun">&lt;/</span><span class="pln">th</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;/</span><span class="pln">tr</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">thead</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;</span><span class="pln">tbody</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;</span><span class="pln">tr</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">td</span><span class="pun">&gt;</span><span class="pln">$</span><span class="pun">{</span><span class="pln">subset</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">state</span><span class="pun">}&lt;/</span><span class="pln">td</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">td</span><span class="pun">&gt;</span><span class="pln">$</span><span class="pun">{</span><span class="pln">subset</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">year</span><span class="pun">}&lt;/</span><span class="pln">td</span><span class="pun">&gt;</span><span class="pln">
            </span><span class="pun">&lt;</span><span class="pln">td</span><span class="pun">&gt;</span><span class="pln">$</span><span class="pun">{</span><span class="pln">subset</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">total</span><span class="pun">}&lt;/</span><span class="pln">td</span><span class="pun">&gt;</span><span class="pln">
          </span><span class="pun">&lt;/</span><span class="pln">tr</span><span class="pun">&gt;</span><span class="pln">
        </span><span class="pun">&lt;/</span><span class="pln">tbody</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;/</span><span class="pln">table</span><span class="pun">&gt;`</span><span class="pln">
  </span><span class="pun">}</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="72185" data-ss1628955989="1" data-ss1628956278="1" href="https://academy.hsoub.com/uploads/monthly_2021_07/003_javascript_taming_data_3.png.d3efe247e10a4e5bdc8ccdf56ef09332.png" rel=""><img alt="003_javascript_taming_data_3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="72185" data-unique="ylo941zw7" src="https://academy.hsoub.com/uploads/monthly_2021_07/003_javascript_taming_data_3.thumb.png.1d70985d520b33402657307be067eb4b.png"></a>
</p>

<p>
	سوف يأخذ حقل الولاية أو العام القيمة الافتراضية "All" عندما يكون فارغًا. ويمكن الاطلاع على الشيفرة البرمجية التالية المُتاحة في الملف <code>js/main.js/</code> وإلقاءِ نظرةٍ على الدالّة <code>()filterData</code> التي نسجّل ضمنها حصة الأسد من عمليتي الترشيح والتجميع.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_9551_25" style="">
<span class="com">// with our data returned from our fetch call, we are going to </span><span class="pln">
</span><span class="com">// filter the data on the values entered in the text boxes</span><span class="pln">
fetchData</span><span class="pun">.</span><span class="pln">filterData </span><span class="pun">=</span><span class="pln"> function</span><span class="pun">(</span><span class="pln">yr</span><span class="pun">,</span><span class="pln"> state</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// if "All" is entered for the year, we will filter on state </span><span class="pln">
 </span><span class="com">// and reduce the years to get a total of all years</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">yr </span><span class="pun">===</span><span class="pln"> </span><span class="str">"All"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let total </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">jsonData</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">
     </span><span class="com">// return all the data where state</span><span class="pln">
     </span><span class="com">// is equal to the input box</span><span class="pln">
      dState </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">dState</span><span class="pun">.</span><span class="pln">state </span><span class="pun">===</span><span class="pln"> state</span><span class="pun">)</span><span class="pln">
        </span><span class="pun">.</span><span class="pln">reduce</span><span class="pun">((</span><span class="pln">accumulator</span><span class="pun">,</span><span class="pln"> currentValue</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
         </span><span class="com">// aggregate the totals for every row that has </span><span class="pln">
         </span><span class="com">// the matched value</span><span class="pln">
          </span><span class="kwd">return</span><span class="pln"> accumulator </span><span class="pun">+</span><span class="pln"> currentValue</span><span class="pun">.</span><span class="pln">total</span><span class="pun">;</span><span class="pln">
        </span><span class="pun">},</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[{</span><span class="str">'year'</span><span class="pun">:</span><span class="pln"> </span><span class="str">'All'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'state'</span><span class="pun">:</span><span class="pln"> state</span><span class="pun">,</span><span class="pln"> </span><span class="str">'total'</span><span class="pun">:</span><span class="pln"> total</span><span class="pun">}];</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="pun">...</span><span class="pln">

 </span><span class="com">// if a specific year and state are supplied, simply</span><span class="pln">
 </span><span class="com">// return the filtered subset for year and state based </span><span class="pln">
 </span><span class="com">// on the supplied values by chaining the two function</span><span class="pln">
 </span><span class="com">// calls together </span><span class="pln">
  let subset </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">jsonData</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">dYr </span><span class="pun">=&gt;</span><span class="pln"> dYr</span><span class="pun">.</span><span class="pln">year </span><span class="pun">===</span><span class="pln"> yr</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">dSt </span><span class="pun">=&gt;</span><span class="pln"> dSt</span><span class="pun">.</span><span class="pln">state </span><span class="pun">===</span><span class="pln"> state</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> subset</span><span class="pun">;</span><span class="pln"> 
</span><span class="pun">};</span><span class="pln">

</span><span class="com">// code that displays the data in the HTML table follows this. See main.js.</span></pre>

<p>
	كما ذُكر سابقًا فإن القيمة الافتراضية لحقل الولاية أو العام هي "All" في حال بقي فارغًا، حيث تُرشح مجموعة البيانات وتُلخص قياسات جميع الأسطر حسب هذا البعد، كما ستُرشح قيمة كلٍ من العام والولاية عند إدخالهما.
</p>

<p>
	لدينا الآن مثال مجرب، حيث:
</p>

<ul>
<li>
		بدأنا بمجموعة بياناتٍ أوليةٍ ذات معاملات.
	</li>
	<li>
		ثم أنشأنا مجموعة بياناتٍ متعددة الأبعاد وشبه مُجمعة.
	</li>
	<li>
		وبنينا نتيجةً كاملةً التكوين ديناميكيًا.
	</li>
</ul>
<p>
	يُمكن التلاعب بالبيانات حالما تصل للمستخدم بعدة طرقٍ دون الحاجة للاتصال المتكرر بالخادم، وهذا مفيد جدًا في حال فقد المستخدم الاتصال، لأنه بذلك لن يفقد القدرة على التفاعل مع البيانات، وهذه ميزةٌ رائعةٌ خصوصًا عند بناء <a data-ss1628956278="1" href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%85%D9%8A%D8%A9-pwa-r832/" rel="">تطبيق ويب تقدُّمي</a> Progressive Web App أو اختصارًا PWA، يحتاج أن يعمل دون اتصالٍ بالانترنت.
</p>

<p>
	يمكن إنشاء أي تحليلٍ لأي مجموعة بيانات حالما تُحكم قبضتك على هذه الطرق الثلاث، لذلك اربط أحد الأبعاد ضمن مجموعة البيانات مع تصنيفٍ أكثر شمولية، ولخّص البيانات باستخدام reduce. تستطيع أيضًا استخدام مكتبة <a data-ss1628955989="1" data-ss1628956278="1" href="https://d3js.org/" rel="external nofollow">D3</a> من أجل ربط البيانات مع جداول ورسوم بيانية تسمح بتصوُّر مرئي مخصص بالكامل لهذه البيانات.
</p>

<h2>
	الخلاصة
</h2>

<p>
	يُطلعنا هذا المقال على الإمكانيات الكامنة في جافاسكربت عند التعامل مع البيانات، وكما ذُكر سابقًا فإن جافاسكربت للواجهة الأمامية ليست بديلًا لتفسير وتحويل البيانات على الخادم حيث تُنجز المهمات الصعبة، كما يجب بنفس الوقت عدم استبعادها عند معالجة مجموعات البيانات بالطريقة الصحيحة.
</p>

<p>
	ترجمة -وبتصرّف- للمقال <a data-ss1628955989="1" data-ss1628956278="1" href="https://alistapart.com/article/taming-data-with-javascript/" rel="external nofollow">Taming Data with JavaScript</a> لصاحبه Brian Greig.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a data-ss1628955989="1" data-ss1628956278="1" href="https://academy.hsoub.com/programming/javascript/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1234/" rel="">هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت</a>
	</li>
	<li>
		<a data-ss1628955989="1" data-ss1628956278="1" href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%A7%D9%87%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA%D8%9F-%D8%A3%D9%85%D8%AB%D9%84%D8%A9-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D8%A7%D8%AA-r525/" rel="">ماهي جافاسكريبت؟ أمثلة على الاستخدامات</a>
	</li>
	<li>
		<a data-ss1628955989="1" data-ss1628956278="1" href="https://academy.hsoub.com/programming/javascript/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-objects-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r645/" rel="">برمجة الكائنات Objects في جافاسكريبت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1271</guid><pubDate>Mon, 12 Jul 2021 15:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x632;&#x644;&#x627;&#x62A; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; &#x648;&#x627;&#x644;&#x623;&#x62E;&#x637;&#x627;&#x621; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B2%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1245/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_06/60bb16a7a944c_----.png.a05a7c1cbeee70858517f7969a323ed6.png" /></p>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		بات إصلاح الأخطاء وتنقيحها في الشيفرة البرمجية أصعب بمرتين من كتابة الشيفرة نفسها، ولا أرى المرء أهلًا لتنقيح الشيفرة إن كان يتذاكى في كتابتها.
	</p>

	<p>
		ـــ برايان كِرنيجان Brian Kernighan، وفيلِب جيمس بلوجر Phillip James Plauger، كتاب عناصر أسلوب البرمجة.
	</p>
</blockquote>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="68508" data-ss1625559345="1" href="https://academy.hsoub.com/uploads/monthly_2021_06/chapter_picture_8.jpg.380ffd603037fefbdba1b3f3e7b8e400.jpg" rel="" data-fileext="jpg"><img alt="chapter_picture_8.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="68508" data-unique="j5bszo2bq" src="https://academy.hsoub.com/uploads/monthly_2021_06/chapter_picture_8.jpg.380ffd603037fefbdba1b3f3e7b8e400.jpg"></a>
</p>

<p>
	تُسمى الأخطاء في برامج الحاسوب عادةً بالزلات bugs، ونحن من نضع هذه الزلات في برامجنا بأيدينا حين نخطئ في شيء ما، أو ننسى رمزًا، أو محرفًا، أو نضع واحدًا في غير محله، وإن كان يحلو للكثير منا أن يظن بأنها تزحف من تلقاء نفسها إلى داخل الشيفرة، ولو قلنا أن البرنامج هو فكرة متبلورة في ذهن المبرمج، فاحتمالية حدوث ثغرة أو خطأ برمجي في هذا البرنامج لن تخرج من أحد شيئين:
</p>

<ul>
	<li>
		الفكرة نفسها معيبة أو مشوهة.
	</li>
	<li>
		حدوث خطأ أثناء ترجمة البرنامج من فكرة إلى شيفرة برمجية.
	</li>
</ul>

<p>
	والحالة الثانية أيسر في اكتشافها وحلها من الأولى، إذ يكون البرنامج سليمًا عدا هذه الثغرة أو الخطأ؛ أما إن كان البرنامج كله مشوهًا نتيجة عيب في كل من الفكرة والمبدأ اللذَين بُني عليهما، فسيكون ذلك أصعب بكثير، ويُطلق على عملية اكتشاف الأخطاء وتصحيحها في الاصطلاح الأجنبي debugging.
</p>

<h2>
	اللغة
</h2>

<p>
	سبب أغلب الأخطاء التي تحدث في البرامج التي نكتبها كما ذكرنا هو نحن المبرمجين، ولو كان الحاسوب يَعقل لقذف تلك المشاكل في وجوهنا، وإذا علمت مرونة جافاسكربت العالية لأدركت مدى صعوبة تنقيح الأخطاء الموجودة في شيفراتك التي تكتبها.
</p>

<p>
	تُعَدّ بنية الرابطات bindings، والخصائص properties مبهمةً إلى الحد الذي يندر معه اكتشاف الأخطاء الكتابية قبل تشغيل البرنامج، وحتى عند التشغيل، إذ تسمح لك بالقيام بأمور غير منطقية دون تنبيهك إليها مثل حساب `true * "monkey"‎.
</p>

<p>
	لكن رغم تلك المرونة الكبيرة في جافاسكربت، إلا أن لها حدودًا لا تتسامح معها، فمثلًا، ستجعل الحاسوب ينبهك فورًا إذا كتبت برنامجًا لا يتبع قواعدها وبنيتها اللغوية؛ كما سيُحدث استدعاء شيء ما غير الدوال أو البحث عن خاصية في قيمة غير معرفة خطأً يُرسَل في تقرير حين يحاول البرنامج تنفيذ هذا الإجراء -أي عند الاستدعاء أو البحث في هاتين الحالتين-.
</p>

<p>
	لكن الغالب أنه لن تُنتج حساباتك الغير منطقية سوى <code>NaN</code> -أي ليس عددًا Not A Number-، أو قيمة غير معرفة undefined value، وسيتابع البرنامج تنفيذه ظانًا أنه يقوم بشيء مفيد، إذ لن تظهر المشكلة إلا لاحقًا بعد مرور تلك القيمة الزائفة على عدة دوال، وقد لا تُطلق إنذار الخطأ على الإطلاق، لكنها تتسبب في خطأ الخرج الناتج من البرنامج في نفس الوقت! وبناءً على ذلك فمن الصعب العثور على مصدر مثل تلك المشاكل.
</p>

<h2>
	الوضع الصارم
</h2>

<p>
	يمكن تقييد جافاسكربت للحد من مرونتها العالية، وذلك من خلال تفعيل الوضع الصارم strict mode فيها، ويكون هذا بوضع السلسلة النصية <code>"use strict"</code> في رأس الملف أو متن الدالة، انظر مثالًا لذلك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_6" style=""><span class="kwd">function</span><span class="pln"> canYouSpotTheProblem</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"use strict"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">counter </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> counter </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln"> counter</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Happy happy"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

canYouSpotTheProblem</span><span class="pun">();</span><span class="pln">
</span><span class="com">// → ReferenceError: counter is not defined</span></pre>

<p>
	إذا نسيت وضع <code>let</code> قبل الرابطة، كما في حالة <code>counter</code> التي في المثال أعلاه، فستُنشِئ جافاسكربت رابطةً عامةً global binding وستستخدِمها؛ أما في الوضع الصارم فلا يحدث ذلك، بل تبلغك اللغة بالخطأ، وذلك أكثر فائدةً لك في البرمجة؛ لكن يجب الانتباه إلى أن هذا لا يحدث حين تكون الرابطة موجودة أصلًا على أساس رابطة عامة، ففي تلك الحالة ستظل الحلقة التكرارية تستبدل قيمة الرابطة.
</p>

<p>
	كما تحمل رابطة <code>this</code> في الوضع الصارم قيمةً غير معرفة <code>undefined</code> في الدوال التي لا تُستدعى على أساس توابع methods؛ أما في الاستدعاء العادي، فستشير <code>this</code> إلى كائن النطاق العام global scope object الذي تكون خصائصه هي الرابطات العامة، فإن استدعيت تابعًا أو بانيًا بالخطأ في الوضع الصارم، فستعطيك جافاسكربت الخطأ بمجرد محاولة قراءة شيء من <code>this</code> بدلًا من الكتابة في النطاق العام، فمثلًا، انظر الشيفرة التالية التي تستدعي دالة باني دون كلمة <code>new</code> المفتاحية كي لا تشير <code>this</code> فيها إلى كائن باني جديد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_9" style=""><span class="kwd">function</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
let osama </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">(</span><span class="str">"Osama"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// oops</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → Osama</span></pre>

<p>
	ينجح هنا هذا الاستدعاء الزائف إلى <code>person</code>، لكنه يعيد قيمةً غير معرَّفة، وينشئ رابطة <code>name</code> العامة؛ أما في الوضع الصارم فستكون النتيجة مختلفةً، انظر كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_11" style=""><span class="str">"use strict"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">(</span><span class="pln">name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">name </span><span class="pun">=</span><span class="pln"> name</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
let osama </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">(</span><span class="str">"Osama"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// forgot new</span><span class="pln">
</span><span class="com">// → TypeError: Cannot set property 'name' of undefined</span></pre>

<p>
	كما ترى فقد أخبرتنا اللغة مباشرةً بوجود خطأ ما، وهذا أكثر فائدةً لنا لا ريب.
</p>

<p>
	لحسن حظنا فستشتكي البواني constructors التي أُنشئت باستخدام صيغة <code>class</code> إذا استُدعيت من غير <code>new</code>، مما يجعل هذه المشكلة أقل إزعاجًا حتى في الوضع العادي أو خارج الوضع الصارم، وإضافةً إلى ما سبق، فيملك الوضع الصارم بعض الخصائص الأخرى، إذ يرفض إعطاء دالة ما عوامل متعددة بالاسم نفسه، كما يزيل بعض مزايا اللغة المسببة لمشاكل مثل تعليمة <code>with</code> التي لن نذكرها مرةً أخرى في هذه السلسلة لكثرة مشاكلها.
</p>

<p>
	لن يؤذيك ولن يضرك استخدام الوضع الصارم عن طريق كتابة `"use strict" في المجمل، وإنما ينفعك ويفيدك في اكتشاف المشاكل.
</p>

<h2>
	الأنواع Types
</h2>

<p>
	تريد بعض اللغات معرفة أنواع الرابطات والتعبيرات قبل تشغيل البرنامج، حيث ستخبرك مباشرةً إذا استُخدِم أحد الأنواع بصورة متناقضة؛ أما جافاسكربت فلا تنظر إلى الأنواع إلا عند تشغيل البرنامج، كما تحاول ضمنيًا تحويل القيم إلى الأنواع التي تتوقعها هي عادةً.
</p>

<p>
	لكن مع هذا، تقدم الأنواع إطار عمل framework مفيدًا عند الحديث عن البرامج، فتَنتج أكثر الأخطاء من الجهل بنوع القيمة الداخلة إلى دالة أو الخارجة منها، فإذا كانت عندك هذه المعلومات مكتوبةً، فسيقل احتمال حدوث تلك الأخطاء لا ريب، حيث تستطيع إضافة تعليق مثل الذي في الشيفرة التالية قبل دالة <code>goalOrientedRobot</code> التي أنشأناها في <a data-ss1625559345="1" href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%A7%D9%84%D8%B1%D9%88%D8%A8%D9%88%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1244/" rel="">المقال السابق</a> لتصف نوعها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_13" style=""><span class="com">// (VillageState, Array) → {direction: string, memory: Array}</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> goalOrientedRobot</span><span class="pun">(</span><span class="pln">state</span><span class="pun">,</span><span class="pln"> memory</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هناك العديد من الاصطلاحات المتّبَعة لإدخال الأنواع في برامج جافاسكربت، وتحتاج الأنواع إلى تحديد مدى تعقيدها لتستطيع وصف ما يكفي من الشيفرة بحيث يكون مفيدًا، فماذا يكون النوع الخاص بدالة <code>randomPick</code> مثلًا التي تعيد عنصرًا عشوائيًا من مصفوفة ما؟
</p>

<p>
	سنحتاج إلى تحديد متغير نوع type variable، وليكن T مثلًا الذي سيمثل أي نوع، وبذلك نستطيع إعطاء <code>randomPick</code> نوعًا مثل <code>‎([T]) → T</code>، وهي دالة من مصفوفة مكونة من Ts إلى T.
</p>

<p>
	إذا كانت الأنواع الموجودة في البرنامج معروفةً، فسيتمكن الحاسوب من التحقق منها بدلًا عنك، وذلك ليُخرج لك الأخطاء قبل تشغيل البرنامج.
</p>

<p>
	هناك العديد من أشكال جافاسكربت التي تضيف الأنواع إلى اللغة وتتحقق منهم، لعل أشهرها لغة TypeScript، وننصحك بتجربتها إن كنت تريد إضافة بعض الصرامة إلى برامجك؛ أما في هذه السلسلة فسنعمل بجافاسكربت العادية.
</p>

<h2>
	الاختبار
</h2>

<p>
	إذا لم تعيننا اللغة على إيجاد الأخطاء، فيجب البحث عنهم بالطريقة الصعبة من خلال تشغيل البرنامج، وعندها سنرى إن كان سيعمل كما خططنا له أم لا، لكن تنفيذ هذا يدويًا مرةً بعد مرة ليس الطريقة المثلى للبرمجة، حيث تُعَدّ مزعجةً وغير عملية بما أنها تستغرق وقتًا طويلًا وجهدًا مضنيًا لاختبار كل شيء في كل مرة تغير فيها شيئًا واحدًا.
</p>

<p>
	كما سنستغل قدرة الحواسيب الهائلة في العمليات التكرارية، بما أن الاختبار في حد ذاته عملية تكرارية، فلمَ لا نجعل هذه العملية مؤتمتةً؟ وسيكون ذلك بكتابة برنامج اختبار يتحقق من البرنامج الخاص بنا.
</p>

<p>
	وقد تقف هنا لحظة لتقول ألم نرد وسيلةً لنقلل بها الجهد الواقع علينا؟ ونجيبك أن بلى، لكن الجهد الذي ستبذله مرةً واحدةً فقط في كتابة هذا الاختبار، سيغنيك طيلة العمل على البرنامج محل المشروع نفسه الذي بين يديك، وستشعر أن بيديك قوةً خارقةً لا تأخذ سوى بضع ثواني، حيث سيتحقق من عمل برنامجك بكفاءة في كل المواقف التي كتبت اختبارات لها، وستلاحظ مباشرةً فور تعطيلك لشيء ما بالخطأ، بدلًا من مرور الخطأ وعدم الشعور به إلا حين تقابله صدفةً مرةً أخرى أثناء العمل على شيء جديد.
</p>

<p>
	تأخذ الاختبارات عادةً صورة برامج صغيرة معنونة لتتحقق من أجزاء بعينها في شيفرتك، فمثلًا، ستكون بعض الاختبارات القياسية لتابع <code>toUpperCase</code> -والتي لعل أحدًا غيرنا اختبرها من قبل- على الصورة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_15" style=""><span class="kwd">function</span><span class="pln"> test</span><span class="pun">(</span><span class="pln">label</span><span class="pun">,</span><span class="pln"> body</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">body</span><span class="pun">())</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Failed</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">label</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

test</span><span class="pun">(</span><span class="str">"convert Latin text to uppercase"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"hello"</span><span class="pun">.</span><span class="pln">toUpperCase</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">"HELLO"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
test</span><span class="pun">(</span><span class="str">"convert Greek text to uppercase"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Χαίρετε"</span><span class="pun">.</span><span class="pln">toUpperCase</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">"ΧΑΊΡΕΤΕ"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
test</span><span class="pun">(</span><span class="str">"don't convert case-less characters"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"مرحبا"</span><span class="pun">.</span><span class="pln">toUpperCase</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">"مرحبا"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تُنتِج كتابة الاختبارات التي تحاكي المثال أعلاه شيفرات متكررةً وغريبةً، ولحل هذه المشكلة فلدينا برامج ستساعدك على بناء وتشغيل تجميعات مختلفة من الاختبارات (حِزم اختبارات)، وذلك من خلال توفير لغة مناسبة في شكل دوال وتوابع تناسب كتابة الاختبارات، وكذلك بإخراج معلومات مفيدة حينما يفشل أحد تلك الاختبارات، ويطلق على ذلك عادةً اسم منفِّذات الاختبارات test runners.
</p>

<p>
	كلما زاد عدد الكائنات الخارجية التي تتعامل الشيفرة معها، صعُب إعداد سياق لاختبارها فيه، وقد كان أسلوب البرمجة الذي عرضناه في <a data-ss1625559345="1" href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%A7%D9%84%D8%B1%D9%88%D8%A8%D9%88%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1244/" rel="">المقال السابع</a> أسهل في الاختبار، حيث استخدَم قيمًا ثابتةً persistent values عوضًا عن كائنات متغيرة.
</p>

<h2>
	التنقيح Debugging
</h2>

<p>
	إذا عرفت أن ثمة شيء في برنامجك يجعله يتصرف على نحو لم تتوقعه أو ينتج أخطاءً، فالخطوة التالية منطقيًا هي معرفة ما هو ذلك الشي أو هذه المشكلة، وقد تكون تلك المشكلة واضحةً أحيانًا، إذ تشير رسالة الخطأ إلى سطر بعينه في برنامجك، حيث سترى المشكلة إذا نظرت إلى وصف الخطأ وذلك السطر بنفسك.
</p>

<p>
	لكن أحيانًا يكون السطر المسبب للمشكلة ضحيةً لاستخدام قيمة متذبذبة وغير مستقرة عليه، بحيث تكون تلك القيمة منتَجةً في مكان آخر، وتُستخدم في هذا السطر بصورة خاطئة، ومن المحتمل رؤيتك لهذا إن جربت حل التمارين التي في الفصول السابقة من هذه السلسلة.
</p>

<p>
	يحول البرنامج في المثال التالي العدد الصحيح إلى سلسلة نصية في نظام ما سواءً كان ثنائيًا، أو عشريًا، أو غيرهما، وذلك بأخذ آخر رقم، ثم تقسيم العدد للتخلص من ذلك الرقم، وهكذا دواليك، لكن يشير الخرج الذي ينتجه البرنامج الآن إلى وجود خطأ ما.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_17" style=""><span class="kwd">function</span><span class="pln"> numberToString</span><span class="pun">(</span><span class="pln">n</span><span class="pun">,</span><span class="pln"> base </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let result </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">,</span><span class="pln"> sign </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    sign </span><span class="pun">=</span><span class="pln"> </span><span class="str">"-"</span><span class="pun">;</span><span class="pln">
    n </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="pln">n</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">do</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    result </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">(</span><span class="pln">n </span><span class="pun">%</span><span class="pln"> base</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
    n </span><span class="pun">/=</span><span class="pln"> base</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> sign </span><span class="pun">+</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">numberToString</span><span class="pun">(</span><span class="lit">13</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 1.5e-3231.3e-3221.3e-3211.3e-3201.3e-3191.3e-3181.3…</span></pre>

<p>
	لعلك انتبهت إلى المشكلة إذا نظرت إلى الشيفرة أعلاه، لكن نريدك التخيل للحظة أنك لا تعرفها ولم تلاحظها.
</p>

<p>
	لنبحث في سياق الحل والتنقيح الذي يجب عمله، إذ نعلم بعدم تصرف برنامجنا على النحو الذي نريده ونريد معرفة السبب، فهنا يجب مقاومة الرغبة في إجراء تغييرات عشوائية في الشيفرة من دون تفكير مسبق، وتحليل للقرار والتغييرات التي تجريها.
</p>

<p>
	نريدك الآن الوقوف للحظة، والتفكير، وتحليل الموقف وما يحدث مع البرنامج، وجمع الملاحظات حول ذلك، للخروج بنظرية حول سبب الخطأ، ثم اختبار تلك النظرية، وستكون إحدى طرق ذلك بوضع بعض استدعاءات <code>console.log</code> في البرنامج لتحصل على معلومات إضافية عما يفعله، كما نريد هنا في حالتنا لـ <code>n</code> أخذ القيم <code>13</code>، و<code>1</code>، ثم <code>0</code>.
</p>

<p>
	لنكتب قيمتها في بداية الحلقة التكرارية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_19" style=""><span class="lit">13</span><span class="pln">
</span><span class="lit">1.3</span><span class="pln">
</span><span class="lit">0.13</span><span class="pln">
</span><span class="lit">0.013</span><span class="pln">
</span><span class="pun">…</span><span class="pln">
</span><span class="lit">1.5e-323</span></pre>

<p>
	لا تعطي قسمة 13 على 10 عددًا صحيحًا، لذلك نريد <code>n = Math.floor(n / base)‎</code> بدلًا من <code>n /= base</code> كي يتحرك العدد إلى اليمين كما نريد.
</p>

<p>
	المزايا التي توفرها أداة المنقِّح debugger والتي تأتي مدمجةً في المتصفحات هي إحدى الوسائل التي نستطيع استخدامها كي ننظر في سلوك البرنامج ونختبره، إذ تكون في هذه المتصفحات مزية إنشاء نقطة توقف breakpoint عند سطر بعينه داخل البرنامج، حيث سيتوقف تنفيذ البرنامج عند وصوله إلى ذلك السطر كي تنظر أنت في قيم الرابطات عند هذه النقطة وتفحصها، ولأن المنقِّحات تختلف من متصفح لآخر، فلن نخوض بك في تفاصيل هذه المزايا أكثر من ذلك، إذ سنترك لك حرية النظر في أدوات المطور في متصفحك، أو البحث في شبكة الويب عن مزيد من المعلومات عن هذا الأمر.
</p>

<p>
	بالعودة إلى فحص سلوك البرنامج، فمن الممكن إنشاء نقطة توقف بوضع تعليمة <code>debugger</code> في برنامجك، وهي تتكون من تلك الكلمة المفتاحية فقط، فإن كانت أدوات المطور مفعَّلة في متصفحك، فسيتوقف البرنامج عند وصوله إلى هذه التعليمة.
</p>

<h2>
	توليد الخطأ
</h2>

<p>
	لا يمكن للمبرمج منع كل الأخطاء الوارد حدوثها في البرنامج، خاصةً إذا كان البرنامج يتواصل مع العالم الخارجي بأيّ طريقة كانت، إذ من الممكن تلقيه مدخلات خاطئة، أو تحميله بأحمال ومهام زائدة عن طاقته، أو تفشل الشبكة التي يتواصل من خلالها، وذلك على سبيل المثال لا الحصر.
</p>

<p>
	لا تشغل بالك بشأن تلك المشاكل إذا كنت تبرمج لنفسك فقط، حيث تستطيع تجاهلها إلى حين حدوثها، ثم تتعامل معها حينها، لكن إن كنت تبني برنامجًا أو أداةً لعميل لك، أو برنامجًا سيستخدمه غيرك، فيجب أن تضع في حساباتك احتمالات التصرفات غير السليمة وغير المتوقعة، فقد يكون الحل الأمثل أحيانًا بتجاهل المدخل ليتابع البرنامج العمل، أو تبليغ المستخدم برسالة تفيد ما حدث، لكن يتوجب على البرنامج فعل شيء ما على أساس استجابة للمشكلة الواقعة في أي حالة كانت.
</p>

<p>
	لنقل مثلًا أن لديك دالةً اسمها <code>promptNumber</code>، حيث تطلب من المستخدِم إدخال عدد ثم تعيده هي، فلو أدخل المستخدم كلمةً مثل "orange" مثلًا، فما الذي ستعيده هذه الدالة؟
</p>

<p>
	أحد الخيارات المتاحة هي جعل الدالة تعيد قيمةً خاصةً، مثل <code>null</code>، أو <code>undefined</code>، أو <code>‎-1</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_21" style=""><span class="kwd">function</span><span class="pln"> promptNumber</span><span class="pun">(</span><span class="pln">question</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let result </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Number</span><span class="pun">(</span><span class="pln">prompt</span><span class="pun">(</span><span class="pln">question</span><span class="pun">));</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Number</span><span class="pun">.</span><span class="pln">isNaN</span><span class="pun">(</span><span class="pln">result</span><span class="pun">))</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">promptNumber</span><span class="pun">(</span><span class="str">"How many trees do you see?"</span><span class="pun">));</span></pre>

<p>
	يجب على أيّ شيفرة تستدعي <code>promptNumber</code> الآن التحقق من قراءة العدد الفعلي، وإذا فشلت فيجب إصلاح ذلك بطريقة ما، ربما بإعادة السؤال، أو بكتابة قيمة افتراضية، أو بإعادة قيمة خاصة إلى مستدعيها للإشارة إلى فشلها في فعل ما طُلب منها.
</p>

<p>
	وسترى أن مواقف عديدة يصلحها إعادة قيمة خاصة للإشارة إلى خطأ ما، خاصةً في حالة شيوع الخطأ ووجوب وضعه في الحسبان صراحةً؛ لكن هذا له سيئاته، فماذا لو كانت الدالة تستطيع إعادة جميع القيم الممكنة؟ فسيكون عليك فعل شيء ما عند التعامل مع تلك الدالة مثل تغليف النتيجة بكائن لتتمكن من تمييز حالة النجاح من الفشل.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_23" style=""><span class="kwd">function</span><span class="pln"> lastElement</span><span class="pun">(</span><span class="pln">array</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">array</span><span class="pun">.</span><span class="pln">length </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">failed</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">element</span><span class="pun">:</span><span class="pln"> array</span><span class="pun">[</span><span class="pln">array</span><span class="pun">.</span><span class="pln">length </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	والمشكلة الثانية عند إعادة قيم خاصة هي أن هذه الإعادة تؤدي إلى شيفرات غريبة، فإن استدعى جزء من الشيفرة الدالة <code>promptNumber</code> عشرة مرات، فعليه التحقق من إعادة <code>null</code> عشرة مرات أيضًا ، وإن كانت إجابته في التحقق من <code>null</code> هي إعادة <code>null</code> نفسها، فعلى من يستدعي الدالة التحقق منها بدورها، وهكذا.
</p>

<h2>
	الاعتراضات Exceptions
</h2>

<p>
	إذا لم تستطع دالة ما تنفيذ وظيفتها على النحو الذي صممت من أجله، فسيكون الحل هو إيقاف ما نفعله وننتقل فورًا إلى المكان الذي يعرف كيف يعالج هذه المشكلة وذلك العجز، وهذا هو دور معالجة الاعتراضات exception handling.
</p>

<p>
	الاعتراضات ما هي إلا آليات تمكّن الشيفرة التي تواجه مشاكل من رفع اعتراض أو تبلغ به، وقد يكون الاعتراض أي قيمة، ويمكن تشبيه هذا البلاغ أو الرفع بإعادة مشحونة نوعًا ما من الدالة، حيث تقفز من الدالة الحالية وممن استدعاها أيضًا لتصل إلى الاستدعاء الأول الذي بدأ التنفيذ الحالي، ويسمى هذا فك المكدس unwinding the stack، ولعلك تذكر مكدس استدعاءات الدالة الذي ذكرناه في <a data-ss1625559345="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1233/" rel="">المقال الثالث</a> من هذه السلسلة، إذ يصغِّر الاعتراض هذا المكدس، ملقيًا لكل سياقات الاستدعاء التي يقابلها.
</p>

<p>
	لن تكون الاعتراضات ذات فائدة إن ذهبت مباشرةً إلى قاع المكدس، وما زادت على أن أتت بطريقة جديدة لبعثرة البرنامج، وإنما تظهر قوتها حين تضع عقبات obstacles لالتقاط هذه الاعتراضات وهي ماضية في المكدس، فبمجرد التقاطك لاعتراض ما، فستستطيع التعامل معه ومعالجته لرؤية أصل المشكلة، ثم تتابع تشغيل البرنامج، انظر مثلًا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_25" style=""><span class="kwd">function</span><span class="pln"> promptDirection</span><span class="pun">(</span><span class="pln">question</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let result </span><span class="pun">=</span><span class="pln"> prompt</span><span class="pun">(</span><span class="pln">question</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">toLowerCase</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">"left"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"L"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">toLowerCase</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">"right"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"R"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Invalid direction: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> look</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">promptDirection</span><span class="pun">(</span><span class="str">"Which way?"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">"L"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"a house"</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"two angry bears"</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"You see"</span><span class="pun">,</span><span class="pln"> look</span><span class="pun">());</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Something went wrong: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> error</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُستخدم كلمة <code>throw</code> المفتاحية لرفع الاعتراضات، وتُلتقط بتغليف جزء من الشيفرة في كتلة <code>try</code>، متبوعةً بكلمة <code>catch</code> المفتاحية، وحين تتسبب الشيفرة التي في كتلة <code>try</code> في رفع اعتراض، فستُقيَّم كتلة <code>catch</code> مع ربط الاسم الموجود بين أقواس بقيمة الاعتراض، وإذا انتهت كتلة <code>catch</code>، أو <code>try</code> دون مشاكل، فسيتابع البرنامج سيره أسفل تعليمة <code>try/catch</code> بكاملها.
</p>

<p>
	استخدمنا في هذه الحالة باني<code>Error</code> لإنشاء قيمة الاعتراض الخاصة بنا، وهو باني جافاسكربت قياسي ينشئ كائنًا مع خاصية <code>message</code>، كما تَجمع نُسَخ هذا الباني في أغلب بيئات جافاسكربت معلومات عن مكدس الاستدعاء الذي كان موجودًا عند إنشاء الاعتراض فيما يسمى بتعقب المكدس stack trace، وتخزن هذه المعلومات في خاصية <code>stack</code>، كما يمكن الاستفادة منها حين محاولة تصحيح مشكلة ما، إذ تخبرنا بالدالة التي حدثت فيها المشكلة وأي الدوال نفّذت هذه الاستدعاء الفاشل.
</p>

<p>
	تتجاهل دالة <code>look</code> احتمال أن <code>promptDirection</code> قد تخطئ، وهذه مزية كبيرة للاعتراضات، إذ لا تكون شيفرة معالجة الخطأ ضروريةً إلا عند النقطة التي يحدث فيها الخطأ وعند النقطة التي يعالَج فيها؛ أما الدوال التي بين ذلك فلا تكاد تكون مهمةً.
</p>

<h2>
	التنظيف وراء الاعتراضات
</h2>

<p>
	يُعَدّ تأثير الاعتراض نوعًا آخرًا من تدفق التحكم، فكل حدث يسبب اعتراض -وهو كل استدعاء دالة تقريبًا وكل وصول لخاصية- قد يجعل التحكم يترك شيفرتك فجأة.
</p>

<p>
	فإن كانت الشيفرة بها عدة آثار جانبية، فقد يمنع اعتراض ما بعض تلك الآثار من الحدوث، حتى لو كان تدفق التحكم المنتظم لها يشير إلى احتمال حدوثها كلها، فمثلًا، انظر إلى المثال التالي لشيفرة مصرفية سيئة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_27" style=""><span class="kwd">const</span><span class="pln"> accounts </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  a</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln">
  b</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">
  c</span><span class="pun">:</span><span class="pln"> </span><span class="lit">20</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> getAccount</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let accountName </span><span class="pun">=</span><span class="pln"> prompt</span><span class="pun">(</span><span class="str">"Enter an account name"</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">accounts</span><span class="pun">.</span><span class="pln">hasOwnProperty</span><span class="pun">(</span><span class="pln">accountName</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(`</span><span class="typ">No</span><span class="pln"> such account</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">accountName</span><span class="pun">}`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> accountName</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> transfer</span><span class="pun">(</span><span class="pln">from</span><span class="pun">,</span><span class="pln"> amount</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">accounts</span><span class="pun">[</span><span class="pln">from</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> amount</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  accounts</span><span class="pun">[</span><span class="pln">from</span><span class="pun">]</span><span class="pln"> </span><span class="pun">-=</span><span class="pln"> amount</span><span class="pun">;</span><span class="pln">
  accounts</span><span class="pun">[</span><span class="pln">getAccount</span><span class="pun">()]</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> amount</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تحوِّل دالة <code>transfer</code> مبلغًا من المال من حساب ما إلى حساب آخر، مع طلب اسم الحساب الآخر أثناء التحويل، وإذا أُدخل اسم حساب غير صالح، فسترفع <code>getAccount</code> اعتراضًا.
</p>

<p>
	تنقل <code>transfer</code> المال أولًا من الحساب الأول، ثم تستدعي <code>getAccount</code> قبل إضافة المال إلى حساب جديد، فإن توقف سير عملها بسبب رفع اعتراض، فسسيختفي المال ويضيع بين الحسابين!
</p>

<p>
	يمكن كتابة تلك الشيفرة بأسلوب أذكى من ذلك من خلال استدعاء <code>getAccount</code> قبل البدء بنقل المال مثلًا، لكن للأسف فقد تحدث المشاكل المشابهة لهذه المشكلة بطريقة غير واضحة، كما تكون أقل أثرًا من أن تُلاحظ بمجرد النظر، فحتى الدوال التي لا يُتوقع منها رفع اعتراضات، قد ترفعها في ظروف استثنائية أو حين تحتوي على خطأ المبرمج.
</p>

<p>
	إحدى الطرق التي يمكن معالجة ذلك بها، هي التقليل من استخدام الآثار الجانبية، باستخدام أسلوب برمجة يحسب القيم الجديدة بدلًا من تغيير البيانات الموجودة فعليًا، على سبيل المثال لا الحصر، فإذا توقف جزء من الشيفرة عن العمل أثناء إنشاء قيمة جديدة، فلن يرى أحد هذه القيمة غير الجاهزة، ولن نرى مشكلةً من الأساس.
</p>

<p>
	لكن هذا قد لا يكون عمليًا في كل مرة، لذا سننظر في ميزة أخرى في تعليمة <code>try</code>، إذ يمكن أن تُتبَع بكتلة <code>finally</code> بدلًا من كتلة <code>catch</code> أو بالإضافة إليها، وتقول كتلة <code>finally</code> "شغِّل هذه الشيفرة مهما حدث، وذلك بعد محاولة تشغيل الشيفرة في كتلة <code>try</code>". انظر كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_29" style=""><span class="kwd">function</span><span class="pln"> transfer</span><span class="pun">(</span><span class="pln">from</span><span class="pun">,</span><span class="pln"> amount</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">accounts</span><span class="pun">[</span><span class="pln">from</span><span class="pun">]</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> amount</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  let progress </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    accounts</span><span class="pun">[</span><span class="pln">from</span><span class="pun">]</span><span class="pln"> </span><span class="pun">-=</span><span class="pln"> amount</span><span class="pun">;</span><span class="pln">
    progress </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    accounts</span><span class="pun">[</span><span class="pln">getAccount</span><span class="pun">()]</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> amount</span><span class="pun">;</span><span class="pln">
    progress </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> finally </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">progress </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      accounts</span><span class="pun">[</span><span class="pln">from</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> amount</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تتتبع هذه النسخة من الدالة مدى تقدمها وسيرها، وإذا تسبب في حالة غير مستقرة للبرنامج عند خروجها، فإنها تصلح أثر هذا الخلل الذي أحدثته.
</p>

<p>
	لاحظ أن شيفرة <code>finally</code> رغم تشغيلها عند رفع اعتراض في كتلة <code>try</code>، إلا أنها لا تتدخل في الاعتراض نفسه، وعليه فإن المكدس سيستمر في تفكيك نفسه بعد تشغيل كتلة <code>finally</code>.
</p>

<p>
	كتابة مثل هذه البرامج التي تعمل بكفاءة ويعتمد عليها حتى في حالات ظهور اعتراضات في أماكن غير متوقعة أمر صعب وليس سهلًا.
</p>

<p>
	لا يبالي الكثير من الناس بهذا، فقد لا تحدث المشكلة إلا نادرًا جدًا بحيث لا يمكن ملاحظتها، وذلك بسبب عدم ظهور الاعتراضات إلا في الحالات الاستثنائية، وإذا أردنا القول بأن هذا شيء جيد أو سيء جدًا، فيجب النظر أولًا إلى الأثر والتخريب الذي يحدث عند فشل البرنامج أو تعطله.
</p>

<h2>
	الالتقاط الانتقائي
</h2>

<p>
	تعالج البيئة الاعتراض الذي يمر في المكدس كله دون التقاطه، ويختلف هنا ما يحدث باختلاف البيئة نفسها، ففي المتصفحات مثلًا، يُكتب وصف الخطأ إلى طرفية جافاسكربت والتي يمكن الوصول إليها من خلال أدوات المتصفح أو قائمة المطورين Developers Menu؛ أما في Node.js فستكون بيئة جافاسكربت الغير موجودة في متصفح والتي سنناقشها في مقال قادم، أكثر حذرًا بشأن تدمير البيانات، فتُخرِج العملية كلها عند حدوث اعتراض غير معالَج unhandled.
</p>

<p>
	بالنسبة لأخطاء المبرمجين، فأفضل شيء يمكن فعله هو ترك الخطأ يمر بسلام دون لمسه، كما يمثل الاعتراض الغير معالَج طريقةً معقولةً ومنطقيةً للإشارة إلى وجود عطل في البرنامج، وستعطيك طرفية جافاسكربت في المتصفحات الحديثة بعض المعلومات عن استدعاءات الدالة التي كانت في المكدس حين حدوث المشكلة؛ أما بالنسبة للمشاكل المتوقع حدوثها أثناء الاستخدام العادي، فسيُنبِئ توقف البرنامج بسبب اعتراض غير معالَج استراتيجيةً سيئةً جدًا.
</p>

<p>
	كما تتسبب الاستخدامات غير الصحيحة للغة مثل الإشارة المرجعية لرابطة غير موجودة، أو البحث عن خاصية في <code>null</code>، أو استدعاء شيء غير الدوال، في رفع اعتراضات، كما يمكن التقاط تلك الاعتراضات.
</p>

<p>
	كل ما نستطيع معرفته عند دخول متن <code>catch</code> هو أن شيئًا ما داخل متن <code>try</code> قد تسبب في رفع اعتراض، لكن لا نستطيع معرفة ماهية الاعتراض نفسه أو ما فعله.
</p>

<p>
	لا توفر جافاسكربت -في إغفال صارخ- دعمًا مباشرًا لاعتراضات الالتقاط الانتقائي، فإما تلتقطها كلها أو لا تدرك منها شيئًا، وقد يحلو للمرء افتراض أن الاعتراض الذي حصل عليه هو الذي كان يفكر فيه ويتوقعه حين كتب كتلة <code>catch</code>، بسبب هذا الخطأ في اللغة، لكن سيخبرك الواقع أنه قد لا يحدث هذا دومًا معك، فلعله تم اختراق افتراض آخر، أو لعلك تسببت في زلة أحدثت اعتراض؛ ويحاول المثال التالي استدعاء <code>promptDirection</code> إلى أن يحصل على إجابة صالحة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_31" style=""><span class="kwd">for</span><span class="pln"> </span><span class="pun">(;;)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let dir </span><span class="pun">=</span><span class="pln"> promtDirection</span><span class="pun">(</span><span class="str">"Where?"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ← typo!</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"You chose "</span><span class="pun">,</span><span class="pln"> dir</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Not a valid direction. Try again."</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُعَدّ بُنية <code>(;;)for</code> طريقةً متعمَّدة لإنشاء حلقة تكرارية لا تنهي نفسها، ولا نخرج منها إلا حين حصولنا على اتجاه صالح، لكننا أخطأنا في تهجئة <code>promptDirection</code> التي أعطتنا الخطأ "متغير غير معرَّف" undefined variable، وتتعامل كتلة <code>catch</code> تعاملًا خاطئًا مع خطأ الرابطة على أنه مؤشر إدخال غير صالح، وذلك بسبب تجاهلها قيمة اعتراضها (<code>e</code>) مفترضةً أنها تعرف المشكلة، كما لا يتسبب هذا في حلقة لا نهائية فحسب، بل يدفن رسالة الخطأ المفيدة التي نريدها عن الرابطة التي أُخطئ في هجائها.
</p>

<p>
	تقول القاعدة العامة لا تَلتقط الاعتراضات التقاطًا كليًا إلا إذا كان بغرض توجيهها إلى مكان ما مثل توجيهها عبر الشبكة مثلًا لإخبار نظام آخر بتعطل برنامجنا، وعليك التفكير مليًا حتى حينئذ حول كيفية إخفاء المعلومات، وعلى ذلك فإننا نريد التقاط نوع محدد من الاعتراضات من خلال التحقق داخل كتلة <code>catch</code> مما إذا كان الاعتراض الذي حصلنا عليه هو الذي نريده أم لا، وإن لم يكن فنعيد رفعه، لكن كيف نعرف الاعتراض الذي نريده أصلًا؟
</p>

<p>
	نستطيع موازنة خاصية <code>message</code> التابعة له برسالة الخطأ التي نتوقعها، لكن ليست هذه هي الطريقة المثلى للبرمجة، ففعلنا هذا ما هو إلا استخدام لمعلومات مخصصة لاطلاع العنصر البشري عليها أي الرسالة، وذلك لبناء قرار برمجي على هذه المعلومات، فإذا غير أحد هذه الرسالة أو ترجمها، فستتعطل الشيفرة مرةً أخرى، والحل البديل هو تعريف نوع جديد من الأخطاء واستخدام <code>instanceof</code> لتعريفه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_33" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">InputError</span><span class="pln"> extends </span><span class="typ">Error</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> promptDirection</span><span class="pun">(</span><span class="pln">question</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let result </span><span class="pun">=</span><span class="pln"> prompt</span><span class="pun">(</span><span class="pln">question</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">toLowerCase</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">"left"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"L"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">result</span><span class="pun">.</span><span class="pln">toLowerCase</span><span class="pun">()</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">"right"</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"R"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">InputError</span><span class="pun">(</span><span class="str">"Invalid direction: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يوسع صنف الخطأ الجديد <code>Error</code>، حيث لا يعرّف بانيًا خاصًا به، مما يعني أنه يرث باني <code>Error</code> الذي يتوقع رسالة نصية على أساس وسيط، كما لا يعرِّف أي شيء أصلًا، وهو -أي الصنف- فارغ.
</p>

<p>
	تتصرف كائنات <code>InputError</code> مثل كائنات <code>Error</code> باستثناء امتلاكها لصنف مختلف يمكننا تمييزها به، وتستطيع الحلقة التكرارية الآن التقاط هؤلاء بطريقة أكثر حذرًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_35" style=""><span class="kwd">for</span><span class="pln"> </span><span class="pun">(;;)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let dir </span><span class="pun">=</span><span class="pln"> promptDirection</span><span class="pun">(</span><span class="str">"Where?"</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"You chose "</span><span class="pun">,</span><span class="pln"> dir</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e instanceof </span><span class="typ">InputError</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Not a valid direction. Try again."</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">throw</span><span class="pln"> e</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وهذا سيلتقط نُسخًا من <code>InputError</code> فقط ويترك الاعتراضات غير المرتبطة به تمر، فإذا أعدت إدخال خطأ الهجاء مرةً أخرى، فسيبَلَّغ عن خطأ رابطة غير معرَّفة هذه المرة.
</p>

<h2>
	التوكيدات Assertions
</h2>

<p>
	التوكيدات هي عمليات تحقق داخل البرنامج، حيث تنظر هل الشيء موجود على الصورة التي يفترض به أن يكون عليها أم لا، وتُستخدَم للبحث عن أخطاء المبرمجين، وليس لمعالجة مواقف يمكن حدوثها في التشغيل العادي، فمثلًا، إذا وُصف <code>firstElement</code> على أساس دالة لا يمكن استدعاؤها على مصفوفة فارغة، فربما نكتبها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_37" style=""><span class="kwd">function</span><span class="pln"> firstElement</span><span class="pun">(</span><span class="pln">array</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">array</span><span class="pun">.</span><span class="pln">length </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"firstElement called with []"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> array</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستبعثِر الشيفرة السابقة برنامجك بمجرد إساءة استخدامها، بدلًا من إعادة <code>undefined</code> بصمت مثل التي تحصل عليها حين تقرأ خاصية مصفوفة غير موجودة، ويجعل ذلك من الصعب لمثل تلك الأخطاء المرور دون ملاحظتها من أحد، وأسهل في معرفة سببها عند وقوعها.
</p>

<p>
	لكن هذا لا يعني أننا ننصح بكتابة توكيدات لكل نوع من المدخلات الخاطئة، فهذا عمل كثير جدًا، كما سينشئ لنا شيفرة غير صافية وملأى بأكواد غير ضرورية، بل احفظ هذه التوكيدات من أجل أخطاء يسهل ارتكابها، أو أخطاء تقع فيها بنفسك.
</p>

<h2>
	خاتمة
</h2>

<p>
	المدخلات الخاطئة والأخطاء عمومًا هي أشياء لازمة لنا في الحياة، ومن المهم في البرمجة العثور عليها، وتشخيصها، وإصلاحها، حيث ستظهر هنا في البرمجة على صورة زلات برمجية bugs. وقد تصير المشاكل أسهل في ملاحظتها إذا كان لديك حزمة اختبار مؤتمتة، أو إذا أضفت توكيدات إلى برامجك.
</p>

<p>
	مشكلة الأخطاء البرمجية موجودة بكل اللغات ومن المهم معرفتها والتعامل معها. يمكنك الاستعانة بالفيديو الآتي لمعرفة الأخطاء في مجال البرمجة عمومًا:
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="603" src="https://academy.hsoub.com/applications/core/interface/index.html" title="كيفية التعامل مع الأخطاء البرمجية" width="1072" data-embed-src="https://www.youtube.com/embed/Pgje6nWuDkg"></iframe>
</p>

<p>
	يجب معالجة المشاكل التي تحدث بسبب عوامل خارجة عن تحكم البرنامج بلطف وحكمة، فقد تكون القيم الخاصة المعادة هي الطريقة المثلى لتتبع هذه المشاكل ومعالجتها، وإلا فربما نود استخدام الاعتراضات.
</p>

<p>
	سيتسبب رفع الاعتراضات في فك مكدس الاستدعاء حتى يصل إلى كتلة <code>try/catch</code> أو إلى نهاية المكدس، وستُعطَى قيمة الاعتراض إلى كتلة <code>catch</code> التي تلتقطها، مما يؤكد لنا أن هذا هو نوع الاعتراض الذي نريده قبل إجراء أي فعل عليه، كما يمكن استخدام كتل <code>finally</code> للمساعدة في تحديد تدفقات التحكم غير المتوقعة التي تحدث بسبب الاعتراضات، وذلك من أجل التأكد من تشغيل جزء من الشيفرة بعد انتهاء الكتلة.
</p>

<h2>
	تدريبات
</h2>

<h3>
	Retry
</h3>

<p>
	لنقل أنه لديك دالة <code>primitiveMultiply</code> التي تضرب عددين معًا في 20 بالمائة من الحالات، وترفع اعتراضًا في الثمانين بالمائة الباقية من نوع <code>MultiplicatorUnitFailure</code>.
</p>

<p>
	اكتب دالة تغلف هذه الدالة وتظل تحاول حتى نجاح أحد الاستدعاءات، كما تعيد النتيجة بعد ذلك، وتأكد من معالجة الاعتراضات التي تريد معالجتها فقط.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a data-ss1625559345="1" href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_39" style=""><span class="kwd">class</span><span class="pln"> </span><span class="typ">MultiplicatorUnitFailure</span><span class="pln"> extends </span><span class="typ">Error</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> primitiveMultiply</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0.2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">*</span><span class="pln"> b</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MultiplicatorUnitFailure</span><span class="pun">(</span><span class="str">"Klunk"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> reliableMultiply</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// شيفرتك هنا.</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">reliableMultiply</span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">8</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 64</span></pre>

<p>
	<strong>إرشادات للحل</strong>
</p>

<p>
	يجب حدوث استدعاء <code>primitiveMultiply</code> داخل كتلة <code>try</code> قطعًا، ويجب على كتلة <code>catch</code> الموافقة لهذا رفع اعتراض مرةً أخرى إذا لم تكن نسخةً من <code>MultiplicatorUnitFailure</code>، كما تتأكد من إعادة محاولة الاستدعاء مرةً أخرى حين تكون نسخةً منها.
</p>

<p>
	استخدِم حلقةً تكراريةً لتكرار المحاولة، بحيث لا تتوقف إلا عند نجاح الاستدعاء، كما في مثال <code>look</code> الذي ذكرناه آنفًا في هذا المقال، أو استخدِم التعاودية recursion على أمل عدم الحصول على سلسلة نصية من الفشل لفترة طويلة بحيث تؤدي إلى طفحان المكدس، وهذا هو الخيار الآمن بالمناسبة.
</p>

<h3>
	الصندوق المغلق
</h3>

<p>
	انظر الكائن التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_42" style=""><span class="kwd">const</span><span class="pln"> box </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  locked</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
  unlock</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">locked </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  lock</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">locked </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">  </span><span class="pun">},</span><span class="pln">
  _content</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
  </span><span class="kwd">get</span><span class="pln"> content</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">locked</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Locked!"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">_content</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	ما هذا إلا صندوق به قفل، وهناك مصفوفة داخل الصندوق، حيث لا تستطيع الوصول إليها إلا حين يُفتح الصندوق، وأنت ممنوع من الوصول إلى خاصية <code>‎_content</code> الخاصة مباشرةً.
</p>

<p>
	اكتب دالة اسمها <code>withBoxUnlocked</code> تأخذ قيمة دالة على أساس وسيط، وتفتح الصندوق، كما تشغِّل الدالة، ثم تتأكد أن الصندوق مقفل مرةً أخرى قبل الإعادة، بغض النظر عن إعادة الدالة الوسيطة بطريقة طبيعية أو رفع اعتراض.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a data-ss1625559345="1" href="https://codepen.i" rel="external nofollow">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3845_44" style=""><span class="kwd">const</span><span class="pln"> box </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  locked</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
  unlock</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">locked </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  lock</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">locked </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">  </span><span class="pun">},</span><span class="pln">
  _content</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[],</span><span class="pln">
  </span><span class="kwd">get</span><span class="pln"> content</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">locked</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Locked!"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">_content</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> withBoxUnlocked</span><span class="pun">(</span><span class="pln">body</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// شيفرتك هنا.</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

withBoxUnlocked</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  box</span><span class="pun">.</span><span class="pln">content</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="str">"gold piece"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  withBoxUnlocked</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(</span><span class="str">"Pirates on the horizon! Abort!"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Error raised: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> e</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">box</span><span class="pun">.</span><span class="pln">locked</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	للمزيد من النقاط، حين يكون الصندوق مغلقًا، تأكد من بقائه مغلقًا إذا استدعيت <code>withBoxUlocked</code>.
</p>

<p>
	<strong>إرشادات للحل</strong>
</p>

<p>
	يستدعي هذا التدريب كتلة <code>finally</code>، ويجب على الدالة فتح الصندوق أولًا، ثم تستدعي الدالة الوسيطة من داخل متن كتلة <code>try</code>، وبعدها تقوم كتلة <code>finally</code> التي تليها بغلق الصندوق.
</p>

<p>
	للتأكد من عدم إغلاق الصندوق إذا لم يكن مغلقًا من البداية، تحقق من إغلاقه عند بدء الدالة، ولا تفتحه وتغلقه إلا إذا كان مغلقًا في البداية.
</p>

<p>
	ترجمة -بتصرف- <a data-ss1625559345="1" href="https://eloquentjavascript.net/08_error.html" rel="external nofollow">للفصل الثامن من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق:<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%8A-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%B1%D8%AC%D9%84-%D8%A2%D9%84%D9%8A-%D8%B1%D9%88%D8%A8%D9%88%D8%AA-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1244/" rel=""> مشروع تطبيقي لبناء رجل آلي (روبوت) عبر جافاسكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1%D8%8C-%D8%AC%D8%B1%D8%A8-%D8%A7%D9%84%D8%AA%D9%82%D8%B7-trycatch-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r908/" rel="">التعامل مع الأخطاء، جرب... التقط try..catch في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%A8%D9%88%D8%A7%D9%86%D9%8A-%D9%88%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-object-initialization-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1109/" rel="">البواني وتهيئة الكائنات Object Initialization في جافا</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1245</guid><pubDate>Mon, 28 Jun 2021 15:03:00 +0000</pubDate></item><item><title>&#x647;&#x62C;&#x645;&#x627;&#x62A; &#x627;&#x644;&#x627;&#x62E;&#x62A;&#x637;&#x627;&#x641; &#x628;&#x627;&#x644;&#x646;&#x642;&#x631; Clickjacking &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D9%87%D8%AC%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%B7%D8%A7%D9%81-%D8%A8%D8%A7%D9%84%D9%86%D9%82%D8%B1-clickjacking-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1254/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_06/60dcaa92256ff_-.png.297bf6596b1f4ad207fe8b777d41aa5f.png" /></p>

<p>
	يسمح "هجوم الاختطاف بالنقر clickjacking" للصفحات المشبوهة بأن تنقر ضمن الصفحة المستهدفة نيابةً عن الزائر. وقد اختُرقت الكثير من الصفحات بهذه الطريقة، بما في ذلك تويتر وفيسبوك وبيبال وغيرها، وبالطبع أصلحت الثغرات التي سببت الهجوم على تلك المواقع.
</p>

<h2>
	فكرة الهجوم
</h2>

<p>
	إن فكرة الهجوم بسيطة جدًا، فقد كانت طريقة الهجوم على فيسبوك مثلًا كما يلي:
</p>

<ol>
<li>
		جُذب المستخدم إلى الصفحة المشبوهة بطريقة ما.
	</li>
	<li>
		احتوت الصفحة رابطًا لا يبدو خطرًا بعناوين، مثل: "طريقك إلى الثروة"، أو "انقر هنا، أمر مضحك جدًا".
	</li>
	<li>
		وضعت الصفحة المشبوهة فوق هذا الرابط نافذةً ضمنيةً <code>&lt;iframe&gt;</code> شفافة ترتبط خاصية <code>src</code> فيه بالموقع "facebook.com"، بحيث ظهر زر "أعجبني" فوق الرابط مباشرةً، وعادة يُنفَّذ ذلك باستخدام الخاصية <code>z-index</code>.
	</li>
	<li>
		وبالتالي نقر الزائر على الزر عندما حاول النقر على الرابط.
	</li>
</ol>
<h2>
	مثال توضيحي
</h2>

<p>
	ستبدو الصفحة المشبوهة كالتالي، مع الانتباه إلى أنّ النافذة الضمنية <code>&lt;iframe&gt;</code> هنا نصف شفافة،أما في الصفحات المشبوهة فستكون النافذة الضمنية شفافةً تمامًا:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2868_12" style="">
<span class="tag">&lt;style&gt;</span><span class="pln">
iframe </span><span class="pun">{</span><span class="pln"> </span><span class="com">/* النافذة الضمنية من الصفحة الضحية */</span><span class="pln">
  width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">400px</span><span class="pun">;</span><span class="pln">
  height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100px</span><span class="pun">;</span><span class="pln">
  position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
  top</span><span class="pun">:</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> left</span><span class="pun">:-</span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
  opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.5</span><span class="pun">;</span><span class="pln"> </span><span class="com">/* الشفافية 0 في الصفحة المشبوهة */</span><span class="pln">
  z</span><span class="pun">-</span><span class="pln">index</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="tag">&lt;/style&gt;</span><span class="pln">

</span><span class="tag">&lt;div&gt;</span><span class="pln">Click to get rich now:</span><span class="tag">&lt;/div&gt;</span><span class="pln">

</span><span class="com">&lt;!-- العنوان على الصفحة الضحية --&gt;</span><span class="pln">
</span><span class="tag">&lt;iframe</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"/clickjacking/facebook.html"</span><span class="tag">&gt;&lt;/iframe&gt;</span><span class="pln">

</span><span class="tag">&lt;button&gt;</span><span class="pln">Click here!</span><span class="tag">&lt;/button&gt;</span><span class="pln">

</span><span class="tag">&lt;div&gt;</span><span class="pln">...And you're cool (I'm a cool hacker actually)!</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634983128="1" data-ss1634983372="1" frameborder="no" height="179" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/dyzNMJa?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-The-clickjacking-attack-ex1">See the Pen JS-P3-01-The-clickjacking-attack-ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يحوي الملف "facebook.html" الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2868_14" style="">
<span class="dec">&lt;!DOCTYPE HTML&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">

</span><span class="tag">&lt;body</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">margin</span><span class="pun">:</span><span class="lit">10px</span><span class="pun">;</span><span class="pln">padding</span><span class="pun">:</span><span class="lit">10px</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"button"</span><span class="pln"> </span><span class="atn">onclick</span><span class="pun">=</span><span class="atv">"</span><span class="pln">alert</span><span class="pun">(</span><span class="str">'Like pressed on facebook.html!'</span><span class="pun">)</span><span class="atv">"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"I LIKE IT !"</span><span class="tag">&gt;</span><span class="pln">

</span><span class="tag">&lt;/body&gt;</span><span class="pln">

</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	ويحوي الملف "index.html" الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2868_16" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">

</span><span class="tag">&lt;head&gt;</span><span class="pln">
  </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">

</span><span class="tag">&lt;body&gt;</span><span class="pln">

  </span><span class="tag">&lt;style&gt;</span><span class="pln">
    iframe </span><span class="pun">{</span><span class="pln">
      width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">400px</span><span class="pun">;</span><span class="pln">
      height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100px</span><span class="pun">;</span><span class="pln">
      position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
      top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5px</span><span class="pun">;</span><span class="pln">
      left</span><span class="pun">:</span><span class="pln"> </span><span class="pun">-</span><span class="lit">14px</span><span class="pun">;</span><span class="pln">
      opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.5</span><span class="pun">;</span><span class="pln">
      z</span><span class="pun">-</span><span class="pln">index</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="tag">&lt;/style&gt;</span><span class="pln">

  </span><span class="tag">&lt;div&gt;</span><span class="pln">Click to get rich now:</span><span class="tag">&lt;/div&gt;</span><span class="pln">

  </span><span class="com">&lt;!-- The url from the victim site --&gt;</span><span class="pln">
  </span><span class="tag">&lt;iframe</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"facebook.html"</span><span class="tag">&gt;&lt;/iframe&gt;</span><span class="pln">

  </span><span class="tag">&lt;button&gt;</span><span class="pln">Click here!</span><span class="tag">&lt;/button&gt;</span><span class="pln">

  </span><span class="tag">&lt;div&gt;</span><span class="pln">...And you're cool (I'm a cool hacker actually)!</span><span class="tag">&lt;/div&gt;</span><span class="pln">

</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	ستظهر النتيجة كالتالي:
</p>

<p>
	<iframe __idm_frm__="138" class="code-tabs__result" data-ss1634983128="1" data-ss1634983372="1" src="https://javascript.info/article/clickjacking/clickjacking-visible/" style="display: block; border: 0px; width: 725px; height: 142px;"></iframe>
</p>

<p style="text-align: center;">
	تظهر النافذة الضمنية <code>&lt;"iframe src="facebook.html&gt;</code> في المثال السابق نصف شفافة، بحيث يمكن رؤيتها عند تحريك المؤشر فوق الزر، وعند النقر على الزر فإننا ننقر في الواقع على النافذة الضمنية التي ستكون مخفيّةً كونها شفافةً تمامًا. فإن أمكن للمستخدم الوصول إلى حسابه مباشرةً دون الحاجة لتسجيل الدخول (عندما تكون ميزة "تذكرني remember me" مفعلة)، فسيضيف النقر على النافذة الضمنية إعجابًا Like، بينما سينقر في تويتر على زر المتابعة Follow.
</p>

<p>
	إليك نتيجة المثال السابق لكن بواقعية أكثر عندما نسند القيمة "0" إلى الخاصية <code>opacity</code>:
</p>

<p>
	<iframe __idm_frm__="139" class="code-tabs__result" data-ss1634983128="1" data-ss1634983372="1" src="https://javascript.info/article/clickjacking/clickjacking/" style="display: block; border: 0px; width: 725px; height: 142px;"></iframe>كل ما يتطلبه شن الهجوم هو وضع النافذة الضمنية <code style="font-size: 16px;">iframe</code> في الصفحة المشبوهة بطريقة تجعل الزر فوق الرابط مباشرةً، وبالتالي سيضغط الزائر الزر بدلًا من الرابط، ويُنجز ذلك عادة باستخدام لغة الأنماط الانسيابية <a data-ss1634983128="1" data-ss1634983372="1" href="https://academy.hsoub.com/files/14-%D8%A7%D9%84%D8%AA%D8%AD%D8%B1%D9%8A%D9%83-%D8%B9%D8%A8%D8%B1-css/" rel="" style="background-color: rgb(255, 255, 255);">CSS</a>.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		يُشن هذا الهجوم بالنقر بزر الفأرة وليس بلوحة المفاتيح، حيث يستفيد هذا الهجوم من نقرات الفأرة، أو ما يشابهها من ناحية لمس الشاشة في الأجهزة المحمولة، أينما تصعب إعادة توجيه الأفعال المرتبطة بالضغط على أزرار لوحة المفاتيح. فلو أردنا من الناحية التقنية اختطاف حقل نصي، فلا بدّ من وضع النافذة الضمنية iframe داخل حقل نص بحيث يتداخل الحقلان، وعندما يحاول المستخدم نقل التركيز إلى الحقل الذي يراه ليبدأ الكتابة، فسييُنقَل إلى الحقل النصي الموجود داخل النافذة الضمنية وعندها تظهر المشكلة، حيث أن كل ما يكتبه الزائر سيكون مخفيًا لأن النافذة الضمنية مخفية، وسيتوقف الزائر عن الكتابة إن لم ير ما يكتبه ضمن الحقل النصي.
	</p>
</blockquote>

<h2>
	الأسلوب الدفاعي للمدرسة التقليدية
</h2>

<p>
	يعود جزء من الآلية الدفاعية القديمة إلى <a data-ss1634983128="1" data-ss1634983372="1" href="https://wiki.hsoub.com/JavaScript" rel="external">JavaScript</a> التي تمنع فتح الصفحة في نافذة ضمنية، وهو ما يُعرف بكسر الإطار framebusting، ويبدو الأمر كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2868_22" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">top </span><span class="pun">!=</span><span class="pln"> window</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  top</span><span class="pun">.</span><span class="pln">location </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">location</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بهذه الشيفرة تتحقق النافذة من كونها الأعلى ترتيبًا بين العناصر، فإن لم تكن كذلك فستجعل نفسها الأعلى تلقائيًا، وهذا أسلوب ضعيف في الدفاع يمكن الالتفاف عليه بسهولة، وسنطلع تاليًا على عدة أساليب أخرى.
</p>

<h3>
	منع الانتقال إلى الأعلى
</h3>

<p>
	يُمكن منع الانتقال الناتج عن تغيير قيمة الخاصية <code>top.location</code> في معالج الحدث <a data-ss1634983128="1" data-ss1634983372="1" href="https://javascript.info/onload-ondomcontentloaded#window.onbeforeunload" rel="external nofollow">beforeunload</a>. حيث ستحدد الصفحة الموجودة في الأعلى (مرفقة بالصفحة التي تعود للمتسلل) معالجًا يمنع الانتقال إليها كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2868_20" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">onbeforeunload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	عندما تحاول النافذة الضمنية تغيير قيمة <code>top.location</code>، فستظهر رسالة للزائر تسأله إن كان يريد مغادرة الصفحة، وفي معظم الحالات سيرفض الزائر ذلك لأنه يجهل تمامًا وجود هذه النافذة، وكل ما يراه هي الصفحة العليا ولا سبب لمغادرتها، لذا لن تتغير عندها قيمة <code>top.location</code> خلال العملية.
</p>

<p>
	سنرى في المثال التالي تنفيذ الفكرة عمليًا، حيث يحتوي الملف "iframe.html" الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2868_25" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">

</span><span class="tag">&lt;head&gt;</span><span class="pln">
  </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">

</span><span class="tag">&lt;body&gt;</span><span class="pln">

  </span><span class="tag">&lt;div&gt;</span><span class="pln">Changes top.location to javascript.info</span><span class="tag">&lt;/div&gt;</span><span class="pln">

  </span><span class="tag">&lt;script&gt;</span><span class="pln">
    top</span><span class="pun">.</span><span class="pln">location </span><span class="pun">=</span><span class="pln"> </span><span class="str">'https://javascript.info'</span><span class="pun">;</span><span class="pln">
  </span><span class="tag">&lt;/script&gt;</span><span class="pln">

</span><span class="tag">&lt;/body&gt;</span><span class="pln">

</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	ويحتوي الملف "index.html" الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2868_27" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">

</span><span class="tag">&lt;head&gt;</span><span class="pln">
  </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;style&gt;</span><span class="pln">
    iframe </span><span class="pun">{</span><span class="pln">
      width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">400px</span><span class="pun">;</span><span class="pln">
      height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100px</span><span class="pun">;</span><span class="pln">
      position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
      top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      left</span><span class="pun">:</span><span class="pln"> </span><span class="pun">-</span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
      opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      z</span><span class="pun">-</span><span class="pln">index</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="tag">&lt;/style&gt;</span><span class="pln">

  </span><span class="tag">&lt;script&gt;</span><span class="pln">
    </span><span class="kwd">function</span><span class="pln"> attack</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

      window</span><span class="pun">.</span><span class="pln">onbeforeunload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        window</span><span class="pun">.</span><span class="pln">onbeforeunload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Want to leave without learning all the secrets (he-he)?"</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">};</span><span class="pln">

      document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">insertAdjacentHTML</span><span class="pun">(</span><span class="str">'beforeend'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'&lt;iframe src="iframe.html"&gt;'</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="tag">&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">

</span><span class="tag">&lt;body&gt;</span><span class="pln">

  </span><span class="tag">&lt;p&gt;</span><span class="pln">After a click on the button the visitor gets a "strange" question about whether they want to leave.</span><span class="tag">&lt;/p&gt;</span><span class="pln">

  </span><span class="tag">&lt;p&gt;</span><span class="pln">Probably they would respond "No", and the iframe protection is hacked.</span><span class="tag">&lt;/p&gt;</span><span class="pln">

  </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">onclick</span><span class="pun">=</span><span class="atv">"</span><span class="pln">attack</span><span class="pun">()</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">Add a "protected" iframe</span><span class="tag">&lt;/button&gt;</span><span class="pln">

</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	وستكون النتيجة كما يلي:
</p>

<p>
	<iframe __idm_frm__="140" class="code-tabs__result" data-ss1634983128="1" data-ss1634983372="1" src="https://javascript.info/article/clickjacking/top-location/" style="display: block; border: 0px; width: 725px; height: 182px;"></iframe>
</p>

<h3>
	الخاصية Sandbox
</h3>

<p>
	تقيِّد الخاصية <code>sandbox</code> عدة أشياء منها عملية التنقل، حيث لا يمكن للنافذة الضمنية المقيّدة تغيير قيمة <code>top.location</code>، ولذلك يمكننا استخدام هذه الخاصية لتخفيف القيود بالشكل <code>sandbox="allow-scripts allow-forms"</code>، وهكذا سنسمح باستخدام النماذج والسكربتات، لكن في الوقت نفسه لا نسمح بالتنقل عندما لا نضيف الخيار <code>allow-top-navigation</code>، وبالتالي سنمنع تغيير القيمة <code>top.location</code>.
</p>

<p>
	والشيفرة المستخدمة في ذلك هي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2868_29" style="">
<span class="pun">&lt;</span><span class="pln">iframe sandbox</span><span class="pun">=</span><span class="str">"allow-scripts allow-forms"</span><span class="pln"> src</span><span class="pun">=</span><span class="str">"facebook.html"</span><span class="pun">&gt;&lt;/</span><span class="pln">iframe</span><span class="pun">&gt;</span></pre>

<p>
	ويمكن طبعًا الالتفاف على أسلوب الحماية البسيط هذا.
</p>

<h2>
	خيارات X-Frame
</h2>

<p>
	يمكن للترويسة <code>X-Frame-Options</code> المتعلقة بالواجهة الخلفية أن تسمح بعرض صفحة ضمن نافذة ضمنية أو تمنعه، كما ينبغي أن تُرسَل على شكل ترويسة HTTP حصرًا، إذ سيتجاهلها المتصفح إن وجدها ضمن العنصر <code>meta</code>، أي ستكون الشيفرة التالية بلا فائدة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9618_8" style="">
<span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">http-equiv</span><span class="pun">=</span><span class="atv">"X-Frame-Options"</span><span class="tag">&gt;</span></pre>

<p>
	يمكن أن تأخذ الترويسة إحدى القيم التالية:
</p>

<ul>
<li>
		<code>DENY</code>: لن تظهر الصفحة ضمن نافذة ضمنية أبدًا.
	</li>
	<li>
		<code>SAMEORIGIN</code>: يمكن أن تُعرَض الصفحة ضمن نافذة ضمنية إن كان للصفحة الأم وللنافذة الضمنية الأصل نفسه.
	</li>
	<li>
		<code>ALLOW-FROM domain</code>: يمكن أن تُعرَض الصفحة داخل نافذة ضمنية إن انتمت الصفحة الأم إلى نطاق محدد سلفًا.
	</li>
</ul>
<p>
	مثلًا: يستخدم موقع تويتر الخيار <code>X-Frame-Options: SAMEORIGIN</code> بحيث تكون النتيجة نافذةً ضمنيةً فارغةـ وتكون النتيجة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2868_35" style="">
<span class="pun">&lt;</span><span class="pln">iframe src</span><span class="pun">=</span><span class="str">"https://twitter.com"</span><span class="pun">&gt;&lt;/</span><span class="pln">iframe</span><span class="pun">&gt;</span></pre>

<p>
	<iframe data-ss1634983128="1" data-ss1634983372="1" src="https://twitter.com"></iframe>
</p>

<p>
	ستكون النتيجة حسب المتصفح المستخدَم، فقد تكون نافذةً ضمنيةً فارغةً، أو تنبيهًا للمستخدم بأنّ المتصفح لم يسمح للصفحة أن تُعرض بهذه الطريقة.
</p>

<h2>
	إظهار النافذة بوظيفة معطلة
</h2>

<p>
	للترويسة <code>X-Frame-Options</code> تأثير جانبي، حيث لن تتمكن الصفحات الأخرى من عرض صفحتنا ضمن نافذة ضمنية حتى لو كان ذلك لسبب وجيه، لذلك سنجد حلولًا أخرى مثل تغطية الصفحة بعنصر <code>&lt;div&gt;</code> له التنسيق: <code>height: 100%; width: 100%;‎</code>، وعندها سيعترض كل نقرات الفأرة، ثم يُزال هذا العنصر عندما يتحقق الشرط <code>window == top</code>، أو عندما لا نجد مبررًا لحماية الصفحة.
</p>

<p>
	سيظهر الأمر كالتالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2868_37" style="">
<span class="tag">&lt;style&gt;</span><span class="pln">
  </span><span class="com">#protector {</span><span class="pln">
    height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
    width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
    position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
    left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    z</span><span class="pun">-</span><span class="pln">index</span><span class="pun">:</span><span class="pln"> </span><span class="lit">99999999</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="tag">&lt;/style&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"protector"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"/"</span><span class="pln"> </span><span class="atn">target</span><span class="pun">=</span><span class="atv">"_blank"</span><span class="tag">&gt;</span><span class="pln">Go to the site</span><span class="tag">&lt;/a&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  </span><span class="com">// there will be an error if top window is from the different origin</span><span class="pln">
</span><span class="com">// سيحدث خطأ إذا كانت النافذة العليا من مصدر مختلف، لكن هنا لا مشكلة</span><span class="pln">
  </span><span class="com">// but that's ok here</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">top</span><span class="pun">.</span><span class="pln">document</span><span class="pun">.</span><span class="pln">domain </span><span class="pun">==</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">domain</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    protector</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	يوضح المثال التالي ما فعلناه، حيث يحتوي الملف "iframe.html" على الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2868_39" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">

</span><span class="tag">&lt;head&gt;</span><span class="pln">
  </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">

  </span><span class="tag">&lt;style&gt;</span><span class="pln">
    </span><span class="com">#protector {</span><span class="pln">
      height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
      width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
      position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
      left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      z</span><span class="pun">-</span><span class="pln">index</span><span class="pun">:</span><span class="pln"> </span><span class="lit">99999999</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="tag">&lt;/style&gt;</span><span class="pln">

</span><span class="tag">&lt;/head&gt;</span><span class="pln">

</span><span class="tag">&lt;body&gt;</span><span class="pln">

</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"protector"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"/"</span><span class="pln"> </span><span class="atn">target</span><span class="pun">=</span><span class="atv">"_blank"</span><span class="tag">&gt;</span><span class="pln">Go to the site</span><span class="tag">&lt;/a&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">top</span><span class="pun">.</span><span class="pln">document</span><span class="pun">.</span><span class="pln">domain </span><span class="pun">==</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">domain</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    protector</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="tag">&lt;/script&gt;</span><span class="pln">

  This text is always visible.

  But if the page was open inside a document from another domain, the div over it would prevent any actions.

  </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">onclick</span><span class="pun">=</span><span class="atv">"</span><span class="pln">alert</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">Click wouldn't work in that case</span><span class="tag">&lt;/button&gt;</span><span class="pln">

</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	وسيحتوي الملف "index.html" على هذه الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2868_41" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">

</span><span class="tag">&lt;head&gt;</span><span class="pln">
  </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">
</span><span class="tag">&lt;body&gt;</span><span class="pln">

  </span><span class="tag">&lt;iframe</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"iframe.html"</span><span class="tag">&gt;&lt;/iframe&gt;</span><span class="pln">

</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	وستظهر النتيجة كالتالي:
</p>

<p>
	<iframe __idm_frm__="141" class="code-tabs__result" data-ss1634983128="1" data-ss1634983372="1" src="https://javascript.info/article/clickjacking/protector/" style="display: block; border: 0px; width: 725px; height: 182px;"></iframe>
</p>

<h2>
	الخاصية samesite لملف تعريف الارتباط
</h2>

<p>
	يمكن للخاصية <code>samesite</code> أن تمنع هجوم الاختطاف بالنقر، حيث لن يُرسَل ملف تعريف الارتباط cookie الذي يحملها إلى أي موقع إلا إذا فُتح مباشرةً، وليس عن طريق نافذة ضمنية أو أي شيء مشابه. يمكن الاطلاع على معلومات أكثر ضمن مقال "<a data-ss1634983128="1" data-ss1634983372="1" href="https://javascript.info/cookie" rel="external nofollow">ملفات تعريف الارتباط وضبطها في مستندات JavaScript</a>".
</p>

<p>
	فلو امتلك موقع مثل فيسبوك الخاصية <code>samesite</code> ضمن ملف تعريف الارتباط على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2868_45" style="">
<span class="typ">Set</span><span class="pun">-</span><span class="typ">Cookie</span><span class="pun">:</span><span class="pln"> authorization</span><span class="pun">=</span><span class="pln">secret</span><span class="pun">;</span><span class="pln"> samesite</span></pre>

<p>
	فلن يُرسل ملف تعريف الارتباط عندما يُفتح فيسبوك في نافذة ضمنية عائدة لموقع آخر، وبالتالي سيخفق الهجوم.
</p>

<p>
	لن يكون للخاصية <code>samesite</code> فائدة إن لم تُستعمل ملفات تعريف الارتباط، وقد يسمح ذلك لمواقع أخرى بعرض صفحاتنا العامة غير المستوثقة في النوافذ الضمنية، مما سيسمح بحدوث هجمات الاختطاف بالنقر لكن في حالات محدودة، إذ ستبقى المواقع التي تدعم عمليات التصويت مثلًا، مجهولة المصدر وتمنع تكرار التصويت لنفس عنوان IP عرضة لهذه الهجمات، لأنها لا تستوثق المستخدمين من خلال ملفات تعريف الارتباط.
</p>

<h2>
	الخلاصة
</h2>

<p>
	يمثل الاختطاف بالنقر طريقةً للاحتيال على المستخدمين للنقر على صفحات مستهدفة دون علمهم، وتأتي خطورتها عند وجود أفعال مهمة تُنفَّذ عند النقر، حيث يمكن للمخترق أن ينشر رابطًا ضمن صفحته المشبوهة ضمن رسالة، أو أن يجذب الزوار إلى صفحته بطرق أخرى.
</p>

<p>
	لا يعَد الهجوم "عميقًا" من ناحية أولية، فكل ما يفعله المخترق هو اعتراض نقرة الفأرة، لكن من ناحية أخرى إذا علم المخترق بظهور عملية أخرى بعد النقر، فربما يستخدم رسائل ماكرةً تجبر المستخدم على النقر عليها أيضًا، وهنا سيكون الهجوم خطرًا، إذ لا نخطط لاعتراض هذه الأفعال عند تصميم واجهة المستخدم UI، وقد تظهر نقاط الضعف في أماكن لا يمكن توقعها أبدًا. لهذايُنصح باستخدام الأمر <code>X-Frame-Options: SAMEORIGIN</code> ضمن الصفحات أو المواقع التي لا نريد عرضها ضمن نوافذ ضمنية، واستخدام العنصر <code>&lt;div&gt;</code> لتغطية الصفحة عند عرضها ضمن نوافذ ضمنية، ومع ذلك يجب توخي الحذر.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a data-ss1634983128="1" data-ss1634983372="1" href="https://javascript.info/clickjacking" rel="external nofollow">clickjacking attack</a> من سلسلة <a data-ss1634983128="1" data-ss1634983372="1" href="https://javascript.info" rel="external nofollow">The Modern JavaScript Tutorial</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a data-ss1634983128="1" data-ss1634983372="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-%D8%A7%D9%84%D9%85%D8%AA%D8%B9%D9%84%D9%82%D8%A9-%D8%A8%D8%A7%D9%84%D9%86%D9%88%D8%A7%D9%81%D8%B0-%D9%88%D8%A7%D9%84%D9%86%D9%88%D8%A7%D9%81%D8%B0-%D8%A7%D9%84%D9%85%D9%86%D8%A8%D8%AB%D9%82%D8%A9-popups-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1253/" rel="">التوابع المتعلقة بالنوافذ والنوافذ المنبثقة Popups في جافاسكريبت</a>
	</li>
	<li>
		<a data-ss1634983128="1" data-ss1634983372="1" href="https://academy.hsoub.com/programming/general/%D8%A7%D8%AD%D9%85-%D9%85%D9%88%D9%82%D8%B9%D9%83-%D9%85%D9%86-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%B1%D8%A7%D9%82-r864/" rel="">احم موقعك من الاختراق</a>
	</li>
	<li>
		<a data-ss1634983128="1" data-ss1634983372="1" href="https://academy.hsoub.com/programming/php/%D9%85%D9%85%D8%A7%D8%B1%D8%B3%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%85%D9%86-%D9%88%D8%A7%D9%84%D8%AD%D9%85%D8%A7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-php-r1205/" rel="">ممارسات الأمن والحماية في تطبيقات PHP</a>
	</li>
	<li>
		<a data-ss1634983128="1" data-ss1634983372="1" href="https://academy.hsoub.com/programming/sql/%D8%AA%D9%86%D8%B8%D9%8A%D9%85-%D8%B4%D9%8A%D9%81%D8%B1%D8%A7%D8%AA-sql-%D9%88%D8%AA%D8%A3%D9%85%D9%8A%D9%86%D9%87%D8%A7-r860/" rel="">تنظيم شيفرات SQL وتأمينها</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1254</guid><pubDate>Sat, 26 Jun 2021 15:08:00 +0000</pubDate></item><item><title>&#x645;&#x634;&#x631;&#x648;&#x639; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x64A; &#x644;&#x628;&#x646;&#x627;&#x621; &#x631;&#x62C;&#x644; &#x622;&#x644;&#x64A; (&#x631;&#x648;&#x628;&#x648;&#x62A;) &#x639;&#x628;&#x631; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D8%B4%D8%B1%D9%88%D8%B9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D9%8A-%D9%84%D8%A8%D9%86%D8%A7%D8%A1-%D8%B1%D8%AC%D9%84-%D8%A2%D9%84%D9%8A-%D8%B1%D9%88%D8%A8%D9%88%D8%AA-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1244/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_06/60bb11efcb741_---.png.cce9b659fd6cc301c8d2abc6e80d86aa.png" /></p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		إن السؤال عن تفكير الآلات يشبه تمامًا سؤالك هل تستطيع الغواصات السباحة أم لا.
	</p>

	<p>
		__ إدزجر ديكسترا Edsger Dijkstra، المخاطر التي تهدد علوم الحاسوب.
	</p>
</blockquote>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="68506" data-ss1624460821="1" href="https://academy.hsoub.com/uploads/monthly_2021_06/60bb11f977c1d_Pictureofapackage-deliveryrobot.jpg.ad7b7e99faec76e92855cb34f7e5b279.jpg" rel=""><img alt="Picture of a package-delivery robot.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="68506" data-unique="yshxeao9u" src="https://academy.hsoub.com/uploads/monthly_2021_06/60bb11f977c1d_Pictureofapackage-deliveryrobot.jpg.ad7b7e99faec76e92855cb34f7e5b279.jpg"></a>
</p>

<p>
	يختلف هذا المقال قليلًا عن باقي الفصول، إذ سأدَع الحديث عن النظريات قليلًا لننشئ نحن وأنت برنامجًا سويًا، فالنظرية وإن كانت مهمة جدًا لتعلم كيفية البرمجة، إلا أنّ قراءة البرامج الحقيقية وفهم شيفراتها لا تقل أهمية، ومشروعنا في هذا المقال هو بناء روبوت ينفِذ مهمة في عالم افتراضي، وستكون تلك المهمة توصيل الطرود واستلامها.
</p>

<h2>
	قرية المرج Meadowfield
</h2>

<p>
	سيعمل الروبوت الخاص بنا في قرية صغيرة تُدعى قرية المرج، حيث يوجد فيها أحد عشر مكانًا بينها أربعة عشر طريقًا، ويمكن وصف القرية بالمصفوفة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_6" style="">
<span class="kwd">const</span><span class="pln"> roads </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="str">"Salma's House-Omar's House"</span><span class="pun">,</span><span class="pln">   </span><span class="str">"Salma's House-Cabin"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Salma's House-Post Office"</span><span class="pun">,</span><span class="pln">   </span><span class="str">"Omar's House-Town Hall"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Sara's House-Mostafa's House"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Sara's House-Town Hall"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Mostafa's House-Sama's House"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Sama's House-Farm"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Sama's House-Shop"</span><span class="pun">,</span><span class="pln">          </span><span class="str">"Marketplace-Farm"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Marketplace-Post Office"</span><span class="pun">,</span><span class="pln">     </span><span class="str">"Marketplace-Shop"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Marketplace-Town Hall"</span><span class="pun">,</span><span class="pln">       </span><span class="str">"Shop-Town Hall"</span><span class="pln">
</span><span class="pun">];</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="68507" data-ss1624460821="1" href="https://academy.hsoub.com/uploads/monthly_2021_06/60bb11fa13e3d_ThevillageofMeadowfield.png.1106aec2e12b1e9e062ed2357f3bc4e9.png" rel=""><img alt="The village of Meadowfield.png" class="ipsImage ipsImage_thumbnailed" data-fileid="68507" data-unique="ejjcf6v2q" src="https://academy.hsoub.com/uploads/monthly_2021_06/60bb11fa13e3d_ThevillageofMeadowfield.png.1106aec2e12b1e9e062ed2357f3bc4e9.png"></a>
</p>

<p>
	وتكوِّن شبكة الطرق في القرية مخططًا graph، وهو عبارة عن تجميعة من نقاط لتمثل الأماكن في القرية، مع خطوط تصل بينها بحيث تمثل الطرق، وسيكون هذا هو العالم الذي سيتحرك الروبوت فيه.
</p>

<p>
	لكن ليس من السهل التعامل مع مصفوفة السلاسل النصية السابقة، إذ لا نريد إلا الوجهات التي يمكننا الذهاب إليها من أي نقطة معطاة لنا، وعلى ذلك فسنحوِّل قائمة الطرق إلى هيكل بيانات يخبرنا بالمناطق التي نستطيع الذهاب إليها من كل نقطة أو مكان، انظركما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_8" style="">
<span class="kwd">function</span><span class="pln"> buildGraph</span><span class="pun">(</span><span class="pln">edges</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let graph </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">create</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">function</span><span class="pln"> addEdge</span><span class="pun">(</span><span class="pln">from</span><span class="pun">,</span><span class="pln"> to</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">graph</span><span class="pun">[</span><span class="pln">from</span><span class="pun">]</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      graph</span><span class="pun">[</span><span class="pln">from</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">to</span><span class="pun">];</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      graph</span><span class="pun">[</span><span class="pln">from</span><span class="pun">].</span><span class="pln">push</span><span class="pun">(</span><span class="pln">to</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let </span><span class="pun">[</span><span class="pln">from</span><span class="pun">,</span><span class="pln"> to</span><span class="pun">]</span><span class="pln"> of edges</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">r </span><span class="pun">=&gt;</span><span class="pln"> r</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">"-"</span><span class="pun">)))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    addEdge</span><span class="pun">(</span><span class="pln">from</span><span class="pun">,</span><span class="pln"> to</span><span class="pun">);</span><span class="pln">
    addEdge</span><span class="pun">(</span><span class="pln">to</span><span class="pun">,</span><span class="pln"> from</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> graph</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> roadGraph </span><span class="pun">=</span><span class="pln"> buildGraph</span><span class="pun">(</span><span class="pln">roads</span><span class="pun">);</span></pre>

<p>
	تُنشئ دالة <code>buildGraph</code> كائن خارطة map object عند إعطائها مصفوفة من الحدود edges، حيث تخزن فيها لكل عقدةٍ مصفوفةً من العقد المتصلة بها. كما تستخدِم تابع <code>method</code> للذهاب من سلسلة الطريق النصية التي تحمل الصيغة <code>"Start-End"</code> إلى مصفوفات من عنصرين تحتوي على البداية والنهاية على أساس سلسلتَين منفصلتين.
</p>

<h2>
	المهمة
</h2>

<p>
	سيتحرك الروبوت داخل القرية، إذ لديه طرود في أماكن مختلفة فيها، حيث يحمل كل طرد عنوانًا يجب نقله إليه؛ وسيلتقط الروبوت الطرودَ حين وصوله إليها، ثم يتركها عند وصوله إلى الوجهة المرسَل إليها، ويجب أن يقرر في كل نقطة وجهته التالية، ولا تنتهي مهمته إلا عند توصيل جميع الطرود.
</p>

<p>
	لكن أولًا يجب تعريف عالم افتراضي يصف هذه العملية، وذلك لنتمكّن من محاكاته، ويكون هذا النموذج قادر على إخبارنا بموقع الروبوت والطرود معًا، كما يجب تحديث هذا النموذج عندما يقرر الروبوت الذهاب إلى مكان جديد.
</p>

<p>
	إن كنت تفكر بأسلوب البرمجة كائنية التوجه، سيكون دافعك الأول هو البدء بتعريف كائنات للعناصر المختلفة في هذا العالم الذي أنشأناه، فصنف للروبوت، وآخر للطرد، وصنف ثالث للأماكن ربما، ثم تحمِل هذه الأصناف بعد ذلك خصائصًا تصف حالتها، كما في حالة كومة الطرود عند موقع ما، والتي يمكننا تغييرها عند إجراء تحديث لهذا العالم.
</p>

<p>
	لكن هذا خطأ في أغلب الحالات على الأقل، فكون شيء ما يحاكي الكائن لا يعني وجوب معاملته على أساس كائن في برنامجك؛ كما أنّ كتابة الأصناف في برنامجك دون داعي حقيقي لها، ستُنشئ تجميعةً من الكائنات المرتبطة ببعضها بعضًا لكل منها حالة داخلية متغيرة، وتكون مثل تلك البرامج صعبة الفهم وسهلة التعطل.
</p>

<p>
	لدينا أسلوبًا أفضل من ذلك، وهو ضغط حالة القرية إلى أقل فئة ممكنة من القيم التي تعرِّفها، فيكون لدينا الموقع الحالي للروبوت، وتجميعة الطرود غير المسلَّمة، والتي يحمل كل منها موقعه الحالي وعنوان التسليم، وحسبنا هذا!
</p>

<p>
	بينما نحن في ذلك، فعلينا التأكد أنه حين يتحرك الروبوت من موقعه، فعلينا حساب حالة جديدة وفق الموقف الذي يكون بعد التحرك، وذلك دون تغيير الحالة الأولى، انظر كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_10" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">VillageState</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">place</span><span class="pun">,</span><span class="pln"> parcels</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">place </span><span class="pun">=</span><span class="pln"> place</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">parcels </span><span class="pun">=</span><span class="pln"> parcels</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  move</span><span class="pun">(</span><span class="pln">destination</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">roadGraph</span><span class="pun">[</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">place</span><span class="pun">].</span><span class="pln">includes</span><span class="pun">(</span><span class="pln">destination</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      let parcels </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">parcels</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">p</span><span class="pun">.</span><span class="pln">place </span><span class="pun">!=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">place</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> p</span><span class="pun">;</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">place</span><span class="pun">:</span><span class="pln"> destination</span><span class="pun">,</span><span class="pln"> address</span><span class="pun">:</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">address</span><span class="pun">};</span><span class="pln">
      </span><span class="pun">}).</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">place </span><span class="pun">!=</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">address</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">VillageState</span><span class="pun">(</span><span class="pln">destination</span><span class="pun">,</span><span class="pln"> parcels</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يتحقق التابع <code>move</code> أولًا إن كان ثمة طريق من الموقع الحالي إلى موقع الوِجهة، وإن لم يكن، فسيعيد الحالة القديمة بما أنّ هذه الخطوة غير صالحة، ثم يُنشئ حالةً جديدةً يكون فيها موقع الوِجهة هو الموقع الجديد للروبوت.
</p>

<p>
	لكن سيحمل هذا الروبوت معه طرودًا أخرى غير هذا الطرد، ويأخذها معه إلى موقع تسليم الطرد المسمى، ثم يتابع حملها معه بعد تسليم الطرد، ليذهب بكل منها إلى موقع تسليمه الذي يخصه، ولكي نحصل على بيانات تلك الطرود عند أي نقطة زمنية نريدها، فسنحتاج إلى إنشاء فئة set جديدة نضع فيها تلك الطرود التي يحملها إلى الموقع الجديد، ثم يترك الطرود الواجب تسليمها في موقع التسليم، أي نحذفها من فئة الطرود غير المسلَّمة. ويتكفل بعملية الانتقال استدعاء <code>map</code>، بينما نستدعي <code>filter</code> ليتولى عملية التسليم.
</p>

<p>
	لا تتغير كائنات الطرود عند نقلها، وإنما يعاد إنشاؤها، ويعطينا التابع <code>move</code> حالة جديدة للقرية مع ترك الحالة القديمة كما هي دون تغيير، انظر كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_12" style="">
<span class="pln">let first </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">VillageState</span><span class="pun">(</span><span class="pln">
  </span><span class="str">"Post Office"</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">[{</span><span class="pln">place</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Post Office"</span><span class="pun">,</span><span class="pln"> address</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Salma's House"</span><span class="pun">}]</span><span class="pln">
</span><span class="pun">);</span><span class="pln">
let next </span><span class="pun">=</span><span class="pln"> first</span><span class="pun">.</span><span class="pln">move</span><span class="pun">(</span><span class="str">"Salma's House"</span><span class="pun">);</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">next</span><span class="pun">.</span><span class="pln">place</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → Salma's House</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">next</span><span class="pun">.</span><span class="pln">parcels</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → []</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">first</span><span class="pun">.</span><span class="pln">place</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → Post Office</span></pre>

<p>
	تُسلَّم الطرود عند مواضع تسليمها مع حركة الروبوت بين تلك المواقع، ويُرى أثر ذلك في الحالة التالية، لكن ستظل الحالة الابتدائية تصف الموقف الذي يكون فيه الروبوت عند مكتب البريد ومعه الطرد الغير مسلَّم بعد.
</p>

<h2>
	البيانات الثابتة Persistent Data
</h2>

<p>
	تُسمى هياكل البيانات التي لا تتغير بالهياكل الثابتة persistent، أو غير القابلة للتغير immutable، وتحاكي السلاسل النصية والأرقام في بقائها كما هي، بدلًا من احتواء أشياء مختلفة في كل مرة.
</p>

<p>
	بما أن كل شيء في جافاسكربت قابل للتغير تقريبًا، فسيتطلب العمل مع كائنات أو بيانات ثابتة جهدًا، وعملًا إضافيًا، ونوعًا من التقييد، حيث لدينا دالة <code>Object.freeze</code> من أجل هذا، إذ تؤثر على الكائن وتجمده، وذلك لتجعله يتجاهل الكتابة على خصائصه، فتستطيع استخدام ذلك لضمان ثبات كائناتك إن أردت، لكن تذكر أن هذا التجميد يعني أن الحاسوب سيبذل مزيدًا من الجهد.
</p>

<p>
	نفضل إخبار العامة بترك الكائن الفلاني وشأنه وعدم العبث به، آملين تذكّرهم ذلك، بدلًا من إخبارهم بتجاهل تحديثات بعينها.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_14" style="">
<span class="pln">let object </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">freeze</span><span class="pun">({</span><span class="pln">value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">});</span><span class="pln">
object</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">object</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 5</span></pre>

<p>
	لكن هنا يبرز سؤال إن كنت منتبهًا، فإن كانت اللغة نفسها تحثنا على جعل كل شيء متغيرًا وتتوقع منا ذلك، فلماذا نخرج عن هذا المسلك لنجعل بعض الكائنات ثابتةً لا تقبل التغيير؟
</p>

<p>
	الإجابة بسيطة وهي أن هذا سيساعدنا في فهم برامجنا أكثر، إذ يتعلق الأمر بإدارة التعقيد لبرامجنا، فإن كانت الكائنات التي في نظامنا ثابتةً ومستقرةً، فيمكننا إجراء عمليات عليها إجراءً معزولًا -حيث سيعطينا التحرك إلى منزل سلمى مثلًا من أي حالة بدء معطاة الحالة الجديدة نفسها في كل مرة-،؛ أما إن كانت الكائنات تتغير مع الوقت، فسيضيف هذا بعدًا جديدًا من التعقيد إلى العمليات والتفكير المنطقي لحل المشكلة.
</p>

<p>
	وربما تقول أن مسألة توصيل الطرود، والروبوت، وهذه القرية الصغيرة، سهل أمرها ويمكن إدارتها، وربما أنت محق، لكن المعيار الذي يحدد نوع النظم الممكن بناؤها هو فهمنا نحن لتلك الأنظمة ومدى ما يمكننا فهمه مطلقًا، فأيّ شيء يسرّع فهم شيفرتك، فسيمهد لك الطريق لبناء نظم أكثر تطورًا.
</p>

<p>
	لكن رغم سهولة فهم النظم المبنية على هياكل بيانات ثابتة، فقد يكون من الصعب تصميم نظام مثل هذا، وخاصةً إن لم تكن لغة البرمجة التي تستخدمها مصمَّمةً لذلك، كما سنبحث عن فرص استخدام هياكل البيانات الثابتة في هذه السلسلة مع استخدامنا للهياكل المتغيرة كذلك.
</p>

<h2>
	المحاكاة
</h2>

<p>
	ينظر روبوت التوصيل إلى العالم، ويقرر الاتجاه الذي يريد السير فيه، وعليه فيمكننا القول أنّ الروبوت هو دالة تأخذ كائن <code>villageState</code>، وتعيد اسم مكان قريب.
</p>

<p>
	لأننا نريد الربوتات أن تكون قادرة على تذكر أشياء بعينها كي تنفِّذ الخطط الموضوعة لها من قِبلنا، فسنمرر إليها ذاكرتها، ونسمح لها بإعادة ذاكرة جديدة، ومن ثم يعيد الروبوت كائنًا يحتوي الاتجاه الذي يريد السير فيه، وقيمة ذاكرة تُعطى إليه في المرة التالية التي يُستدعى فيها.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_16" style="">
<span class="kwd">function</span><span class="pln"> runRobot</span><span class="pun">(</span><span class="pln">state</span><span class="pun">,</span><span class="pln"> robot</span><span class="pun">,</span><span class="pln"> memory</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let turn </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;;</span><span class="pln"> turn</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">state</span><span class="pun">.</span><span class="pln">parcels</span><span class="pun">.</span><span class="pln">length </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Done</span><span class="pln"> in $</span><span class="pun">{</span><span class="pln">turn</span><span class="pun">}</span><span class="pln"> turns</span><span class="pun">`);</span><span class="pln">
      </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    let action </span><span class="pun">=</span><span class="pln"> robot</span><span class="pun">(</span><span class="pln">state</span><span class="pun">,</span><span class="pln"> memory</span><span class="pun">);</span><span class="pln">
    state </span><span class="pun">=</span><span class="pln"> state</span><span class="pun">.</span><span class="pln">move</span><span class="pun">(</span><span class="pln">action</span><span class="pun">.</span><span class="pln">direction</span><span class="pun">);</span><span class="pln">
    memory </span><span class="pun">=</span><span class="pln"> action</span><span class="pun">.</span><span class="pln">memory</span><span class="pun">;</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Moved</span><span class="pln"> to $</span><span class="pun">{</span><span class="pln">action</span><span class="pun">.</span><span class="pln">direction</span><span class="pun">}`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لننظر ما الذي يجب على الروبوت فعله كي "يحل" حالة ما:
</p>

<p>
	يجب عليه التقاط جميع الطرود أولًا من خلال الذهاب إلى كل موقع فيه طرد، ثم يسلِّم تلك الطرود بالذهاب إلى كل عنوان من العناوين المرسل إليها هذه الطرود، حيث لا يذهب إلى موقع التسليم إلا بعد التقاط الطرد الخاص به.
</p>

<p>
	تُرى ما هي أغبى خطة يمكن أن تنجح هنا؟ إنها العشوائية لا شك، حيث يتخِذ الروبوت اتجاهًا عشوائيًا عند كل منعطف، مما يعني أنه سيمر لا محالة على جميع الطرود بعد عدد من المحاولات، وسيصل كذلك إلى موقع تسليمها في مرحلة ما. انظر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_18" style="">
<span class="kwd">function</span><span class="pln"> randomPick</span><span class="pun">(</span><span class="pln">array</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let choice </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> array</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> array</span><span class="pun">[</span><span class="pln">choice</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> randomRobot</span><span class="pun">(</span><span class="pln">state</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">direction</span><span class="pun">:</span><span class="pln"> randomPick</span><span class="pun">(</span><span class="pln">roadGraph</span><span class="pun">[</span><span class="pln">state</span><span class="pun">.</span><span class="pln">place</span><span class="pun">])};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تعيد <code>Math.random()‎</code> عددًا بين الصفر والواحد، ويكون دومًا أقل من الواحد، كما يعطينا ضرب عدد مثل هذا في طول أي مصفوفة ثم تطبيق <code>Math.floor</code> عليه موقعًا عشوائيًا للمصفوفة. وبما أنّ هذا الروبوت لا يحتاج إلى تذكر أي شيء، فسيَتجاهل الوسيط الثاني له ويهمل خاصية <code>memory</code> في كائنه المعاد؛ وتذكّر أنّه يمكن استدعاء دوال جافاسكربت بوسائط إضافية دون آثار جانبية مقلقة.
</p>

<p>
	نحتاج الآن إلى طريقة لإنشاء حالة جديدة بها بعض الطرود، وذلك من أجل إطلاق هذا الروبوت للعمل، والتابع الساكن -المكتوب هنا بإضافة خاصيةٍ مباشرةً للمنشئ- مكان مناسب لوضع هذه الوظيفة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_20" style="">
<span class="typ">VillageState</span><span class="pun">.</span><span class="pln">random </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">parcelCount </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let parcels </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> parcelCount</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let address </span><span class="pun">=</span><span class="pln"> randomPick</span><span class="pun">(</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">(</span><span class="pln">roadGraph</span><span class="pun">));</span><span class="pln">
    let place</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">do</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      place </span><span class="pun">=</span><span class="pln"> randomPick</span><span class="pun">(</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">(</span><span class="pln">roadGraph</span><span class="pun">));</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">place </span><span class="pun">==</span><span class="pln"> address</span><span class="pun">);</span><span class="pln">
    parcels</span><span class="pun">.</span><span class="pln">push</span><span class="pun">({</span><span class="pln">place</span><span class="pun">,</span><span class="pln"> address</span><span class="pun">});</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">VillageState</span><span class="pun">(</span><span class="str">"Post Office"</span><span class="pun">,</span><span class="pln"> parcels</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	لا نريد أي طرود مرسلة من المكان نفسه الذي ترسَل إليه، ولهذا تختار حلقة <code>do</code> التكرارية أماكنًا جديدةً في كل مرة تحصل على مكان مطابق للعنوان.
</p>

<p>
	دعنا نبدأ عالمًا افتراضيًا، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_22" style="">
<span class="pln">runRobot</span><span class="pun">(</span><span class="typ">VillageState</span><span class="pun">.</span><span class="pln">random</span><span class="pun">(),</span><span class="pln"> randomRobot</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → Moved to Marketplace</span><span class="pln">
</span><span class="com">// → Moved to Town Hall</span><span class="pln">
</span><span class="com">// → …</span><span class="pln">
</span><span class="com">// → Done in 63 turns</span></pre>

<p>
	كما نرى من المثال، إذ غيّر الروبوت اتجاهه ثلاثًا وستين مرة، وذلك الرقم الكبير بسبب عدم التخطيط الجيد للخطوة التالية، كما سننظر في هذا قريبًا.
</p>

<p>
	يمكنك استخدام دالة <code>runRobotAnimation</code> المتاحة في <a data-ss1624460821="1" href="https://eloquentjavascript.net/2nd_edition/code/" rel="external nofollow">البيئة البرمجية</a> لهذا المقال، حيث ستنفِّذ المحاكاة، وستعرض لك الروبوت وهو يتحرك في خارطة القرية، بدلًا من إخراج نص فقط.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_24" style="">
<span class="pln">runRobotAnimation</span><span class="pun">(</span><span class="typ">VillageState</span><span class="pun">.</span><span class="pln">random</span><span class="pun">(),</span><span class="pln"> randomRobot</span><span class="pun">);</span></pre>

<p>
	سندع طريقة تنفيذ <code>runRobotAnimation</code> مبهمةً في الوقت الحالي، حيث ستستطيع معرفة كيفية عملها بعد قراءة المقال الرابع عشر من هذه السلسلة والذي نناقش فيه تكامل جافاسكربت في المتصفحات.
</p>

<h2>
	طريق شاحنة البريد
</h2>

<p>
	لا شك أنّ فكرة الروبوت هذه لتوصيل البريد فكرة بدائية، وأجدر بنا تطوير هذا البرنامج قليلًا، فلم لا ننظر إلى توصيل البريد في العالم الحقيقي لنستوحي منه أفكارًا لعالمنا الصغير؟
</p>

<p>
	أحد هذه الحلول هو البحث عن طريق يمر على جميع الأماكن في القرية، وحينها يأخذ الروبوت هذه الطريق مرتين. انظر مثلًا على هذا الطريق بدءًا من مكتب البريد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_26" style="">
<span class="kwd">const</span><span class="pln"> mailRoute </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="str">"Salma's House"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Cabin"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Salma's House"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Omar's House"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Town Hall"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Sara's House"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Mostafa's House"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Sama's House"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Shop"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Sama's House"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Farm"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"Marketplace"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Post Office"</span><span class="pln">
</span><span class="pun">];</span></pre>

<p>
	نحتاج إلى الاستفادة من ذاكرة الروبوت إذا أردنا استخدام الروبوت المتتبع للطريق route-following، حيث يحذف الروبوت العنصر الأول عند كل منعطف ويحتفظ ببقية الطريق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_28" style="">
<span class="kwd">function</span><span class="pln"> routeRobot</span><span class="pun">(</span><span class="pln">state</span><span class="pun">,</span><span class="pln"> memory</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">memory</span><span class="pun">.</span><span class="pln">length </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    memory </span><span class="pun">=</span><span class="pln"> mailRoute</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">direction</span><span class="pun">:</span><span class="pln"> memory</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln"> memory</span><span class="pun">:</span><span class="pln"> memory</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيكون هذا الروبوت بهذا الأسلوب الجديد أسرع من الأسلوب الأول، إذ سيكون أقصى عدد من الاتجاهات التي يسلكها أو المنعطفات التي يأخذها هو 26 -وهو ضعف عدد الأماكن في طريق القرية-، وإن كان في العادة أقل من هذا العدد، فليست جميع الأماكن بها طرود بريد في كل مرة.
</p>

<h2>
	اكتشاف الطريق Pathfinding
</h2>

<p>
	لنبحث في طريقة أكثر تطورًا من المرور على جميع الأماكن في القرية في كل مرة، فلا شك في أن الروبوت سيكون أكثر كفاءة إذا عدّل سلوكه ليناسب العمل الحقيقي المراد تنفيذه؛ لذا يجب امتلاكه القدرة على التحرك نحو طرد معين أو موقع تسليم بعينه، وعليه نحتاج دالة لإيجاد الطريق المناسبة.
</p>

<p>
	تُعَدّ مشكلة إيجاد الطريق باستخدام مخطط هي مشكلة البحث، حيث نستطيع تحديد ما إذا كان الحل المعطى -أي الطريق- مناسبًا أم لا، لكن لا نستطيع حساب الحل بالطريقة نفسها عند حساب حاصل جمع 2+2 مثلًا، وإنما يجب إنشاء حلولًا محتملةً لإيجاد واحد صالح.
</p>

<p>
	تتضح هنا المشكلة أكثر، إذ لا نهايةً لعدد الاحتمالات الممكنة للطرق من خلال مخطط، لكن يمكن تضييق عدد الاحتمالات إذا أردنا الطرق المؤدية من نقطة أ إلى نقطة ب مثلًا، فعندئذ لن يهمنا سوى الطرق التي تبدأ من النقطة أ، كما لا نريد الطرق التي تمر المكان نفسه مرتين، إذ لا تُعَدّ هي الطرق الأكثر كفاءة في أي مكان، ويقلل هذا عدد الطرق التي يجب اعتمادها من قِبَل دالة البحث أكثر وأكثر.
</p>

<p>
	نريد في الواقع أقصر طريق فقط، وعليه يجب النظر في الطرق الأقصر أولًا قبل النظر في الطرق الأطول، وأحد الأساليب لفعل ذلك هو بدء طرق من نقطة تحرك الروبوت لتُستكشف جميع الأماكن التي يمكن الوصول إليها ولم يُذهب إليها بعد، حتى تصل إحدى تلك الطرق إلى المكان المراد تسليم الطرد إليه، وهكذا نستكشف الطرق التي يحتمل أن تكون مفيدة لنا، ونتخذ أقصرها أو واحدًا من أقصر تلك الطرق. كما في الدالة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_30" style="">
<span class="kwd">function</span><span class="pln"> findRoute</span><span class="pun">(</span><span class="pln">graph</span><span class="pun">,</span><span class="pln"> from</span><span class="pun">,</span><span class="pln"> to</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let work </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[{</span><span class="pln">at</span><span class="pun">:</span><span class="pln"> from</span><span class="pun">,</span><span class="pln"> route</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[]}];</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> work</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let </span><span class="pun">{</span><span class="pln">at</span><span class="pun">,</span><span class="pln"> route</span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> work</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let place of graph</span><span class="pun">[</span><span class="pln">at</span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">place </span><span class="pun">==</span><span class="pln"> to</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> route</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">place</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">work</span><span class="pun">.</span><span class="pln">some</span><span class="pun">(</span><span class="pln">w </span><span class="pun">=&gt;</span><span class="pln"> w</span><span class="pun">.</span><span class="pln">at </span><span class="pun">==</span><span class="pln"> place</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        work</span><span class="pun">.</span><span class="pln">push</span><span class="pun">({</span><span class="pln">at</span><span class="pun">:</span><span class="pln"> place</span><span class="pun">,</span><span class="pln"> route</span><span class="pun">:</span><span class="pln"> route</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">place</span><span class="pun">)});</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يجب أن يتم الاستكشاف بترتيب صحيح، فالأماكن التي يصل إليها أولًا يجب استكشافها أولًا، لكن انتبه إلى أن مكان س مثلًا لا يُستكشف فور الوصول إليه، إذ سيعني هذا أنه علينا استكشاف أماكن يمكن الوصول إليها من س وهكذا، رغم احتمال وجود مسارات أقصر لم تُستكشف بعد.
</p>

<p>
	وعلى هذا فإن الدالة تحتفظ بقائمة عمل work list، وهي مصفوفة من الأماكن التي يجب استكشافها فيما بعد مع الطرق التي توصلنا إليها، حيث تبدأ بموقع ابتدائي للبدء منه وطريق فارغة، ثم يبدأ البحث بعدها بأخذ العنصر التالي في القائمة واستكشافه، مما يعني النظر في جميع الطرق الخارجة من هذا المكان، فإن كان أحدها هو الهدف المراد، فيُعاد طريق تامة finished road، وإلا فسيضاف عنصر جديد إلى القائمة إن لم يُنظر في هذا المكان من قبل.
</p>

<p>
	كذلك، إن كنا قد نظرنا فيه من قبل، فقد وجدنا إما طريق أطول إلى هذا المكان أو واحدة بنفس طول الطريق الموجودة -وبما أننا نبحث عن الطرق الأقصر أولًا- ولا نحتاج إلى استكشافها عندئذ.
</p>

<p>
	تستطيع تخيل هذا بصريًا على أساس شبكة من الطرق المعروفة، حيث تخرج من موقع ابتدائي، وتنمو بانتظام في جميع الجوانب، لكن لا تتشابك مع بعضها أو تنعكس على نفسها؛ وبمجرد وصول خيط من تلك الشبكة إلى الموقع الهدف، فيُتتبَّع ذلك الخيط إلى نقطة البداية ليعطينا الطريق التي نريدها.
</p>

<p>
	لا تعالج شيفرتنا الموقف الذي لا تكون فيه عناصر عمل على قائمة العمل، وذلك لعِلمنا باتصال المخطط الخاص بنا، أي يمكن الوصول إلى كل موقع من جميع المواقع الأخرى، وسنكون قادرين دائمًا على إيجاد طريق ممتدة بين نقطتين، فلا يفشل البحث الذي نجريه.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_32" style="">
<span class="kwd">function</span><span class="pln"> goalOrientedRobot</span><span class="pun">({</span><span class="pln">place</span><span class="pun">,</span><span class="pln"> parcels</span><span class="pun">},</span><span class="pln"> route</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">route</span><span class="pun">.</span><span class="pln">length </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let parcel </span><span class="pun">=</span><span class="pln"> parcels</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">parcel</span><span class="pun">.</span><span class="pln">place </span><span class="pun">!=</span><span class="pln"> place</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      route </span><span class="pun">=</span><span class="pln"> findRoute</span><span class="pun">(</span><span class="pln">roadGraph</span><span class="pun">,</span><span class="pln"> place</span><span class="pun">,</span><span class="pln"> parcel</span><span class="pun">.</span><span class="pln">place</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      route </span><span class="pun">=</span><span class="pln"> findRoute</span><span class="pun">(</span><span class="pln">roadGraph</span><span class="pun">,</span><span class="pln"> place</span><span class="pun">,</span><span class="pln"> parcel</span><span class="pun">.</span><span class="pln">address</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">direction</span><span class="pun">:</span><span class="pln"> route</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln"> memory</span><span class="pun">:</span><span class="pln"> route</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)};</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يستخدِم هذا الروبوت قيمة ذاكرته على أساس قائمة من الاتجاهات ليتحرك وفقًا لها، تمامًا مثل الروبوت المتتبع للطريق الذي ذكرناه آنفًا، وعليه معرفة الخطوة التالية إذا وجد القائمة فارغة؛ كما ينظر في أول طرد غير مسلَّم في الفئة التي معه، فإن لم يكن قد التقطه، فسيرسم طريقًا إليه، وإن كان قد التقطه، فسينشئ طريقًا إلى موقع التسليم، انظر كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_34" style="">
<span class="pln">runRobotAnimation</span><span class="pun">(</span><span class="typ">VillageState</span><span class="pun">.</span><span class="pln">random</span><span class="pun">(),</span><span class="pln">
                  goalOrientedRobot</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[]);</span></pre>

<p>
	ينهي هذا الروبوت مهمة تسليم خمسة طرود في نحو ستة عشر منعطفًا، وهذا أفضل بقليل من <code>routeRobot</code>، لكن لا زال هناك فرصة للتحسين أكثر من ذلك.
</p>

<h2>
	تدريبات
</h2>

<h3>
	معايرة الروبوت
</h3>

<p>
	من الصعب موازنة الروبوتات بجعلها تحل بعض السيناريوهات البسيطة، فلعل أحدها حصل على مهام سهلة دون الآخر.
</p>

<p>
	اكتب دالة <code>compareRobots</code> التي تأخذ روبوتين مع ذاكرتهما الابتدائية، وتولد مائة مهمة، ثم اجعل كل واحد من الروبوتين يحل هذه المهام كلها واحدة واحدة، ويجب أن يكون الخرج عند انتهائهما هو العدد المتوسط للخطوات التي قطعها كل واحد لكل مهمة.
</p>

<p>
	تأكد من إعطاء المهمة نفسها لكلا الروبوتين في تلك المهام المئة لضمان العدل والدقة في النتيجة، بدلًا من توليد مهام مختلفة لكل روبوت.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a data-ss1624460821="1" href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_36" style="">
<span class="kwd">function</span><span class="pln"> compareRobots</span><span class="pun">(</span><span class="pln">robot1</span><span class="pun">,</span><span class="pln"> memory1</span><span class="pun">,</span><span class="pln"> robot2</span><span class="pun">,</span><span class="pln"> memory2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// شيفرتك هنا</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

compareRobots</span><span class="pun">(</span><span class="pln">routeRobot</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[],</span><span class="pln"> goalOrientedRobot</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[]);</span></pre>

<p>
	<strong>إرشادات للحل</strong>
</p>

<p>
	سيكون عليك كتابة صورة من دالة <code>runRobot</code>، بحيث تعيد عدد الخطوات التي قطعها الروبوت لإتمام المهمة، بدلًا من تسجيل الأحداث في الطرفية. عندئذ تستطيع دالة القياس توليد حالات جديدة في حلقة تكرارية، وعدّ خطوات كل روبوت؛ وحين تولد قياسات كافية، فيمكنها استخدام <code>console.log</code> لإخراج المتوسط لكل روبوت، والذي سيكون ناتج قسمة العدد الكلي للخطوات المقطوعة على عدد القياسات.
</p>

<h3>
	كفاءة الروبوت
</h3>

<p>
	هل تستطيع كتابة روبوت ينهي مهمة التوصيل أسرع من <code>goalOrientedRobot</code>؟ ما الأشياء التي تبدو غبيةً بوضوح؟ وكيف يمكن تطويرها؟
</p>

<p>
	إن حللت التدريبات السابقة، فربما تود استخدام دالة <code>compareRobots</code> التي أنشأتَها قبل قليل للتحقق إن كنت قد حسّنت الروبوت أم لا.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a data-ss1624460821="1" href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_38" style="">
<span class="com">// شيفرتك هنا</span><span class="pln">

runRobotAnimation</span><span class="pun">(</span><span class="typ">VillageState</span><span class="pun">.</span><span class="pln">random</span><span class="pun">(),</span><span class="pln"> yourRobot</span><span class="pun">,</span><span class="pln"> memory</span><span class="pun">);</span></pre>

<p>
	<strong>إرشادات للحل</strong>
</p>

<p>
	إن القيد الرئيسي لـ <code>goalOrientedRobot</code> هو تعاملها مع طرد واحد في كل مرة، وستمضي ذهابًا وإيابًا في القرية، وذلك لأن الطرد الذي تريده موجود على الناحية الأخرى من الخارطة، حتى لو كان في طريقها طرود أخرى أقرب.
</p>

<p>
	أحد الحلول الممكنة هنا هو حساب طرق جميع الطرود ثم أخذ أقصرها، ويمكن الحصول على نتائج أفضل إذا كان لدينا عدة طرق قصيرة، فسنختار حينها الطرق التي فيها التقاط طرد بدلًا من التي فيها تسليم طرد.
</p>

<h3>
	المجموعة الثابتة
</h3>

<p>
	ستجد أغلب هياكل البيانات الموجودة في بيئة جافاسكربت القياسية لا تناسب الاستخدام الثابت، حيث تملك المصفوفات التابعَين <code>slice</code> و<code>concat</code> اللذَين يسمحان لنا بإنشاء مصفوفات جديدة دون تدمير القديمة، لكن <code>Set</code> مثلًا ليس فيه توابع لإنشاء فئة جديدة فيها عنصر مضاف إلى الفئة الأولى أو محذوف منها.
</p>

<p>
	اكتب صنف جديد باسم <code>PGroup</code> يشبه الصنف <code>Group</code> من <a data-ss1624460821="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AD%D9%8A%D8%A7%D8%A9-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%A9-%D9%84%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1243/" rel="">المقال السادس</a>، حيث يخزن مجموعة من القيم، وتكون له التوابع <code>add</code>، و<code>delete</code>، و<code>has</code>، كما في الصنف <code>Group</code> تمامًا.
</p>

<p>
	يعيد التابع <code>add</code> فيه نسخةً جديدةً من <code>PGroup</code> مع إضافة العضو المعطى given member وترك القديم دون المساس به، وبالمثل، فيجب على التابع <code>delete</code> إنشاء نسخةً جديدةً ليس فيها العضو المعطى.
</p>

<p>
	يجب أن يعمل هذا الصنف مع أي نوع من القيم وليس السلاسل النصية فقط، ولا تُشترط كفاءته عند استخدامه مع كميات كبيرة من القيم؛ كذلك ليس شرطًا أن يكون الباني constructor جزءًا من واجهة الصنف رغم أنك تريد استخدام ذلك داخليًا، وإنما هناك نسخة فارغة <code>PGroup.empty</code> يمكن استخدامها على أساس قيمة ابتدائية.
</p>

<p>
	لماذا تظن أننا نحتاج إلى قيمة <code>PGroup.empty</code> واحدة فقط بدلًا من دالة تنشئ خارطةً جديدةً وفارغةً في كل مرة؟
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a data-ss1624460821="1" href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5764_40" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">PGroup</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// شيفرتك هنا</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let a </span><span class="pun">=</span><span class="pln"> </span><span class="typ">PGroup</span><span class="pun">.</span><span class="pln">empty</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"a"</span><span class="pun">);</span><span class="pln">
let ab </span><span class="pun">=</span><span class="pln"> a</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"b"</span><span class="pun">);</span><span class="pln">
let b </span><span class="pun">=</span><span class="pln"> ab</span><span class="pun">.</span><span class="kwd">delete</span><span class="pun">(</span><span class="str">"a"</span><span class="pun">);</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">b</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="str">"b"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">a</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="str">"b"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">b</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="str">"a"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span></pre>

<p>
	<strong>إرشادات للحل</strong>
</p>

<p>
	ستكون أفضل طريقة لتمثيل مجموعة من القيم الأعضاء مصفوفةً لسهولة نسخها. تستطيع إنشاء مجموعةً جديدةً حين تضاف قيمة ما إليها، وذلك بنسخ المصفوفة الأصلية التي فيها القيمة المضافة -باستخدام <code>concat</code> مثلًا-، وتستطيع ترشيحها من المصفوفة حين تُحذف تلك القيمة.
</p>

<p>
	يستطيع باني الصنف أخذ مثل هذه المصفوفة على أساس وسيط، ويخزنها على أنها الخاصية الوحيدة للنسخة، ولا تُحدَّث هذه المصفوفة بعدها.
</p>

<p>
	يحب إضافة الخاصية <code>empty</code> إلى باني غير تابع بعد تعريف الصنف، مثل وسيط عادي.
</p>

<p>
	تحتاج إلى نسخة <code>empty</code> واحدة فقط بسبب تطابق المجموعات الفارغة وعدم تغير نُسَخ الصنف، كما تستطيع إنشاء مجموعات مختلفة عديدة من هذه المجموعة الفارغة دون التأثير عليها.
</p>

<p>
	ترجمة -بتصرف- <a data-ss1624460821="1" href="https://eloquentjavascript.net/07_robot.html" rel="external nofollow">للفصل السابع من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>
]]></description><guid isPermaLink="false">1244</guid><pubDate>Wed, 23 Jun 2021 15:08:18 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x62E;&#x627;&#x637;&#x628; &#x628;&#x64A;&#x646; &#x646;&#x648;&#x627;&#x641;&#x630; &#x627;&#x644;&#x645;&#x62A;&#x635;&#x641;&#x62D; &#x639;&#x628;&#x631; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D9%86%D9%88%D8%A7%D9%81%D8%B0-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1250/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_06/1.jpg.0dfe98b24fdb066eeb7659b129f4bc3d.jpg" /></p>

<p>
	تقيِّد سياسة الأصل المشترك same origin أو سياسة الموقع المشترك same site من إمكانية وصول النوافذ أو الإطارات إلى بعضها إن لم تكن ضمن الموقع نفسه، فلو فتح مستخدم صفحتين، بحيث الأولى مصدرها الموقع <code>academy.hsoub.com</code>، والأخرى مصدرها الموقع <code>gmail.com</code>؛ فلا نريد هنا بالطبع لأية شيفرة من الصفحة <code>academy.hsoub.com</code> أن تقرأ بريده الإلكتروني الذي تعرضه الصفحة <code>gmail.com</code>، فالغاية إذًا من سياسة الأصل المشترك هي حماية المستخدم من لصوص المعلومات.
</p>

<h2>
	الأصل المشترك
</h2>

<p>
	نقول أنّ لعنواني URL أصلًا مشتركًا إن كان لهما نفس البروتوكول، والنطاق domain، والمنفذ. لذا يمكن القول أنّ للعناوين التالية أصلًا مشتركًا:
</p>

<ul>
<li>
		<code><a data-ss1625065644="1" data-ss1634982736="1" href="http://site.com" ipsnoembed="true" rel="external nofollow">http://site.com</a></code>
	</li>
	<li>
		<code><a data-ss1625065644="1" data-ss1634982736="1" href="http://site.com" ipsnoembed="true" rel="external nofollow">http://site.com/</a></code><span style="display: none;"> </span>
	</li>
	<li>
		<code><a data-ss1625065644="1" data-ss1634982736="1" href="http://site.com/my/page.html" ipsnoembed="true" rel="external nofollow">http://site.com/my/page.html</a></code>
	</li>
</ul>
<p>
	بينما لا تشترك العناوين التالية مع السابقة بالأصل:
</p>

<ul>
<li>
		<code><a data-ss1625065644="1" data-ss1634982736="1" href="http://www.site.com" ipsnoembed="true" rel="external nofollow">http://www.site.com</a></code> (النطاق مختلف لوجود <code>www</code> هنا).
	</li>
	<li>
		<code><a data-ss1625065644="1" data-ss1634982736="1" href="http://site.org" ipsnoembed="true" rel="external nofollow">http://site.org</a></code> (النطاق مختلف لوجود <code>org</code> بدلًا من com).
	</li>
	<li>
		<code><a data-ss1625065644="1" data-ss1634982736="1" href="https://site.com" ipsnoembed="true" rel="external nofollow">https://site.com</a></code> (البروتوكول مختلف هنا <code>https</code>).
	</li>
	<li>
		<code><a data-ss1625065644="1" data-ss1634982736="1" href="http://site.com:8080" ipsnoembed="true" rel="external nofollow">http://site.com:8080</a></code> (المنفذ مختلف <code>8080</code>).
	</li>
</ul>
<p>
	وتنص سياسة الأصل المشترك على مايلي:
</p>

<ul>
<li>
		إذا وُجِد مرجع إلى نافذة أخرى، مثل: النوافذ المنبثقة المُنشأة باستخدام الأمر <code>window.open</code>، أو الموجودة داخل وسم النافذة الضمنية <code>&lt;iframe&gt;</code>، وكان للنافذتين الأصل نفسه، فسيمنحنا ذلك إمكانية الوصول الكامل إلى تلك النافذة.
	</li>
	<li>
		إذا لم يكن للنافذتين الأصل نفسه، فلا يمكن لإحداهما الوصول إلى محتوى الأخرى، بما في ذلك المتغيرات والمستندات، ويُستثنى من ذلك موضع النافذة <code>location</code>، إذ يمكننا تغييره (وبالتالي إعادة توجيه المستخدم)، دون القدرة على قراءة موضع النافذة (وبالتالي لا يمكننا معرفة مكان المستخدم الآن، وبالتالي لن تتسرب المعلومات).
	</li>
</ul>
<h2>
	العمل مع النوافذ الضمنية iframe
</h2>

<p>
	يستضيف الوسم <code>iframe</code> نافذةً ضمنيةً منفصلةً عن النافذة الأصلية، ولها كائنان، هما <code>document</code> و<code>window</code> مستقلان، ويمكن الوصول إليهما بالشكل التالي:
</p>

<ul>
<li>
		<code>iframe.contentWindow</code> للحصول على الكائن <code>window</code> الموجودة ضمن الإطار <code>iframe</code>.
	</li>
	<li>
		<code>iframe.contentDocument</code> للحصول على الكائن<code>document</code> ضمن الإطار <code>iframe</code>، وهو اختصار للأمر <code>iframe.contentWindow.document</code>.
	</li>
</ul>
<p>
	سيتحقق المتصفح أنّ للإطار والنافذة الأصل نفسه عندما نَلِج إلى شيء ما داخل نافذة مضمّنة، وسيرفض الوصول إن لم يتحقق ذلك (تُستنثنى هنا عملية تغيير الخاصية <code>location</code> فلا تزال مسموحة).
</p>

<p>
	لنحاول مثلًا القراءة والكتابة إلى الإطار <code>iframe</code> من مورد لا يشترك معه بالأصل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4063_6" style="">
<span class="pun">&lt;</span><span class="pln">iframe src</span><span class="pun">=</span><span class="str">"https://example.com"</span><span class="pln"> id</span><span class="pun">=</span><span class="str">"iframe"</span><span class="pun">&gt;&lt;/</span><span class="pln">iframe</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  iframe</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// يمكننا الحصول على مرجع إلى النافذة الداخلية</span><span class="pln">
    let iframeWindow </span><span class="pun">=</span><span class="pln"> iframe</span><span class="pun">.</span><span class="pln">contentWindow</span><span class="pun">;</span><span class="pln"> </span><span class="com">// صحيح</span><span class="pln">
    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// ...لكن ليس للمستند الموجود ضمنه</span><span class="pln">
      let doc </span><span class="pun">=</span><span class="pln"> iframe</span><span class="pun">.</span><span class="pln">contentDocument</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      alert</span><span class="pun">(</span><span class="pln">e</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (خطأ أمان (أصل مختلف </span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">// لايمكننا أيضًا قراءة العنوان ضمن الإطار</span><span class="pln">
    </span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">//  Location لا يمكن قراءة عنوان الموقع من كائن الموضع</span><span class="pln">
      let href </span><span class="pun">=</span><span class="pln"> iframe</span><span class="pun">.</span><span class="pln">contentWindow</span><span class="pun">.</span><span class="pln">location</span><span class="pun">.</span><span class="pln">href</span><span class="pun">;</span><span class="pln"> </span><span class="com">// خطأ</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      alert</span><span class="pun">(</span><span class="pln">e</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Security Error</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="com">// يمكن الكتابة ضمن خاصية الموضع وبالتالي تحميل شيء آخر ضمن الإطار</span><span class="pln">

    iframe</span><span class="pun">.</span><span class="pln">contentWindow</span><span class="pun">.</span><span class="pln">location </span><span class="pun">=</span><span class="pln"> </span><span class="str">'/'</span><span class="pun">;</span><span class="pln"> </span><span class="com">// صحيح</span><span class="pln">

    iframe</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln"> </span><span class="com">//امسح معالج الحدث، لا تنفذه بعد تغيير الموضع</span><span class="pln">

  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634982736="1" frameborder="no" height="271" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/mdMRPwZ?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-cross-window-communications-ex1">See the Pen JS-P3-01-cross-window-communications-ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	ستعطي الشيفرة السابقة أخطاءً عند تنفيذها عدا حالتي:
</p>

<ul>
<li>
		الحصول على مرجع إلى النافذة الداخلية <code>iframe.contentWindow</code> فهو أمر مسموح.
	</li>
	<li>
		تغيير الموضع <code>location</code>.
	</li>
</ul>
<p>
	وبالمقابل، إن كان للإطار <code>iframe</code> نفس أصل النافذة الداخلية، فيمكننا أن نفعل فيها ما نشاء:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4063_8" style="">
<span class="pun">&lt;!--</span><span class="pln"> iframe from the same site </span><span class="pun">--&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">iframe src</span><span class="pun">=</span><span class="str">"/"</span><span class="pln"> id</span><span class="pun">=</span><span class="str">"iframe"</span><span class="pun">&gt;&lt;/</span><span class="pln">iframe</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  iframe</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// افعل ما تشاء</span><span class="pln">
    iframe</span><span class="pun">.</span><span class="pln">contentDocument</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">prepend</span><span class="pun">(</span><span class="str">"Hello, world!"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634982736="1" frameborder="no" height="256" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/ZEJLWJJ?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-cross-window-communications-ex2">See the Pen JS-P3-01-cross-window-communications-ex2 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h4>
	الموازنة بين الحدثين <code>iframe.onload</code> و<code>iframe.contentWindow.onload</code>
</h4>

<p>
	يؤدي الحدث <code>iframe.onload</code> على الوسم <code>iframe</code> مبدئيًا، نفس ما يؤديه الحدث <code>iframe.contentWindow.onload</code> على كائن النافذة المضمنة، إذ يقع الحدث عندما تُحمّل النافذة المضمنة بكل مواردها، لكن لا يمكن الوصول إلى الحدث <code>iframe.contentWindow.onload</code> لإطار من آخر ذي أصل مختلف، وبالتالي سنستخدم عندها <code>iframe.onload</code>.
</p>

<h2>
	النوافذ ضمن النطاقات الفرعية والخاصية document.domain
</h2>

<p>
	وجدنا من التعريف أنّ العناوين المختلفة تقود إلى موارد غير مشتركة بالأصل، لكن لو تشاركت النوافذ بالنطاق الفرعي أو النطاق من المستوى الثاني -مثل النطاقين الفرعيين التاليين: <code>john.site.com</code> و<code>peter.site.com</code> (المشتركان بالنطاق الفرعي <code>site.com</code>)-، فيمكن حينها أن نطلب من المتصفح أن يتجاهل الفرق، وبالتالي سيعدهما من أصل مشترك، وذلك لأغراض التخاطب بين النوافذ.
</p>

<p>
	ولكي ننفذ ذلك لا بدّ من وجود الشيفرة التالية في كل نافذة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4063_10" style="">
<span class="pln">document</span><span class="pun">.</span><span class="pln">domain </span><span class="pun">=</span><span class="pln"> </span><span class="str">'site.com'</span><span class="pun">;</span></pre>

<p>
	ستتخاطب النوافذ مع بعضها الآن دون معوقات، ولا بد أن نذكر أن هذا ممكن فقط في الصفحات التي تشترك بنطاق من المستوى الثاني.
</p>

<h2>
	مشكلة كائن "document" خاطئ في النوافذ الضمنية
</h2>

<p>
	عندما نتعامل مع نافذة ضمنية لها الأصل نفسه، يمكننا الوصول إلى الكائن <code>document</code> الخاص بها، وعندها ستواجهنا مشكلة لا تتعلق بالأصل المشترك -لكن لا بدّ من الإشارة إليها-، وهي أن النافذة الضمنية سيكون لها كائن <code>document</code> مباشرةً بعد إنشائها، لكنه سيختلف عن الكائن الذي سيُحمَّل ضمنها، وبالتالي قد يضيع الأمر الذي ننفِّذه على الكائن <code>document</code> مباشرةً. لنتأمل الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4063_12" style="">
<span class="pun">&lt;</span><span class="pln">iframe src</span><span class="pun">=</span><span class="str">"/"</span><span class="pln"> id</span><span class="pun">=</span><span class="str">"iframe"</span><span class="pun">&gt;&lt;/</span><span class="pln">iframe</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  let oldDoc </span><span class="pun">=</span><span class="pln"> iframe</span><span class="pun">.</span><span class="pln">contentDocument</span><span class="pun">;</span><span class="pln">
  iframe</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let newDoc </span><span class="pun">=</span><span class="pln"> iframe</span><span class="pun">.</span><span class="pln">contentDocument</span><span class="pun">;</span><span class="pln">
    </span><span class="com">// كائن المستند الذي حُمِّل مختلف عن الكائن الأساسي</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">oldDoc </span><span class="pun">==</span><span class="pln"> newDoc</span><span class="pun">);</span><span class="pln"> </span><span class="com">// false النتيجة </span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634982736="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/qBXRZXM?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-cross-window-communications-ex3">See the Pen JS-P3-01-cross-window-communications-ex3 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لا ينبغي التعامل مع الكائن <code>document</code> لأي إطار قبل التحميل الكامل لمحتوياته لتجنب التعامل مع الكائن الخاطئ، إذ سيتجاهل المتصفح أية معالجات أحداث قد نستخدمها ضمن الكائن.
</p>

<p>
	لكن كيف نحدد اللحظة التي يجهز فيها الكائن <code>document</code>؟ سيظهر المستند بالتأكيد عند وقوع الحدث <code>iframe.onload</code>، والذي لا يطلق إلا عندما تُحمَّل موارد الإطار كلها. كما يمكن التقاط اللحظة المناسبة بالتحقق المستمر خلال فترات زمنية محددة عبر التعليمة <code>setInterval</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4063_14" style="">
<span class="pun">&lt;</span><span class="pln">iframe src</span><span class="pun">=</span><span class="str">"/"</span><span class="pln"> id</span><span class="pun">=</span><span class="str">"iframe"</span><span class="pun">&gt;&lt;/</span><span class="pln">iframe</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  let oldDoc </span><span class="pun">=</span><span class="pln"> iframe</span><span class="pun">.</span><span class="pln">contentDocument</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// تحقق أن المستند الجديد جاهز كل 100 ميلي ثانية</span><span class="pln">
  let timer </span><span class="pun">=</span><span class="pln"> setInterval</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let newDoc </span><span class="pun">=</span><span class="pln"> iframe</span><span class="pun">.</span><span class="pln">contentDocument</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">newDoc </span><span class="pun">==</span><span class="pln"> oldDoc</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">

    alert</span><span class="pun">(</span><span class="str">"New document is here!"</span><span class="pun">);</span><span class="pln">

    clearInterval</span><span class="pun">(</span><span class="pln">timer</span><span class="pun">);</span><span class="pln"> </span><span class="com">//   فلن تحتاجها الآن setInterval  ألغ  </span><span class="pln">
  </span><span class="pun">},</span><span class="pln"> </span><span class="lit">100</span><span class="pun">);</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634982736="1" frameborder="no" height="236" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/wvqgGrK?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-cross-window-communications-ex4">See the Pen JS-P3-01-cross-window-communications-ex4 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h2>
	استخدام المجموعة window.frames
</h2>

<p>
	يمكن استخدام طريقة بديلة للوصول إلى الكائن <code>window</code> الخاص بالنافذة الضمنية <code>iframe</code> عبر المجموعة <code>window.frames</code>:
</p>

<ul>
<li>
		باستخدام الرقم: إذ سيعطي الأمر <code>[window.frames[0</code> كائن <code>window</code> الخاص بأول إطار في المستند.
	</li>
	<li>
		باستخدام الاسم: إذ يعطي الأمر <code>window.frames.iframeName</code> كائن <code>window</code> الخاص بالإطار الذي يحمل الاسم <code>"name="iframeName</code>.
	</li>
</ul>
<p>
	مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4063_16" style="">
<span class="pun">&lt;</span><span class="pln">iframe src</span><span class="pun">=</span><span class="str">"/"</span><span class="pln"> style</span><span class="pun">=</span><span class="str">"height:80px"</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"win"</span><span class="pln"> id</span><span class="pun">=</span><span class="str">"iframe"</span><span class="pun">&gt;&lt;/</span><span class="pln">iframe</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">iframe</span><span class="pun">.</span><span class="pln">contentWindow </span><span class="pun">==</span><span class="pln"> frames</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln"> </span><span class="com">// true</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">iframe</span><span class="pun">.</span><span class="pln">contentWindow </span><span class="pun">==</span><span class="pln"> frames</span><span class="pun">.</span><span class="pln">win</span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634982736="1" frameborder="no" height="227" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/porRyWe?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-cross-window-communications-ex5">See the Pen JS-P3-01-cross-window-communications-ex5 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	قد تحوي نافذة ضمنية ما نوافذ ضمنيةً أخرى، وعندها ستكون الكائنات <code>window</code> الناتجة عن الانتقال الشجري بين الإطارات هي:
</p>

<ul>
<li>
		<code>window.frames</code>: وتمثل مجموعة النوافذ الأبناء للإطارات المتداخلة nested.
	</li>
	<li>
		<code>window.parent</code>: المرجع إلى النافذة الأم المباشرة (الخارجية).
	</li>
	<li>
		<code>window.top</code>: المرجع إلى النافذة الأم الأعلى ترتيبًا.
	</li>
</ul>
<p>
	مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4063_18" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">frames</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">parent </span><span class="pun">===</span><span class="pln"> window</span><span class="pun">;</span><span class="pln"> </span><span class="com">// محقق</span></pre>

<p>
	يمكن استخدام الخاصية <code>top</code> للتحقق من أنّ المستند الحالي مفتوح ضمن إطار أم لا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4063_20" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">window </span><span class="pun">==</span><span class="pln"> top</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// current window == window.top?</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">'The script is in the topmost window, not in a frame'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">'The script runs in a frame!'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634982736="1" frameborder="no" height="217" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/oNeBxGJ?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-cross-window-communications-ex6">See the Pen JS-P3-01-cross-window-communications-ex6 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h2>
	الخاصية sandbox المتعلقة بالنافذة الضمنية iframe
</h2>

<p>
	تسمح الخاصية <code>sandbox</code> باستبعاد تنفيذ أفعال معينة داخل النافذة الضمنية <code>&lt;iframe&gt;</code>، وذلك لمنع تنفيذ الشيفرة غير الموثوقة، إذ تقيَّد الخاصية sandbox للإطار كونه آتيًا من أصل مختلف مع/أو بالإضافة إلى قيود أخرى.
</p>

<p>
	تتوفر عدة تقييدات افتراضية ضمن الأمر <code>&lt;"..."=iframe sandbox src&gt;</code>، ويفضل أن نترك فراغًا بين عناصر قائمة القيود التي لا نريد تطبيقها كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4063_22" style="">
<span class="pun">&lt;</span><span class="pln">iframe sandbox</span><span class="pun">=</span><span class="str">"allow-forms allow-popups"</span><span class="pun">&gt;</span></pre>

<p>
	ستطبّق أكثر التقييدات صرامةً عندما تكون الخاصية <code>sandbox</code> فارغة "دون قائمة قيود" ، ويمكننا رفع ما نشاء منها بذكرها ضمن قائمة يفصل بين عناصرها فراغات.
</p>

<p>
	وإليك قائمة الخيارات المتعلقة بالتقييدات:
</p>

<ul>
<li>
		<code>allow-same-origin</code>: تجبر الخاصية <code>sandbox</code> المتصفح على احتساب النافذة الضمنية من أصل مختلف تلقائيًا، حتى لو دلت الصفة <code>src</code> على الأصل نفسه، لكن هذا الخيار يزيل التطبيق الافتراضي لهذه الميزة.
	</li>
	<li>
		<code>allow-top-navigation</code>: يسمح هذا الخيار للنافذة الضمنية بتغيير قيمة <code>parent.location</code>.
	</li>
	<li>
		<code>allow-forms</code>: يسمح هذا الخيار بإرسال نماذج forms انطلاقًا من الإطار.
	</li>
	<li>
		<code>allow-scripts</code>: يسمح هذا الخيار بتشغيل سكربتات انطلاقًا من الإطار.
	</li>
	<li>
		<code>allow-popups</code>: يسمح بفتح نوافذ منبثقة باستخدام الأمر <code>window.open</code> انطلاقًا من الإطار.
	</li>
</ul>
<p>
	اطلع على <a data-ss1625065644="1" data-ss1634982736="1" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe" rel="external nofollow">تعليمات التنفيذ</a> إن أردت التعرف على المزيد.
</p>

<p>
	يُظهر المثال التالي إطارًا مقيَّدًا بمجموعة القيود الافتراضية <code>&lt;"..."=iframe sandbox src&gt;</code>، ويتضمن المثال شيفرةً ونموذجًا وملفين، وستلاحظ أنّ الشيفرة لن تعمل نظرًا لصرامة القيود الافتراضية.
</p>

<p>
	الشيفرة الموجودة في الملف "index.html":
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4063_24" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">

</span><span class="tag">&lt;head&gt;</span><span class="pln">
  </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">

</span><span class="tag">&lt;body&gt;</span><span class="pln">

  </span><span class="tag">&lt;div&gt;</span><span class="pln">The iframe below has the </span><span class="tag">&lt;code&gt;</span><span class="pln">sandbox</span><span class="tag">&lt;/code&gt;</span><span class="pln"> attribute.</span><span class="tag">&lt;/div&gt;</span><span class="pln">

  </span><span class="tag">&lt;iframe</span><span class="pln"> </span><span class="atn">sandbox</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"sandboxed.html"</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">height</span><span class="pun">:</span><span class="lit">60px</span><span class="pun">;</span><span class="pln">width</span><span class="pun">:</span><span class="lit">90</span><span class="pun">%</span><span class="atv">"</span><span class="tag">&gt;&lt;/iframe&gt;</span><span class="pln">

</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	وفي الملف "sandbox.html":
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4063_26" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">

</span><span class="tag">&lt;head&gt;</span><span class="pln">
  </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">

</span><span class="tag">&lt;body&gt;</span><span class="pln">

  </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">onclick</span><span class="pun">=</span><span class="atv">"</span><span class="pln">alert</span><span class="pun">(</span><span class="lit">123</span><span class="pun">)</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">Click to run a script (doesn't work)</span><span class="tag">&lt;/button&gt;</span><span class="pln">

  </span><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">action</span><span class="pun">=</span><span class="atv">"http://google.com"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"submit"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Submit (doesn't work)"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;/form&gt;</span><span class="pln">

</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	وستكون النتيجة:
</p>

<p>
	<iframe __idm_frm__="89" class="code-tabs__result" data-ss1625065644="1" data-ss1634982736="1" src="https://javascript.info/article/cross-window-communication/sandbox/" style="display: block; border: 0px; width: 725px; height: 122px;"></iframe>
</p>

<p>
	<strong>ملاحظة</strong>: إنّ الغاية من وجود الصفة <code>sandbox</code> هي إضافة المزيد من القيود فقط، إذ لا يمكنها إزالة هذه القيود، لكنها تخفف بعض القيود المتعلقة بالأصل المشترك إن لم يكن للإطار الأصل نفسه.
</p>

<h2>
	تبادل الرسائل بين النوافذ
</h2>

<p>
	تسمح واجهة التخاطب <code>postMessage</code> للنوافذ بالتخاطب مع بعضها أيًا كان أصلها، فهي إذًا أسلوب للالتفاف على سياسة "الأصل المشترك"، إذ يمكن لنافذة من النطاق academy.hsoub.com<code>أن تتخاطب مع النافذة</code>gmail.com` وتتبادل المعلومات معها، لكن بعد موافقة كلتا النافذتين ووجود دوال JavaScript الملائمة فيهما، وهذا بالطبع أكثر أمانًا للمستخدم.
</p>

<p>
	تتكون الواجهة من قسمين رئيسيين:
</p>

<h3>
	القسم المتعلق بالتابع postMessage
</h3>

<p>
	تستدعي النافذة التي تريد إرسال رسالةٍ التابع <a data-ss1625065644="1" data-ss1634982736="1" href="https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage" rel="external nofollow">postMessage</a> العائد للنافذة الهدف، أي إن أردنا إرسال رسالة إلى <code>win</code>، فعلينا تنفيذ الأمر <code>(win.postMessage(data, targetOrigin</code>، وإليك توضيح وسطاء هذا التابع:
</p>

<ul>
<li>
		<code>data</code>: وهي البيانات المراد إرسالها، والتي قد تكون أي كائن. حيث تُنسخ البيانات باستخدام خوارزمية التفكيك البنيوي "structured serialization". لا يدعم المتصفح Internet Explorer سوى النصوص، لذلك لا بدّ من استخدام التابع <code>JSON.stringify</code> مع الكائنات المركبة التي سنرسلها لدعم هذا المتصفح.
	</li>
	<li>
		<code>targetOrigin</code>: يُحدد أصل النافذة الهدف، وبالتالي ستصل الرسالة إلى نافذة من أصل محدد فقط، ويعَدّ هذا مقياسًا للأمان، فعندما تأتي النافذة الهدف من أصل مختلف، فلا يمكن للنافذة المرسِلة قراءة موقعها <code>location</code>، ولن نعرف بالتأكيد الموقع الذي سيُفتح حاليًا في النافذة المحددة، وقد ينجرف المستخدم بعيدًا دون علم النافذة المرسِلة. إذًا سيضمن تحديد الوسيط <code>targetOrigin</code> أن النافذة الهدف ستستقبل البيانات ما دامت في الموقع الصحيح، وهذا ضروري عندما تكون البيانات على درجة من الحساسية.
	</li>
</ul>
<p>
	في هذا المثال ستستقبل النافذة الرسالة إن كان مستندها (الكائن <code>document</code> الخاص بها) منتميًا إلى الموقع "http://example.com":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4063_28" style="">
<span class="pun">&lt;</span><span class="pln">iframe src</span><span class="pun">=</span><span class="str">"http://example.com"</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"example"</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  let win </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">frames</span><span class="pun">.</span><span class="pln">example</span><span class="pun">;</span><span class="pln">

  win</span><span class="pun">.</span><span class="pln">postMessage</span><span class="pun">(</span><span class="str">"message"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"http://example.com"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	يمكن إلغاء التحقق من الأصل بإسناد القيمة "*" إلى الوسيط <code>targetOrigin</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4063_30" style="">
<span class="pun">&lt;</span><span class="pln">iframe src</span><span class="pun">=</span><span class="str">"http://example.com"</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"example"</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  let win </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">frames</span><span class="pun">.</span><span class="pln">example</span><span class="pun">;</span><span class="pln">

  win</span><span class="pun">.</span><span class="pln">postMessage</span><span class="pun">(</span><span class="str">"message"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"*"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<h3>
	القسم المتعلق بالحدث onmessage
</h3>

<p>
	ينبغي أن تمتلك النافذة التي ستستقبل الرسالة، معالجًا للحدث <code>message</code>، والذي يقع عند استدعاء التابع <code>postMessage</code> (ويجري التحقق بنجاح من قيمة الوسيط <code>targetOrigin</code>).
</p>

<p>
	لكائن الحدث خصائص مميزة، هي:
</p>

<ul>
<li>
		<code>data</code>: وهي البيانات التي يحضرها التابع <code>postMessage</code>.
	</li>
	<li>
		<code>origin</code>: أصل المرسل، مثلًا: <code><a data-ss1625065644="1" data-ss1634982736="1" href="http://javascript.info" ipsnoembed="true" rel="external nofollow">http://javascript.info</a></code>.
	</li>
	<li>
		<code>source</code>: المرجع إلى النافذة المرسِلة، إذ يمكننا الرد مباشرة بالشكل التالي <code>(...)source.postMessage</code> إن أردنا.
	</li>
</ul>
<p>
	ولتحديد معالج الحدث السابق لا بدّ من استخدام <code>addEventListener</code>، إذ لن يعمل الأمر دونه، فمثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4063_32" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"message"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">origin </span><span class="pun">!=</span><span class="pln"> </span><span class="str">'http://javascript.info'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// كائن من نطاق مختلف، سنتجاهله</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln"> </span><span class="str">"received: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">data </span><span class="pun">);</span><span class="pln">

  </span><span class="com">// يمكن إعادة الإرسال باستخدام event.source.postMessage(...)</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يتكون المثال من الملف "iframe.html":
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4063_34" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">

</span><span class="tag">&lt;head&gt;</span><span class="pln">
  </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">

</span><span class="tag">&lt;body&gt;</span><span class="pln">

  Receiving iframe.
  </span><span class="tag">&lt;script&gt;</span><span class="pln">
    window</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'message'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      alert</span><span class="pun">(`</span><span class="typ">Received</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">event</span><span class="pun">.</span><span class="pln">data</span><span class="pun">}</span><span class="pln"> from $</span><span class="pun">{</span><span class="pln">event</span><span class="pun">.</span><span class="pln">origin</span><span class="pun">}`);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="tag">&lt;/script&gt;</span><span class="pln">

</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	والملف "index.html":
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4063_36" style="">
<span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">

</span><span class="tag">&lt;head&gt;</span><span class="pln">
  </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">

</span><span class="tag">&lt;body&gt;</span><span class="pln">

  </span><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"form"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"Enter message"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"message"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"submit"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Click to send"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;/form&gt;</span><span class="pln">

  </span><span class="tag">&lt;iframe</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"iframe.html"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"iframe"</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">display</span><span class="pun">:</span><span class="pln">block</span><span class="pun">;</span><span class="pln">height</span><span class="pun">:</span><span class="lit">60px</span><span class="atv">"</span><span class="tag">&gt;&lt;/iframe&gt;</span><span class="pln">

  </span><span class="tag">&lt;script&gt;</span><span class="pln">
    form</span><span class="pun">.</span><span class="pln">onsubmit </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      iframe</span><span class="pun">.</span><span class="pln">contentWindow</span><span class="pun">.</span><span class="pln">postMessage</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">message</span><span class="pun">.</span><span class="pln">value</span><span class="pun">,</span><span class="pln"> </span><span class="str">'*'</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">};</span><span class="pln">
  </span><span class="tag">&lt;/script&gt;</span><span class="pln">

</span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	وستكون النتيجة كالتالي:
</p>

<p>
	<iframe __idm_frm__="90" class="code-tabs__result" data-ss1625065644="1" data-ss1634982736="1" src="https://javascript.info/article/cross-window-communication/postmessage/" style="display: block; border: 0px; width: 725px; height: 102px;"></iframe>
</p>

<h2>
	الخلاصة
</h2>

<p>
	لاستدعاء التوابع التي تسمح بالوصول إلى نوافذ أخرى، لا بدّ من وجود مرجع إليها، حيث نحصل على هذا المرجع بالنسبة للنوافذ المنبثقة باستخدام:
</p>

<ul>
<li>
		<code>window.open</code>: يُنفَّذ ضمن النافذة الأساسية، وسيفتح نافذة جديدة ويعيد مرجعًا إليها.
	</li>
	<li>
		<code>window.opener</code>: يُنفّذ ضمن النافذة المنبثقة، ويعطي مرجعًا إلى النافذة التي أنشأتها.
	</li>
</ul>
<p>
	يمكن الوصول إلى النوافذ الآباء والأبناء ضمن النوافذ الضمنية <code>&lt;iframe&gt;</code> بالشكل التالي:
</p>

<ul>
<li>
		<code>window.frames</code>: تمثل مجموعة من الكائنات <code>window</code> المتداخلة.
	</li>
	<li>
		<code>window.parent</code> و<code>window.top</code>: يمثلان مرجعين إلى النافذة الأم المباشرة والنافذة الأم العليا.
	</li>
	<li>
		<code>iframe.contentWindow</code>: تمثل النافذة الموجودة داخل وسم النافذة الضمنية <code>iframe</code>.
	</li>
</ul>
<p>
	إذا كان لنافذتين الأصل نفسه (البرتوكول والمنفذ والنطاق)، فيمكنهما التحكم ببعضهما كليًا أما إن لم يكن لهما أصل مشترك، فيمكن إجراء مايلي:
</p>

<ul>
<li>
		تغيير موقع <code>location</code> نافذة أخرى (سماحية للكتابة فقط).
	</li>
	<li>
		إرسال رسالة إليها.
	</li>
</ul>
<p>
	لكن توجد بعض الاستثناءات:
</p>

<ul>
<li>
		إذا كان لنافذتين النطاق الفرعي (نطاق من المستوى الثاني) ذاته، مثلًا: <code>a.site.com</code> و<code>b.site.com</code>، فيمكن أن ندفع المتصفح لأن يعدّهما من الأصل ذاته بإضافة الأمر <code>'document.domain='site.com'</code> في كلتيهما.
	</li>
	<li>
		إن امتلك الإطار الخاصية <code>sandbox</code>، فسيُعَدّ قسرًا من أصل مختلف، إلا في الحالة التي نستخدم فيها القيمة <code>allow-same-origin</code> لهذه الخاصية، ويستخدَم ذلك لتشغيل شيفرة غير موثوقة ضمن نافذة ضمنية على الموقع نفسه.
	</li>
</ul>
<p>
	تسمح الواجهة <code>postMessage</code> لنافذتين -ليس لهما بالضرورة الأصل ذاته- بالتخاطب مع بعضهما، حيث:
</p>

<ol>
<li>
		تستدعي النافذة المرسِلة التابع <code>(targetWin.postMessage(data, targetOrigin</code>
	</li>
	<li>
		إن لم يكن للوسيط <code>targetOrigin</code> القيمة "*"، فسيتحقق المتصفح من أن للنافذة الهدف نفس الأصل المحدد كقيمة له.
	</li>
	<li>
		إن كان للنافذتين نفس الأصل أو لم يأخذ الوسيط <code>targetOrigin</code> القيمة "*"، فسيقع الحدث <code>message</code> الذي يمتلك الخصائص التالية:
	</li>
</ol>
<ul style="margin-right: 40px;">
<li>
		<code>origin</code>: نفس أصل النافذة المرسِلة مثل "http://my.site.com".
	</li>
	<li>
		<code>source</code>: وهو المرجع إلى النافذة المرسِلة.
	</li>
	<li>
		<code>data</code>: البيانات المرسَلة، ويمكن أن تكون كائنًا من أي نوع، عدا في المتصفح Internet Explorer الذي لا يقبل سوى الكائنات النصية.
	</li>
</ul>
<p>
	ولا بدّ لنا أيضًا من استخدام <code>addEventListener</code> لضبط معالج هذا الحدث داخل النافذة الهدف.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a data-ss1625065644="1" data-ss1634982736="1" href="https://javascript.info/cross-window-communication" rel="external nofollow">cross-window communication</a> من سلسلة <a data-ss1625065644="1" data-ss1634982736="1" href="https://javascript.info/" rel="external nofollow">The Modern JavaScript Tutorial.</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a data-ss1625065644="1" data-ss1634982736="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D8%A7%D9%84%D9%85%D8%AA%D8%B9%D9%84%D9%82%D8%A9-%D8%A8%D8%AF%D9%88%D8%B1%D8%A9-%D8%AD%D9%8A%D8%A7%D8%A9-%D8%B5%D9%81%D8%AD%D8%A9-html-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D9%87%D8%A7-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1236/" rel="">الأحداث المتعلقة بدورة حياة صفحة HTML وكيفية التحكم بها عبر جافاسكربت</a>
	</li>
	<li>
		<a data-ss1625065644="1" data-ss1634982736="1" href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1071/" rel="">أساسيات بناء تطبيقات الويب</a>
	</li>
	<li>
		<a data-ss1625065644="1" data-ss1634982736="1" href="https://academy.hsoub.com/programming/html/bootstrap/10-%D8%A3%D8%AE%D8%B7%D8%A7%D8%A1-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D8%B9%D9%86%D8%AF-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-bootstrap-r190/" rel="">10 أخطاء شائعة عند استخدام إطار العمل Bootstrap</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1250</guid><pubDate>Tue, 22 Jun 2021 15:02:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62D;&#x64A;&#x627;&#x629; &#x627;&#x644;&#x633;&#x631;&#x64A;&#x629; &#x644;&#x644;&#x643;&#x627;&#x626;&#x646;&#x627;&#x62A; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AD%D9%8A%D8%A7%D8%A9-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%A9-%D9%84%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1243/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_06/60bb098aeb94b_----.png.ef197024c48fb12d0167b3d6885483da.png" /></p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		يلاحَظ نوع البيانات المجرد بكتابة برنامج خاص يعرِّف النوع من حيث العمليات التي يمكن تنفيذها عليه.
	</p>

	<p>
		__ باربرا ليزكوف Barbara Liskov، البرمجة بالأنواع المجردة للبيانات.
	</p>
</blockquote>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="68505" data-ss1625654548="1" href="https://academy.hsoub.com/uploads/monthly_2021_06/chapter_picture_6.jpg.c2f5fc4dfb8b4cac6e78331275b5ba96.jpg" rel=""><img alt="chapter_picture_6.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="68505" data-unique="hfaxtg5xy" src="https://academy.hsoub.com/uploads/monthly_2021_06/chapter_picture_6.jpg.c2f5fc4dfb8b4cac6e78331275b5ba96.jpg"></a>
</p>

<p>
	تحدثنا في <a data-ss1625654548="1" href="https://academy.hsoub.com/programming/javascript/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1234/" rel="">المقال الرابع</a> عن الكائنات في جافاسكربت، ولدينا في ثقافة البرمجة شيء يسمى بالبرمجة كائنية التوجه، وهي مجموعة تقنيات تستخدم الكائنات والمفاهيم المرتبطة بها مثل مبدأ مركزي لتنظيم البرامج. ورغم عدم وجود إجماع على التعريف الدقيق للبرمجة كائنية التوجه هذه، إلا أنها قد غيرت شكل لغات برمجة كثيرة من حيث تصميمها، بما فيها جافاسكربت، وسنتعرض في هذا المقال للطرق التي يمكن تطبيق أفكار هذا المفهوم في جافاسكربت.
</p>

<h2>
	التغليف Encapsulation
</h2>

<p>
	تتلخص فكرة البرمجة كائنية التوجه في تقسيم البرامج إلى أجزاء صغيرة وجعل كل جزء مسؤولًا عن إدارة حالته الخاصة، وهكذا يمكن حفظ المعلومات الخاصة بالأسلوب الذي يعمل به جزء ما من البرنامج داخل ذلك الجزء فقط محليًا، بحيث إذا عمل شخص ما على جزء آخر من البرنامج، فليس عليه معرفة أو إدراك حتى هذه البيانات والمعلومات؛ وإذا تغيرت تلك التفاصيل المحلية، فلن نحتاج سوى إلى تعديل جزء الشيفرة المتعلق بها فقط. ويُطلق على فصل الواجهة عن الاستخدام نفسه أو التطبيق بـ<strong>التغليف</strong>، وهو فكرة عظيمة.
</p>

<p>
	تتفاعل الأجزاء المختلفة من البرامج مع بعضها البعض من خلال واجهات interfaces، وهي مجموعات محدودة من الدوال أو الرابطات bindings التي توفر أداءً مفيدًا في المستويات العليا التجريدية التي تخفي استخدامها الدقيق والمباشر. كما نُمذِجت مثل تلك الأجزاء باستخدام كائنات، وواجهاتها مكونة من مجموعات محددة من التوابع والخصائص، حيث يوجد نوعان من هذه الخصائص، إما <strong>عامة</strong> عندما تكون جزءًا من الواجهة، أو <strong>خاصة</strong> يجب ألا يقربها أي شيء خارج الشيفرة.
</p>

<p>
	توفر الكثير من اللغات طريقةً للتمييز بين الخصائص العامة والخاصة، وذلك لمنع الشيفرات الخارجية من الوصول إلى الخصائص الخاصة؛ أما جافاسكربت فلا تفعل ما سبق، إذ تتبع أسلوبها البسيط في ذلك حاليًا، ويوجد ثمة أعمال لإضافة ذلك إليها.
</p>

<p>
	رغم عدم دعم اللغة لهذه الخاصية في التفرقة، إلا أنّ مبرمجي جافاسكربت يفعلون ذلك من حيث المبدأ، فالواجهة المتاحة موصوفة ومشروحة في التوثيق أو التعليقات، ومن الشائع كذلك وضع شرطة سفلية (<code>_</code>) في بداية أسماء الخصائص للإشارة إلى أنها "خاصة".
</p>

<h2>
	التوابع Methods
</h2>

<p>
	التوابع ليست إلا خصائص حاملة لقيم الدوال، انظر المثال التالي لتابع بسيط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_6" style="">
<span class="pln">let rabbit </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
rabbit</span><span class="pun">.</span><span class="pln">speak </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">line</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">The</span><span class="pln"> rabbit says </span><span class="str">'${line}'</span><span class="pun">`);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

rabbit</span><span class="pun">.</span><span class="pln">speak</span><span class="pun">(</span><span class="str">"I'm alive."</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → The rabbit says 'I'm alive.'</span></pre>

<p>
	يُتوقع من التابع فعل شيء بالكائن الذي استدعي له، فحين تُستدعى دالة على أساس تابع -يُبحث عنها على أساس خاصية، ثم تُستدعى مباشرةً كما في حالة <code>object.method()‎</code>-، ستشير الرابطة التي استدعت <code>this</code> في متنها مباشرةً إلى الكائن الذي استُدعي عليه.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_8" style="">
<span class="kwd">function</span><span class="pln"> speak</span><span class="pun">(</span><span class="pln">line</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">The</span><span class="pln"> $</span><span class="pun">{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">type</span><span class="pun">}</span><span class="pln"> rabbit says </span><span class="str">'${line}'</span><span class="pun">`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
let whiteRabbit </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"white"</span><span class="pun">,</span><span class="pln"> speak</span><span class="pun">};</span><span class="pln">
let hungryRabbit </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"hungry"</span><span class="pun">,</span><span class="pln"> speak</span><span class="pun">};</span><span class="pln">

whiteRabbit</span><span class="pun">.</span><span class="pln">speak</span><span class="pun">(</span><span class="str">"Oh my ears and whiskers, "</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
                  </span><span class="str">"how late it's getting!"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → The white rabbit says 'Oh my ears and whiskers, how</span><span class="pln">
</span><span class="com">//   late it's getting!'</span><span class="pln">
hungryRabbit</span><span class="pun">.</span><span class="pln">speak</span><span class="pun">(</span><span class="str">"I could use a carrot right now."</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → The hungry rabbit says 'I could use a carrot right now.'</span></pre>

<p>
	فكر في <code>this</code> على أساس معامِل إضافي يُمرَّر في صورة مختلف، فإذا أردت تمريره صراحةً، فستستخدِم تابع <code>call</code> الخاص بالدالة والذي يأخذ قيمة <code>this</code> على أساس وسيطها الأول، وتعامِل الوسائط التالية على أساس معامِلات عادية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_10" style="">
<span class="pln">speak</span><span class="pun">.</span><span class="pln">call</span><span class="pun">(</span><span class="pln">hungryRabbit</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Burp!"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → The hungry rabbit says 'Burp!'</span></pre>

<p>
	وبما أنّ كل دالة لها رابطة <code>this</code> الخاصة بها، والتي تعتمد قيمتها على الطريقة التي المًستدعاة بها، فلا تستطيع الإشارة إلى <code>this</code> بنطاق مغلِّف في دالة عادية معرَّفة بكلمة <code>function</code> المفتاحية؛ أما الدوال السهمية فتختلف في عدم ارتباط <code>this</code> الخاص بها، لكنها تستطيع رؤية رابطة <code>this</code> للنطاق الذي حولها، وعليه ستستطيع تنفيذ شيء مثل ما في الشيفرة التالية، حيث تشير إلى <code>this</code> مرجعيًا من داخل دالة محلية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_12" style="">
<span class="kwd">function</span><span class="pln"> normalize</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">coords</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">n </span><span class="pun">=&gt;</span><span class="pln"> n </span><span class="pun">/</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">length</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
normalize</span><span class="pun">.</span><span class="pln">call</span><span class="pun">({</span><span class="pln">coords</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">],</span><span class="pln"> length</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">});</span><span class="pln">
</span><span class="com">// → [0, 0.4, 0.6]</span></pre>

<p>
	فلو كتبنا الوسيط إلى <code>map</code> باستخدام كلمة <code>function</code> المفتاحية، فلن تعمل الشيفرة.
</p>

<h2>
	الوراثة عبر سلسلة prototype
</h2>

<p>
	انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_14" style="">
<span class="pln">let empty </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">empty</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → function toString(){…}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">empty</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → [object Object]</span></pre>

<p>
	أرأيت كيف سحبنا خاصيةً من كائن فارغ؟! حيث لم نزد على الطريقة التي تعمل بها كائنات جافاسكربت في حفظ البيانات الخاصة، لأن أغلب الكائنات ترث من سلسلة prototype بكل ما تحويه من دوال وخاصيات وكائنات أخرى إضافةً إلى مجموعة خصائصها، وتلك ما هي إلا كائنات أخرى مستخدَمة على أساس مصدر احتياطي fallback للخصائص، فإذا طُلِب من كائن خاصية لا يملكها، فسيُبحث في سلسلة prototype عن تلك الخاصية، ثم في سلاسل prototype الخاصة بكل واحدة فيها على حدى، وهكذا.
</p>

<p>
	طيب، ما سلسلة prototype لذاك الكائن الفارغ؟ إنه <code>object.prototype</code> الذي يسبق الكائنات كلها، فإذا قلنا أنّ علاقات سلاسل prototype في جافاسكربت تكوِّن هيكلًا شجريًا، فسيكون جذر تلك الشجرة هو <code>Object.prototype</code>، إذ يوفِّر بعضَ التوابع التي تظهر في جميع الكائنات الأخرى مثل <code>toString</code> الذي يحول الكائن إلى تمثيل نصي string representation.
</p>

<p>
	لا تملك العديد من الكائنات <code>Object.prototype</code> مثل نموذجها الأولي (يطلق على سلسلة prototype نموذج أولي مجازًا بوصفه أول نموذج يمثل خاصيات ودوال يرثه الكائن عند إنشائه)، بل يكون لها كائنٌ آخر يوفر مجموعةً مختلفةً من الخصائص الافتراضية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_16" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">getPrototypeOf</span><span class="pun">({})</span><span class="pln"> </span><span class="pun">==</span><span class="pln">
            </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">getPrototypeOf</span><span class="pun">(</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → null</span></pre>

<p>
	كما تتوقع من المثال السابق، سيُعيد <code>Object.getPrototypeOf</code> سلسلة prototype للكائن.
</p>

<p>
	تنحدر الدوال من <code>Function.prototype</code> أما المصفوفات فتنحدر من <code>Array.prototype</code>، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_18" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">getPrototypeOf</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">max</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln">
            </span><span class="typ">Function</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">getPrototypeOf</span><span class="pun">([])</span><span class="pln"> </span><span class="pun">==</span><span class="pln">
            </span><span class="typ">Array</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	سيكون لكائن النموذج الأولي المشابه لهذا، نموذج أولي خاص به وهو <code>Object.prototype</code> غالبًا، وذلك لاستمراره بتوفير توابع مثل <code>toString</code>؛ وتستطيع استخدام <code>Object.create</code> لإنشاء كائن مع نموذج أولي بعينه، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_20" style="">
<span class="pln">let protoRabbit </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  speak</span><span class="pun">(</span><span class="pln">line</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">The</span><span class="pln"> $</span><span class="pun">{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">type</span><span class="pun">}</span><span class="pln"> rabbit says </span><span class="str">'${line}'</span><span class="pun">`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
let killerRabbit </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">create</span><span class="pun">(</span><span class="pln">protoRabbit</span><span class="pun">);</span><span class="pln">
killerRabbit</span><span class="pun">.</span><span class="pln">type </span><span class="pun">=</span><span class="pln"> </span><span class="str">"killer"</span><span class="pun">;</span><span class="pln">
killerRabbit</span><span class="pun">.</span><span class="pln">speak</span><span class="pun">(</span><span class="str">"SKREEEE!"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → The killer rabbit says 'SKREEEE!'</span></pre>

<p>
	تُعَدّ خاصية مثل <code>speak(line)‎</code> في تعبير الكائن طريقةً مختصرةً لتعريف تابع ما، إذ تنشِئ خاصيةً اسمها <code>speak</code>، وتعطيها دالةً على أساس قيمة لها؛ كما يتصرف الأرنب "proto" في المثال السابق على أساس حاوية للخصائص التي تشترك فيها جميع الأرانب؛ أما في حالة مثل الأرنب القاتل killer rabbit، فيحتوي على خصائص لا تنطبق إلا عليه -نوعه في هذه الحالة-، كما يأخذ خصائصًا مشتركةً من نموذجه الأولي.
</p>

<h2>
	الأصناف Classes
</h2>

<p>
	يحاكي نظام النماذج الأولية في جافاسكربت (أي سلسلة prototype كما أشرنا في الأعلى) مفهوم الأصناف Classes في البرمجة كائنية التوجه، حيث تحدِّد هذه الأصناف الشكل الذي سيكون عليه نوع ما من كائن، وذلك بتحديد توابعه وخصائصه، كما يُدعى مثل ذلك الكائن بنسخة instance من الصنف.
</p>

<p>
	تُعَدّ النماذج الأولية مفيدةً هنا في تحديد الخصائص المشتركة بين جميع نُسَخ الصنف التي لها القيمة نفسها مثل التوابع؛ أما الخصائص المختلفة بين كل نسخة -كما في حالة خاصية <code>type</code> لأرنبنا في المثال السابق-، فيجب تخزينها في الكائن نفسه مباشرةً.
</p>

<p>
	لذا عليك إنشاء كائنًا مشتقًا من النموذج الأولي المناسب من أجل إنشاء نسخة من صنف ما، لكن في الوقت نفسه يجب التأكد من امتلاكه الخصائص الواجب وجودها في نُسَخ ذلك الصنف، وهذا ما يمثل وظيفة دالة الباني constructor، انظر ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_22" style="">
<span class="kwd">function</span><span class="pln"> makeRabbit</span><span class="pun">(</span><span class="pln">type</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let rabbit </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">create</span><span class="pun">(</span><span class="pln">protoRabbit</span><span class="pun">);</span><span class="pln">
  rabbit</span><span class="pun">.</span><span class="pln">type </span><span class="pun">=</span><span class="pln"> type</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> rabbit</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	توفر جافاسكربت طريقةً لتسهيل تعريف هذا النوع من الدوال، فإذا وضعتَ كلمة <code>new</code> المفتاحية أمام استدعاء الدالة مباشرةً، فستُعامَل الدالة على أساس باني، وهذا يعني أنه سيُنشَأ الكائن الذي يحمل النموذج الأولي المناسب تلقائيًا، بحيث يكون مقيدًا بـ <code>this</code> في الدالة، ثم يُعاد في نهاية الدالة، ويمكن العثور على كائن النموذج الأولي المستخدَم عند بناء الكائنات من خلال أخذ خاصية <code>protoype</code> لدالة الباني.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_24" style="">
<span class="kwd">function</span><span class="pln"> </span><span class="typ">Rabbit</span><span class="pun">(</span><span class="pln">type</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">type </span><span class="pun">=</span><span class="pln"> type</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="typ">Rabbit</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">.</span><span class="pln">speak </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">line</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">The</span><span class="pln"> $</span><span class="pun">{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">type</span><span class="pun">}</span><span class="pln"> rabbit says </span><span class="str">'${line}'</span><span class="pun">`);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

let weirdRabbit </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Rabbit</span><span class="pun">(</span><span class="str">"weird"</span><span class="pun">);</span></pre>

<p>
	تحصل البواني، بل كل الدوال، على خاصية اسمها <code>prototype</code> تحمل بدورها كائنًا فارغًا مشتقًا من <code>Object.prototype</code>، وتستطيع استبدال كائن جديد به إن شئت أو إضافة خصائص إلى الكائن الجديد كما في المثال.
</p>

<p>
	تتكوّن أسماء البواني من الحروف الكبيرة لتمييزها عما سواها، ومن المهم إدراك الفرق بين الطريقة التي يرتبط بها النموذج الأولي بالباني من خلال خاصية <code>prototype</code>، والطريقة التي يكون للكائنات فيها نماذج أولية -والتي يمكن إيجادها باستخدام <code>Object.getPrototypeOf</code>.
</p>

<p>
	<code>Function.Prototype</code> هو النموذج الأولي الفعلي للباني بما أنّ البواني ما هي إلا دوال في الأصل، وتحمل خاصية <code>prototype</code> الخاصة به النموذج الأولي المستخدَم للنسخ التي أنشِئت من خلاله.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_26" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">getPrototypeOf</span><span class="pun">(</span><span class="typ">Rabbit</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln">
            </span><span class="typ">Function</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">getPrototypeOf</span><span class="pun">(</span><span class="pln">weirdRabbit</span><span class="pun">)</span><span class="pln"> </span><span class="pun">==</span><span class="pln">
            </span><span class="typ">Rabbit</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → true</span></pre>

<h2>
	صياغة الصنف Class Notation
</h2>

<p>
	ذكرنا أن أصناف جافاسكربت ما هي إلا دوال بانية مع خاصية النموذج الأولي، وقد كان ذلك حتى عام 2015؛ أما الآن فقد تحسنت الصيغة التي صارت عليها كثيرًا، انظر إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_28" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Rabbit</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">type</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">type </span><span class="pun">=</span><span class="pln"> type</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  speak</span><span class="pun">(</span><span class="pln">line</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">The</span><span class="pln"> $</span><span class="pun">{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">type</span><span class="pun">}</span><span class="pln"> rabbit says </span><span class="str">'${line}'</span><span class="pun">`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let killerRabbit </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Rabbit</span><span class="pun">(</span><span class="str">"killer"</span><span class="pun">);</span><span class="pln">
let blackRabbit </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Rabbit</span><span class="pun">(</span><span class="str">"black"</span><span class="pun">);</span></pre>

<p>
	تبدأ كلمة <code>class</code> المفتاحية تصريح صنفٍ يسمح لنا بتعريف باني ومجموعة توابع في مكان واحد، كما يمكن كتابة أيّ عدد من التوابع بين قوسي التصريح، لكن يُعامَل التابع الحامل لاسم <code>constructor</code> معاملةً خاصةً، إذ يوفِّر وظيفة الباني الفعلية التي ستكون مقيدة بالاسم <code>Rabbit</code>، في حين تُحزَّم التوابع الأخرى في النموذج الأولي لذلك الباني، ومن ثم يكون تصريح الصنف الذي ذكرناه قبل قليل مكافئًا لتعريف الباني من القسم السابق، كونه يبدو أفضل للقارئ.
</p>

<p>
	ولا تسمح تصريحات الأصناف حاليًا إلا بإضافة التوابع إلى النموذج الأولي، وهي الخصائص التي تحمل دوالًا، رغم أن ذلك قد يكون مرهقًا إذا أردت حفظ قيمة غير دالّية non-function هناك، وقد يتحسن ذلك في الإصدار القادم من اللغة، لكن حتى ذلك الحين تستطيع إنشاء مثل تلك الخصائص بتغيير النموذج الأولي مباشرةً بعد تعريف الصنف.
</p>

<p>
	يمكن استخدام <code>class</code> في التعليمات والتعابير على حد سواء، وشأنها في ذلك شأن <code>function</code>، حيث لا تعرِّف رابطةً عند استخدامها على أساس تعبير، وإنما تنتج الباني كقيمة فقط. وتستطيع إهمال اسم الصنف في تعبير الصنف، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_30" style="">
<span class="pln">let object </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> getWord</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"hello"</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">object</span><span class="pun">.</span><span class="pln">getWord</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → hello</span></pre>

<h2>
	إعادة تعريف الخصائص المشتقة
</h2>

<p>
	تُضاف الخاصية إلى الكائن نفسه عند إضافتها إليه سواءً كان موجودًا في النموذج الأولي أم غير موجود، فإن كان ثمة خاصية موجودة بالاسم نفسه في النموذج الأولي، فلن تؤثِّر هذه الخاصية في الكائن بما أنها مخفية الآن خلف الخاصية التي يملكها الكائن.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_32" style="">
<span class="typ">Rabbit</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">.</span><span class="pln">teeth </span><span class="pun">=</span><span class="pln"> </span><span class="str">"small"</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">killerRabbit</span><span class="pun">.</span><span class="pln">teeth</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → small</span><span class="pln">
killerRabbit</span><span class="pun">.</span><span class="pln">teeth </span><span class="pun">=</span><span class="pln"> </span><span class="str">"long, sharp, and bloody"</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">killerRabbit</span><span class="pun">.</span><span class="pln">teeth</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → long, sharp, and bloody</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">blackRabbit</span><span class="pun">.</span><span class="pln">teeth</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → small</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Rabbit</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">.</span><span class="pln">teeth</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → small</span></pre>

<p>
	يبيّن المخطط التالي الموقف بعد تشغيل الشيفرة السابقة، إذ يقبع النموذجين الأوليَين لـ <code>Rabbit</code>، و<code>Object</code> خلف <code>killerRabbit</code> على أساس حاجز خلفي له، بينما يُبحث عن الخصائص التي ليست موجودة في الكائن.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="68905" data-ss1625654548="1" href="https://academy.hsoub.com/uploads/monthly_2021_06/rabbits.png.6ac362e472b6f475d494adfad441020b.png" rel=""><img alt="rabbits.png" class="ipsImage ipsImage_thumbnailed" data-fileid="68905" data-unique="33y5mw7xv" src="https://academy.hsoub.com/uploads/monthly_2021_06/rabbits.thumb.png.5a8d512ade39e3657ca22f1467e52ae7.png" style=""></a>
</p>

<p>
	وتبدو فائدة إعادة تعريف الخصائص overriding properties الموجودة في النموذج الأولي في التعبير عن الخصائص الاستثنائية في نُسَخ الأصناف العامة للكائنات، كما في مثال أسنان الأرنب rabbit teeth السابق، مع السماح للكائنات غير الاستثنائية بأخذ قيمة قياسية من نموذجها الأولي.
</p>

<p>
	كما يمكن استخدام إعادة التعريف لإعطاء تابع <code>toString</code> للنماذج الأولية للدالة والمصفوفة القياسيتين، بحيث يختلف عن النموذج الأساسي للكائن.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_34" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Array</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">.</span><span class="pln">toString </span><span class="pun">==</span><span class="pln">
            </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">].</span><span class="pln">toString</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → 1,2</span></pre>

<p>
	يعطي استدعاء <code>toString</code> على مصفوفة نتيجةً محاكيةً لاستدعاء <code>join(",")‎.</code> عليها، إذ تضع فواصل إنجليزية بين القيم الموجودة في المصفوفة؛ أما الاستدعاء المباشر لـ <code>Object.prototype.toString</code> مع مصفوفة، فينتج سلسلةً نصيةً مختلفةً، حيث تضع كلمة object واسم النوع بين أقواس مربعة، وذلك لعدم معرفة تلك الدالة بشأن المصفوفات، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_37" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">.</span><span class="pln">call</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">]));</span><span class="pln">
</span><span class="com">// → [object Array]</span></pre>

<h2>
	الخرائط Maps
</h2>

<p>
	استخدمنا كلمة map في <a data-ss1625654548="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B9%D9%84%D9%8A%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1242/" rel="">المقال السابق</a> في عملية تحويل هيكل البيانات بتطبيق دالة على عناصره، رغم بعد معنى الكلمة نفسها، التحويل، الدال عن الفعل الذي تنفذه، وهنا أيضًا وفي البرمجة عمومًا، فتُستخدَم هذه الكلمة كذلك لغرض مختلف لكنه قريب مما رأينا، وكلمة map على أساس اسم هي أحد أنواع هياكل البيانات الذي يربط القيم (المفاتيح) بقيم أخرى، فإذا أردت ربط الأسماء بأعمار مقابلة لها، فتستطيع استخدام كائنات لذلك، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_39" style="">
<span class="pln">let ages </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">Ziad</span><span class="pun">:</span><span class="pln"> </span><span class="lit">39</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">Hasan</span><span class="pun">:</span><span class="pln"> </span><span class="lit">22</span><span class="pun">,</span><span class="pln">
  </span><span class="typ">Sumaia</span><span class="pun">:</span><span class="pln"> </span><span class="lit">62</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Sumaia</span><span class="pln"> is $</span><span class="pun">{</span><span class="pln">ages</span><span class="pun">[</span><span class="str">"Sumaia"</span><span class="pun">]}`);</span><span class="pln">
</span><span class="com">// → Sumaia is 62</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Is Jack's age known?"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Jack"</span><span class="pln"> in ages</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → Is Jack's age known? false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Is toString's age known?"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"toString"</span><span class="pln"> in ages</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → Is toString's age known? true</span></pre>

<p>
	أسماء خصائص الكائن هنا هي أسماء الناس المذكورة في المثال، وقيم الخصائص هي أعمارهم، لكننا بالتأكيد لم نذكر أيّ شخص اسمه toString في تلك الرابطة، لكن لأن الكائنات العادية مشتقة من <code>Object.prototype</code> فيبدو الأمر وكأن الخاصية موجودة هناك، لهذا فمن الخطر معاملة الكائنات العادية مثل معاملة خرائط -النوع Map- هنا.
</p>

<p>
	لدينا عدة طرق مختلفة لتجنب هذه المشكلة، فمن الممكن مثلًا إنشاء كائنات بدون نموذج أولي، حتى إذا مرّرت <code>null</code> إلى <code>Object.create</code>، فلن يكون الكائن الناتج مشتقًا من <code>Object.prototype</code>، وعليه يمكن استخدامه بأمان على أساس خارطة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_41" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"toString"</span><span class="pln"> in </span><span class="typ">Object</span><span class="pun">.</span><span class="pln">create</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span></pre>

<p>
	يجب أن تكون أسماء خصائص الكائنات سلاسل نصية، فإن أردت ربطًا لا يمكن تحويل مفاتيحه بسهولة إلى سلاسل نصية -مثل الكائنات- فلا تستخدم كائنًا على أساس خارطة، ولحسن الحظ فتملك جافاسكربت صنفًا اسمه <code>Map</code> مكتوب لهذا الغرض خاصة، حيث يخزِّن حالة الربط ويسمح بأي نوع من المفاتيح.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_43" style="">
<span class="pln">let ages </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Map</span><span class="pun">();</span><span class="pln">
ages</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="str">"Ziad"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">39</span><span class="pun">);</span><span class="pln">
ages</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="str">"Hasan"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">22</span><span class="pun">);</span><span class="pln">
ages</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="str">"Sumaia"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">62</span><span class="pun">);</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Sumaia</span><span class="pln"> is $</span><span class="pun">{</span><span class="pln">ages</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">"Sumaia"</span><span class="pun">)}`);</span><span class="pln">
</span><span class="com">// → Sumaia is 62</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Is Jack's age known?"</span><span class="pun">,</span><span class="pln"> ages</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="str">"Jack"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → Is Jack's age known? false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">ages</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="str">"toString"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span></pre>

<p>
	تُعَدّ التوابع <code>set</code>، و<code>get</code>، و<code>has</code> جزءًا من واجهة كائن <code>Map</code>، فليس من السهل كتابة هيكل بيانات لتحديث مجموعة كبيرة من القيم والبحث فيها، ولكن لا تقلق، فقد كفانا شخص آخر مؤنة ذلك، حيث نستطيع استخدام ما كتبه من خلال تلك الواجهة البسيطة.
</p>

<p>
	إذا أردت معاملة كائن عادي لديك على أساس خارطة (النوع Map) لسبب ما، فمن المهم معرفة أن <code>Object.keys</code> يعيد المفاتيح الخاصة بالكائن فقط، وليس تلك الموجودة في النموذج الأولي، كما تستطيع استخدام التابع <code>hasOwnProperty</code> على أساس بديل لعامِل <code>in</code>، حيث يتجاهل النموذج الأولي للكائن، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_45" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">({</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">}.</span><span class="pln">hasOwnProperty</span><span class="pun">(</span><span class="str">"x"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">({</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">}.</span><span class="pln">hasOwnProperty</span><span class="pun">(</span><span class="str">"toString"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span></pre>

<h2>
	تعددية الأشكال Polymorphism
</h2>

<p>
	إذا استدعيتَ دالة <code>String</code> -التي تحوِّل القيمة إلى سلسلة نصية- على كائن ما، فستستدعي التابع <code>toString</code> على ذلك الكائن لمحاولة إنشاء سلسلة نصية مفيدة منه.
</p>

<p>
	كما ذكرنا سابقًا، تعرِّف بعض النماذج الأولية القياسية (سلاسل prototype) إصدارًا من <code>toString</code> خاصًا بها، وذلك لتستطيع إنشاء سلسلة نصية تحتوي بيانات مفيدة أكثر من <code>"[object Object]"</code>، كما تستطيع فعل ذلك بنفسك إن شئت.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_47" style="">
<span class="typ">Rabbit</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">.</span><span class="pln">toString </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">`</span><span class="pln">a $</span><span class="pun">{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">type</span><span class="pun">}</span><span class="pln"> rabbit</span><span class="pun">`;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">String</span><span class="pun">(</span><span class="pln">blackRabbit</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → a black rabbit</span></pre>

<p>
	وهذه صورة بسيطة من مفهوم بالغ القوة والأثر، فإن كُتب جزء من شيفرة ما ليعمل مع كائنات بها واجهة معينة -تابع <code>toString</code> في هذه الحالة-، فيمكن إلحاق أي نوع من الكائنات الداعمة لتلك الواجهة بالشيفرة، حيث ستعمل دون مشاكل؛ وتسمى تلك التقنية بتعددية الأشكال، وتعمل الشيفرة المتعددة الأشكال مع قيم ذات أشكال مختلفة طالما أنها تدعم الواجهة التي تتوقعها.
</p>

<p>
	كما ذكرنا في <a data-ss1625654548="1" href="https://academy.hsoub.com/programming/javascript/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1234/" rel="">المقال الرابع</a>، تستطيع حلقة <code>for</code>/<code>of</code> التكرار على عدة أنواع من هياكل البيانات، وتلك حالة أخرى من تعددية الأشكال، حيث تتوقع مثل تلك الحلقات التكرارية من هيكل البيانات أن يكشف واجهة معينة، وهو ما تفعله المصفوفات والسلاسل النصية؛ كما نستطيع إضافة تلك الواجهة إلى كائناتنا الخاصة، لكننا نحتاج إلى معرفة ما هي الرموز symbols قبل فعل ذلك.
</p>

<h2>
	الرموز Symbols
</h2>

<p>
	تستطيع عدة واجهات استخدام اسم الخاصية نفسها لأشياء عدة، فمثلًا، نستطيع تعريف واجهة بحيث يحوِّل فيها التابع <code>toString</code> الكائن إلى قطعة من خيوط الغزل، لكن من غير الممكن لكائن أن يتوافق مع تلك الواجهة ومع الاستخدام القياسي لـ <code>toString</code>.
</p>

<p>
	هذه المشكلة سيئة لكنها لا تشغل بال من يكتب بجافاسكربت لأنها غير شائعة، ورغم هذا فقد وفر مصممو جافاسكربت لنا حلًا لهذه المشكلة، إذ أن تلك من وظيفتهم على أي حال.
</p>

<p>
	حين زعمنا أن أسماء الخصائص هي سلاسل نصية لم نكن محقين 100%، فرغم أنها حقًا سلاسل نصية إلا قد تكون رموزًا أيضًا، وهي -أي الرموز- قيم أنشِئت بواسطة دالة <code>Symbol</code>، كما تُعَدّ الرموز المنشَئة حديثًا فريدةً، على عكس السلاسل النصية، بحيث لا تستطيع إنشاء الرمز نفسه مرتين.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_49" style="">
<span class="pln">let sym </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Symbol</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">sym </span><span class="pun">==</span><span class="pln"> </span><span class="typ">Symbol</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
</span><span class="typ">Rabbit</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">[</span><span class="pln">sym</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">55</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">blackRabbit</span><span class="pun">[</span><span class="pln">sym</span><span class="pun">]);</span><span class="pln">
</span><span class="com">// → 55</span></pre>

<p>
	تُضمَّن السلسلة النصية الممررة إلى <code>Symbol</code> تلقائيًا حين تحوّلها إلى سلسلة نصية، كما تسهِّل التعرف على الرمز عند عرضه في الطرفية console مثلًا؛ ولأن الرموز فريدة ويمكن استخدامها على أساس أسماء للخصائص، فهي مناسبة لتعريف الواجهات التي يمكن وجودها مع الخصائص الأخرى مهما كانت أسماؤها.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_51" style="">
<span class="kwd">const</span><span class="pln"> toStringSymbol </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Symbol</span><span class="pun">(</span><span class="str">"toString"</span><span class="pun">);</span><span class="pln">
</span><span class="typ">Array</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">[</span><span class="pln">toStringSymbol</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">length</span><span class="pun">}</span><span class="pln"> cm of blue yarn</span><span class="pun">`;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">].</span><span class="pln">toString</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → 1,2</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">][</span><span class="pln">toStringSymbol</span><span class="pun">]());</span><span class="pln">
</span><span class="com">// → 2 cm of blue yarn</span></pre>

<p>
	ومن الممكن إضافة خصائص رمز ما في الأصناف وتعبيرات الكائنات باستخدام أقواس مربعة حول اسم الخاصية، وبسبب ذلك سيُقيَّم اسم الخاصية مثل صيغة الوصول إلى الخاصية التي تستخدِم قوسين مربعين، حيث سيسمح لنا هذا بالإشارة إلى الرابطة التي تحمل الرمز.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_53" style="">
<span class="pln">let stringObject </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="pun">[</span><span class="pln">toStringSymbol</span><span class="pun">]()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"a jute rope"</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">stringObject</span><span class="pun">[</span><span class="pln">toStringSymbol</span><span class="pun">]());</span><span class="pln">
</span><span class="com">// → a jute rope</span></pre>

<h2>
	واجهة المكرر
</h2>

<p>
	يُتوقع من الكائن المعطى لحلقة <code>for</code>/<code>of</code> قابليته للتكرار، ويعني ذلك أنّ به تابعًا مسمى مع الرمز <code>Symbol.iterator</code>، وهو قيمة رمز معرَّفة من قِبَل اللغة، ومخزَّنة على أساس خاصية لدالة <code>Symbol</code>، كما يجب على ذلك التابع إعادة كائن يوفر واجهةً ثانية تكون هي المكرِّر iterator الذي يقوم بعملية التكرار، ولديه تابع <code>next</code> الذي يعيد النتيجة التالية التي يجب أن تكون بدورها كائنًا مع خاصية <code>value</code> التي توفر القيمة التالية إن كانت موجودة، وخاصية <code>done</code> التي تعيد true إن لم تكن ثمة نتائج أخرى، وتعيد false إن كان ثَمَّ نتائج بعد.
</p>

<p>
	لاحظ أن أسماء الخصائص: <code>next</code>، و<code>value</code>، و<code>done</code>، هي سلاسل نصية عادية وليست رموزًا؛ أما الرمز الوحيد هنا فهو <code>Symbol.iterator</code>، والذي سيضاف غالبًا إلى كائنات كثيرة، كما نستطيع استخدام تلك الواجهة بأنفسنا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_55" style="">
<span class="pln">let okIterator </span><span class="pun">=</span><span class="pln"> </span><span class="str">"OK"</span><span class="pun">[</span><span class="typ">Symbol</span><span class="pun">.</span><span class="pln">iterator</span><span class="pun">]();</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">okIterator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → {value: "O", done: false}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">okIterator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → {value: "K", done: false}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">okIterator</span><span class="pun">.</span><span class="pln">next</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → {value: undefined, done: true}</span></pre>

<p>
	دعنا نطبق هنا هيكل بيانات قابلًا للتكرار، حيث سنبني صنفَ matrix يتصرف على أساس مصفوفة ثنائية الأبعاد.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_57" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Matrix</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">width</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">,</span><span class="pln"> element </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">undefined</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">width </span><span class="pun">=</span><span class="pln"> width</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">height </span><span class="pun">=</span><span class="pln"> height</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">content </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> y </span><span class="pun">&lt;</span><span class="pln"> height</span><span class="pun">;</span><span class="pln"> y</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> x </span><span class="pun">&lt;</span><span class="pln"> width</span><span class="pun">;</span><span class="pln"> x</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">content</span><span class="pun">[</span><span class="pln">y </span><span class="pun">*</span><span class="pln"> width </span><span class="pun">+</span><span class="pln"> x</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> element</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">get</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">content</span><span class="pun">[</span><span class="pln">y </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">width </span><span class="pun">+</span><span class="pln"> x</span><span class="pun">];</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">set</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">content</span><span class="pun">[</span><span class="pln">y </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">width </span><span class="pun">+</span><span class="pln"> x</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> value</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يخزِّن الصنف محتوياته في مصفوفة واحدة من عنصرين فقط، هما: العرض، والطول width*height، وتُخزَّن العناصر صفًا صفًا، فيُخزن العنصر الثالث في الصف الخامس مثلًا -باستخدام الفهرسة الصفرية التي تبدأ من الصفر- في الموضع ‎4 * width + 2‎.
</p>

<p>
	تأخذ دالة الباني العرض، والطول، ودالة <code>element</code> اختيارية ستُستخدم لكتابة القيم الابتدائية؛ أما لجلب العناصر وتحديثها في المصفوفة الثنائية، فلدينا التابعان <code>get</code>، و<code>set</code>.
</p>

<p>
	حين نكرر على مصفوفة ما، فنحن بحاجة إلى معرفة موضع العناصر إضافة إلى العناصر نفسها، لذا سنجعل المكرِّر ينتج كائنات لها خصائص <code>x</code>، و<code>y</code>، و<code>value</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_59" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">MatrixIterator</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">matrix</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">y </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">matrix </span><span class="pun">=</span><span class="pln"> matrix</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  next</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">y </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">matrix</span><span class="pun">.</span><span class="pln">height</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">};</span><span class="pln">

    let value </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">x</span><span class="pun">,</span><span class="pln">
                 y</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">y</span><span class="pun">,</span><span class="pln">
                 value</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">matrix</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">y</span><span class="pun">)};</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">x</span><span class="pun">++;</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">x </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">matrix</span><span class="pun">.</span><span class="pln">width</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
      </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">y</span><span class="pun">++;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">value</span><span class="pun">,</span><span class="pln"> done</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">};</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يتتبع الصنف سير التكرار على المصفوفة الثنائية في الخصائص <code>x</code>، و<code>y</code>، ويبدأ التابع <code>next</code> بالتحقق من الوصول لأسفل المصفوفة الثنائية، فإن لم يصل إليه، فسينشِئ الكائن الذي يحمل القيمة الحالية أولًا، ثم يحدِّث موضعه، وبعد ذلك ينقله إلى السطر التالي إن تطلب الأمر.
</p>

<p>
	دعنا نهيئ صنف <code>Matrix</code> ليكون قابلًا للتكرار، وانتبه إلى استخدامنا المعالجة اللاحقة للنموذج الأولي بين الحين والآخر في هذه السلسلة لإضافة توابع إلى الأصناف، وذلك لتبقى الأجزاء المفردة من الشيفرة صغيرةً ومستقِلة؛ أما في البرامج العادية التي لا تحتاج فيها إلى تقسيم الشيفرة إلى أجزاء صغيرة، فستصرِّح عن هذه التوابع مباشرةً في الصنف.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_62" style="">
<span class="typ">Matrix</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">[</span><span class="typ">Symbol</span><span class="pun">.</span><span class="pln">iterator</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MatrixIterator</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	نستطيع الآن تطبيق التكرار على مصفوفة ما باستخدام <code>for</code>/<code>of</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_64" style="">
<span class="pln">let matrix </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Matrix</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">`</span><span class="pln">value $</span><span class="pun">{</span><span class="pln">x</span><span class="pun">},</span><span class="pln">$</span><span class="pun">{</span><span class="pln">y</span><span class="pun">}`);</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let </span><span class="pun">{</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">}</span><span class="pln"> of matrix</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// → 0 0 value 0,0</span><span class="pln">
</span><span class="com">// → 1 0 value 1,0</span><span class="pln">
</span><span class="com">// → 0 1 value 0,1</span><span class="pln">
</span><span class="com">// → 1 1 value 1,1</span></pre>

<h2>
	التوابع الجالبة والضابطة والساكنة
</h2>

<p>
	تتكون الواجهات من التوابع غالبًا، وقد تتضمن خصائص بها قيم غير دالية، فمثلًا، تملك كائنات <code>Map</code> خاصية <code>size</code>، والتي تخبرك كم عدد المفاتيح المخزَّنة فيها.
</p>

<p>
	ليس من الضروري لمثل هذا الكائن أن يحسب ويخزن خاصية مشابهة لتلك مباشرةً في النسخة instance التي لديه، بل حتى الخصائص التي يمكن الوصول إليها مباشرةً قد تخفي استدعاءً إلى تابع، حيث تسمى مثل تلك التوابع بالتوابع الجالبة getters، وتُعرَّف بكتابة <code>get</code> أمام اسم التابع في تعبير الكائن أو تصريح الصنف.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_66" style="">
<span class="pln">let varyingSize </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">get</span><span class="pln"> size</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">100</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">varyingSize</span><span class="pun">.</span><span class="pln">size</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 73</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">varyingSize</span><span class="pun">.</span><span class="pln">size</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 49</span></pre>

<p>
	يُستدعى التابع المرتبط بخاصية <code>size</code> للكائن كلما قرأ أحد من منها، وتستطيع تنفيذ شيء مشابه حين يكتب أحدهم في خاصية ما باستخدام تابع ضابط setter.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_68" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Temperature</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">celsius</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">celsius </span><span class="pun">=</span><span class="pln"> celsius</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">get</span><span class="pln"> fahrenheit</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">celsius </span><span class="pun">*</span><span class="pln"> </span><span class="lit">1.8</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">32</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">set</span><span class="pln"> fahrenheit</span><span class="pun">(</span><span class="pln">value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">celsius </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">value </span><span class="pun">-</span><span class="pln"> </span><span class="lit">32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">1.8</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">static</span><span class="pln"> fromFahrenheit</span><span class="pun">(</span><span class="pln">value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Temperature</span><span class="pun">((</span><span class="pln">value </span><span class="pun">-</span><span class="pln"> </span><span class="lit">32</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">1.8</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let temp </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Temperature</span><span class="pun">(</span><span class="lit">22</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">temp</span><span class="pun">.</span><span class="pln">fahrenheit</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 71.6</span><span class="pln">
temp</span><span class="pun">.</span><span class="pln">fahrenheit </span><span class="pun">=</span><span class="pln"> </span><span class="lit">86</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">temp</span><span class="pun">.</span><span class="pln">celsius</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 30</span></pre>

<p>
	يسمح لك صنف <code>Temperature</code> في المثال أعلاه بقراءة درجة الحرارة وكتابتها سواءً بمقياس السليزيوس أو الفهرنهايت، لكنها تخزِّن داخلها درجات السليزيوس فقط، وتحوِّل من وإلى سليزيوس في التابع الجالب والضابط لـ <code>fahrenheit</code> تلقائيًا.
</p>

<p>
	قد تحتاج أحيانًا إلى إلحاق بعض الخصائص لدالة الباني الخاصة بك مباشرةً بدلًا من النموذج الأولي، ولا تملك مثل تلك التوابع وصولًا إلى نسخة صنف، لكن يمكن استخدامها لتوفير طرق بديلة وإضافية لإنشاء النسخ.
</p>

<p>
	تُخزَّن التوابع المكتوبة قبل اسمها <code>static</code> على الباني، وذلك داخل التصريح عن الصنف، وعليه فيسمح لك صنف <code>Temperature</code> بكتابة <code>Temperature.fromFahrenheit(100)‎</code> لإنشاء درجة حرارة باستخدام مقياس فهرنهايت.
</p>

<h2>
	الوراثة Inheritance
</h2>

<p>
	تتميز بعض المصفوفات بأنها تماثلية symmetric، بحيث إذا عكست إحداها حول قطرها الذي يبدأ من أعلى اليسار، فستبقى كما هي ولا تتغير، أي ستبقى القيمة المخزنة في الموضع (x،y) كما هي في الموضع (y،x).
</p>

<p>
	تخيل أننا نحتاج إلى هيكل بيانات مثل <code>Matrix</code>، لكن يجب ضمان تماثلية المصفوفة وبقائها كذلك، وهنا نستطيع كتابة هذا من الصفر، لكننا سنكرر شيفرةً مشابهةً كثيرًا لما كتبناه سابقًا.
</p>

<p>
	يسمح نظام النموذج الأولي في جافاسكربت بإنشاء صنف جديد محاكي لصنف قديم لكن مع تعريفات جديدة لبعض خصائصه، ويكون النموذج الأولي للصنف الجديد مشتقًا من القديم لكن مع إضافة تعريف جديد إلى التابع <code>set</code> مثلًا، ويسمى ذلك بالاكتساب أو الوراثة inheritance، إذ يرث الصنف الجديد خصائصه وسلوكه من الصنف القديم.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_70" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">SymmetricMatrix</span><span class="pln"> extends </span><span class="typ">Matrix</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  constructor</span><span class="pun">(</span><span class="pln">size</span><span class="pun">,</span><span class="pln"> element </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">undefined</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">(</span><span class="pln">size</span><span class="pun">,</span><span class="pln"> size</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x </span><span class="pun">&lt;</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> element</span><span class="pun">(</span><span class="pln">y</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">);</span><span class="pln">
      </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> element</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">set</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    super</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x </span><span class="pun">!=</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      super</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="pln">y</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let matrix </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SymmetricMatrix</span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">x</span><span class="pun">},</span><span class="pln">$</span><span class="pun">{</span><span class="pln">y</span><span class="pun">}`);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">matrix</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 3,2</span></pre>

<p>
	يشير استخدام كلمة <code>extends</code> إلى وجوب عدم اعتماد هذا الصنف على النموذج الأولي الافتراضي <code>Object</code> مباشرةً، وإنما على صنف آخر يسمى بالصنف الأب superclass؛ أما الصنف المشتق فيكون اسمه الصنف الفرعي، أو الابن subclass.
</p>

<p>
	يستدعي الباني لتهيئة نسخة من <code>SymmetricMatrix</code> باني صنف الأب من خلال كلمة <code>super</code> المفتاحية، وهذا ضروري لأنّ الكائن الجديد سيحتاج إلى خصائص النسخة التي تملكها المصفوفات، إذا تصرَّف مثل <code>Matrix</code>. كما يغلِّف الباني دالة <code>element</code> لتبديل إحداثيات القيم أسفل خط القطر، وذلك لضمان تماثل المصفوفة.
</p>

<p>
	يُستخدَم <code>super</code> مرةً أخرى من التابع <code>set</code>، وذلك لاستدعاء تابع معين من مجموعة توابع الصنف الأب؛ كما سنعيد تعريف <code>set</code> لكن لن نستخدم السلوك الأصلي، حيث لن ينجح استدعاؤه بسبب إشارة <code>this.set</code> إلى <code>set</code> الجديد، كذلك يوفر <code>super</code> الواقع داخل توابع الصنف، طريقةً لاستدعاء التوابع كما عُرِّفت في الصنف الأب.
</p>

<p>
	وتسمح لنا الوراثة ببناء أنواع بيانات مختلفة من أنواع موجودة مسبقًا بقليل من الجهد، وهذه -أي الوراثة- جزء أساسي في ثقافة البرمجة كائنية التوجه جنبًا إلى جنب مع التغليف وتعددية الأشكال، لكن لأن هذين الآخرَين يُعتد بهما كثيرًا في البرمجة على أساس أساليب مهمة ومفيدة، فإنّ الوراثة قد صارت محل نظر، ففي حين يُستخدَم كل من التغليف وتعددية الأشكال في فصل أجزاء الشيفرات عن بعضها مما يقلل من تعقيد البرنامج عمومًا، فالوراثة على العكس من ذلك، إذ تربط الأصناف معًا منشِئًة مزيدًا من التعقيد، لأن عليك في الغالب معرفة كيفية عمل ذلك الصنف حين تحتاج إلى الوراثة منه، بخلاف إن لم تفعل شيئًا سوى استخدامه.
</p>

<p>
	وإننا نستخدمه بين الحين والآخر في برامجنا، لكن لا يحملنك ذلك على التفكير فيه أول شيء، فليس من الحكمة جعل بناء هرميات من الأصناف (شجرة عائلة من الأصناف) خيارك الأول في حل المشاكل.
</p>

<h2>
	عامل instanceof
</h2>

<p>
	توفر جافاسكربت عاملًا ثنائيًا يسمى <code>instanceof</code>، حيث نستخدمه إذا أردنا معرفة إن كان الكائن مشتقًا من صنف بعينه.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_72" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">
  </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SymmetricMatrix</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> instanceof </span><span class="typ">SymmetricMatrix</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">SymmetricMatrix</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> instanceof </span><span class="typ">Matrix</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Matrix</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> instanceof </span><span class="typ">SymmetricMatrix</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">([</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> instanceof </span><span class="typ">Array</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	سينظر العامل في الأنواع المكتسبة، وسيجد أن <code>symmetricMatrix</code> نسخةٌ من <code>Matrix</code>، كما يمكن استخدام العامل مع البواني القياسية مثل <code>Array</code>، فكل كائن تقريبًا ما هو إلا نسخة من <code>Object</code>.
</p>

<h2>
	خاتمة
</h2>

<p>
	لقد رأينا أنّ نطاق تأثيرالكائنات يتعدى حمل خصائصها، إذ لها نماذج أولية -والتي بدورها كائنات أيضًا-، وتتصرف كما لو كان لديها خصائص ليست لديها على الحقيقة طالما أن النموذج الأولي به تلك الخصائص، كما تمكّنا من معرفة الكائنات البسيطة لها <code>Object.prototype</code> على أساس نموذج أولي لها.
</p>

<p>
	يمكن استخدام البواني -وهي دوال تبدأ أسماؤها بحرف إنجليزي كبير- مع عامل <code>new</code> لإنشاء كائنات جديدة، وسيكون النموذج الأولي للكائن هو الكائن الموجود في خاصية <code>prototype</code> للباني، ونستطيع الاستفادة من ذلك بوضع جميع الخصائص التي تتشاركها القيم المعطاة -من النوع نفسه- في نماذجها الأولية. كذلك عرفنا صيغة <code>class</code> التي توفر طريقةً واضحةً لتعريف الباني ونموذجه الأولي.
</p>

<p>
	تستطيع تعريف الجالبات والضابطات لاستدعاء التوابع سرًا في كل مرة يصل فيها إلى خاصية كائن ما، وقد عرفنا أن التوابع الساكنة ما هي إلا توابع مخزَّنة في باني الصنف بدلًا من نموذجه الأولي، ثم شرحنا كيف أن عامل <code>instanceof</code> يستطيع إخبارك إن أعطيته كائنًا وبانيًا، وما إذا كان الكائن نسخةً من الباني أم لا.
</p>

<p>
	واعلم أنك تستطيع باستخدام الكائنات تحديد واجهة لها، وتخبر جميع أجزاء الشيفرة بأن عليهم التحدث إلى كائنك من خلال تلك الواجهة فقط، كما تُغلَّف بقية التفاصيل التي يتكون منها الكائن وتختفي خلف الواجهة. ويمكن لأكثر من نوع استخدام تلك الواجهة، فتعرف الشيفرة التي كُتبت لتستخدِم واجهةً ما كيف تعمل مع أي عدد من الكائنات المختلفة التي توفر الواجهة تلقائيًا ، وهذا ما يسمى بتعددية الأشكال.
</p>

<p>
	حين تستخدم عدة أصناف لا تختلف فيما بينها إلا في بعض التفاصيل، فيمكن كتابة أصناف جديدة منها على أساس أصناف فرعية، ترث جزءًا من سلوكها.
</p>

<h2>
	تدريبات
</h2>

<h3>
	النوع المتجهي
</h3>

<p>
	اكتب الصنف <code>Vec</code> الذي يمثل متجهًا في فضاء ثنائي الأبعاد، حيث يأخذ المعامِلين <code>x</code>، و<code>y</code> -وهما أرقام-، ويحفظهما في خصائص بالاسم نفسه.
</p>

<p>
	أعطِ النموذج الأولي للصنف <code>Vec</code> تابعَين، هما: <code>plus</code>، و<code>minus</code>، اللذان يأخذان متجهًا آخر على أساس معامِل، ويُعيدان متجهًا جديدًا له مجموع قيم x، وy للمتجهين (<code>this</code>، والمعامِل)؛ أو الفرق بينهما.
</p>

<p>
	أضف الخاصية الجالبة <code>length</code> إلى النموذج الأولي الذي يحسب طول المتجه، وهو المسافة بين النقطة (x,y) والإحداثيات الصفرية (0,0).
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a data-ss1625654548="1" href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_74" style="">
<span class="com">// ضع شيفرتك هنا.</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">).</span><span class="pln">plus</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)));</span><span class="pln">
</span><span class="com">// → Vec{x: 3, y: 5}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">).</span><span class="pln">minus</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)));</span><span class="pln">
</span><span class="com">// → Vec{x: -1, y: -1}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Vec</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">).</span><span class="pln">length</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 5</span></pre>

<p>
	<strong>إرشادات للحل</strong>
</p>

<ul>
<li>
		إذا لم تكن تعرف كيف تبدو تصريحات <code>class</code>، فانظر إلى مثال صنف <code>Rabbit</code>.
	</li>
	<li>
		يمكن إضافة خاصية جالبة إلى الباني من خلال وضع كلمة <code>get</code> قبل اسم التابع، ولحساب المسافة من (0,0) إلى (x,y)، فيمكن استخدام نظرية فيثاغورث التي تقول: أن مربع المسافة التي نريدها يساوي مجموع مربعي x و y، وعلى ذلك يكون ‎√(x<sup>2</sup> + y<sup>2</sup>)‎ هو العدد الذي نريده، ويُحسَب الجذر التربيعي في جافاسكربت باستخدام <code>Math.sqrt</code>.
	</li>
</ul>
<h3>
	المجموعات
</h3>

<p>
	توفر بيئة جافاسكربت القياسية هيكل بيانات اسمه <code>Set</code>، إذ يحمل مجموعةً من القيم مثل نسخة من <code>Map</code>، لكن على عكس <code>Map</code> فهو لا يربط قيمًا أخرى بها، بل يتتبع القيم ليعرف أيها تكون جزءًا من المجموعة. ولا يمكن للقيمة الواحدة أن تكون جزءًا من مجموعة ما أكثر من مرة واحدة، ولا يحدث أي تأثير حين تضاف مرةً أخرى.
</p>

<p>
	اكتب صنفًا اسمه <code>Group</code> -بما أنّ <code>Set</code> مأخوذ من قبل-، واجعل له التوابع الآتية: <code>add</code>، و<code>delete</code>، و<code>has</code>، ليكون مثل Set، بحيثما ينشئ بانيه مجموعةً فارغةً، ويضيف <code>add</code> قيمةً إلى المجموعة فقط إن لم تكن عضوًا بالفعل في المجموعة، كما يحذف <code>delete</code> وسيطه من المجموعة إن كان عضوًا فيها، ويعيد <code>has</code> قيمةً بوليانيةً توضح هل وسيطه عضو في المجموعة أم لا.
</p>

<p>
	استخدم عامِل <code>===</code>، أو شيئًا يحاكيه مثل <code>indexof</code>، لمعرفة ما إذا كانت قيمتان متطابقين، وأعط الصنف التابع الساكن <code>from</code> الذي يأخذ كائنًا قابلًا للتكرار على أساس وسيط، كما ينشئ مجموعةً تحتوي على جميع القيم المنتَجة من خلال التكرار عليها.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a data-ss1625654548="1" href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_76" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Group</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ضع شيفرتك هنا.</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let group </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Group</span><span class="pun">.</span><span class="pln">from</span><span class="pun">([</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">]);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">group</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="lit">10</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">group</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="lit">30</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
group</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span><span class="pln">
group</span><span class="pun">.</span><span class="kwd">delete</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">group</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="lit">10</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span></pre>

<p>
	<strong>إرشادات للحل</strong>
</p>

<p>
	تكون الطريقة الأسهل لحل هذا التدريب بتخزين مصفوفة من أعضاء المجموعة في خاصية لإحدى النُسَخ، ويمكن استخدام التابع <code>includes</code>، أو <code>indexOf</code> للتحقق من وجود قيمة ما في المصفوفة.
</p>

<p>
	ويمكن لباني الصنف الخاص بك إسناد تجميعة الأعضاء إلى مصفوفة فارغة، وعند استدعاء <code>add</code> فيجب التحقق هل القيمة المعطاة موجودة في المصفوفة أم يضيفها باستخدام <code>push</code> مثلًا.
</p>

<p>
	قد يكون حذف عنصر من مصفوفة في <code>delete</code> مبهمًا قليلًا، لكن تستطيع استخدام <code>filter</code> لإنشاء مصفوفة جديدة بدون القيمة، ولا تنس كتابة النسخة الجديدة من المصفوفة لتحل محل الخاصية التي تحمل الأعضاء.
</p>

<p>
	يمكن للتابع <code>from</code> استخدام حلقة <code>for/of</code> التكرارية للحصول على القيم من الكائن القابل للتكرار، ويستدعي <code>add</code> لوضعها في مجموعة منشأة حديثًا.
</p>

<h3>
	المجموعات القابلة للتكرار
</h3>

<p>
	أنشئ الصنف <code>Group</code> من التدريب السابق، واستعن بالقسم الخاص بواجهة المكرر من هذا المقال إن احتجت إلى رؤية الصيغة الدقيقة للواجهة.
</p>

<p>
	إذا استخدمت مصفوفةً لتمثيل أعضاء المجموعة، فلا تُعِد المكرِّر المنشَأ باستدعاء التابع <code>Symbol.iterator</code> على المصفوفة، فهذا وإن كان سيعمل بدون مشاكل، إلا أنه سينافي الهدف من التدريب.
</p>

<p>
	لا بأس إن تصرَّف المكرر الخاص بك تصرفًا غير مألوف عند تعديل المجموعة أثناء التكرار.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a data-ss1625654548="1" href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_78" style="">
<span class="com">// ضع شيفرتك هنا، والشيفرة التي من .المثال السابق</span><span class="pln">

</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let value of </span><span class="typ">Group</span><span class="pun">.</span><span class="pln">from</span><span class="pun">([</span><span class="str">"a"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"b"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"c"</span><span class="pun">]))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// → a</span><span class="pln">
</span><span class="com">// → b</span><span class="pln">
</span><span class="com">// → c</span></pre>

<p>
	<strong>إرشادات للحل</strong>
</p>

<p>
	ربما من الأفضل تعريف صنف <code>GroupIterator</code> جديد، كما يجب أن يكون لنُسَخ المكرر خاصية تتبع الموضع الحالي في المجموعة، بحيث تتحقق في كل مرة يُستدعى فيها <code>next</code> مما إذا كانت قد انتهت أم لا، فإن لم تنته فستتحرك متجاوزةً القيمة الحالية وتعيدها.
</p>

<p>
	يحصل الصنف <code>Group</code> على تابع يسمى من قِبل Symbol.iterator`، ويعيد عند استدعائه نسخةً جديدةً من صنف المكرر لتلك المجموعة.
</p>

<h3>
	استعارة تابع
</h3>

<p>
	ذكرنا أعلاه هنا أن <code>hasOwnProperty</code> لكائن يمكن استخدامه على أساس بديل قوي لعامِل <code>in</code> إذا أردت تجاهل خصائص النموذج الأولي، لكن ماذا لو كانت خارطتك map تحتاج إلى كلمة <code>hasOwnProperty</code>؟ لن تستطيع حينها استدعاء هذا التابع بما أن خاصية الكائن تخفي قيمة التابع.
</p>

<p>
	هل تستطيع التفكير في طريقة لاستدعاء <code>hasOwnProperty</code> على كائن له خاصية بهذا الاسم؟ تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a data-ss1625654548="1" href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3260_81" style="">
<span class="pln">let map </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">one</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> two</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> hasOwnProperty</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">};</span><span class="pln">

</span><span class="com">// أصلح هذا الاستدعاء</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">map</span><span class="pun">.</span><span class="pln">hasOwnProperty</span><span class="pun">(</span><span class="str">"one"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	<strong>إرشادات للحل</strong>
</p>

<p>
	تذكّر أن التوابع الموجودة في الكائنات المجردة تأتي من <code>Object.prototype</code>، كما تستطيع استدعاء دالة مع رابطة <code>this</code> خاصة من خلال استخدام التابع <code>call</code>.
</p>

<p>
	ترجمة -بتصرف- <a data-ss1625654548="1" href="https://eloquentjavascript.net/06_object.html" rel="external nofollow">للفصل السادس من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<p>
			المقال السابق: <a data-ss1625654548="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1233/" rel="">الدوال في جافاسكريبت</a>.
		</p>
	</li>
	<li>
		<p>
			<a data-ss1625654548="1" href="https://academy.hsoub.com/programming/javascript/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1234/" rel="">هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت</a>.
		</p>
	</li>
</ul>
]]></description><guid isPermaLink="false">1243</guid><pubDate>Mon, 21 Jun 2021 15:04:00 +0000</pubDate></item><item><title>&#x62D;&#x644;&#x642;&#x629; &#x627;&#xFEF7;&#x62D;&#x62F;&#x627;&#x62B; event loop: &#x637;&#x631;&#x64A;&#x642;&#x643; &#x644;&#x641;&#x647;&#x645; &#x643;&#x64A;&#x641;&#x64A;&#x629; &#x62A;&#x646;&#x641;&#x64A;&#x630; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; &#x641;&#x64A; &#x627;&#x644;&#x645;&#x62A;&#x635;&#x641;&#x62D;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AD%D9%84%D9%82%D8%A9-%D8%A7%EF%BB%B7%D8%AD%D8%AF%D8%A7%D8%AB-event-loop-%D8%B7%D8%B1%D9%8A%D9%82%D9%83-%D9%84%D9%81%D9%87%D9%85-%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-r1241/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_05/Event-loop--microtasks-and-macrotasks.png.9fc66e9339e4549c2bab3abefb23a12d.png" /></p>

<p>
	 يعتمد مجرى تنفيذ جافاسكربت في المتصفّح، وكذلك في Node.js، على <strong>حلقة اﻷحداث event loop</strong>.
</p>

<p>
	يُعدّ الفهم الجيّد لكيفيّة عمل حلقة اﻷحداث مهمّا عند تطبيق التحسينات optimizations وأحيانا من أجل هندسة صحيحة لما نبنيه.
</p>

<p>
	في هذا المقال، سنتناول أوّلا التفاصيل النظريّة لكيفيّة عمل اﻷمور، ثمّ سنرى تطبيقات عمليّة لتلك المعرفة.
</p>

<h2>
	حلقة اﻷحداث
</h2>

<p>
	مفهوم حلقة اﻷحداث بسيط للغاية. توجد هناك حلقة لا نهاية لها، حيث ينتظر محرّك جافاسكربت المهامّ، وينفّذها ثم ينام، منتظرا المزيد من المهامّ.
</p>

<p>
	الخوارزميّة العامّة للمحرّك:
</p>

<ol>
<li>
		مادام أنّ هناك مهامّا:
		<ul>
<li>
				ينفّذها، بدءًا بالمهمّة الأقدم.
			</li>
		</ul>
</li>
	<li>
		ينام إلى أن تظهر مهمّة ما، فينتقل عند ذلك إلى 1.
	</li>
</ol>
<p>
	هذا ما يحصل عندما نتصفّح صفحة ما، إذ لا يقوم محرّك جافاسكربت بأيّ شيء في غالب اﻷحيان، بل يشتغل فقط إذا لتنفيذ سكربت/معالج/حدث ما. أمثلة للمهام:
</p>

<ul>
<li>
		عندما يُحمَّل سكربت خارجيّ <code>&lt;script src="..."‎&gt;</code>، تكون المهمّة هي تنفيذه.
	</li>
	<li>
		عندما يحرّك المستخدم فأرته، تكون الهمّة هي إرسال الحدث <code>mousemove</code> وتنفيذ المعالجات.
	</li>
	<li>
		عندما يحين وقت الدالة <code>setTimeout</code> المُنتظَر، تكون المهمة هي تنفيذ رد النداء الخاص بها.
	</li>
	<li>
		وما إلى ذلك.
	</li>
</ul>
<p>
	تُضبط المهام ويعالجها المحرّك ثمّ ينتظر المزيد من المهامّ (بينما ينام ولا يستهلك أيّ شيئ تقريبا من وحدة المعالجة المركزيّة).
</p>

<p>
	قد يحصل وتأتي مهمّة ما بينما المحرّك مشغول، فتضاف تلك المهمّة إلى رتل آنذاك، إذ تشكّل المهام رتلًا queue، يُطلق عليه "رتل المهامّ الكبرى" macrotasks (مصطلح v8) أو رتل المهام ببساطة (بحسب <a data-ss1623482985="1" href="https://html.spec.whatwg.org/multipage/webappapis.html#task-queue" rel="external nofollow">مواصفة WHATWG</a>):
</p>

<p style="text-align: center;">
	<img alt="eventLoop.png" class="ipsImage ipsImage_thumbnailed" data-fileid="69083" data-unique="86r4pmgsq" src="https://academy.hsoub.com/uploads/monthly_2021_06/eventLoop.png.012ead87315bc019baa66121c73215f0.png"></p>

<p>
	على سبيل المثال، بينما المحرّك مشغول بتنفيذ السكربت <code>script</code>، قد يحرّك المستخدم فأرته متسبّبا في حدوث الحدث <code>mousemove</code>، كما قد يحين وقت <code>setTimeout</code> إلى غير ذلك، تُشكّل هذه المهامّ رتلا، كما هو موضّح في الصورة أعلاه.
</p>

<p>
	تُعالج المهامّ في الرتل حسب ترتيب "من يأتي أوّلا - يُخدَم أوّلا". وعندما يفرغ محرّك المتصفّح من <code>script</code>، يعالج حدث <code>mousemove</code>، ثمّ يعالج <code>setTimeout</code>، وهكذا.
</p>

<p>
	إلى حدّ الآن، اﻷمر في غاية البساطة، صحيح؟
</p>

<p>
	اثنان من التفاصيل الإضافيّة:
</p>

<ol>
<li>
		لا يحدث التصيير أو الإخراج rendering أبدًا بينما ينفّذ المحرّك مهمّة ما. لا يهمّ إن كانت المهمّة تستغرق وقتا طويلا. لا تظهر التغييرات في DOM إلّا بعد تمام المهمّة.
	</li>
	<li>
		إذا كانت المهمّة تستغرق الكثير من الوقت، فلا يمكن للمتصفّح القيام بمهامّ أخرى، كمعالجة أفعال المستخدم. وبذلك بعد وقت معيّن، سيرفع تنبيها من نحو "لا تستجيب الصفحة"، مقترحا إنهاء المهمّة مع كامل الصفحة. يحصل هذا عندما يكون هناك الكثير من الحسابات المعقّدة أو خطأ برمجيّ يؤدّي إلى حلقة لا متناهية.
	</li>
</ol>
<p>
	كان هذا هو النظريّ. لنرى الآن كيف يمكننا تطبيق هذه المعرفة.
</p>

<h2>
	حالة الاستعمال 1: تقسيم المهام الشرهة لوحدة المعالجة المركزية
</h2>

<p>
	لنفترض أنّ لدينا مهمّة شرهة لوحدة المعالجة المركزيّة. على سبيل المثال، إبراز الصيغة (التي استُخدمت في تلوين أمثلة الشيفرات في هذه الصفحة) ثقيلة جدّا على وحدة المعالجة المركزيّة. لإبراز الشيفرة، يكون هناك تحليل، وتُنشئ العديد من العناصر الملوّنة، وتضاف إلى الصفحة لمقدار هائل من النصّ، سيأخذ ذلك الكثير من الوقت.
</p>

<p>
	مادام المحرّك مشغولا بإبراز الصيغة، لا يمكنه فعل أشياء أخرى متعلّقة بـ DOM، أو معالجة أحداث المستخدمين أو غير ذلك. بل قد يؤدّي ذلك بالمتصفّح إلى "التلكّئ"، أو حتى "التوقّف" قليلا، وهو ما لا يُعدّ مقبولا.
</p>

<p>
	يمكننا تجنّب هذه المشاكل بواسطة تقسيم المهامّ الكبيرة إلى أجزاء. كإبراز المئة سطر اﻷولى، ثمّ برمجة <code>setTimeout</code> (بتأخير منعدم) للمئة سطر التالية، وهكذا.
</p>

<p>
	لعرض هذه الطريقة، وطلبا للبساطة، بدل إبراز النص، لنأخذ دالّة تعمل على التعداد من <code>1</code> إلى <code>1000000000</code>.
</p>

<p>
	لو نفّذت الشيفرة في الأسفل، سوف "يتوقّف" المحرّك لبعض الوقت. أما في جافاسكربت المشتغلة في جانب الخادم سيكون ذلك ظاهرا بوضوح، وإذا كنت تشغّلها في المتصفّح، جرّب النقر على الأزرار اﻷخرى في الصفحة وسترى أنّه لن تُعالج أي من اﻷحداث اﻷخرى إلى حين انتهاء العد.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1929_7" style="">
<span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

let start </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">();</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> count</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="com">// do a heavy job</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let j </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> j </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1e9</span><span class="pun">;</span><span class="pln"> j</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    i</span><span class="pun">++;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  alert</span><span class="pun">(</span><span class="str">"Done in "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> start</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'ms'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

count</span><span class="pun">();</span></pre>

<p>
	 
</p>

<p>
	<em><iframe allowfullscreen="true" allowtransparency="true" data-ss1622215149="1" data-ss1623482985="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/ExWwvOp?height=265&amp;theme-id=light&amp;default-tab=js" style="width: 100%;" title="JS-p2-event-loop-ex1">See the Pen JS-p2-event-loop-ex1 by Hsoub (@Hsoub) on CodePen.</iframe></em>
</p>

<p>
	بل إنّ المتصفّح قد يظهر التحذير "استغرق هذا السكربت طويلا".
</p>

<p>
	لنقسّم هذه المهمّة باستخدام استدعاءات متداخلة لـ <code>setTimeout</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1929_9" style="">
<em><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

let start </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">();</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> count</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="com">// القيام بجزء من المهمّة الثقيلة (*)</span><span class="pln">
  </span><span class="kwd">do</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    i</span><span class="pun">++;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">%</span><span class="pln"> </span><span class="lit">1e6</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1e9</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="str">"Done in "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> start</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'ms'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setTimeout</span><span class="pun">(</span><span class="pln">count</span><span class="pun">);</span><span class="pln"> </span><span class="com">// برمجة الاستدعاء الجديد (**)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln">

count</span><span class="pun">();</span></em></pre>

<p>
	<em><iframe allowfullscreen="true" allowtransparency="true" data-ss1622215149="1" data-ss1623482985="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/bGqorOw?height=265&amp;theme-id=light&amp;default-tab=js" style="width: 100%;" title="JS-p2-event-loop-ex2">See the Pen JS-p2-event-loop-ex2 by Hsoub (@Hsoub) on CodePen.</iframe></em>
</p>

<p>
	تعمل واجهة المستخدم الآن كليّا خلال عمليّة "العدّ".
</p>

<p>
	يؤدّي إجراءٌ واحد للدالة <code>count</code> جزءًا من المهمّة <code>(*)</code>، ثمّ يعيد برمجة نفسه <code>(**)</code> عند الحاجة:
</p>

<ol>
<li>
		يعدُّ الإجراء اﻷوّل: <code>i=1...1000000</code>.
	</li>
	<li>
		يعدُّ الإجراء الثاني: <code>i=1000001..2000000</code>.
	</li>
	<li>
		وهكذا.
	</li>
</ol>
<p>
	لو ظهرت الآن مهمّة جانبيّة جديدة (حدث <code>onclick</code> مثلا) بينما المحرّك مشغول بتنفيذ الجزء الأوّل، فإنّها تُصَفّ في الرتل ثمّ تُنفّذ عند الانتهاء من الجزء اﻷوّل، قبل الجزء التالي. يعطي الرجوع الدوريّ لحلقة الأحداث ما يكفي من "النفَس" لمحرّك جافاسكربت للقيام بشيء آخر، للاستجابة لأفعال المستخدم اﻷخرى.
</p>

<p>
	الشيء الملاحظ هو أنّ كلا النسختين - مع أو دون تقسيم المهمّة بواسطة <code>setTimeout</code> - متقاربتان في السرعة. ليس هناك فرق كبير في الوقت الكلّيّ للعدّ. لتقريبهما أكثر، لنقم بشيء من التحسين.
</p>

<p>
	سننقل برمجة <code>setTimeout</code> إلى بداية العدّ <code>count()‎</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1929_11" style="">
<em><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

let start </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">();</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> count</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="com">// نقل البرمجة إلى البداية</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1e9</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1e6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    setTimeout</span><span class="pun">(</span><span class="pln">count</span><span class="pun">);</span><span class="pln"> </span><span class="com">// برمجة الاستدعاء التالي</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">do</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    i</span><span class="pun">++;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">%</span><span class="pln"> </span><span class="lit">1e6</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1e9</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="str">"Done in "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="typ">Date</span><span class="pun">.</span><span class="pln">now</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> start</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'ms'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

</span><span class="pun">}</span><span class="pln">

count</span><span class="pun">();</span></em></pre>

<p>
	<em><iframe allowfullscreen="true" allowtransparency="true" data-ss1622215149="1" data-ss1623482985="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/abJLyPR?height=265&amp;theme-id=light&amp;default-tab=js" style="width: 100%;" title="JS-p2-event-loop-ex3">See the Pen JS-p2-event-loop-ex3 by Hsoub (@Hsoub) on CodePen.</iframe></em>
</p>

<p>
	حاليّا، عندما نبدأ العدّ <code>count()‎</code> ونرى أنّنا سنحتاج المزيد من العدّ، نبرمج ذلك مباشرة، قبل أداء المهمّة. إذا فعلت ذلك، من السهل أن تلاحظ أنّه يأخذ وقتا أقلّ بكثير. تسأل لماذا؟
</p>

<p>
	هذا بسيط: كما تذكر، يوجد في المتصفّح تأخير صغير بمقدار 4ms عند تعدّد الاستدعاءات المتداخلة للدالة <code>setTimeout</code>. حتى لو وضعنا <code>0</code>، فسيكون <code>4ms</code> (أو أكثر بقليل). فكلّما برمجناه أبكر، جرى بشكل أسرع.
</p>

<p>
	في النهاية، لقد قسّمنا مهمّة شرهة لوحدة المعالجة المركزيّة لأجزاء ولا تحبس الآن واجهة المستخدم كما أنّ الوقت الكليّ لتنفيذها ليس أطول بكثير.
</p>

<h2>
	حالة الاستخدام 2: الإشارة إلى المعالجة
</h2>

<p>
	من الفوائد اﻷخرى لتقسيم المهامّ الثقيلة لسكربتات المتصفّح هو أنّه يمكّننا من إظهار إشارة إلى المعالجة.
</p>

<p>
	كما ذكرنا آنفا، لا تُرسم التغييرات في DOM حتى تتمّ المهمّة الجارية حاليّا، بغضّ النظر عن طول استغراقها. من جهة، هذا جيّد، لأنّ دالّتنا قد تنشئ عدّة عناصر، وتضيفها الواحد تلو الآخر إلى الصفحة وتغيّر تنسيقاتها ولن يرى المستخدمّ أيّة حالة "بينيّة" غير تامة. هذا أمر مهمّ، صحيح؟
</p>

<p>
	في هذا المثال، لن تظهر التغيّرات في <code>i</code> حتى تنتهي الدالّة، وبذلك لن نرى سوى القيمة النهائيّة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1929_13" style="">
<em><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"progress"</span><span class="tag">&gt;&lt;/div&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">

  </span><span class="kwd">function</span><span class="pln"> count</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1e6</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      i</span><span class="pun">++;</span><span class="pln">
      progress</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  count</span><span class="pun">();</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></em></pre>

<p>
	<em><iframe allowfullscreen="true" allowtransparency="true" data-ss1622215149="1" data-ss1623482985="1" frameborder="no" height="166" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/RwpLZvw?height=166&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-event-loop-ex4">See the Pen JS-p2-event-loop-ex4 by Hsoub (@Hsoub) on CodePen.</iframe></em>
</p>

<p>
	لكن قد نودّ أيضا إظهار شيء ما خلال المهمّة، كشريط تقدّم مثلا.
</p>

<p>
	إذا قسّمنا المهمّة الثقيلة إلى أجزاء باستخدام <code>setTimeout</code>، فسوف تُرسم التغييرات في ما بينها.
</p>

<p>
	تبدو هذه أحسن:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1929_15" style="">
<em><span class="tag">&lt;script&gt;</span><span class="pln">
  let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">function</span><span class="pln"> count</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="com">// القيام بجزء من المهمّة الثقيلة (*)</span><span class="pln">
    </span><span class="kwd">do</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      i</span><span class="pun">++;</span><span class="pln">
      progress</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">%</span><span class="pln"> </span><span class="lit">1e3</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1e7</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      setTimeout</span><span class="pun">(</span><span class="pln">count</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

  </span><span class="pun">}</span><span class="pln">

  count</span><span class="pun">();</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></em></pre>

<p>
	<em><iframe allowfullscreen="true" allowtransparency="true" data-ss1622215149="1" data-ss1623482985="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/NWpavod?height=265&amp;theme-id=light&amp;default-tab=html,result" style="width: 100%;" title="JS-p2-event-loop-ex5">See the Pen JS-p2-event-loop-ex5 by Hsoub (@Hsoub) on CodePen.</iframe></em>
</p>

<p>
	يُظهر <code>&lt;div&gt;</code> الآن قيما تصاعديّة لـ <code>i</code>، كنوع من شريط التقدّم.
</p>

<h2>
	حالة الاستخدام 3: فعل شيء ما بعد الحدث
</h2>

<p>
	في معالج حدثٍ ما، قد نقرّر تأجيل بعض اﻷفعال إلى حين انتشار الحدث نحو الأعلى ومعالجته في جميع المستويات. يمكننا فعل ذلك من خلال إحاطة الشيفرة بالدالة <code>setTimeout</code> بتأخير منعدم.
</p>

<p>
	قد رأينا في مقال <a data-ss1622215149="1" data-ss1623482985="1" href="https://academy.hsoub.com/programming/javascript/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D9%85%D8%AE%D8%B5%D8%B5%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1144/" rel="">إرسال اﻷحداث المخصّصة</a> مثالًا لذلك: اُرسل الحدث المخصّص <code>must-open</code> داخل <code>setTimeout</code>، لكي يقع بعد المعالجة الكليّة لحدث "النقر".
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1929_17" style="">
<em><span class="pln">menu</span><span class="pun">.</span><span class="pln">onclick </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">

  </span><span class="com">// إنشاء حدث مخصّص ببيانات عنصر القائمة الذي نُقر</span><span class="pln">
  let customEvent </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">CustomEvent</span><span class="pun">(</span><span class="str">"menu-open"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    bubbles</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  </span><span class="com">// إرسال الحدث المخصّص بشكل لا متزامن</span><span class="pln">
  setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> menu</span><span class="pun">.</span><span class="pln">dispatchEvent</span><span class="pun">(</span><span class="pln">customEvent</span><span class="pun">));</span><span class="pln">
</span><span class="pun">};</span></em></pre>

<p>
	<em><iframe allowfullscreen="true" allowtransparency="true" data-ss1622215149="1" data-ss1623482985="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/WNpZEmp?height=265&amp;theme-id=light&amp;default-tab=js" style="width: 100%;" title="JS-p2-event-loop-ex6">See the Pen JS-p2-event-loop-ex6 by Hsoub (@Hsoub) on CodePen.</iframe></em>
</p>

<h2>
	المهام الكبرى والمهام الصغرى
</h2>

<p>
	إلى جانب <strong>المهامّ الكبرى</strong> macrotasks، التي بُيّنت في هذا المقال، هناك <strong>المهامّ الصغرى</strong> microtasks، المذكورة في مقال <a data-ss1622215149="1" data-ss1623482985="1" href="#" rel="">المهامّ الصغرى</a>.
</p>

<p>
	لا تأتي المهامّ الصغرى إلّا من شيفرتنا. تُنشأ عادة بواسطة <a data-ss1623482985="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-promise-%D9%81%D9%8A-javascript-r740/" rel="">الوعود</a> promises، حيث يصير تنفيذ معالج <code>‎then/catch/finally</code> مهمّة صغرى. تُستخدم المهامّ الصغرى تحت غطاء <code><a data-ss1623482985="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%84%D8%A7%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%88%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D8%B8%D8%A7%D8%B1-asyncawait-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r921/" rel="">await</a></code> كذلك، إذ يُعدّ ذلك صورة أخرى لمعالجة الوعود.
</p>

<p>
	هناك أيضا الدالّة الخاصّة <code>queueMicrotask(func)‎</code> التي تصفّ الدالة <code>func</code> الممررة إليها لكي تُنفَّذ في رتل المهامّ الصغرى.
</p>

<p>
	<strong>بعد كلّ مهمّة كبرى مباشرة، ينفّذ المحرّك جميع المهامّ الموجودة في رتل المهام الصغرى، قبل إجراء أيّ مهامّ كبرى أخرى أو التصيير أو أيّ شيء آخر.</strong>
</p>

<p>
	على سبيل المثال، ألق نظرةً على:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1929_19" style="">
<em><span class="pln">setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">"timeout"</span><span class="pun">));</span><span class="pln">

</span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">resolve</span><span class="pun">()</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alert</span><span class="pun">(</span><span class="str">"promise"</span><span class="pun">));</span><span class="pln">

alert</span><span class="pun">(</span><span class="str">"code"</span><span class="pun">);</span></em></pre>

<p>
	<em><iframe allowfullscreen="true" allowtransparency="true" data-ss1622215149="1" data-ss1623482985="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/ZEeXJPr?height=265&amp;theme-id=light&amp;default-tab=js" style="width: 100%;" title="JS-p2-event-loop-ex7">See the Pen JS-p2-event-loop-ex7 by Hsoub (@Hsoub) on CodePen.</iframe></em>
</p>

<p>
	كيف سيكون الترتيب هنا؟
</p>

<ol>
<li>
		يظهر <code>code</code> أوّلا، ﻷنّه استدعاء لا متزامن عاديّ.
	</li>
	<li>
		يظهر <code>promise</code> ثانيا، لأنّ <code>‎.then</code> تمرّ عبر رتل المهامّ الصغرى، وتجري بعد الشيفرة الحاليّة.
	</li>
	<li>
		يظهر <code>timeout</code> أخيرا، لأنّه مهمّة كبرى.
	</li>
</ol>
<p>
	هذا ما تبدو عليه الصورة الثريّة أكثر لحلقة الأحداث (الترتيب هو من اﻷعلى إلى اﻷسفل، بمعنى: السكربت أوّلا، ثمّ المهامّ الصغرى، ثمّ التصيير وهكذا):
</p>

<p style="text-align: center;">
	<em><img alt="eventLoop-full.png" class="ipsImage ipsImage_thumbnailed" data-fileid="69084" data-unique="543qsj7fg" src="https://academy.hsoub.com/uploads/monthly_2021_06/eventLoop-full.png.637bc9bfa9839d89dc47bcffd44d94b9.png"></em>
</p>

<p>
	تتمّ جميع المهامّ الصغرى قبل أيّ معالج حدث آخر أو تصيير أو أيّ مهمّة كبرى أخرى.
</p>

<p>
	هذا مهمّ، ﻷنّه يضمن أنّ بيئة التطبيق هي نفسها أساسا (لا تغيّر في إحداثيات الفأرة، لا بيانات جديدة من الشبكة، إلى غير ذلك) بين المهام الكبرى.
</p>

<p>
	لو أردنا تنفيذ دالّة بشكل لا متزامن (بعد الشيفرة الحاليّة)، لكن قبل تصيير التغييرات أو معالجة اﻷحداث الجديدة، يمكننا برمجة ذلك بواسطة <code>queueMicrotask</code>.
</p>

<p>
	هذا مثال مع "شريط تقدّم العدّ"، كما في الذي عرضناه سابقا، لكن باستخدام <code>queueMicrotask</code> بدل <code>setTimeout</code>. يمكنك رؤية أنّ التصيير يحصل في آخر المطاف. تماما كالشيفرة المتزامنة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1929_21" style="">
<em><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"progress"</span><span class="tag">&gt;&lt;/div&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">function</span><span class="pln"> count</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="com">// القيام بجزء من مهمّة ثقيلة (*)</span><span class="pln">
    </span><span class="kwd">do</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      i</span><span class="pun">++;</span><span class="pln">
      progress</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> i</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">%</span><span class="pln"> </span><span class="lit">1e3</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1e6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      queueMicrotask</span><span class="pun">(</span><span class="pln">count</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

  </span><span class="pun">}</span><span class="pln">

  count</span><span class="pun">();</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></em></pre>

<p>
	<em><iframe allowfullscreen="true" allowtransparency="true" data-ss1622215149="1" data-ss1623482985="1" frameborder="no" height="178" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/dyvVzrE?height=178&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-event-loop-ex8">See the Pen JS-p2-event-loop-ex8 by Hsoub (@Hsoub) on CodePen.</iframe></em>
</p>

<h2>
	<em>الملخص</em>
</h2>

<p>
	خوارزميّة أكثر تفصيلا لحلقة اﻷحداث (لكن تبقى مبسّطة مقارنة <a data-ss1622215149="1" data-ss1623482985="1" href="https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model" rel="external nofollow">بالمواصفة</a>):
</p>

<ol>
<li>
		خذ المهّمة اﻷقدم من رتل المهام الكبرى ونفذّها ("سكربت" مثلا).
	</li>
	<li>
		نفّذ جميع المهامّ الصغرى:
		<ul>
<li>
				مادام أنّ رتل المهامّ الصغرى ليس فارغا:
				<ul>
<li>
						خذ المهمّة اﻷقدم من الرتل ونفّذها.
					</li>
				</ul>
</li>
		</ul>
</li>
	<li>
		صيّر التغييرات إن وُجدت.
	</li>
	<li>
		إذا كان رتل المهامّ الكبرى فارغا، انتظر قدوم مهمّة كبرى.
	</li>
	<li>
		اذهب إلى الخطوة 1.
	</li>
</ol>
<p>
	لبرمجة مهمّة كبرى جديدة:
</p>

<ul>
<li>
		استخدم <code>setTimeout(f)‎</code> منعدمة التأخير.
	</li>
</ul>
<p>
	يمكن أن يُستخدم ذلك لتقسيم مهمّة كبيرة ومثقلة بالحسابات إلى أجزاء، حتى يتسنّى للمتصفّح الاستجابة لأحداث المستخدم وإظهار التقدّم بينها.
</p>

<p>
	تُستخدم كذلك، في معالجات اﻷحداث لبرمجة فعل ما بعد معالجة الحدث كليّة (انتهاء الانتشار نحو اﻷعلى).
</p>

<p>
	لبرمجة مهمّة صغرى جديدة:
</p>

<ul>
<li>
		استخدم <code>queueMicrotask(f)‎</code>.
	</li>
	<li>
		تمرّ معالجات الوعود أيضا عبر رتل المهامّ الصغرى.
	</li>
</ul>
<p>
	ليست هناك معالجة لأحداث واجهة المستخدم أو الشبكة بين المهامّ الصغرى: تجري الواحدة تلو اﻷخرى مباشرة.
</p>

<p>
	وبذلك، قد يودّ أحدهم استخدام <code>queueMicrotask</code> لتنفيذ دالّة بشكل لا متزامن، لكن ضمن نفس حالة البيئة.
</p>

<hr>
<p>
	<strong>ملاحظة: عاملات الويب web workers</strong>
</p>

<p>
	للحسابات الطويلة والثقيلة التي لا ينبغي أن تحبس حلقة اﻷحداث، يمكننا استخدام <a data-ss1622215149="1" data-ss1623482985="1" href="https://html.spec.whatwg.org/multipage/workers.html" rel="external nofollow">عاملات الويب</a> فهذه طريقة لإجراء الشيفرة في عملية أو خيط thread آخر متوازي.
</p>

<p>
	يمكن أن تتبادل عاملات الويب الرسائل مع العمليّة اﻷساسيّة، لكن لديها متغيّراتها الخاصّة، ولديها حلقة أحداث خاصّة.
</p>

<p>
	لا يمكن لعاملات الويب الوصول إلى DOM، فهي مفيدة، بشكل أساسيّ، في الحسابات، لاستخدام عدّة أنوية لوحدة المعالجة المركزيّة في نفس الوقت.
</p>

<hr>
<p>
	ترجمة -وبتصرف- للمقال <a data-ss1622215149="1" data-ss1623482985="1" href="https://javascript.info/event-loop" rel="external nofollow"> Event loop: microtasks and macrotasks</a> من سلسلة <a data-ss1622215149="1" data-ss1623482985="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B1%D8%A7%D9%82%D8%A8-%D8%A7%D9%84%D8%AA%D8%AD%D9%88%D9%84-mutationobserver-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D9%84%D9%85%D8%B1%D8%A7%D9%82%D8%A8%D8%A9-%D8%B4%D8%AC%D8%B1%D8%A9-dom-r1239/" rel="">مراقب التحول MutationObserver عبر جافاسكربت لمراقبة شجرة DOM.</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1241</guid><pubDate>Sat, 19 Jun 2021 11:04:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x646;&#x648;&#x627;&#x641;&#x630; &#x627;&#x644;&#x645;&#x62A;&#x635;&#x641;&#x62D; &#x648;&#x627;&#x644;&#x646;&#x648;&#x627;&#x641;&#x630; &#x627;&#x644;&#x645;&#x646;&#x628;&#x62B;&#x642;&#x629; Popups &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%86%D9%88%D8%A7%D9%81%D8%B0-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D9%88%D8%A7%D9%84%D9%86%D9%88%D8%A7%D9%81%D8%B0-%D8%A7%D9%84%D9%85%D9%86%D8%A8%D8%AB%D9%82%D8%A9-popups-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1253/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_06/2.jpg.1f3a7c9e949805383eb97ee38784c014.jpg" /></p>

<p>
	تستخدم الأساليب القديمة في JavaScript النوافذ المنبثقة لإظهار صفحات أو مستندات أخرى للمستخدم، إذ يمكن فتح نافذة جديدة تعرض المورد المرتبط بالعنوان المحدَّد بسهولة عن طريق تنفيذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4143_6" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="str">'https://javascript.info/'</span><span class="pun">)</span></pre>

<p>
	تعرض معظم المتصفحات الحديثة محتويات الموارد الموجودة على عنوان محدد في نوافذ متجاورة، بدلًا من فتح نافذة جديدة لكل منها.
</p>

<p>
	إنّ فكرة النوافذ المنبثقة قديمة، وقد استخدمت لعرض محتوىً مختلف للمستخدم دون إغلاق النافذة الرئيسية، لكن حاليًا تستخدَم أساليب أخرى، حيث يمكن تحميل المحتوى ديناميكيًا من خلال التابع <a data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" href="https://javascript.info/fetch" rel="external nofollow">fetch</a> ثم إظهاره ضمن معرَّف <code>&lt;div&gt;</code> يُولَّد ديناميكيًا، لذا لم يعد استخدام النوافذ المنبثقة شائعًا. كما أن استخدامها في الأجهزة النقالة التي لا تعرض عدة نوافذ معًا مربك أحيانًا.
</p>

<p>
	مع ذلك يبقى للنوافذ المنبثقة استخداماتها في إنجاز بعض المهام كالاستيثاق "OAuth"، المستخدم لتسجيل الدخول على Google أو Facebook وغيرها، لأنّ:
</p>

<ol>
<li>
		النوافذ المنبثقة هي نوافذ منفصلة لها بيئة JavaScript مستقلة، وبالتالي سيكون فتح نافذة منبثقة مصدرها طرف ثالث أو موقع غير موثوق آمنًا.
	</li>
	<li>
		من السهل جدًا فتح نافذة منبثقة.
	</li>
	<li>
		يمكن للنوافذ المنبثقة الانتقال إلى عنوان آخر، وإرسال رسالة إلى النافذة الأصلية.
	</li>
</ol>
<h2>
	منع ظهور النوافذ المنبثقة
</h2>

<p>
	أساءت المواقع المشبوهة استخدام النوافذ المنبثقة سابقًا، حيث أمكنها فتح آلاف النوافذ المنبثقة التي تحمل إعلانات أو منشورات دعائية، لذلك تحاول معظم المتصفحات الحديثة حاليًا منع ظهور هذه النوافذ لحماية المستخدم.
</p>

<p>
	تحجب معظم المتصفحات النوافذ المنبثقة إذا استُدعيت خارج شيفرة معالجات الأحداث event handler التي تستجيب لأفعال المستخدِم مثل <code>onClick</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4143_8" style="">
<span class="com">// تُحجب النافذة المنبثقة</span><span class="pln">
window</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="str">'https://javascript.info'</span><span class="pun">);</span><span class="pln">

</span><span class="com">// يُسمح بظهور النافذة المنبثقة</span><span class="pln">
button</span><span class="pun">.</span><span class="pln">onclick </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  window</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="str">'https://javascript.info'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	وهكذا ستتمكن المتصفحات من حماية المستخدم نوعًا ما، لكن هذا الأسلوب لن يعطل وظيفة النوافذ المنبثقة كاملةً. لكن ماذا لو فتحنا نافذة منبثقة ضمن شيفرة <code>onClcik</code>، لكن بعد تحديد زمن ظهورها باستخدام التعليمة <code>setTimeOut</code>؟ سيبدو الأمر مربكًا لاختلاف السلوك على متصفحات مختلفة.
</p>

<p>
	جرّب الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4143_10" style="">
<span class="com">// ستظهر النافذة لثلاث ثوان</span><span class="pln">
setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="str">'http://google.com'</span><span class="pun">),</span><span class="pln"> </span><span class="lit">3000</span><span class="pun">);</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" frameborder="no" height="156" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/ExvZKKo?default-tab=html%2Cresult&amp;editable=true" style="width: 100%;" title="JS-P3-01-Popups-and-window-methods-ex1">See the Pen JS-P3-01-Popups-and-window-methods-ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	تظهر النافذة المنبثقة على Chrome وسيحجبها Firefox، لكن لو قللنا زمن ظهورها فستظهر أيضًا على Firefox:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4143_12" style="">
<span class="com">// ستظهر النافذة لثانية</span><span class="pln">
setTimeout</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="str">'http://google.com'</span><span class="pun">),</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span></pre>

<p>
	إن سبب الاختلاف السابق هو أنّ Firefox سيقبل أية قيمة لزمن الظهور التي تساوي ثانيتين أو أقل، لكنه سيرفع ثقته عن النوافذ المنبثقة معتبرًا أنها ظهرت رغم إرادة المستخدم إن زاد زمن الظهور، لذلك فقد حُجبت في الحالة الأولى وظهرت في الثانية.
</p>

<h2>
	التابع window.open
</h2>

<p>
	تُستخدم العبارة البرمجية التالية لفتح نافذة منبثقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4143_14" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> params</span><span class="pun">)</span></pre>

<p>
	حيث:
</p>

<ul>
<li>
		url: عنوان المورد الذي سيُحمّل ضمن الصفحة.
	</li>
	<li>
		name: اسم النافذة التي ستُفتح، فلكل نافذة اسم <code>window.name</code>، وبذلك يمكننا تحديد النافذة التي سنستخدمها لإظهار المحتويات المنبثقة، فإذا وجدت نافذة بهذا الاسم فستُحمّل محتويات المورد ضمنها، وإلا ستُفتح نافذة جديدة بالاسم المحدد.
	</li>
	<li>
		params: سلسلة نصية تمثل تعليمات تهيئة النافذة الجديدة، وتحتوي على إعدادات تفصل بينها فاصلة، ولا ينبغي وجود فراغات بين الإعدادات، مثل السلسلة <code>width=200,height=100</code>، والمعاملات <code>params</code> التي يمكن ضبطها هي:
	</li>
	<li>
		الموضع position:
	</li>
	<li>
		<code>left/top</code>: قيمة عددية تمثل إحداثيات الزاوية العليا اليسرى للنافذة على شاشة العرض، لكن لا يمكن وضع النافذة خارج شاشة العرض.
		<ul>
<li>
				<code>width/height</code>: تحدد عرض النافذة الجديدة وارتفاعها، وهناك طبعًا حد أدنى لقيمة كل منهما، فلا يمكن إنشاء نافذة غير مرئية.
			</li>
		</ul>
</li>
	<li>
		ميزات النافذة Window features:
		<ul>
<li>
				<code>menubar</code>: تأخذ إحدى القيمتين yes/no، ومهمتها إظهار قائمة المتصفح في النافذة الجديدة أو إخفاؤها.
			</li>
			<li>
				<code>toolbar</code>: تأخذ إحدى القيمتين yes/no، ومهمتها إظهار شريط التنقل navigation bar في النافذة الجديدة أو إخفاؤه.
			</li>
			<li>
				<code>location</code>: تأخذ إحدى القيمتين yes/no، ومهمتها إظهار شريط العنوان في النافذة الجديدة أو إخفاؤه، ويجب الانتباه إلى أن المتصفحين Firefox وInternet Explorer لا يسمحان بذلك افتراضيًا.
			</li>
			<li>
				<code>status</code>: تأخذ إحدى القيمتين yes/no، ومهمتها إظهار شريط الحالة status bar أو إخفاؤه، وتفرض معظم المتصفحات إظهاره في النوافذ الجديدة.
			</li>
			<li>
				<code>resizable</code>: تأخذ إحدى القيمتين yes/no، تمنع تغيير حجم النافذة الجديدة، ولا يُنصح بتمكين هذه الميزة.
			</li>
			<li>
				<code>scrollbars</code>: تأخذ إحدى القيمتين yes/no، تمنع ظهور أشرطة التمرير في النافذة الجديدة، ولا يُنصح بتمكين هذه الميزة. توجد أيضًا مجموعة من الميزات الأقل دعمًا كونها متعلقة بمتصفحات محددة ولا تستخدم عادةً، ويمكن الاطلاع عليها عن طريق البحث عن التابع <code>window.open</code> ضمن <a data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" href="https://developer.mozilla.org/en/DOM/window.open" rel="external nofollow">شبكة مطوري Mozilla</a>
			</li>
		</ul>
</li>
</ul>
<h2>
	مثال نموذجي لنافذة بأقل ميزات
</h2>

<p>
	لنفتح نافذةً بأقل مجموعة ممكنة من الميزات، وذلك لمعرفة تلك التي يسمح المتصفح بتعطيلها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4143_17" style="">
<span class="pln">let params </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">scrollbars</span><span class="pun">=</span><span class="pln">no</span><span class="pun">,</span><span class="pln">resizable</span><span class="pun">=</span><span class="pln">no</span><span class="pun">,</span><span class="pln">status</span><span class="pun">=</span><span class="pln">no</span><span class="pun">,</span><span class="pln">location</span><span class="pun">=</span><span class="pln">no</span><span class="pun">,</span><span class="pln">toolbar</span><span class="pun">=</span><span class="pln">no</span><span class="pun">,</span><span class="pln">menubar</span><span class="pun">=</span><span class="pln">no</span><span class="pun">,</span><span class="pln">
width</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln">height</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln">left</span><span class="pun">=-</span><span class="lit">1000</span><span class="pun">,</span><span class="pln">top</span><span class="pun">=-</span><span class="lit">1000</span><span class="pun">`;</span><span class="pln">

open</span><span class="pun">(</span><span class="str">'/'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'test'</span><span class="pun">,</span><span class="pln"> params</span><span class="pun">);</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" frameborder="no" height="210" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/VwzPaKV?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-Popups-and-window-methods-ex2">See the Pen JS-P3-01-Popups-and-window-methods-ex2 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	عطلنا في الشيفرة السابقة معظم ميزات النافذة ووضعناها خارج مجال عرض الشاشة، إذا نفّذنا الشيفرة السابقة وشاهدنا ما يحدث، فسنجد أن معظم المتصفحات ستعالج تلقائيًا القيم الخاطئة -كإعطاء الطول أو العرض القيمة 0 أو القيمة التي تجعل من الزاوية العليا اليسرى للنافذة خارج مجال العرض- فمثلًا: سيفتح Chrome هذه النوافذ بأعلى قيم ممكنة للطول والعرض لتملأ الشاشة.
</p>

<p>
	لنضع الآن النافذة في مكان مناسب ونعطي قيمًا مقبولة للإحداثيات <code>width</code> و<code>height</code> و<code>left</code> و<code>top</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4143_19" style="">
<span class="pln">let params </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">scrollbars</span><span class="pun">=</span><span class="pln">no</span><span class="pun">,</span><span class="pln">resizable</span><span class="pun">=</span><span class="pln">no</span><span class="pun">,</span><span class="pln">status</span><span class="pun">=</span><span class="pln">no</span><span class="pun">,</span><span class="pln">location</span><span class="pun">=</span><span class="pln">no</span><span class="pun">,</span><span class="pln">toolbar</span><span class="pun">=</span><span class="pln">no</span><span class="pun">,</span><span class="pln">menubar</span><span class="pun">=</span><span class="pln">no</span><span class="pun">,</span><span class="pln">
width</span><span class="pun">=</span><span class="lit">600</span><span class="pun">,</span><span class="pln">height</span><span class="pun">=</span><span class="lit">300</span><span class="pun">,</span><span class="pln">left</span><span class="pun">=</span><span class="lit">100</span><span class="pun">,</span><span class="pln">top</span><span class="pun">=</span><span class="lit">100</span><span class="pun">`;</span><span class="pln">

open</span><span class="pun">(</span><span class="str">'/'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'test'</span><span class="pun">,</span><span class="pln"> params</span><span class="pun">);</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" frameborder="no" height="204" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/XWapdNz?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-Popups-and-window-methods-ex3">See the Pen JS-P3-01-Popups-and-window-methods-ex3 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	ستُظهر معظم المتصفحات النافذة بالشكل المطلوب.
</p>

<p>
	إليك مجموعة من القواعد عند تجاهل بعض الإعدادات:
</p>

<ul>
<li>
		إذا لم تضع قيمة للوسيط الثالث <code>params</code> عند استدعاء التابع <code>open</code>، فستُستخدم المعاملات الافتراضية للنافذة.
	</li>
	<li>
		إذا وضعت السلسلة النصية التي تصف المعاملات <code>params</code>، لكنك أغفلت بعض الميزات التي تأخذ إحدى القيمتين "yes/no"، فستأخذ هذه الميزات القيمة "no"، وبالتالي إن أردت معاملات محددة، فانتبه إلى التصريح علنًا عن الميزات المطلوبة وإسناد القيمة "yes" لها.
	</li>
	<li>
		إذا لم تحدَّد الخاصية <code>left/top</code> في المعاملات <code>params</code>، فسيحاول المتصفح فتح النافذة الجديدة بجانب آخر نافذة قد فتحتها.
	</li>
	<li>
		إن لم تحدّد الخاصية <code>width/height</code> في المعاملات <code>params</code>، فسيكون حجم النافذة الجديدة مساويًا لحجم آخر نافذة فتحتها.
	</li>
</ul>
<h2>
	الوصول إلى النافذة المنبثقة من النافذة الأصل
</h2>

<p>
	يعيد استدعاء التابع <code>open</code> مرجعًا إلى النافذة الجديدة، يمكن استخدامه للتحكم بخصائص تلك النافذة، وتغيير موضعها وأشياء أخرى.
</p>

<p>
	سنوّلد في هذا المثال نافذةً منبثقةً ذات محتوىً معين باستخدام JavaScript:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4143_21" style="">
<span class="pln">let newWin </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="str">"about:blank"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"hello"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"width=200,height=200"</span><span class="pun">);</span><span class="pln">

newWin</span><span class="pun">.</span><span class="pln">document</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">"Hello, world!"</span><span class="pun">);</span></pre>

<p>
	سنغيّر الآن محتوى النافذة بعد انتهاء تحميله:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4143_23" style="">
<span class="pln">let newWindow </span><span class="pun">=</span><span class="pln"> open</span><span class="pun">(</span><span class="str">'/'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'example'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'width=300,height=300'</span><span class="pun">)</span><span class="pln">
newWindow</span><span class="pun">.</span><span class="pln">focus</span><span class="pun">();</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">newWindow</span><span class="pun">.</span><span class="pln">location</span><span class="pun">.</span><span class="pln">href</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (*) about:blank, لم يبدأ التحميل بعد</span><span class="pln">

newWindow</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let html </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`&lt;</span><span class="pln">div style</span><span class="pun">=</span><span class="str">"font-size:30px"</span><span class="pun">&gt;</span><span class="typ">Welcome</span><span class="pun">!&lt;/</span><span class="pln">div</span><span class="pun">&gt;`;</span><span class="pln">
  newWindow</span><span class="pun">.</span><span class="pln">document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">insertAdjacentHTML</span><span class="pun">(</span><span class="str">'afterbegin'</span><span class="pun">,</span><span class="pln"> html</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" frameborder="no" height="364" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/PoKWNWY?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-Popups-and-window-methods-ex4">See the Pen JS-P3-01-Popups-and-window-methods-ex4 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لن يكون محتوى النافذة الجديدة قد حُمِّل بعد استدعاء <code>window.open</code> مباشرةً، وقد وضحنا ذلك باستخدام التنبيه <code>alert</code> في السطر "(*)"، لذا عليك الانتظار حتى يعدل التابع المطلوب، كما يمكننا استخدام معالج الحدث <code>DOMContentLoaded</code> مع الخاصية <code>newWin.document</code>.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		سياسة الأصل المشترك same origin policy يمكن أن تصل نافذة ما إلى محتوى أخرى إن اشتركتا بالأصل نفسه -أي لهما نفس بروتوكول النقل والنطاق والمنفذ)- فمثلًا: لن تتمكن نافذة من الموقع "site.com" من الوصول إلى محتوى نافذة منبثقة مصدرها "gmail.com" وذلك لحماية المستخدم، وللاطلاع على تفاصيل أكثر يمكن الرجوع إلى فصل "الاتصال بين النوافذ المختلفة".
	</p>
</blockquote>

<h2>
	الوصول إلى نافذة من نافذة منبثقة
</h2>

<p>
	يمكن أن تصل النافذة المنبثقة إلى النافذة التي أنتجتها باستخدام المرجع الذي يعيده التابع <code>window.opener</code>، والذي يحمل القيمة "null" لجميع النوافذ عدا النوافذ المنبثقة.
</p>

<p>
	لاستبدال محتوى النافذة التي أنتجت النافذة المنبثقة بالكلمة "Test" نستخدم الشيفرة التالية :
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4143_25" style="">
<span class="pln">let newWin </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="str">"about:blank"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"hello"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"width=200,height=200"</span><span class="pun">);</span><span class="pln">

newWin</span><span class="pun">.</span><span class="pln">document</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">
  </span><span class="str">"&lt;script&gt;window.opener.document.body.innerHTML = 'Test'&lt;\/script&gt;"</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" frameborder="no" height="233" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/ZEJLWey?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-Popups-and-window-methods-ex5">See the Pen JS-P3-01-Popups-and-window-methods-ex5 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	إذًا فالعلاقة بين النوافذ الأصلية والمنبثقة ثنائية الاتجاه، لأن كلّا منهما تحمل مرجعًا إلى الأخرى.
</p>

<h2>
	إغلاق نافذة منبثقة
</h2>

<ul>
<li>
		نفذ الأمر <code>win.close</code> لإغلاق نافذة.
	</li>
	<li>
		نفذ الأمر <code>win.closed</code> للتحقق من إغلاق النافذة.
	</li>
</ul>
<p>
	يمكن عمليًا لأي نافذة استخدام التابع <code>()close</code>، لكن معظم المتصفحات ستتجاهله عندما لا تُنشأ النافذة عن طريق التابع <code>()window.open</code>، وبالتالي سيعمل على النوافذ المنبثقة فقط، وستكون قيمة الخاصية <code>closed</code> هي "true" إن أُغلقت النافذة، ولهذه الخاصية فائدتها في التحقق من إغلاق النوافذ والنوافذ المنبثقة، لذلك ينبغي على الشيفرة أن تأخذ في الحسبان احتمال إغلاق المستخدم للنافذة في أي لحظة.
</p>

<p>
	ستحمِّل الشيفرة التالية نافذة ثم ستغلقها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4143_27" style="">
<span class="pln">let newWindow </span><span class="pun">=</span><span class="pln"> open</span><span class="pun">(</span><span class="str">'/'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'example'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'width=300,height=300'</span><span class="pun">);</span><span class="pln">

newWindow</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  newWindow</span><span class="pun">.</span><span class="pln">close</span><span class="pun">();</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">newWindow</span><span class="pun">.</span><span class="pln">closed</span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" frameborder="no" height="237" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/xxLgVqj?default-tab=html%2Cresult" style="width: 100%;" title="JS-P3-01-Popups-and-window-methods-ex6">See the Pen JS-P3-01-Popups-and-window-methods-ex6 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h2>
	تحريك النافذة وتغيير حجمها
</h2>

<p>
	توجد عدة خيارات لتحريك نافذة أو تغيير حجمها، هي:
</p>

<ul>
<li>
		<code>(win.moveBy(x,y</code>: يحرّك النافذة "x" بيكسل نحو اليمين و"y" بيكسل نحو الأسفل انطلاقًا من الموقع الحالي، ويمكن استخدام قيمة سالبة للتحرك نحو الأعلى واليسار.
	</li>
	<li>
		<code>(win.moveTo(x,y</code>: لنقل النافذة إلى الموقع المحدد بالإحداثيتين (x,y) ضمن الشاشة.
	</li>
	<li>
		<code>(win.resizeBy(width,heigh</code>: تغيير حجم النافذة بتغيير قيمتي الارتفاع والعرض بالنسبة إلى القيمتين الحاليتين، ويُسمح باستخدام قيم سالبة.
	</li>
	<li>
		<code>(win.resizeTo(width,height</code>: تغيير حجم النافذة إلى الأبعاد المحددة.
	</li>
</ul>
<p>
	يمكن الاستفادة أيضًا من الحدث <code>window.onresize</code> عندما يتغير حجم النافذة.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>خاص بالنوافذ المنبثقة</strong>: تحجب المتصفحات التوابع السابقة لتمنع استخدامها بشكل غير سليم، وستعمل فقط على النوافذ المنبثقة التي نفتحها، والتي لا تحتوي على صفحات متعددة ضمنها.
	</p>

	<p>
		<strong>لا تكبير أو تصغير للنوافذ</strong>: لا يوجد في JavaScript أي وسائل لتكبير النافذة أو تصغيرها، حيث يُمنع مبرمجو الواجهة الأمامية من الوصول إلى دوال كهذه على مستوى نظام التشغيل، كما لا تعمل توابع تحريك النافذة وتغيير حجمها عندما تكون النافذة في وضع التكبير الكامل Maximized أو التصغير الكامل Minimized.
	</p>
</blockquote>

<h2>
	تمرير محتويات نافذة Scrolling a window
</h2>

<p>
	تحدثنا سابقًا عن الموضوع في فصل "أحجام النوافذ وقابلية التمرير"، والتوابع التي تنفذ المهمة هي:
</p>

<ul>
<li>
		<code>(win.scrollBy(x,y</code>: يمرر محتويات النافذة "x" بكسل إلى اليمين و"y" بكسل إلى الأسفل، ويسمح التابع باستخدام القيم السالبة.
	</li>
	<li>
		<code>(win.scrollTo(x,y</code>: يمرر محتويات النافذة إلى الموقع الذي إحداثياته (x,y).
	</li>
	<li>
		<code>(elem.scrollIntoView(top = true</code>: يمرر محتويات النافذة ليظهر العنصر <code>elem</code> في أعلى الصفحة، وهي الحالة الافتراضية، أو أسفل الصفحة عندما تكون top=false. كما يمكن الاستفادة من الحدث <code>window.onscroll</code>.
	</li>
</ul>
<h2>
	نقل تركيز الدخل إلى نافذة وتغشيتها focus/blur
</h2>

<p>
	يمكن نظريًا استخدام التابعين<code>()window.blur</code> و<code>()window.focus</code> للتركيز على نافذة أو رفع التركيز عنها، كما يمكن استخدام الحدث <code>focus/blur</code> الذي يسمح بالتقاط اللحظة التي ينقل فيها المستخدم التركيز إلى نافذة محددة أو ينتقل إلى أخرى. لكن لو انتقلنا إلى التطبيق العملي فسنجد محدوديةً كبيرةً في تطبيقهما، لأنّ الصفحات المشبوهة أساءت استخدامهما. لنتأمل الشيفرة التالية مثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4143_29" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">onblur </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">focus</span><span class="pun">();</span></pre>

<p>
	فعندما يحاول المستخدم تبديل النافذة والانتقال إلى أخرى (<code>window.onblur</code>)، ستعيد الشيفرة السابقة التركيز إلى نفس النافذة.
</p>

<p>
	لذلك وضعت المتصفحات قيودًا لمنع استخدام الشيفرة بالطريقة السابقة، ولحماية المستخدم من النوافذ الدعائية والصفحات المشبوهة.
</p>

<p>
	مثلًا: يتجاهل متصفح الهواتف المحمولة التابع <code>()window.focus</code> بشكل كامل، كما لا يمكن التركيز على نافذة منبثقة ستظهر في صفحة جديدة ضمن النافذة نفسها بدلًا من ظهورها في نافذة جديدة.
</p>

<p>
	ومع ذلك يمكننا الاستفادة من التابعين السابقين في بعض الحالات، فمثلًا:
</p>

<ul>
<li>
		من الجيد تنفيذ الأمر <code>()newWindow.focus</code>عند فتح نافذة منبثقة جديدة، لضمان وصول المستخدم إلى النافذة الجديدة في بعض المتصفحات العاملة على بعض أنظمة التشغيل.
	</li>
	<li>
		يمكن تعقب تنفيذ الحدثين <code>window.onfocus/onblur</code> إذا أردنا التأكد من أنّ زائري الموقع يستخدمون تطبيقنا أم لا، مما سيسمح بإيقاف أو إعادة استخدام بعض محتويات التطبيق كالرسوم المتحركة وغيرها، مع وجوب الانتباه إلى أنّ الحدث <code>blur</code> سيشير إلى مغادرة الزائر للنافذة المحددة، ومع إمكانية ملاحظة الزائر للنافذة إذا بقيت في خلفية الشاشة.
	</li>
</ul>
<h2>
	خلاصة
</h2>

<p>
	لا تستخدم النوافذ المنبثقة إلا نادرًا، لوجود بدائل عنها، مثل: تحميل وإظهار المعلومات ضمن الصفحة أو استخدام إطارات <code>iframe</code>.
</p>

<p>
	ويعتبر إبلاغ المستخدم بفتح نافذة منبثقة أسلوبًا جيدًا، إذ يسمح وجود أيقونة "فتح نافذة" بجوار أي رابط أو زر أن يتوقع المستخدم انتقال التركيز من النافذة التي يعمل عليها إلى أخرى، وبالتالي سينتبه إلى كلتيهما.
</p>

<ul>
<li>
		يمكن فتح نافذة منبثقة باستدعاء التابع <code>( open(url, name, params</code> الذي يعيد مرجعًا إلى النافذة الجديدة.
	</li>
	<li>
		تمنع المتصفحات استدعاء التابع من خلال شيفرة خارج نطاق أفعال المستخدم (شيفرة الاستجابة لأحداث)، كما تُظهر المتصفحات عادة تنبيهًا للمستخدم بهذا الأمر، ويكون الخيار له بالسماح بتنفيذ الشيفرة أم لا.
	</li>
	<li>
		تفتح المتصفحات صفحات جديدة ضمن النافذة نفسها بشكل افتراضي، لكن عندما يُحدد حجم للنافذة ستظهر على شكل نافذة منبثقة.
	</li>
	<li>
		يمكن للنافذة الجديدة الوصول إلى الأصلية باستخدام الخاصية <code>window.opener</code>.
	</li>
	<li>
		يمكن للنافذتين الجديدة والأصلية قراءة وتعديل محتويات الأخرى إذا كان لهما الأصل ذاته، ويمكنهما فقط تغيير موقع بعضهما و<a data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" href="https://javascript.info/cross-window-communication" rel="external nofollow">تبادل الرسائل</a> إذا لم يكن لهما الأصل نفسه .
	</li>
	<li>
		يستخدم التابع <code>()close</code> لإغلاق نافذة منبثقة، كما يمكن إغلاقها بالطريقة التقليدية، وفي كلا الحالتين ستكون قيمة الخاصية <code>window.closed</code> هي "true".
	</li>
	<li>
		يستخدم التابعان <code>()foucs</code> و<code>()blur</code> لإعطاء التركيز إلى نافذة او تحويله عنها، لكنهما لا يعملان كما هو مطلوب دائمًا.
	</li>
	<li>
		يستخدم الحدثان <code>focus</code> و<code>blur</code> لتتبع عدد مرات دخول النافذة أو الخروج منها، مع الانتباه إلى أنّ أي نافذة يمكن أن تبقى مرئية في الخلفية بعد تنفيذ الأمر <code>blur</code>.
	</li>
</ul>
<p>
	ترجمة -وبتصرف- للفصل <a data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" href="https://javascript.info/popup-windows" rel="external nofollow">popups and window methods</a> من سلسلة <a data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" href="https://javascript.info" rel="external nofollow">The Modern JavaScript Tutorial</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال التالي: <a data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D9%88%D8%A7%D9%81%D8%B0-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1250/" rel="">التخاطب بين النوافذ في جافاسكريبت</a>
	</li>
	<li>
		<a data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D8%A7%D9%84%D9%85%D8%AA%D8%B9%D9%84%D9%82%D8%A9-%D8%A8%D8%AF%D9%88%D8%B1%D8%A9-%D8%AD%D9%8A%D8%A7%D8%A9-%D8%B5%D9%81%D8%AD%D8%A9-html-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D9%87%D8%A7-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1236/" rel="">الأحداث المتعلقة بدورة حياة صفحة HTML وكيفية التحكم بها عبر جافاسكربت</a>
	</li>
	<li>
		<a data-ss1634981868="1" data-ss1634982269="1" data-ss1634982302="1" data-ss1634982385="1" data-ss1634982671="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B9%D9%84%D9%8A%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1242/" rel="">الدوال العليا في جافاسكريبت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1253</guid><pubDate>Fri, 18 Jun 2021 15:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62F;&#x648;&#x627;&#x644; &#x627;&#x644;&#x639;&#x644;&#x64A;&#x627; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B9%D9%84%D9%8A%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1242/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_06/60bb01f99953f_-.png.12c94b25c8aed15dd7a1d5d8774750a1.png" /></p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		قال تزو-لي متبجحًا لتزو-سو أنّ برنامجه الأخير يحتوي على مائتي ألف سطر برمجي خلاف التعليقات، فرد عليه تزو-سو بأن برنامجه به حوالي مليون سطر برمجي! حينئذ قال السيد يوان-ما أنّ أفضل برامجه ليس به سوى خمس مائة سطر فقط، فلما سمعا ذلك عرفا أنّ قولهما ليس بشيء.
	</p>

	<p>
		__ يوان-ما Yuan-Ma، كتاب البرمجة The Book Of Programming
	</p>

	<p>
		هناك طريقتان لتصميم البرمجيات، تتمثل إحداهما في جعل البرنامج سهلًا جدًا، بحيث لا تجد فيه أي عيوب، والأخرى بجعله معقدًا جدًا بحيث لا تستطيع رؤية عيوب ظاهرة.
	</p>

	<p>
		توني هور C. A. R. Hoare، محاضرة جائزة تيورنج، 1980 في رابطة الآلات البرمجية ACM.
	</p>
</blockquote>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="68503" href="https://academy.hsoub.com/uploads/monthly_2021_06/chapter_picture_5.jpg.b760875c06f1b42019fa83ffb1b7fb17.jpg" rel=""><img alt="chapter_picture_5.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="68503" data-unique="10b40uhad" src="https://academy.hsoub.com/uploads/monthly_2021_06/chapter_picture_5.jpg.b760875c06f1b42019fa83ffb1b7fb17.jpg"></a>
</p>

<p>
	تتكلف البرامج بموارد أكثر كلما زاد حجمها، وذلك ليس بسبب الوقت الذي تستغرقه من أجل بنائها، بل لأنّ الحجم الكبير يتبعه تعقيد أكثر، ويحيّر ذلك التعقيد المبرمجين العاملين عليه، حيث تجعلهم تلك الحيرة يرتكبون أخطاءً في صورة زلات برمجية Bugs، وعليه يكون البرنامج الكبير فرصةً كبيرةً لهذه الزلات بالاختفاء وسط الشيفرات، مما يصعِّب الوصول إليها.
</p>

<p>
	لنَعُدْ إلى المثالين الأخيرَين المذكورَين في مقدمة هذه السلسلة، حيث يحتوي المثال الأول منهما على ستة أسطر، وهو مستقل بذاته، انظر كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_7" style="">
<span class="pln">let total </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">count </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  total </span><span class="pun">+=</span><span class="pln"> count</span><span class="pun">;</span><span class="pln">
  count </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">total</span><span class="pun">);</span></pre>

<p>
	أما الثاني فيعتمد على دالتين خارجيتين، ويتكوّن من سطر واحد فقط، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_9" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">sum</span><span class="pun">(</span><span class="pln">range</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">)));</span></pre>

<p>
	برأيك، أيهما أكثر عرضةً لتكون فيه زلة برمجية؟
</p>

<p>
	إذا حسبنا حجم تعريفي <code>sum</code> و<code>range</code>، فسيكون البرنامج الثاني أكبر من الأول، لكن لا زلنا نراه أكثر صِحة، حيث عبَّر عن الحل بألفاظ تتوافق مع المشكلة المحلولة، فلا يتعلق استدعاء مجال من الأعداد بالحلقات التكرارية والعدادات بقدر ما يتعلق بالمجالات والمجموع الإجمالي؛ وتحتوي تعاريف هذه الألفاظ (دالتي <code>sum</code>، و<code>range</code>) على حلقات تكرارية، وعدادات، وتفاصيل أخرى، وبما أنها تعبر عن مفاهيم أبسط من البرنامج ككل، فهي أدنى ألا تحتوي على أخطاء.
</p>

<h2>
	التجريد
</h2>

<p>
	تسمى هذه الأنواع من الألفاظ في السياقات البرمجية بالتجريدات Abstractions، وهي تخفي التفاصيل الدقيقة وتعطينا القدرة على الحديث عن المشاكل على مستوى أعلى أو أكثر تجريدًا. انظر هاتين الوصفتين أدناه لتقريب الأمر:
</p>

<ul>
<li>
		الوصفة الأولى:
	</li>
</ul>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		ضع 1 كوب من البازلاء لكل شخص في حاوية. أضف الماء حتى تغطي البازلاء. اترك البازلاء في الماء لاثنتي عشرة ساعة على الأقل. أخرج البازلاء من الماء وضعها في إناء الطهي. أضف 4 أكواب من الماء لكل شخص. ضع الغطاء على الإناء واترك البازلاء على نار هادئة جدًا لمدة ساعتين. خذ نصف بصلة لكل شخص وقطعها إلى قطع صغيرة باستخدام سكين. أضف البصل المقطع إلى البازلاء. خذ جزرة لكل شخص. قطع الجزر إلى قطع صغيرة باستخدام سكين. أضف الجزر إلى البازلاء. اترك ذلك كله على النار لعشرة دقائق أخرى.
	</p>
</blockquote>

<ul>
<li>
		الوصفة الثانية:
	</li>
</ul>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		جهز لكل شخص 1 كوب من البازلاء المجففة، ونصف بصلة مقطعة، وساق من الكرفس وجزرة.
	</p>

	<p>
		انقع البازلاء في الماء لمدة 12 ساعة، واطهها على نار هادئة جدًا لساعتين مع مقدار 4 أكواب من الماء، وبعدها قطع الخضروات وأضفها، ثم اترك ذلك كله لعشرة دقائق إضافية.
	</p>
</blockquote>

<p>
	لا شك أن الوصفة الثانية أقصر وأيسر في التفسير والفهم، لكن ستحتاج إلى فهم المصطلحات الخاصة بالطهي، مثل: النقع، والطهي، والتقطيع، والتجفيف (هذه المصطلحات للتقريب مثالًا، وشاهدها أن تكون على دراية بمصطلحات مجال المشكلة التي تريد حلها، وإلا فهي معروفة لكل أحد).
</p>

<p>
	يقع الكثير من المبرمجين في خطأ الوصفة الأولى عند سردهم للخطوات الدقيقة والصغيرة التي على الحاسوب تنفيذها خطوةً بخطوة، وذلك بسبب عدم ملاحظتهم للمفاهيم العليا في المشكلة التي بين أيديهم، ويُعَدّ الانتباه عند حلك لمشكلة بهذا الأسلوب مهارةً مفيدةً جدًا.
</p>

<h2>
	تجريد التكرار
</h2>

<p>
	تُعَدّ الدوال البسيطة طريقة ممتازة لبناء تجريدات، غير أنها تعجز عن ذلك أحيانًا، فمن الشائع أن يفعل البرنامج شيئًا ما بعدد معيّن من المرات باستخدام حلقة <code>for</code>، فمثلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_11" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	فهل نستطيع تجريد مفهوم "فعل شيء ما عددًا من المرات قدره N" في صورة دالة؟ بدايةً، من السهل كتابة دالة تستدعي <code>console.log</code> عددًا من المرات قدره N:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_13" style="">
<span class="kwd">function</span><span class="pln"> repeatLog</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لكن ماذا لو أردنا فعل شيء آخر غير تسجيل الأعداد؟ بما أنّه يمكن تمثيل "فعل شيء ما" على أساس دالة، والدوال ما هي إلا قيم، فسنستطيع تمرير إجراءنا على أساس قيمة دالة، وذلك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_16" style="">
<span class="kwd">function</span><span class="pln"> repeat</span><span class="pun">(</span><span class="pln">n</span><span class="pun">,</span><span class="pln"> action</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> n</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    action</span><span class="pun">(</span><span class="pln">i</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

repeat</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 0</span><span class="pln">
</span><span class="com">// → 1</span><span class="pln">
</span><span class="com">// → 2</span></pre>

<p>
	لسنا في حاجة إلى تمرير دالة معرَّفة مسبقًا إلى <code>repeat</code>، فغالبًا من السهل إنشاء قيمة دالة عند الحاجة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_18" style="">
<span class="pln">let labels </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
repeat</span><span class="pun">(</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> i </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  labels</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(`</span><span class="typ">Unit</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">i </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">labels</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → ["Unit 1", "Unit 2", "Unit 3", "Unit 4", "Unit 5"]</span></pre>

<p>
	وكما ترى، فهذا مهيكل في صورة محاكية لحلقة <code>for</code>، إذ يصف نوع الحلقة التكرارية أولًا، ثم يوفر متنًا لها، غير أنّ المتن الآن مكتوب على أساس قيمة دالة مغلّفة بأقواس الاستدعاء إلى <code>repeat</code>، وهذا هو السبب الذي يجعل من الواجب إغلاقها بقوس إغلاق معقوص <code>‎}‎</code> وقوس إغلاق عادي <code>‎)‎</code> بأقواس إغلاق، وفي حالة هذا المثال عندما يكون المتن تعبيرًا واحدًا وصغيرًا، فيمكنك إهمال الأقواس المعقوصة وكتابة الحلقة في سطر واحد.
</p>

<h2>
	الدوال العليا
</h2>

<p>
	تسمى الدوال التي تعمل على دوال أخرى سواءً بأخذها على أساس وسائط أو بإعادتها لها، باسم الدوال العليا higher-order functions، وبما أنّ الدوال ما هي إلا قيم منتظمة، فلا شيء جديد في وجود هذا النوع منها، والمصطلح قادم من الرياضيات حين يؤخذ الفرق بين الدوال والقيم الأخرى على محمل الجد.
</p>

<p>
	وتسمح لنا الدوال العليا بعملية التجريد على القيم والإجراءات أيضًا، وتأتي في عدة أشكال وصور، فقد تنشِئ الدوال دوالًا أخرى جديدةً، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_20" style="">
<span class="kwd">function</span><span class="pln"> greaterThan</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> m </span><span class="pun">=&gt;</span><span class="pln"> m </span><span class="pun">&gt;</span><span class="pln"> n</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
let greaterThan10 </span><span class="pun">=</span><span class="pln"> greaterThan</span><span class="pun">(</span><span class="lit">10</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">greaterThan10</span><span class="pun">(</span><span class="lit">11</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	وقد تغيّر الدوال دوالًا أخرى، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_22" style="">
<span class="kwd">function</span><span class="pln"> noisy</span><span class="pun">(</span><span class="pln">f</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(...</span><span class="pln">args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"calling with"</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">);</span><span class="pln">
    let result </span><span class="pun">=</span><span class="pln"> f</span><span class="pun">(...</span><span class="pln">args</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"called with"</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">,</span><span class="pln"> </span><span class="str">", returned"</span><span class="pun">,</span><span class="pln"> result</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> result</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
noisy</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">min</span><span class="pun">)(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → calling with [3, 2, 1]</span><span class="pln">
</span><span class="com">// → called with [3, 2, 1] , returned 1</span></pre>

<p>
	كما توفر الدوال أنواعًا جديدةً من تدفق التحكم control flow:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_24" style="">
<span class="kwd">function</span><span class="pln"> unless</span><span class="pun">(</span><span class="pln">test</span><span class="pun">,</span><span class="pln"> then</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">test</span><span class="pun">)</span><span class="pln"> then</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

repeat</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> n </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  unless</span><span class="pun">(</span><span class="pln">n </span><span class="pun">%</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">n</span><span class="pun">,</span><span class="pln"> </span><span class="str">"is even"</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
</span><span class="com">// → 0 is even</span><span class="pln">
</span><span class="com">// → 2 is even</span></pre>

<p>
	يوفر تابع مصفوفة مدمج <code>forEach</code> شيئًا مثل حلقة <code>for</code>/<code>of</code> التكرارية على أساس دالة عليا، وذلك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_26" style="">
<span class="pun">[</span><span class="str">"A"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"B"</span><span class="pun">].</span><span class="pln">forEach</span><span class="pun">(</span><span class="pln">l </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">l</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → A</span><span class="pln">
</span><span class="com">// → B</span></pre>

<h2>
	مجموعات البيانات النصية
</h2>

<p>
	تُعَدّ معالجة البيانات إحدى الجوانب التي تبرز فيها أهمية الدوال العليا، ولضرْب مثال على ذلك سنحتاج إلى بعض البيانات الفعلية، كما سنستخدم في هذا المقال مجموعة بيانات عن نصوص وأنظمة كتابة، مثل: اللاتينية، والعربية، والسيريلية (حروف اللغات الأوراسية، مثل: الروسية، والبلغارية).
</p>

<p>
	ترتبط أغلب محارف اللغات المكتوبة بنص معين، ولعلك تذكر حديثنا عن الترميز الموحد Unicode الذي يسند عددًا لكل محرف من محارف هذه اللغات، حيث يحتوي هذا المعيار على 140 نصًا مختلفًا، من بينها 81 لا تزال مستخدمةً، في حين صارت 59 منها مهملةً أو تاريخيةً، أي لم تَعُدْ مستخدمة؛ فعلى سبيل المثال، انظر هذه الكتابة من اللغة التاميلية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="68504" href="https://academy.hsoub.com/uploads/monthly_2021_06/tamil.png.924e2e89f362351f16d1b2ac7527032d.png" rel=""><img alt="tamil.png" class="ipsImage ipsImage_thumbnailed" data-fileid="68504" data-unique="e2gvob2hz" src="https://academy.hsoub.com/uploads/monthly_2021_06/tamil.png.924e2e89f362351f16d1b2ac7527032d.png"></a>
</p>

<p>
	تحتوي مجموعة البيانات على بعض أجزاء البيانات من النصوص المئة والأربعين المعرَّفة في اليونيكود، وهي متاحة في <a href="https://eloquentjavascript.net/code#5" rel="external nofollow">صندوق اختبار</a> هذا المقال على صورة رابطة <code>SCRIPTS</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_28" style="">
<span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Coptic"</span><span class="pun">,</span><span class="pln">
  ranges</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[[</span><span class="lit">994</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1008</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">11392</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11508</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">11513</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11520</span><span class="pun">]],</span><span class="pln">
  direction</span><span class="pun">:</span><span class="pln"> </span><span class="str">"ltr"</span><span class="pun">,</span><span class="pln">
  year</span><span class="pun">:</span><span class="pln"> </span><span class="pun">-</span><span class="lit">200</span><span class="pun">,</span><span class="pln">
  living</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
  link</span><span class="pun">:</span><span class="pln"> </span><span class="str">"https://en.wikipedia.org/wiki/Coptic_alphabet"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يخبرنا الكائن السابق بكل من: اسم النص، ومجالات اليونيكود المسنَدة إليه، واتجاه الكتابة، والزمن التقريبي لنشأة هذه اللغة، وما إذا كانت مستخدمةً أم لا، ورابط إلى مزيد من التفاصيل والبيانات عنها؛ وقد يكون اتجاه الكتابة من اليسار إلى اليمين "ltr"، أو من اليمين إلى اليسار "rtl"، كما في حالة اللغتين العربية والعبرية، أو من الأعلى إلى الأسفل "ttb" كما في حالة اللغة المنغولية.
</p>

<p>
	تحتوي خاصية <code>ranges</code> على مصفوفة من مجالات المحارف، حيث يكون كل منها مصفوفةً من عنصرين، هما الحدين الأدنى والأعلى، ويُسنَد أيّ رمز للمحارف بين هذه المجالات إلى النص، كما يُضمَّن الحد الأدنى فيها؛ أما الحد الأعلى فلا، أي يُعَدّ رمز 994 محرفًا قبطيًا Coptic في المثال السابق؛ أما الرمز 1008 فلا.
</p>

<h2>
	ترشيح المصفوفات
</h2>

<p>
	نستخدم دالة <code>filter</code> لإيجاد النصوص واللغات التي مازالت مستخدَمةً في مجموعات البيانات، إذ تُرشِّح عناصر المصفوفة التي لا تجتاز اختبارًا تجريه عليها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_30" style="">
<span class="kwd">function</span><span class="pln"> filter</span><span class="pun">(</span><span class="pln">array</span><span class="pun">,</span><span class="pln"> test</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let passed </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let element of array</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">test</span><span class="pun">(</span><span class="pln">element</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      passed</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">element</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> passed</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">SCRIPTS</span><span class="pun">,</span><span class="pln"> script </span><span class="pun">=&gt;</span><span class="pln"> script</span><span class="pun">.</span><span class="pln">living</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → [{name: "Adlam", …}, …]</span></pre>

<p>
	تستخدم الدالة وسيطًا اسمه <code>test</code>، وهو قيمة دالةٍ لملء الفراغ "gap" أثناء عملية اختيار العناصر. إذ تلاحظ كيف تبني دالة <code>filter</code> مصفوفةً جديدةً من العناصر التي تجتاز اختبارها بدلًا من حذف العناصر من المصفوفة القديمة، وهذا يشير إلى أنّ هذه الدالة دالةً نقيةً pure function، إذ لا تُعدِّل المصفوفة المُمرَرة إليها.
</p>

<p>
	تشبه هذه الدالة التابع <code>forEach</code> في كونها تابع مصفوفة قياسي، وقد عرَّف المثال السابق الدالة لتوضيح كيفية عملها من الداخل ليس إلا؛ أما من الآن فصاعدًا فسنستخدمها فقط كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_32" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">SCRIPTS</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">s </span><span class="pun">=&gt;</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">direction </span><span class="pun">==</span><span class="pln"> </span><span class="str">"ttb"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → [{name: "Mongolian", …}, …]</span></pre>

<h2>
	التحويل مع map
</h2>

<p>
	ليكن لدينا مصفوفة كائنات تمثِّل عدة نصوص، حيث أُنتجت بترشيح مصفوفة <code>SCRIPTS</code>، لكننا نريد مصفوفةً من الأسماء لأنها أسهل في البحث والتدقيق.
</p>

<p>
	هنا يأتي دور التابع <code>map</code> الذي يحوّل مصفوفةً بتطبيق دالة على جميع عناصرها، ثم يبني مصفوفةً جديدةً من القيم المعادة، وتكون المصفوفة الجديدة بطول المصفوفة المدخلة، مع إعادة توجيه محتوياتها في شكل form جديد بواسطة الدالة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_34" style="">
<span class="kwd">function</span><span class="pln"> map</span><span class="pun">(</span><span class="pln">array</span><span class="pun">,</span><span class="pln"> transform</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let mapped </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let element of array</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    mapped</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">transform</span><span class="pun">(</span><span class="pln">element</span><span class="pun">));</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> mapped</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let rtlScripts </span><span class="pun">=</span><span class="pln"> SCRIPTS</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">s </span><span class="pun">=&gt;</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">direction </span><span class="pun">==</span><span class="pln"> </span><span class="str">"rtl"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">map</span><span class="pun">(</span><span class="pln">rtlScripts</span><span class="pun">,</span><span class="pln"> s </span><span class="pun">=&gt;</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">name</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → ["Adlam", "Arabic", "Imperial Aramaic", …]</span></pre>

<p>
	وبالمثل، يُعَدّ التابع <code>map</code> تابع مصفوفة قياسي، أي يحاكي كلًا من: <code>filter</code>، و<code>forEach</code>.
</p>

<h2>
	التلخيص باستخدام reduce
</h2>

<p>
	يُعَدّ إجراء حسابات على قيمة واحدة من المصفوفات من العمليات الشائعة على هذه المصفوفات، ومثالنا التعاودي الذي يستدعي تجميعةً من الأعداد هو مثال على هذا، وكذلك إيجاد النص الحاوي على أكبر عدد من المحارف.
</p>

<p>
	تُدعى العملية العليا الممثِلة لهذا النمط بـ reduce، وتُدعى fold أحيانًا، إذ تُنتِج قيمةً بتكرار أخذ عنصر ما من المصفوفة، ومن ثم جمعه مع القيمة الحالية، فتبدأ عند إيجاد مجموع الأعداد من الصفر، وتضيف كل عنصر إلى المجموع الإجمالي.
</p>

<p>
	تأخذ <code>reduce</code> جزءًا من المصفوفة، ودالة جامعة combining function، وقيمة بدء start value، على أساس معامِلات، وتختلف هذه الدالة عن دالتي <code>filter</code>، و<code>map</code> المتّسمتين بالوضوح والمباشرة أكثر، كما في الدالة التالية:
</p>

<pre class="ipsCode prettyprint lang-lua prettyprinted" id="ips_uid_4191_36" style="">
<span class="kwd">function</span><span class="pln"> reduce</span><span class="pun">(</span><span class="pln">array</span><span class="pun">,</span><span class="pln"> combine</span><span class="pun">,</span><span class="pln"> start</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">let</span><span class="pln"> current </span><span class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">let</span><span class="pln"> element of array</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    current </span><span class="pun">=</span><span class="pln"> combine</span><span class="pun">(</span><span class="pln">current</span><span class="pun">,</span><span class="pln"> element</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> current</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">reduce</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">],</span><span class="pln"> </span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 10</span></pre>

<p>
	يملك تابع المصفوفة القياسي <code>reduce</code> الموافق لهذه الدالة خاصيةً مميزة، إذ يسمح لك بإهمال وسيط <code>start</code> إن كان في مصفوفتك عنصرًا واحدًا على الأقل، حيث سيأخذ العنصر الأول من المصفوفة على أساس قيمة بدء له، ويبدأ التقليل من العنصر الثاني، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_38" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">].</span><span class="pln">reduce</span><span class="pun">((</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 10</span></pre>

<p>
	يمكننا استخدام <code>reduce</code> مرتين لحساب عدد محارف أو كلمات نص ما، كما في المثال التالي:
</p>

<pre class="ipsCode">
function characterCount(script) {
  return script.ranges.reduce((count, [from, to]) =&gt; {
    return count + (to - from);
  }, 0);
}

console.log(SCRIPTS.reduce((a, b) =&gt; {
  return characterCount(a) &lt; characterCount(b) ? b : a;
}));
// → {name: "Han", …}
</pre>

<p>
	تقلل دالة <code>charactercount</code> المجالات المسندَة إلى نص ما بجمع أحجامها، حيث تلاحظ استخدام التفكيك في قائمة المعامِلات للدالة المقلِّلة، ثم يُستَدعى التابع <code>reduce</code> مرةً ثانية لإيجاد أكبر نص من خلال موازنة نصين في كل مرة وإعادة الأكبر بينهما.
</p>

<p>
	تحتوي لغات الهان -نظام الكتابة الصيني، والياباني، والكوري- على أكثر من 89 ألف محرف مسنَد إليها في معيار يونيكود، مما يجعلها أكبر نظام كتابة في مجموعة البيانات.
</p>

<p>
	وقد قرر مجمع الترميز الموحد Unicode Consortium معاملة تلك اللغات على أنها نظام كتابة واحد لتوفير رموز المحارف، رغم مضايقة هذا لبعض العامة، وسُمي ذلك القرار بتوحيد الهان Han Unification.
</p>

<h2>
	قابلية التركيب
</h2>

<p>
	لن يبدو مثال إيجاد أكبر نص سيئًا إذا كتبناه دون استخدام الدوال العليا فيه، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_40" style="">
<span class="pln">let biggest </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let script of SCRIPTS</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">biggest </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">null</span><span class="pln"> </span><span class="pun">||</span><span class="pln">
      characterCount</span><span class="pun">(</span><span class="pln">biggest</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> characterCount</span><span class="pun">(</span><span class="pln">script</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    biggest </span><span class="pun">=</span><span class="pln"> script</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">biggest</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → {name: "Han", …}</span></pre>

<p>
	لا يزال البرنامج سهل القراءة على الرغم من استخدام أربع رابطات جديدة، وزيادة أربعة أسطر أخرى، حيث تبرز الدوال العليا عند الحاجة لإجراء عمليات تركيب، فمثلًا، دعنا نكتب شيفرة للبحث عن السنة المتوسطة لإنشاء لغة ما سواءً كانت حيةً أو ميتةً في مجموعة البيانات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_42" style="">
<span class="kwd">function</span><span class="pln"> average</span><span class="pun">(</span><span class="pln">array</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> array</span><span class="pun">.</span><span class="pln">reduce</span><span class="pun">((</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> a </span><span class="pun">+</span><span class="pln"> b</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> array</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">round</span><span class="pun">(</span><span class="pln">average</span><span class="pun">(</span><span class="pln">
  SCRIPTS</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">s </span><span class="pun">=&gt;</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">living</span><span class="pun">).</span><span class="pln">map</span><span class="pun">(</span><span class="pln">s </span><span class="pun">=&gt;</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">year</span><span class="pun">))));</span><span class="pln">
</span><span class="com">// → 1165</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">round</span><span class="pun">(</span><span class="pln">average</span><span class="pun">(</span><span class="pln">
  SCRIPTS</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">s </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">!</span><span class="pln">s</span><span class="pun">.</span><span class="pln">living</span><span class="pun">).</span><span class="pln">map</span><span class="pun">(</span><span class="pln">s </span><span class="pun">=&gt;</span><span class="pln"> s</span><span class="pun">.</span><span class="pln">year</span><span class="pun">))));</span><span class="pln">
</span><span class="com">// → 204</span></pre>

<p>
	نتبين مما سبق أنّ متوسط اللغات الميتة في اليونيكود أقدم من الحية، وهذا متوقع لا شك، كما أنّ الشيفرة السابقة ليست صعبة القراءة، إذ يمكن النظر إليها على أنها أنبوب، حيث نبدأ فيها بجميع اللغات، ثم نرشّح الحية منها أو الميتة، وبعدها نأخذ أعوام هؤلاء ونحسب المتوسط، ثم نقرب النتيجة لأقرب رقم صحيح. كما نستطيع كتابة هذه العملية الحسابية على صورة حلقة تكرارية واحدة كبيرة، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_44" style="">
<span class="pln">let total </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let script of SCRIPTS</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">script</span><span class="pun">.</span><span class="pln">living</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    total </span><span class="pun">+=</span><span class="pln"> script</span><span class="pun">.</span><span class="pln">year</span><span class="pun">;</span><span class="pln">
    count </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">round</span><span class="pun">(</span><span class="pln">total </span><span class="pun">/</span><span class="pln"> count</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 1165</span></pre>

<p>
	لكن من الصعب قراءة هذا الأسلوب لمعرفة ما الذي يُحسب فيه، وبما أنّ النتائج البينية غير ممثلة على أساس قيم مترابطة، فستدور حول نفسك لاستخراج شيء مثل <code>average</code> إلى دالة منفصلة.
</p>

<p>
	يختلف هذان المنظوران من حيث ما يفعله الحاسوب، إذ يبني الأول مصفوفات جديدة عند تشغيل <code>filter</code>، و<code>map</code>؛ بينما يحسب الثاني بعض الأعداد فقط وينجز عملًا أقل، ولا شك في تفضيلك للشيفرة المقروءة، لكن إن كنت تعالج مصفوفات ضخمة بتواتر، فيكون الأسلوب الأقل تجريدًا هنا أفضل بسبب السرعة الزائدة.
</p>

<h2>
	السلاسل النصية ورموز المحارف
</h2>

<p>
	تُعَدّ معرفة اللغة التي يستخدمها نص ما، إحدى استخدامات مجموعات البيانات، حيث يمَكّننا رمز المحرف المعطى من كتابة دالة لإيجاد اللغة الموافقة لهذا المحرف إن وجِدت، وذلك بسبب ارتباط كل لغة بمصفوفة من مجالات رموز المحارف، أي كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_46" style="">
<span class="kwd">function</span><span class="pln"> characterScript</span><span class="pun">(</span><span class="pln">code</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let script of SCRIPTS</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">script</span><span class="pun">.</span><span class="pln">ranges</span><span class="pun">.</span><span class="pln">some</span><span class="pun">(([</span><span class="pln">from</span><span class="pun">,</span><span class="pln"> to</span><span class="pun">])</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> code </span><span class="pun">&gt;=</span><span class="pln"> from </span><span class="pun">&amp;&amp;</span><span class="pln"> code </span><span class="pun">&lt;</span><span class="pln"> to</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> script</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">characterScript</span><span class="pun">(</span><span class="lit">121</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → {name: "Latin", …}</span></pre>

<p>
	يُعَدّ تابع <code>some</code> أعلاه دالةً عليا، إذ تأخذ دالة اختبار لتخبرك إن كانت الدالة تعيد <code>true</code> لأيّ عنصر في المصفوفة، ولكن كيف سنحصل على رموز المحارف في سلسلة نصية؟
</p>

<p>
	ذكرنا في <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D9%88%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%88%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1226/" rel="">المقال الأول</a> أنّ سلاسل جافاسكربت النصية مرمّزة على أساس تسلسلات من أعداد 16-بت، وتسمى هذه الأعداد بالأعداد البِتّية لمحارف السلسلة code units، حيث صُممِّت رموز المحارف character code في اليونيكود لتتوافق مع وحدة unit -مثل التي تعطيك 65 ألف محرف-؛ ولكن عارض بعض العامة زيادة الذاكرة المخصصة لكل محرف بعدما تبين عدم كفاية هذا، فقد ابتُكِرت لمعالجة هذه المشكلة صيغة UTF-16 التي استخدمتها جافاسكربت، حيث تصف أكثر المحارف شيوعًا باستخدام عدد بِتّي لمحرف 16-بت واحد، لكنها تستخدم زوجًا من تلك الأعداد البِتّية لغيرها.
</p>

<p>
	تُعَدّ UTF-16 فكرةً سيئةً حاليًا، إذ يبدو أنّها اختُرعت لخلق أخطاء! فمن السهل كتابة برامج تدّعي أنّ الأعداد البِتّية للمحارف والمحارف هما الشيء نفسه، وإن لم تكن لغتك تستخدم محارف من وحدتين فلا بأس؛ لكن سينهار البرنامج عند محاولة أحدهم استخدامه مع المحارف الصينية الأقل شيوعًا، ولحسن الحظ، فقد بدأ الجميع مع اختراع الإيموجي (الرموز التعبيرية) باستخدام المحارف ذات الوحدتين.
</p>

<p>
	لكن العمليات الواضحة في سلاسل جافاسكربت النصية، مثل: الحصول على طولها باستخدام خاصية <code>length</code>، والوصول إلى محتواها باستخدام الأقواس المربعة، لا تتعامل إلا مع الأعداد البِتية للمحارف، أنظر إلى ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_48" style="">
<span class="com">// محرفي إيموجي، حصان وحذاء</span><span class="pln">
let horseShoe </span><span class="pun">=</span><span class="pln"> </span><span class="str">"??"</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">horseShoe</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 4</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">horseShoe</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln">
</span><span class="com">// → (Invalid half-character)</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">horseShoe</span><span class="pun">.</span><span class="pln">charCodeAt</span><span class="pun">(</span><span class="lit">0</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 55357 (رمز لنصف محرف)</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">horseShoe</span><span class="pun">.</span><span class="pln">codePointAt</span><span class="pun">(</span><span class="lit">0</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 128052 (الرمز الحقيقي لرمز الحصان)</span></pre>

<p>
	يعطينا تابع <code>charCodeAt</code> عداد بِتي للمحرف فقط وليس الرمز الكامل للمحرف؛ أما تابع <code>codePointAt</code> الذي أضيف لاحقًا فيعطي محرف يونيكود كامل، لذا نستطيع استخدامه للحصول على المحارف من سلسلة نصية، لكن لا يزال الوسيط الممرر إلى <code>codePointAt</code> فهرسًا داخل تسلسل من الأعداد البِتّية لمحارف السلسلة، لذا فلا زلنا في حاجة إلى النظر هل يأخذ المحرف وحدتين رمزيتين أم وحدةً واحدةً للمرور على جميع المحارف في سلسلة نصية ما.
</p>

<p>
	ذكرنا في <a href="https://academy.hsoub.com/programming/javascript/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1234/" rel="">المقال السابق</a> أنه يمكن استخدام حلقة <code>for</code>/<code>of</code> التكرارية على السلاسل النصية، وقد أُدخل هذا النوع من الحلقات -شأنه في هذا شأن <code>codePointAt</code>- في الوقت الذي كانت العامة فيه على علم بمشكلة UTF-16، لذا سيعطيك محارف حقيقية حين استخدامه للتكرار على سلسلة نصية، بدلًا من أعداد بِتية لمحارف السلسلة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_50" style="">
<span class="pln">let roseDragon </span><span class="pun">=</span><span class="pln"> </span><span class="str">"??"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let </span><span class="kwd">char</span><span class="pln"> of roseDragon</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">char</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// → ?</span><span class="pln">
</span><span class="com">// → ?</span></pre>

<p>
	وإن كان لديك محرف -وما هو إلا سلسلة نصية من وحدة رمزية أو اثنتين-، فستستطيع استخدام <code>codePointAt(0)‎</code> للحصول على رمزه.
</p>

<h2>
	التعرف على النصوص
</h2>

<p>
	لدينا دالة <code>characterScript</code>، وطريقةً للتكرار الصحيح على المحارف، فالخطوة التالية إذًا هي عدّ المحارف المنتمية لكل لغة، كما في التجريد أدناه للعد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_52" style="">
<span class="kwd">function</span><span class="pln"> countBy</span><span class="pun">(</span><span class="pln">items</span><span class="pun">,</span><span class="pln"> groupName</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let counts </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let item of items</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let name </span><span class="pun">=</span><span class="pln"> groupName</span><span class="pun">(</span><span class="pln">item</span><span class="pun">);</span><span class="pln">
    let known </span><span class="pun">=</span><span class="pln"> counts</span><span class="pun">.</span><span class="pln">findIndex</span><span class="pun">(</span><span class="pln">c </span><span class="pun">=&gt;</span><span class="pln"> c</span><span class="pun">.</span><span class="pln">name </span><span class="pun">==</span><span class="pln"> name</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">known </span><span class="pun">==</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      counts</span><span class="pun">.</span><span class="pln">push</span><span class="pun">({</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> count</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">});</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      counts</span><span class="pun">[</span><span class="pln">known</span><span class="pun">].</span><span class="pln">count</span><span class="pun">++;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> counts</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">countBy</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">],</span><span class="pln"> n </span><span class="pun">=&gt;</span><span class="pln"> n </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">2</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → [{name: false, count: 2}, {name: true, count: 3}]</span></pre>

<p>
	تتوقع دالة <code>countBy</code> تجميعةً collection -من أي شيء نستطيع التكرار عليه باستخدام <code>for</code>/<code>of</code>-، ودالةً لحساب اسم المجموعة group للعنصر المعطى، حيث تعيد مصفوفةً من الكائنات وكل منها هو اسم لمجموعة، وتخبرك بعدد العناصر الموجودة في تلك المجموعة.
</p>

<p>
	تستخدِم هذه الدالة تابع مصفوفة اسمه <code>findIndex</code>، حيث يحاكي <code>indexof</code> نوعًا ما، لكنه يبحث في القيمة الأولى التي تعيد <code>true</code> في الدالة المعطاة بدلًا من البحث عن قيمة معينة، كما يتشابه معه في إعادة <code>‎-1</code> عند عدم وجود مثل هذا العنصر، ونستطيع باستخدام <code>countBy</code> كتابة الدالة التي تخبرنا أيّ اللغات مستخدمة في نص ما.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_54" style="">
<span class="kwd">function</span><span class="pln"> textScripts</span><span class="pun">(</span><span class="pln">text</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let scripts </span><span class="pun">=</span><span class="pln"> countBy</span><span class="pun">(</span><span class="pln">text</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let script </span><span class="pun">=</span><span class="pln"> characterScript</span><span class="pun">(</span><span class="kwd">char</span><span class="pun">.</span><span class="pln">codePointAt</span><span class="pun">(</span><span class="lit">0</span><span class="pun">));</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> script </span><span class="pun">?</span><span class="pln"> script</span><span class="pun">.</span><span class="pln">name </span><span class="pun">:</span><span class="pln"> </span><span class="str">"none"</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}).</span><span class="pln">filter</span><span class="pun">(({</span><span class="pln">name</span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> name </span><span class="pun">!=</span><span class="pln"> </span><span class="str">"none"</span><span class="pun">);</span><span class="pln">

  let total </span><span class="pun">=</span><span class="pln"> scripts</span><span class="pun">.</span><span class="pln">reduce</span><span class="pun">((</span><span class="pln">n</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">count</span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> n </span><span class="pun">+</span><span class="pln"> count</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">total </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"No scripts found"</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> scripts</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(({</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> count</span><span class="pun">})</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">round</span><span class="pun">(</span><span class="pln">count </span><span class="pun">*</span><span class="pln"> </span><span class="lit">100</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> total</span><span class="pun">)}%</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">name</span><span class="pun">}`;</span><span class="pln">
  </span><span class="pun">}).</span><span class="pln">join</span><span class="pun">(</span><span class="str">", "</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">textScripts</span><span class="pun">(</span><span class="str">'英国的狗说"woof", 俄罗斯的狗说"тяв"'</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 61% Han, 22% Latin, 17% Cyrillic</span></pre>

<p>
	تَعُدّ الدالة أولًا المحارف من خلال الاسم باستخدام <code>characterScript</code> لتعيينها اسمًا وتعود إلى السلسلة <code>"none"</code> من أجل المحارف التي ليست جزءًا من أي لغة، ثم يحذف استدعاء <code>filter</code> الإدخال الخاص بـ <code>"none"</code> من المصفوفة الناتجة بما أننا لا نهتم بتلك المحارف.
</p>

<p>
	سنحتاج إلى العدد الإجمالي للمحارف المنتمية إلى لغة ما لنستطيع حساب النسب، ويمكن معرفة ذلك من خلال <code>reduce</code>، وإن لم نجد هذه المحارف، فستعيد الدالة سلسلةً نصيةً محدّدةً، وإلا فستحوِّل مدخلات العد إلى سلاسل نصية مقروءة باستخدام <code>map</code>، ومن ثم تدمجها باستخدام <code>join</code>.
</p>

<h2>
	خاتمة
</h2>

<p>
	تبين لنا مما سبق أنّ تمرير قيم دالة ما إلى دوال أخرى مفيد جدًا، إذ يسمح لنا بكتابة دوال تُنمذِج الحسابات التي بها فراغات، إذ تستطيع الشيفرة التي تستدعي هذه الدوال ملء تلك الفراغات بتوفير قيم الدوال؛ أما المصفوفات فتعطينا عددًا من التوابع العليا، ويمكننا استخدام <code>forEach</code> للتكرار على عناصر داخل مصفوفة ما؛ ويعيد تابع <code>filter</code> مصفوفةً جديدةً تحتوي العناصر التي تمرِّر دالة التوقّع predicate function؛ كما نستطيع تحويل مصفوفة ما من خلال وضع كل عنصر في دالة باستخدام <code>map</code>؛ وكذلك نستطيع استخدام <code>reduce</code> لجمع عناصر مصفوفة ما داخل قيمة واحدة؛ أما تابع <code>some</code> فينظر هل ثَمّ عنصر مطابق لدالة توقع معطاة أم لا؛ ويبحث <code>findIndex</code> عن موضع أول عنصر مطابق لتوقّع ما.
</p>

<h2>
	تدريبات
</h2>

<h3>
	التبسيط
</h3>

<p>
	استخدم تابع <code>method</code>، و<code>concat</code> لتبسيط مصفوفة بها مصفوفات أخرى، إلى مصفوفة واحدة بها جميع العناصر الموجودة في تلك المصفوفات كلها.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_56" style="">
<span class="pln">let arrays </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="lit">6</span><span class="pun">]];</span><span class="pln">
</span><span class="com">// ضع شيفرتك هنا.</span><span class="pln">
</span><span class="com">// → [1, 2, 3, 4, 5, 6]</span></pre>

<h3>
	الحلقة التكرارية الخاصة بك
</h3>

<p>
	اكتب دالة <code>loop</code> العليا التي تعطي شيئًَا مثل تعليمة حلقة <code>for</code> التكرارية، إذ تأخذ قيمةً، ودالة اختبار، ودالة تحديث، ومتن دالة.
</p>

<p>
	تستخدِم في كل تكرار دالة الاختبار أولًا على قيمة التكرار الحالية، وتتوقف إن لم تتحقق -أي أعادت false-، ثم تستدعي متن الدالة لتعطيه القيمة الحالية، وأخيرًا تستدعي دالة التحديث لإنشاء قيمة جديدة والبدء من جديد.
</p>

<p>
	تستطيع عند تعريف الدالة استخدام حلقة تكرارية عادية لتنفيذ التكرار الفعلي.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_58" style="">
<span class="com">// شيفرتك هنا.</span><span class="pln">

loop</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> n </span><span class="pun">=&gt;</span><span class="pln"> n </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> n </span><span class="pun">=&gt;</span><span class="pln"> n </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 3</span><span class="pln">
</span><span class="com">// → 2</span><span class="pln">
</span><span class="com">// → 1</span></pre>

<h3>
	كل شيء
</h3>

<p>
	تحتوي المصفوفات على تابع <code>every</code> بالتماثل مع تابع <code>some</code>، ويتحقق <code>every</code> إذا تحققت الدالة المعطاة لكل عنصر في المصفوفة، ويمكن النظر إلى <code>some</code> في سلوكه على المصفوفات على أنه عامِل <code>||</code>، في حين يكون <code>every</code> عامِل <code>&amp;&amp;</code>.
</p>

<p>
	استخدم <code>every</code> على أساس دالة تأخذ مصفوفة ودالة توقّع على أساس معامِلات، واكتب نسختين، إحداهما باستخدام حلقة تكرارية، والأخرى باستخدام تابع <code>some</code>.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_60" style="">
<span class="kwd">function</span><span class="pln"> every</span><span class="pun">(</span><span class="pln">array</span><span class="pun">,</span><span class="pln"> test</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ضع شيفرتك هنا.</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">every</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">],</span><span class="pln"> n </span><span class="pun">=&gt;</span><span class="pln"> n </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">every</span><span class="pun">([</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">],</span><span class="pln"> n </span><span class="pun">=&gt;</span><span class="pln"> n </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">every</span><span class="pun">([],</span><span class="pln"> n </span><span class="pun">=&gt;</span><span class="pln"> n </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	<strong>إرشادات للحل</strong> يستطيع التابع <code>every</code> إيقاف تقييم المزيد من العناصر بمجرد إيجاد عنصر واحد غير مطابق، تمامًا كما في حالة عامل <code>&amp;&amp;</code>، لذا تستطيع النسخة المبنية على الحلقة التكرارية القفز خارجها -باستخدام <code>break</code>، أو <code>return</code>- عند إيجاد العنصر الذي تعيد له دالة التوقّع false، فإذا انتهت الحلقة التكرارية دون مقابلة عنصر كهذا، فسنعرف بتطابق جميع العناصر ويجب لإعادة true.
</p>

<p>
	نستخدم قوانين دي مورجَن De Morgan لبناء <code>every</code> فوق <code>some</code>، والذي ينص على أنّ <code>a &amp;&amp; b</code> تساوي <code>‎!(!a || !b)‎</code>، ويمكن أن يُعمَّم هذا للمصفوفات، حيث تكون كل العناصر في المصفوفة مطابقةً إذا لم يكن في المصفوفة عنصرًا غير مطابق.
</p>

<h3>
	اتجاه الكتابة السائد
</h3>

<p>
	اكتب دالة تحسب اتجاه الكتابة السائد في نص ما، وتذكّر أنّه لدى كل كائن من كائنات اللغات خاصية <code>direction</code>، والتي من الممكن أن تكون: ltr، أو rtl، أو ttb، كما ذكرنا في سابق شرحنا هنا.
</p>

<p>
	الاتجاه السائد هو اتجاه أغلب المحارف المرتبطة بلغة ما، وستستفيد من دالتي: <code>characterScript</code>، و<code>countBy</code> المعرَّفتَين في هذا المقال.
</p>

<p>
	تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="codepen.io" rel="">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4191_62" style="">
<span class="kwd">function</span><span class="pln"> dominantDirection</span><span class="pun">(</span><span class="pln">text</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ضع شيفرتك هنا.</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">dominantDirection</span><span class="pun">(</span><span class="str">"Hello!"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → ltr</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">dominantDirection</span><span class="pun">(</span><span class="str">"Hey, مساء الخير"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → rtl</span></pre>

<p>
	<strong>إرشادات للحل</strong> قد يبدو حلك مثل النصف الأول من مثال <code>textScripts</code>، فعليك عدّ المحارف بمقياس مبني على <code>characterScript</code>، ثم ترشيح الجزء الذي يشير إلى المحارف غير المهمة (غير النصية).
</p>

<p>
	يمكن إيجاد الاتجاه الذي يحمل أعلى عدد من المحارف بواسطة <code>reduce</code>، فإذا لم يكن ذلك واضحًا، فارجع إلى المثال السابق في هذا المقال حيث استُخدِم <code>reduce</code> لإيجاد النص الذي فيه أكثر المحارف.
</p>

<p>
	ترجمة -بتصرف- <a href="https://eloquentjavascript.net/05_higher_order.html" rel="external nofollow">للفصل الخامس من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1233/" rel="">الدوال في جافاسكريبت</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%86%D9%88%D8%A7%D9%81%D8%B0-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1250/" rel="">التخاطب بين النوافذ في جافاسكريبت</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%82%D9%8A%D9%85-%D9%88%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%88%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1226/" rel="">القيم والأنواع والعوامل في جافاسكريبت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1242</guid><pubDate>Thu, 17 Jun 2021 15:01:00 +0000</pubDate></item></channel></rss>
