Skip to content

دليل متعمق لوقت تشغيل JavaScript

مقدمة

لقد تعلمت القواعد الأساسية للغة JavaScript، لكن هل تساءلت يومًا:

  • أين يتم تشغيل الكود بالضبط؟
  • لماذا يتصرف نفس الكود بشكل مختلف في المتصفح وNode.js؟
  • لماذا "يعلق" الكود أحيانًا، بينما يمكنه أحيانًا التنفيذ "بالتوازي"؟

ستأخذك هذه المقالة في رحلة عميقة لفهم بيئة وقت تشغيل JavaScript، بما في ذلك حلقة الأحداث (Event Loop)، ومكدس الاستدعاء (Call Stack)، وإدارة الذاكرة. بعد قراءة هذا الدليل، ستفهم لماذا يتم تنفيذ الكود بترتيب معين، وستتمكن من تحديد الأخطاء المتعلقة بالعمليات غير المتزامنة بسرعة، وتحسين أداء الكود وتجنب تسرب الذاكرة.

ماذا ستتعلم من هذه المقالة؟

الفصلالمحتوىماذا ستتمكن من فعله بعد التعلم
الفصل 1نظرة عامة على وقت التشغيلفهم أين يتم تشغيل كود JavaScript
الفصل 2وقت تشغيل المتصفحمعرفة واجهات Web API التي يوفرها المتصفح
الفصل 3وقت تشغيل Node.jsفهم بيئة JavaScript على جانب الخادم
الفصل 4التعمق في حلقة الأحداثإتقان ترتيب تنفيذ المهام الكبيرة (Macrotask) والمهام الدقيقة (Microtask)
الفصل 5مكدس الاستدعاء والذاكرةفهم عملية تنفيذ الكود وإدارة الذاكرة
الفصل 6مهارات عمليةتحسين الأداء وتصحيح تسرب الذاكرة

1. نظرة عامة على وقت التشغيل

🤔 السؤال الأساسي

ما هو "وقت التشغيل"؟ JavaScript مجرد لغة، فلماذا يتصرف نفس الكود بشكل مختلف في بيئات مختلفة؟

1.1 ما هو وقت التشغيل

وقت التشغيل = محرك JavaScript + واجهات API التي توفرها البيئة

إذا شبهنا JavaScript بـ"لغة البرمجة"، فإن وقت التشغيل هو "نظام التشغيل" — إنه يحدد ما يمكن لكودك فعله وما لا يمكنه فعله.

┌─────────────────────────────────────┐
│            كود JavaScript           │
├─────────────────────────────────────┤
│        محرك JavaScript (V8)         │  ← مسؤول عن تحليل وتنفيذ الكود
├─────────────────────────────────────┤
│     بيئة وقت التشغيل (المتصفح/Node.js) │  ← توفر قدرات إضافية
└─────────────────────────────────────┘

تشبيه: JavaScript هي "اللغة العربية الفصحى"، ووقت التشغيل هو "المدينة"

  • قواعد JavaScript (الفصحى) هي نفسها في كل مكان
  • لكن المرافق التي توفرها المدن المختلفة تختلف:
    • المتصفح = يحتوي على DOM وwindow وfetch (مثل مدينة بها مراكز تسوق ومكتبات)
    • Node.js = يحتوي على fs وhttp وpath (مثل مدينة بها مصانع وطرق سريعة)

1.2 وقتا التشغيل الرئيسيان

الخاصيةالمتصفحNode.js
الاستخدام الرئيسيتفاعل صفحات الويب، واجهة المستخدمتطبيقات جانب الخادم، أدوات سطر الأوامر
الكائن العامwindowglobal
DOM API✅ مدعوم❌ غير مدعوم
نظام الملفات❌ محدود✅ دعم كامل
نظام الوحداتES ModulesCommonJS + ES Modules
المؤقتاتsetTimeout, setIntervalsetTimeout, setInterval
طلبات الشبكةfetch, XMLHttpRequestوحدات http, https

👇 جرب بنفسك: قارن بين اختلافات البيئة في المتصفح وNode.js

运行时环境对比

浏览器环境

window
浏览器全局对象
window.location.href
document
DOM 操作
document.querySelector("h1")
localStorage
本地存储
localStorage.setItem("key", "value")
fetch
网络请求
fetch("/api/data")
setTimeout
定时器
setTimeout(() => {}, 1000)
特点:
  • ✅ 有 DOM 和 BOM API,可以操作网页
  • ✅ 有 Web Storage (localStorage, sessionStorage)
  • ✅ 有 fetch 和 XMLHttpRequest 进行网络请求
  • ❌ 没有文件系统访问权限
  • ❌ 不能直接创建 HTTP 服务器

代码演示:不同环境的差异

🌐浏览器结果
点击"在浏览器运行"查看结果
🟢Node.js 结果
需要在 Node.js 环境中运行

核心区别:

浏览器运行时专注于用户界面和网页交互,提供 DOM、BOM、fetch 等前端专用 API。

Node.js 运行时专注于服务器端开发,提供文件系统、HTTP 服务器、进程管理等后端专用 API。

同样的 JavaScript 语法,但能用的 API 完全不同——这就是"环境判断"的重要性。

💡 الفكرة الأساسية

وقت التشغيل يحدد واجهات API التي يمكنك استخدامها. واجهات DOM API التي يمكن استخدامها في المتصفح لا يمكن استخدامها في Node.js؛ وواجهات الملفات التي يمكن استخدامها في Node.js لا يمكن استخدامها في المتصفح. لهذا السبب يحتاج بعض الكود إلى "التحقق من البيئة".


2. وقت تشغيل المتصفح

🤔 السؤال الأساسي

ما القدرات التي يوفرها المتصفح لتمكين JavaScript من التعامل مع صفحات الويب؟

2.1 مكونات وقت تشغيل المتصفح

┌─────────────────────────────────────────────┐
│              محرك JavaScript                │
│           (V8 / SpiderMonkey)               │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│                 Web APIs                      │
│  ┌─────────┐ ┌──────────┐ ┌──────────┐     │
│  │   DOM   │ │   BOM    │ │ Network  │     │
│  │ معالجة  │ │  معالجة   │ │ طلبات    │     │
│  │ الصفحة  │ │ المتصفح  │ │ الشبكة   │     │
│  └─────────┘ └──────────┘ └──────────┘     │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│           حلقة الأحداث (Event Loop)          │
│     مسؤولة عن تنسيق تنفيذ الكود ومعالجة      │
│     الأحداث وجدولة المهام                    │
└─────────────────────────────────────────────┘

2.2 الفئات الثلاث الرئيسية لواجهات Web APIs

1. DOM API - معالجة محتوى صفحة الويب

javascript
// البحث عن عنصر
const title = document.querySelector('h1')

// تعديل المحتوى
title.textContent = 'عنوان جديد'

// إضافة نمط
title.style.color = 'red'

2. BOM API - معالجة المتصفح

javascript
// الانتقال إلى صفحة أخرى
window.location.href = 'https://example.com'

// تخزين المتصفح
localStorage.setItem('key', 'value')

// سجل المتصفح
history.back()

3. Network API - طلبات الشبكة

javascript
// إرسال طلب HTTP
fetch('/api/data')
  .then(response => response.json())
  .then(data => console.log(data))

2.3 آلية الأحداث الخاصة بالمتصفح

إحدى أقوى ميزات وقت تشغيل المتصفح هي "البرمجة المدفوعة بالأحداث" — لا يحتاج الكود إلى التشغيل المستمر، بل ينتظر حتى يقوم المستخدم بإجراء ما ليتم تنفيذه.

javascript
button.addEventListener('click', () => {
  console.log('تم النقر على الزر')
})

أنواع الأحداث الشائعة:

نوع الحدثوقت التشغيلسيناريو عملي
clickالنقر بالماوستفاعل الأزرار
inputتغير محتوى حقل الإدخالالبحث الفوري
scrollتمرير الصفحةالتحميل الكسول
loadاكتمال تحميل المواردتهيئة البيانات
errorحدوث خطأمعالجة الأخطاء

3. وقت تشغيل Node.js

🤔 السؤال الأساسي

ما الذي يمكّن JavaScript من العمل على جانب الخادم؟

3.1 مكونات Node.js

┌─────────────────────────────────────────────┐
│              محرك JavaScript                │
│                   (V8)                      │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│            وحدات Node.js المضمنة             │
│  ┌─────────┐ ┌──────────┐ ┌──────────┐     │
│  │   fs    │ │   http   │ │   path   │     │
│  │ عمليات  │ │  خادم    │ │  معالجة  │     │
│  │ الملفات │ │ الشبكة   │ │ المسارات │     │
│  └─────────┘ └──────────┘ └──────────┘     │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│            مكتبة libuv لحلقة الأحداث         │
│        دعم الإدخال/الإخراج غير المتزامن       │
│        عبر المنصات المختلفة                  │
└─────────────────────────────────────────────┘

3.2 القدرات الفريدة لـ Node.js

1. عمليات نظام الملفات

javascript
const fs = require('fs')

// قراءة ملف
fs.readFile('./data.txt', 'utf8', (err, data) => {
  if (err) throw err
  console.log(data)
})

// كتابة ملف
fs.writeFile('./output.txt', 'Hello', (err) => {
  if (err) throw err
  console.log('تمت الكتابة بنجاح')
})

2. خادم HTTP

javascript
const http = require('http')

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/html' })
  res.end('<h1>Hello World</h1>')
})

server.listen(3000)

3. نظام الوحدات

javascript
// CommonJS (الافتراضي في Node.js)
const fs = require('fs')
module.exports = { myFunction }

// ES Modules (الطريقة الحديثة)
import fs from 'fs'
export { myFunction }

3.3 مقارنة بين المتصفح وNode.js

الخاصيةالمتصفحNode.js
ملف الدخولملف HTMLملف JavaScript
الكائن العامwindow, documentglobal, process
تحميل الوحداتوسم <script>require() / import
الأمانبيئة معزولة (sandbox)، مقيدةيمكن الوصول إلى موارد النظام
الاستخدامواجهة المستخدمخدمات الخلفية، الأدوات

4. التعمق في حلقة الأحداث

🤔 السؤال الأساسي

JavaScript أحادية الخيط، فكيف يمكنها أن تكون "غير محظورة"؟

4.1 ما هي حلقة الأحداث

حلقة الأحداث = "مركز جدولة المهام" في JavaScript

JavaScript أحادية الخيط، مما يعني أنها تستطيع فعل شيء واحد فقط في كل مرة. لكن حلقة الأحداث تجعلها تبدو وكأنها تفعل أشياء كثيرة "في نفس الوقت".

الآلية الأساسية:

  1. تنفيذ الكود المتزامن (مكدس الاستدعاء)
  2. معالجة المهام غير المتزامنة (طابور المهام)
  3. انتظار مهام جديدة (التكرار المستمر)
مكدس الاستدعاء                  طابور المهام
┌─────────┐              ┌──────────┐
│ مهمة 1  │              │مهمة كبيرة 1│
│ مهمة 2  │ ←────────────  │مهمة كبيرة 2│
│ مهمة 3  │  بعد انتهاء   │مهمة كبيرة 3│
└─────────┘  واحدة تؤخذ   └──────────┘
      ↓      التالية           ↑
      └────────────────────────┘
        حلقة الأحداث تتحقق باستمرار

4.2 المهام الكبيرة (Macrotask) مقابل المهام الدقيقة (Microtask)

هذا أكثر مفهوم يسبب الارتباك في المقابلات والتطوير الفعلي!

المهام الكبيرة (Macrotask):

  • setTimeout, setInterval
  • عمليات الإدخال/الإخراج (I/O)
  • عرض واجهة المستخدم (UI Rendering)

المهام الدقيقة (Microtask):

  • Promise.then
  • MutationObserver
  • queueMicrotask

ترتيب التنفيذ: الكود المتزامن → المهام الدقيقة → المهام الكبيرة

👇 جرب بنفسك: لاحظ ترتيب تنفيذ المهام الكبيرة والمهام الدقيقة

任务队列:宏任务 vs 微任务

代码示例

1console.log("1")同步
2setTimeout(() => console.log("2"), 0)宏任务
3Promise.resolve().then(() => console.log("3"))微任务
4console.log("4")同步
5setTimeout(() => console.log("5"), 0)宏任务

调用栈 (正在执行)

执行 console.log("1")

微任务队列 Microtask

队列为空

宏任务队列 Macrotask

队列为空

输出日志 (执行顺序)

等待输出...

执行顺序规则

1执行所有同步代码
2执行微任务队列中的所有任务
3执行一个宏任务
4重复步骤 2-3

核心要点: 微任务优先级高于宏任务。每次执行完一个宏任务后,都会检查并执行所有微任务,然后再执行下一个宏任务。

4.3 سؤال مقابلة كلاسيكي

javascript
console.log('1')

setTimeout(() => console.log('2'), 0)

Promise.resolve().then(() => console.log('3'))

console.log('4')

// الناتج: 1, 4, 3, 2

لماذا هذا الترتيب؟

  1. تنفيذ الكود المتزامن: console.log('1'), console.log('4') → الناتج 1, 4
  2. التحقق من طابور المهام الدقيقة: Promise.then → الناتج 3
  3. التحقق من طابور المهام الكبيرة: setTimeout → الناتج 2

💡 نصائح عملية

  • إذا أردت تنفيذ الكود بأسرع وقت، استخدم المهام الدقيقة (Promise.then)
  • إذا أردت تأخير التنفيذ، استخدم المهام الكبيرة (setTimeout)
  • لا تخلط الكثير من العمليات غير المتزامنة أبدًا، وإلا ستقع في "جحيم ردود النداء"

5. مكدس الاستدعاء والذاكرة

🤔 السؤال الأساسي

كيف يتم تنفيذ الكود؟ أين تُخزن المتغيرات؟ متى يتم تحريرها؟

5.1 مكدس الاستدعاء: "آثار أقدام" تنفيذ الدوال

مكدس الاستدعاء = "دفتر ملاحظات" يسجل استدعاءات الدوال

في كل مرة يتم استدعاء دالة، تُضاف سجل جديد إلى المكدس؛ وعندما تنتهي الدالة من التنفيذ، يُزال السجل.

javascript
function a() {
  b()
}

function b() {
  c()
}

function c() {
  console.log('اكتمل التنفيذ')
}

a()

تغيرات مكدس الاستدعاء:

الخطوة 1: استدعاء ()a
┌─────────┐
│    a    │
└─────────┘

الخطوة 2: ()a تستدعي ()b
┌─────────┐
│    b    │
│    a    │
└─────────┘

الخطوة 3: ()b تستدعي ()c
┌─────────┐
│    c    │
│    b    │
│    a    │
└─────────┘

الخطوة 4: ()c تنتهي، ويتم إخراجها بالتتابع
┌─────────┐
│    b    │
│    a    │
└─────────┘

👇 جرب بنفسك: لاحظ تغيرات مكدس الاستدعاء

调用栈:函数执行的足迹

代码

1main()
2function a() {
3function b() {
4function c() {
5console.log("执行完毕")
6}
7}
8}
9}

调用栈

栈底
栈为空
栈顶

当前状态:

调用 main()

输出

等待输出...

调用栈工作原理:

  • 每次调用函数,就会在栈上"压入"一个新的"栈帧"
  • 栈帧记录了函数的执行状态、局部变量等信息
  • 函数执行完毕,栈帧就会从栈上"弹出"
  • 栈是"后进先出"(LIFO)的数据结构
  • 如果递归太深,会导致"栈溢出"错误

调用栈就像一摞盘子:最后放上去的盘子最先被取走。每个函数就是一个盘子,执行完就取走,然后继续执行下面的函数。

5.2 إدارة الذاكرة: أين تذهب القمامة؟

JavaScript لديها آلية "جمع القمامة تلقائيًا" — لا تحتاج إلى تحرير الذاكرة يدويًا، فالمحرك يقوم بذلك نيابة عنك.

مبدأ جمع القمامة: خوارزمية الوسم والإزالة (Mark-and-Sweep)

  1. مرحلة الوسم: البدء من "الجذر"، والعثور على جميع المتغيرات التي يمكن الوصول إليها
  2. مرحلة الإزالة: المتغيرات التي لم يتم وسمها تعتبر "قمامة" وسيتم جمعها
javascript
// مثال على جمع القمامة
let obj1 = { name: 'كائن 1' }
let obj2 = { name: 'كائن 2' }

// تمت إعادة تعيين obj1، الكائن الأصلي فقد مرجعه
obj1 = null  // الكائن الأصلي { name: 'كائن 1' } سيتم جمعه

// obj2 لا يزال قيد الاستخدام، ولن يتم جمعه
console.log(obj2.name)

👇 جرب بنفسك: لاحظ عملية جمع القمامة

垃圾回收机制

标记阶段从根对象开始,标记所有可达对象
清除阶段回收未标记的对象

对象引用关系

未标记
已标记(可达)
已回收
🌳
Root
📦
obj1
📦
obj2
📦
obj3
📦
obj4
📦
obj5
📦
obj6
当前操作:从根对象开始标记

标记-清除算法 (Mark-and-Sweep)

1
标记阶段

从根对象(Root)开始,遍历所有可达对象,标记为"活动对象"

2
清除阶段

遍历整个堆内存,回收所有未被标记的对象

3
重置标记

清除所有标记位,为下一次垃圾回收做准备

核心要点
  • 根对象(Root): 全局变量、栈上的变量等,总是被认为是可达的
  • 可达对象: 从根对象出发,通过引用链能访问到的对象
  • 垃圾对象: 无法从根对象访问到的对象,会被回收
  • 循环引用: 如果两个对象互相引用但都不可达,仍会被回收

实际应用技巧

💡
及时解除引用

对象不再使用时,将其设为 null

🔒
避免意外的全局变量

使用 const/let 代替 var

🧹
清理事件监听

组件销毁时移除所有监听器

📊
定期检查内存

用 DevTools Memory 面板监控

5.3 تسرب الذاكرة: عواقب نسيان التنظيف

تسرب الذاكرة = ذاكرة كان يجب تحريرها لكنها لم تُحرر، وتتراكم أكثر فأكثر

الأسباب الشائعة:

1. كثرة المتغيرات العامة

javascript
// ❌ خطأ: المتغيرات العامة لا يتم جمعها
globalCache = []

function addItem(item) {
  globalCache.push(item)
}

2. عدم إزالة مستمعي الأحداث

javascript
// ❌ خطأ: لم يتم إزالة المستمع
button.addEventListener('click', handleClick)

// ✅ صحيح: إزالة المستمع عند عدم الحاجة
button.removeEventListener('click', handleClick)

3. إغلاقات (Closures) تشير إلى كائنات كبيرة

javascript
// ❌ خطأ: الإغلاق يشير دائمًا إلى كائن كبير، ولن يتم جمعه
function createHandler() {
  const bigData = new Array(1000000).fill('data')
  return function() {
    console.log('قيد المعالجة')
  }
}

const handler = createHandler()  // bigData موجود دائمًا في الذاكرة

👇 جرب بنفسك: لاحظ كيف يحدث تسرب الذاكرة

内存泄漏演示

内存使用情况0%

全局变量泄漏

问题:全局变量不会被垃圾回收,会一直占用内存

示例:不断往全局数组添加数据,从不清理

全局变量 (0 项)
暂无全局变量
❌ 错误做法
// 全局变量不会被回收
globalCache = []
function addItem() {
  globalCache.push(largeData)
}

如何避免内存泄漏

  • 避免全局变量: 使用 const/let 代替 var,尽量使用局部变量
  • 及时清理监听器: 组件销毁时移除所有事件监听
  • 释放闭包引用: 不需要时将闭包变量设为 null
  • 使用 WeakMap/WeakSet: 自动清理不再被引用的对象
  • 定期检查: 用 DevTools Memory 面板检查内存泄漏

💡 نصائح عملية

  • الفحص الدوري: افتح DevTools في المتصفح → Memory → Take Heap Snapshot، للتحقق من استهلاك الذاكرة
  • تجنب المتغيرات العامة: استخدم const وlet قدر الإمكان، ولا تستخدم var
  • التنظيف في الوقت المناسب: أزل مستمعي الأحداث والمؤقتات عند الانتهاء من استخدامها
  • المراجع الضعيفة: استخدم WeakMap وWeakSet لتخزين مراجع الكائنات

6. مهارات عملية

🤔 السؤال الأساسي

كيف تكتب كود JavaScript عالي الأداء؟ كيف تصحح الأخطاء عند مواجهة المشاكل؟

6.1 نصائح تحسين الأداء

1. تقليل إعادة التخطيط وإعادة الرسم (Reflow & Repaint)

javascript
// ❌ خطأ: كل دورة تشغل إعادة تخطيط
for (let i = 0; i < 1000; i++) {
  element.style.top = i + 'px'
}

// ✅ صحيح: تعديل دفعة واحدة
element.style.transform = `translateY(${position}px)`

2. استخدام تفويض الأحداث (Event Delegation)

javascript
// ❌ خطأ: إضافة مستمع لكل زر
buttons.forEach(btn => {
  btn.addEventListener('click', handleClick)
})

// ✅ صحيح: إضافة مستمع واحد فقط للعنصر الأب
container.addEventListener('click', (e) => {
  if (e.target.matches('.button')) {
    handleClick(e)
  }
})

3. مانع الاهتزاز (Debounce) والاختناق (Throttle)

javascript
// مانع الاهتزاز: التنفيذ بعد توقف المستخدم عن الإدخال
function debounce(fn, delay) {
  let timer
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(() => fn.apply(this, args), delay)
  }
}

// الاختناق: تقييد تكرار التنفيذ
function throttle(fn, delay) {
  let lastTime = 0
  return function(...args) {
    const now = Date.now()
    if (now - lastTime >= delay) {
      fn.apply(this, args)
      lastTime = now
    }
  }
}

6.2 نصائح التصحيح

1. استخدام DevTools لعرض مكدس الاستدعاء

javascript
function a() {
  b()
}

function b() {
  c()
}

function c() {
  debugger  // توقف هنا لعرض مكدس الاستدعاء
}

a()

2. استخدام console.trace() لتتبع مسار التنفيذ

javascript
function trackExecution() {
  console.trace('مسار التنفيذ')
  // سيُخرج مكدس الاستدعاء الكامل
}

3. استخدام Performance لتحليل الأداء

javascript
performance.mark('start')

// تنفيذ بعض الكود
for (let i = 0; i < 10000; i++) {
  // ...
}

performance.mark('end')
performance.measure('أداء الحلقة', 'start', 'end')

const measure = performance.getEntriesByName('أداء الحلقة')[0]
console.log(`زمن التنفيذ: ${measure.duration}ms`)

6.3 دليل سريع للمشاكل الشائعة

المشكلةالسبب المحتملالحل
استهلاك عالٍ للذاكرةتسرب الذاكرة، تخزين مؤقت كثير جدًاتحقق من المتغيرات العامة، أزل المستمعات
تجميد الصفحةمهام طويلة تحجب الخيط الرئيسيقسم المهام، استخدم Web Workers
عدم تشغيل الأحداثالمستمع غير مربوط، العنصر غير موجودتحقق من توقيت تحميل DOM
ترتيب غير صحيح للعمليات غير المتزامنةخلط المهام الكبيرة والدقيقةاستخدم Promise أو async/await بشكل موحد
عدم دقة المؤقتاتحجب الخيط الرئيسياستخدم Web Workers أو requestAnimationFrame

الخلاصة

يجب أن تكون الآن قادرًا على فهم:

  • وقت التشغيل = المحرك + واجهات API البيئية، وأوقات التشغيل المختلفة توفر قدرات مختلفة
  • حلقة الأحداث مسؤولة عن تنسيق ترتيب تنفيذ الكود المتزامن والمهام الدقيقة والمهام الكبيرة
  • مكدس الاستدعاء يسجل عملية تنفيذ الدوال، وتجاوز سعة المكدس يحدث بسبب التعمق الزائد في الاستدعاء الذاتي (recursion)
  • جمع القمامة ينظف المتغيرات غير المستخدمة تلقائيًا، لكن يجب الانتباه إلى تسرب الذاكرة
  • مفتاح تحسين الأداء هو تقليل إعادة التخطيط وإعادة الرسم، واستخدام العمليات غير المتزامنة بشكل معقول

💡 عند مواجهة مشكلة، تحدث إلى الذكاء الاصطناعي بهذه الطريقة

  • "هذه الدالة تنفذ ببطء شديد، ساعدني في معرفة كيفية تحسين الأداء"
  • "استهلاك الذاكرة في ارتفاع مستمر، قد يكون هناك تسرب في الذاكرة، ساعدني في التحقق"
  • "ترتيب العمليات غير المتزامنة غير صحيح، يجب أن يكون A أولاً ثم B، لكن حاليًا A وB يبدآن في نفس الوقت تقريبًا"
  • "مستمع الحدث لا يعمل، تحقق مما إذا كان العنصر قد تم تحميله في DOM"