JavaScript چیست؟ آموزش کامل جاوا اسکریپت با ساخت دستیار هوش مصنوعی
در این آموزش JavaScript را از صفر تا سطح کاربردی یاد میگیرید؛ از متغیر، تابع، آرایه و Object تا DOM، Promise، Async/Await و Fetch API. در پایان یک دستیار هوش مصنوعی واقعی با JavaScript، Node.js و API درواره میسازیم.
JavaScript یا جاوا اسکریپت یکی از مهمترین زبانهای برنامهنویسی جهان و زبان اصلی ایجاد رفتارهای تعاملی در صفحات وب است. تقریباً هر زمان که بدون بارگذاری مجدد صفحه فرمی ارسال میشود، منویی باز میشود، اطلاعاتی از API دریافت میشود یا یک رابط کاربری تغییر میکند، JavaScript در اجرای آن نقش دارد.
اما کاربرد JavaScript به مرورگر محدود نیست. با استفاده از Node.js میتوان از جاوا اسکریپت برای توسعه Backend، ساخت API، ابزارهای خط فرمان، اپلیکیشن دسکتاپ، برنامه موبایل، افزونه مرورگر و سرویسهای مبتنی بر هوش مصنوعی استفاده کرد.
در این آموزش، ابتدا مفاهیم اصلی JavaScript را از صفر یاد میگیریم و سپس یک پروژه واقعی میسازیم: یک دستیار هوش مصنوعی فارسی که رابط آن در مرورگر اجرا میشود، Backend آن با Node.js نوشته شده و از طریق API درواره به مدل انتخابی متصل میشود.
JavaScript چیست؟
JavaScript یک زبان برنامهنویسی سطح بالا، پویا و چندمنظوره است. این زبان ابتدا برای ایجاد تعامل در صفحات وب طراحی شد، اما امروزه در مرورگر، سرور، اپلیکیشن موبایل، نرمافزار دسکتاپ و بسیاری از محیطهای دیگر اجرا میشود.
در یک وبسایت معمولاً سه فناوری اصلی کنار یکدیگر قرار میگیرند:
| فناوری | وظیفه اصلی |
|---|---|
| HTML | ساختار و معنای محتوا |
| CSS | ظاهر، رنگ و چیدمان |
| JavaScript | منطق، رفتار و تعامل |
برای مثال:
<button id="hello-button">کلیک کنید</button>
<p id="result"></p>
JavaScript میتواند به کلیک کاربر واکنش نشان دهد:
const button = document.querySelector("#hello-button");
const result = document.querySelector("#result");
button.addEventListener("click", () => {
result.textContent = "سلام از JavaScript!";
});
براساس راهنمای رسمی JavaScript در MDN، مباحث اصلی این زبان شامل انواع داده، متغیرها، کنترل جریان، حلقهها، توابع، آرایهها، Objectها، Classها، Promiseها و Moduleها است.
تفاوت JavaScript و Java
JavaScript و Java دو زبان متفاوت هستند و نباید بهدلیل شباهت اسمی با یکدیگر اشتباه گرفته شوند.
| ویژگی | JavaScript | Java |
|---|---|---|
| نوع زبان | پویا و مبتنی بر Prototype | ایستا و مبتنی بر Class |
| کاربرد مشهور | وب، Node.js و رابط کاربری | Backend، سازمانی و Android قدیمی |
| اجرای رایج | مرورگر یا Runtime مانند Node.js | JVM |
| نوعدهی | Dynamic Typing | Static Typing |
| فایل متداول | .js | .java |
یادگیری Java برای شروع JavaScript لازم نیست.
ECMAScript چیست؟
ECMAScript استانداردی است که رفتار و ویژگیهای اصلی زبان JavaScript را تعریف میکند. JavaScript یکی از پیادهسازیهای این استاندارد است.
قابلیتهایی مانند موارد زیر در نسخههای مختلف استاندارد ECMAScript توسعه یافتهاند:
letوconst- Arrow Function
- Class
- Promise
- Async/Await
- Module
- Optional Chaining
- Nullish Coalescing
- Destructuring
در پروژههای جدید بهتر است از JavaScript مدرن استفاده کنید و سازگاری ویژگیها با مرورگرها یا Runtime هدف را بررسی کنید.
JavaScript کجا اجرا میشود؟
اجرا در مرورگر
مرورگرهایی مانند Chrome، Firefox، Edge و Safari دارای موتور JavaScript هستند. کد JavaScript مرورگر میتواند با صفحه، رویدادهای کاربر و Web APIها تعامل داشته باشد.
اجرا در Node.js
Node.js محیطی برای اجرای JavaScript خارج از مرورگر است. با Node.js میتوان:
- وبسرور ساخت.
- به دیتابیس متصل شد.
- فایل خواند و نوشت.
- API ایجاد کرد.
- درخواست HTTP ارسال کرد.
- Worker و Job پردازشی اجرا کرد.
- ابزار خط فرمان ساخت.
محیطهای دیگر
JavaScript در محیطهای دیگری نیز استفاده میشود:
- افزونههای مرورگر
- اپلیکیشن دسکتاپ با Electron
- اپلیکیشن موبایل با React Native
- Serverless Function
- Edge Runtime
- ابزارهای Build
- تست خودکار مرورگر
- اپلیکیشنهای تلویزیون هوشمند
چگونه JavaScript را به HTML اضافه کنیم؟
JavaScript داخلی
<script>
console.log("Hello JavaScript");
</script>
این روش برای مثال کوچک قابلاستفاده است، اما در پروژه واقعی بهتر است کد در فایل جداگانه قرار گیرد.
فایل JavaScript خارجی
<script src="./app.js" defer></script>
ویژگی defer باعث میشود فایل بدون متوقفکردن پردازش HTML دریافت شود و پس از آمادهشدن ساختار صفحه اجرا شود.
JavaScript Module
<script type="module" src="./main.js"></script>
در Module میتوان از import و export استفاده کرد:
import { sendMessage } from "./api.js";
Moduleها به تقسیم کد، استفاده مجدد و نگهداری بهتر پروژه کمک میکنند. جزئیات آنها در راهنمای JavaScript Modules در MDN آمده است.
اولین برنامه JavaScript
یک فایل با نام app.js بسازید:
const userName = "امیر";
console.log(`سلام ${userName}`);
برای اجرای آن در مرورگر، فایل را به HTML متصل کنید. برای اجرا با Node.js:
node app.js
خروجی:
سلام امیر
کامنت در JavaScript
کامنت تکخطی:
// این یک کامنت تکخطی است
const modelName = "YOUR_MODEL_ID";
کامنت چندخطی:
/*
این بخش تنظیمات
برنامه را نگهداری میکند.
*/
کامنت باید دلیل تصمیم مهم را توضیح دهد، نه اینکه کد واضح را دوباره به زبان طبیعی تکرار کند.
متغیر در JavaScript
برای تعریف متغیر معمولاً از const و let استفاده میشود.
const
اگر قرار نیست متغیر دوباره مقداردهی شود:
const apiBaseUrl = "/api";
const maxMessageLength = 4000;
let
اگر مقدار متغیر در ادامه تغییر میکند:
let isLoading = false;
isLoading = true;
var
var روش قدیمیتری برای تعریف متغیر است و بهدلیل Scope و Hoisting متفاوت، در کدهای جدید معمولاً const و let ترجیح داده میشوند.
var oldStyleVariable = "value";
قاعده عملی:
- ابتدا از
constاستفاده کنید. - فقط زمانی که مقدار باید تغییر کند از
letاستفاده کنید. - در کد جدید معمولاً از
varاستفاده نکنید.
انواع داده در JavaScript
JavaScript دارای چند نوع داده اصلی است.
String
const message = "سلام";
const modelId = "YOUR_MODEL_ID";
Number
const temperature = 0.4;
const maxTokens = 1000;
در JavaScript بیشتر عددهای معمولی از نوع number هستند.
BigInt
برای عددهای صحیح بسیار بزرگ:
const largeNumber = 9007199254740993n;
Boolean
const isConnected = true;
const hasError = false;
Undefined
let response;
console.log(response); // undefined
Null
const selectedConversation = null;
null معمولاً نشان میدهد نبودن مقدار بهصورت آگاهانه تعریف شده است.
Symbol
Symbol برای ساخت شناسههای یکتا و بعضی الگوهای پیشرفته زبان استفاده میشود:
const internalId = Symbol("internal-id");
Object
const model = {
id: "YOUR_MODEL_ID",
temperature: 0.4,
streaming: false
};
آرایه، تابع و بسیاری از ساختارهای JavaScript نیز از نظر فنی با سیستم Object زبان ارتباط دارند.
Dynamic Typing چیست؟
JavaScript دارای نوعدهی پویا است. یعنی نوع متغیر براساس مقدار آن تعیین میشود:
let value = "متن";
value = 42;
value = true;
این انعطاف میتواند توسعه سریع را ساده کند، اما در پروژههای بزرگ ممکن است باعث خطاهای Runtime شود.
TypeScript با اضافهکردن بررسی نوع در زمان توسعه، بخشی از این مشکلات را کاهش میدهد. بااینحال، برای یادگیری TypeScript باید ابتدا مفاهیم اصلی JavaScript را بشناسید.
بررسی نوع داده با typeof
console.log(typeof "hello"); // string
console.log(typeof 42); // number
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
console.log(typeof {}); // object
console.log(typeof []); // object
برای بررسی آرایه از روش زیر استفاده کنید:
const messages = [];
console.log(Array.isArray(messages)); // true
یکی از رفتارهای قدیمی زبان:
console.log(typeof null); // object
برای بررسی null مستقیماً مقایسه کنید:
if (value === null) {
console.log("مقدار null است");
}
عملگرهای مهم
عملگرهای محاسباتی
const total = 10 + 5;
const difference = 10 - 5;
const product = 10 * 5;
const division = 10 / 5;
const remainder = 10 % 3;
const power = 2 ** 3;
مقایسه
console.log(10 > 5);
console.log(10 >= 10);
console.log(5 < 10);
console.log(5 <= 5);
برابری سختگیرانه
console.log(5 === 5); // true
console.log(5 === "5"); // false
بهتر است در بیشتر موارد از === و !== استفاده کنید:
if (statusCode !== 200) {
console.log("پاسخ موفق نبود");
}
عملگر == تبدیل نوع ضمنی انجام میدهد و ممکن است نتیجه غیرمنتظره ایجاد کند:
console.log(5 == "5"); // true
عملگرهای منطقی
const canSend =
isConnected && !isLoading;
const displayName =
userName || "کاربر";
const retryCount =
options.retryCount ?? 2;
تفاوت || و ?? مهم است. عملگر || همه مقادیر Falsy را رد میکند، اما ?? فقط در صورت null یا undefined به مقدار جایگزین میرسد.
const temperature = 0;
console.log(temperature || 0.7); // 0.7
console.log(temperature ?? 0.7); // 0
Template Literal
Template Literal با Backtick نوشته میشود:
const name = "کاربر";
const message = `سلام ${name}`;
متن چندخطی:
const systemPrompt = `
شما یک دستیار فارسی هستید.
پاسخ را دقیق و روشن ارائه کنید.
`;
شرط در JavaScript
if و else
const messageLength = 120;
if (messageLength === 0) {
console.log("پیام خالی است");
} else if (messageLength > 4000) {
console.log("پیام بیش از حد طولانی است");
} else {
console.log("پیام معتبر است");
}
عملگر سهتایی
const label =
isLoading ? "در حال ارسال..." : "ارسال";
عملگر سهتایی برای تصمیمهای کوتاه مناسب است. برای منطق پیچیده، if خواناتر خواهد بود.
switch
function getStatusMessage(statusCode) {
switch (statusCode) {
case 200:
return "درخواست موفق بود";
case 401:
return "دسترسی تأیید نشد";
case 429:
return "تعداد درخواستها بیش از حد مجاز است";
default:
return "خطای غیرمنتظره";
}
}
Truthy و Falsy
مقادیر زیر Falsy هستند:
false
0
-0
0n
""
null
undefined
NaN
مثال:
const prompt = "";
if (!prompt) {
console.log("پرامپت خالی است");
}
اما مراقب باشید مقدار صفر ممکن است معتبر باشد:
const temperature = 0;
if (temperature === undefined) {
console.log("Temperature مشخص نشده است");
}
حلقهها
for
for (let index = 0; index < 3; index += 1) {
console.log(index);
}
for...of
برای پیمایش مقدارهای Array:
const messages = [
"سلام",
"JavaScript چیست؟",
"یک مثال بده"
];
for (const message of messages) {
console.log(message);
}
for...in
برای پیمایش کلیدهای Object استفاده میشود:
const settings = {
temperature: 0.4,
maxTokens: 1000
};
for (const key in settings) {
console.log(key, settings[key]);
}
برای Array معمولاً از for...in استفاده نکنید.
while
let attempt = 0;
while (attempt < 3) {
attempt += 1;
console.log(`تلاش شماره ${attempt}`);
}
تابع در JavaScript
تابع بخشی از منطق را به واحدی قابلاستفاده مجدد تبدیل میکند.
Function Declaration
function normalizePrompt(prompt) {
return prompt.trim();
}
Function Expression
const normalizePrompt = function (prompt) {
return prompt.trim();
};
Arrow Function
const normalizePrompt = (prompt) => {
return prompt.trim();
};
نسخه کوتاه:
const normalizePrompt = (prompt) => prompt.trim();
پارامتر پیشفرض
function createMessage(
content,
role = "user"
) {
return {
role,
content
};
}
Rest Parameter
function combineMessages(...messages) {
return messages.join("\n");
}
const result = combineMessages(
"پیام اول",
"پیام دوم",
"پیام سوم"
);
Scope چیست؟
Scope مشخص میکند یک متغیر در کدام بخش برنامه قابلدسترسی است.
const appName = "Darvareh Assistant";
function startApp() {
const status = "ready";
console.log(appName);
console.log(status);
}
startApp();
// status در اینجا قابل دسترسی نیست.
متغیرهای تعریفشده با let و const دارای Block Scope هستند:
if (true) {
const localValue = 10;
console.log(localValue);
}
// localValue خارج از Block وجود ندارد.
Closure چیست؟
Closure زمانی شکل میگیرد که یک تابع به متغیرهای Scope بیرونی خود دسترسی را حفظ کند.
function createCounter() {
let count = 0;
return function increment() {
count += 1;
return count;
};
}
const nextRequestId = createCounter();
console.log(nextRequestId()); // 1
console.log(nextRequestId()); // 2
Closure در Event Handler، Factory Function و نگهداری State خصوصی کاربرد دارد.
Array در JavaScript
Array مجموعهای مرتب از مقادیر است:
const messages = [
{
role: "user",
content: "سلام"
},
{
role: "assistant",
content: "سلام! چگونه کمک کنم؟"
}
];
اضافهکردن مقدار
messages.push({
role: "user",
content: "JavaScript چیست؟"
});
حذف آخرین مقدار
const lastMessage = messages.pop();
map
برای تبدیل هر عضو:
const contents = messages.map(
(message) => message.content
);
filter
برای انتخاب بعضی اعضا:
const userMessages = messages.filter(
(message) => message.role === "user"
);
find
برای یافتن اولین عضو مطابق شرط:
const firstAssistantMessage = messages.find(
(message) => message.role === "assistant"
);
some
const hasSystemMessage = messages.some(
(message) => message.role === "system"
);
every
const allMessagesAreValid = messages.every(
(message) =>
typeof message.content === "string" &&
message.content.trim().length > 0
);
reduce
const totalCharacters = messages.reduce(
(total, message) =>
total + message.content.length,
0
);
متدهای map و filter آرایه جدید میسازند، اما push آرایه اصلی را تغییر میدهد.
Object در JavaScript
Object اطلاعات مرتبط را بهصورت کلید و مقدار نگهداری میکند:
const requestOptions = {
model: "YOUR_MODEL_ID",
temperature: 0.4,
maxTokens: 1200
};
دسترسی به Property:
console.log(requestOptions.model);
console.log(requestOptions["temperature"]);
تغییر مقدار:
requestOptions.temperature = 0.2;
Destructuring
const {
model,
temperature,
maxTokens
} = requestOptions;
Object Shorthand
const model = "YOUR_MODEL_ID";
const temperature = 0.4;
const request = {
model,
temperature
};
Spread Syntax
const defaultOptions = {
temperature: 0.4,
maxTokens: 1200
};
const customOptions = {
...defaultOptions,
temperature: 0.2
};
Spread یک کپی سطحی ایجاد میکند. Objectهای تودرتو همچنان ممکن است Reference مشترک داشته باشند.
Optional Chaining
برای دسترسی ایمنتر به Property تودرتو:
const content =
response?.choices?.[0]?.message?.content;
اگر یکی از بخشهای مسیر null یا undefined باشد، نتیجه undefined خواهد بود.
میتوان آن را با Nullish Coalescing ترکیب کرد:
const content =
response?.choices?.[0]?.message?.content
?? "پاسخی دریافت نشد";
این روش جای اعتبارسنجی کامل پاسخ API را نمیگیرد، اما از بعضی خطاهای دسترسی جلوگیری میکند.
JSON چیست؟
JSON قالبی متنی برای تبادل داده است.
Object جاوا اسکریپت:
const message = {
role: "user",
content: "سلام"
};
تبدیل به JSON:
const json = JSON.stringify(message);
خروجی:
{"role":"user","content":"سلام"}
تبدیل JSON به مقدار JavaScript:
const parsed = JSON.parse(json);
JSON.parse ممکن است برای ورودی نامعتبر خطا ایجاد کند:
try {
const data = JSON.parse(input);
console.log(data);
} catch (error) {
console.error("JSON معتبر نیست");
}
DOM چیست؟
DOM مخفف Document Object Model است. مرورگر HTML را به ساختاری از Objectها تبدیل میکند تا JavaScript بتواند عناصر صفحه را بخواند و تغییر دهد.
انتخاب عنصر
const form = document.querySelector("#chat-form");
const input = document.querySelector("#prompt");
انتخاب چند عنصر:
const buttons =
document.querySelectorAll(".suggestion");
تغییر متن
const status = document.querySelector("#status");
status.textContent = "آماده";
تغییر Class
status.classList.add("status--loading");
status.classList.remove("status--loading");
status.classList.toggle("status--active");
ساخت عنصر
const article = document.createElement("article");
article.className = "message";
article.textContent = "پیام جدید";
document
.querySelector("#message-list")
.append(article);
استفاده ایمن از textContent
برای قراردادن متن دریافتشده از کاربر یا مدل، textContent انتخاب امنتری است:
messageElement.textContent = modelResponse;
قرار دادن مستقیم ورودی خارجی در innerHTML میتواند باعث تفسیر آن بهعنوان HTML شود:
// برای محتوای کنترلنشده مناسب نیست
messageElement.innerHTML = modelResponse;
اگر به نمایش Markdown نیاز دارید، از Parser معتبر استفاده کنید و HTML تولیدشده را قبل از نمایش پاکسازی یا Sanitize کنید.
Event در JavaScript
Event رویدادی مانند کلیک، تایپ یا ارسال فرم است.
const button = document.querySelector("#send-button");
button.addEventListener("click", () => {
console.log("دکمه کلیک شد");
});
ارسال فرم:
form.addEventListener("submit", (event) => {
event.preventDefault();
const prompt = input.value.trim();
if (!prompt) {
return;
}
console.log(prompt);
});
preventDefault() رفتار پیشفرض فرم، یعنی بارگذاری مجدد صفحه، را متوقف میکند.
Event Delegation
اگر تعداد زیادی آیتم پویا دارید، میتوانید یک Listener روی والد قرار دهید:
const messageList =
document.querySelector("#message-list");
messageList.addEventListener("click", (event) => {
const copyButton =
event.target.closest("[data-copy-message]");
if (!copyButton) {
return;
}
const messageId = copyButton.dataset.copyMessage;
console.log("Copy:", messageId);
});
این روش برای فهرست پیامهایی که بعداً به DOM اضافه میشوند مناسب است.
همزمانی و Event Loop
JavaScript معمولاً کد عادی را روی یک Thread اصلی اجرا میکند، اما عملیاتهایی مانند درخواست شبکه و Timer از طریق محیط اجرا مدیریت میشوند.
مثال:
console.log("اول");
setTimeout(() => {
console.log("سوم");
}, 0);
console.log("دوم");
خروجی:
اول
دوم
سوم
حتی با زمان صفر، Callback پس از پایان اجرای کد همزمان فعلی وارد صف اجرا میشود.
برای توسعه کاربردی لازم نیست در ابتدا تمام جزئیات Event Loop را حفظ کنید، اما باید بدانید درخواست شبکه منتظر نمیماند تا کل برنامه را متوقف کند.
Promise چیست؟
Promise نتیجه آینده یک عملیات غیرهمزمان را نمایش میدهد.
Promise سه حالت اصلی دارد:
- Pending
- Fulfilled
- Rejected
مثال:
fetch("/api/status")
.then((response) => response.json())
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error(error);
});
Async و Await
async و await نوشتن کد Promise-based را خواناتر میکنند:
async function getStatus() {
try {
const response = await fetch("/api/status");
if (!response.ok) {
throw new Error(
`HTTP error: ${response.status}`
);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Request failed:", error);
throw error;
}
}
تابع async همیشه Promise برمیگرداند. توضیحات تکمیلی در مستندات async function در MDN موجود است.
مدیریت چند عملیات غیرهمزمان
اگر عملیاتها مستقل هستند، میتوان آنها را همزمان آغاز کرد:
const [models, usage] = await Promise.all([
fetch("/api/models").then((response) =>
response.json()
),
fetch("/api/usage").then((response) =>
response.json()
)
]);
Promise.all با ردشدن یکی از Promiseها رد میشود.
برای دریافت نتیجه همه عملیات، حتی در صورت خطای بعضی از آنها:
const results = await Promise.allSettled([
loadModels(),
loadUsage(),
loadProfile()
]);
Fetch API چیست؟
Fetch API برای ارسال درخواست HTTP در مرورگر و برخی Runtimeهای JavaScript استفاده میشود.
درخواست GET:
const response = await fetch("/api/models");
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const models = await response.json();
درخواست POST:
const response = await fetch("/api/chat", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
messages: [
{
role: "user",
content: "سلام"
}
]
})
});
نکته مهم: fetch معمولاً برای پاسخ HTTP مانند ۴۰۰ یا ۵۰۰ بهتنهایی Promise را Reject نمیکند. باید response.ok یا response.status را بررسی کنید.
راهنمای کامل این API در مستندات Fetch در MDN قرار دارد.
لغو درخواست با AbortController
کاربر ممکن است بخواهد درخواست طولانی را متوقف کند:
const controller = new AbortController();
const response = await fetch("/api/chat", {
method: "POST",
signal: controller.signal
});
// در صورت نیاز:
controller.abort();
برای Timeout:
async function fetchWithTimeout(
url,
options = {},
timeoutMs = 60000
) {
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, timeoutMs);
try {
return await fetch(url, {
...options,
signal: controller.signal
});
} finally {
clearTimeout(timeoutId);
}
}
خطا در JavaScript
ساخت خطا
throw new Error("متن پیام نمیتواند خالی باشد");
try و catch
try {
const response = await sendMessage();
console.log(response);
} catch (error) {
console.error(error.message);
}
finally
setLoading(true);
try {
await sendMessage();
} catch (error) {
showError(error.message);
} finally {
setLoading(false);
}
finally چه درخواست موفق شود و چه خطا دهد اجرا خواهد شد.
Module در JavaScript
بهجای نگهداری تمام کد در یک فایل، آن را به Moduleهای کوچک تقسیم کنید.
فایل validation.js:
export function validatePrompt(prompt) {
const normalized = prompt.trim();
if (!normalized) {
throw new Error("پیام نمیتواند خالی باشد.");
}
if (normalized.length > 4000) {
throw new Error(
"طول پیام نباید بیشتر از ۴۰۰۰ کاراکتر باشد."
);
}
return normalized;
}
فایل main.js:
import { validatePrompt } from "./validation.js";
const prompt = validatePrompt(" سلام ");
console.log(prompt);
HTML:
<script type="module" src="./main.js"></script>
Local Storage
برای ذخیره تنظیمات کوچک در مرورگر:
localStorage.setItem("theme", "dark");
const theme =
localStorage.getItem("theme") ?? "light";
ذخیره Object:
const settings = {
theme: "dark",
compactMode: true
};
localStorage.setItem(
"settings",
JSON.stringify(settings)
);
بازیابی:
const rawSettings =
localStorage.getItem("settings");
const settings = rawSettings
? JSON.parse(rawSettings)
: {};
کلید API، رمز عبور و Secret را در Local Storage نگهداری نکنید. دادههای Local Storage برای JavaScript همان Origin قابلدسترسی هستند.
پروژه عملی: ساخت دستیار هوش مصنوعی با JavaScript
اکنون یک پروژه کامل میسازیم که شامل موارد زیر است:
- رابط HTML
- JavaScript سمت مرورگر
- مدیریت DOM و Event
- تاریخچه کوتاه مکالمه
- Fetch API
- Async/Await
- مدیریت Loading و خطا
- امکان توقف درخواست
- Backend با Node.js و Express
- اعتبارسنجی ورودی
- نگهداری API Key در سرور
- اتصال به API درواره
معماری پروژه:
مرورگر کاربر
↓
JavaScript Frontend
↓
Backend با Node.js و Express
↓
API درواره
↓
مدل هوش مصنوعی انتخابشده
ساختار پروژه
javascript-ai-assistant/
├── public/
│ ├── index.html
│ ├── styles.css
│ └── app.js
├── .env
├── .gitignore
├── package.json
└── server.js
مرحله اول: ایجاد پروژه
mkdir javascript-ai-assistant
cd javascript-ai-assistant
npm init -y
نصب وابستگیها:
npm install express dotenv
ساخت پوشه Frontend:
mkdir public
مرحله دوم: تنظیم package.json
فایل package.json را به شکل زیر تنظیم کنید:
{
"name": "javascript-ai-assistant",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"start": "node server.js",
"dev": "node --watch server.js"
},
"dependencies": {
"dotenv": "^17.0.0",
"express": "^5.0.0"
}
}
نسخه دقیق وابستگیها ممکن است هنگام نصب تغییر کرده باشد. پس از اجرای npm install، نسخههای واقعی در package.json و package-lock.json ثبت میشوند.
وجود "type": "module" امکان استفاده از import و export را در Node.js فراهم میکند.
مرحله سوم: ساخت رابط HTML
فایل public/index.html:
<!doctype html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta
name="viewport"
content="width=device-width, initial-scale=1"
>
<meta
name="description"
content="دستیار هوش مصنوعی فارسی با JavaScript و API درواره"
>
<title>دستیار JavaScript درواره</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<main class="app">
<header class="app__header">
<div>
<p class="eyebrow">پروژه آموزش JavaScript</p>
<h1>دستیار هوش مصنوعی درواره</h1>
</div>
<span class="status" id="status">
آماده
</span>
</header>
<section
class="messages"
id="messages"
aria-label="پیامهای گفتوگو"
aria-live="polite"
>
<article class="message message--assistant">
<strong>دستیار درواره</strong>
<p>
سلام! پیام خود را بنویسید تا گفتوگو را شروع کنیم.
</p>
</article>
</section>
<form class="composer" id="chat-form">
<label for="prompt">
پیام شما
</label>
<textarea
id="prompt"
name="prompt"
rows="3"
maxlength="4000"
placeholder="برای مثال: Promise در JavaScript چیست؟"
required
></textarea>
<div class="composer__actions">
<button
class="button button--secondary"
id="stop-button"
type="button"
disabled
>
توقف
</button>
<button
class="button button--primary"
id="send-button"
type="submit"
>
ارسال پیام
</button>
</div>
</form>
<p class="notice">
پاسخ هوش مصنوعی ممکن است نیازمند بررسی باشد.
</p>
</main>
<script
type="module"
src="/app.js"
></script>
</body>
</html>
مرحله چهارم: استایل رابط
فایل public/styles.css:
:root {
--primary: #6d4aff;
--primary-dark: #5637df;
--background: #f5f6fb;
--surface: #ffffff;
--text: #182033;
--muted: #697386;
--border: #e2e6ef;
--error: #b42318;
--success: #087443;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
min-width: 320px;
min-height: 100dvh;
margin: 0;
padding: 24px;
color: var(--text);
background:
radial-gradient(
circle at top right,
rgb(109 74 255 / 12%),
transparent 30%
),
var(--background);
font-family:
Vazirmatn,
Tahoma,
Arial,
sans-serif;
line-height: 1.8;
}
button,
textarea {
font: inherit;
}
.app {
display: grid;
grid-template-rows: auto minmax(320px, 1fr) auto auto;
gap: 20px;
width: min(100%, 900px);
min-height: calc(100dvh - 48px);
margin-inline: auto;
padding: clamp(18px, 4vw, 36px);
background-color: rgb(255 255 255 / 92%);
border: 1px solid var(--border);
border-radius: 24px;
box-shadow:
0 24px 70px rgb(20 24 40 / 9%);
}
.app__header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
}
.app__header h1 {
margin: 0;
font-size: clamp(1.3rem, 4vw, 2rem);
}
.eyebrow {
margin: 0 0 4px;
color: var(--primary);
font-size: 0.8rem;
font-weight: 700;
}
.status {
padding: 6px 12px;
color: var(--success);
background-color: #ecfdf3;
border: 1px solid #abefc6;
border-radius: 999px;
font-size: 0.78rem;
}
.status--loading {
color: #6941c6;
background-color: #f4f0ff;
border-color: #d9d0ff;
}
.status--error {
color: var(--error);
background-color: #fef3f2;
border-color: #fecdca;
}
.messages {
min-height: 0;
padding: 16px;
background-color: #f8f9fc;
border: 1px solid var(--border);
border-radius: 18px;
overflow-y: auto;
overscroll-behavior: contain;
}
.message {
width: fit-content;
max-width: min(82%, 680px);
margin-block-end: 16px;
padding: 13px 16px;
background-color: var(--surface);
border: 1px solid var(--border);
border-radius: 16px;
overflow-wrap: anywhere;
}
.message strong {
display: block;
margin-block-end: 4px;
font-size: 0.75rem;
}
.message p {
margin: 0;
white-space: pre-wrap;
}
.message--user {
margin-inline-start: auto;
color: #ffffff;
background-color: var(--primary);
border-color: var(--primary);
}
.message--error {
color: var(--error);
background-color: #fef3f2;
border-color: #fecdca;
}
.composer {
display: grid;
gap: 10px;
}
.composer label {
font-size: 0.85rem;
font-weight: 700;
}
.composer textarea {
width: 100%;
min-height: 110px;
max-height: 220px;
padding: 14px 16px;
color: var(--text);
background-color: var(--surface);
border: 1px solid var(--border);
border-radius: 14px;
line-height: 1.8;
resize: vertical;
}
.composer textarea:focus {
border-color: var(--primary);
outline: 4px solid rgb(109 74 255 / 10%);
}
.composer__actions {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.button {
min-height: 44px;
padding: 9px 18px;
border: 0;
border-radius: 12px;
cursor: pointer;
}
.button--primary {
color: #ffffff;
background-color: var(--primary);
}
.button--primary:hover:not(:disabled) {
background-color: var(--primary-dark);
}
.button--secondary {
color: var(--text);
background-color: #eef0f5;
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.button:focus-visible {
outline: 3px solid rgb(109 74 255 / 30%);
outline-offset: 3px;
}
.notice {
margin: 0;
color: var(--muted);
font-size: 0.75rem;
text-align: center;
}
@media (max-width: 600px) {
body {
padding: 0;
}
.app {
min-height: 100dvh;
padding: 18px;
border: 0;
border-radius: 0;
}
.app__header {
align-items: flex-start;
}
.message {
max-width: 90%;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
scroll-behavior: auto !important;
transition-duration: 0.01ms !important;
animation-duration: 0.01ms !important;
}
}
مرحله پنجم: برنامه Frontend
فایل public/app.js:
const form = document.querySelector("#chat-form");
const input = document.querySelector("#prompt");
const messagesElement =
document.querySelector("#messages");
const sendButton =
document.querySelector("#send-button");
const stopButton =
document.querySelector("#stop-button");
const statusElement =
document.querySelector("#status");
const conversation = [];
let activeController = null;
let isLoading = false;
function setStatus(text, type = "ready") {
statusElement.textContent = text;
statusElement.classList.toggle(
"status--loading",
type === "loading"
);
statusElement.classList.toggle(
"status--error",
type === "error"
);
}
function setLoading(value) {
isLoading = value;
sendButton.disabled = value;
input.disabled = value;
stopButton.disabled = !value;
if (value) {
setStatus("در حال دریافت پاسخ", "loading");
} else {
setStatus("آماده");
input.disabled = false;
input.focus();
}
}
function scrollToLatestMessage() {
messagesElement.scrollTop =
messagesElement.scrollHeight;
}
function createMessageElement(
role,
content,
options = {}
) {
const article =
document.createElement("article");
article.className = `message message--${role}`;
if (options.isError) {
article.classList.add("message--error");
}
const title =
document.createElement("strong");
title.textContent =
role === "user"
? "شما"
: "دستیار درواره";
const paragraph =
document.createElement("p");
paragraph.textContent = content;
article.append(title, paragraph);
return article;
}
function appendMessage(role, content, options) {
const message = createMessageElement(
role,
content,
options
);
messagesElement.append(message);
scrollToLatestMessage();
return message;
}
function validatePrompt(value) {
const prompt = value.trim();
if (!prompt) {
throw new Error("پیام نمیتواند خالی باشد.");
}
if (prompt.length > 4000) {
throw new Error(
"پیام نباید بیشتر از ۴۰۰۰ کاراکتر باشد."
);
}
return prompt;
}
async function parseResponse(response) {
const contentType =
response.headers.get("content-type") || "";
if (!contentType.includes("application/json")) {
throw new Error(
"پاسخ سرور در قالب مورد انتظار نیست."
);
}
const data = await response.json();
if (!response.ok) {
throw new Error(
data.error ||
`درخواست ناموفق بود. کد: ${response.status}`
);
}
if (
typeof data.answer !== "string" ||
!data.answer.trim()
) {
throw new Error(
"متن معتبری در پاسخ سرور وجود ندارد."
);
}
return data.answer;
}
async function requestAssistant(messages) {
activeController = new AbortController();
const response = await fetch("/api/chat", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
messages
}),
signal: activeController.signal
});
return parseResponse(response);
}
async function handleSubmit(event) {
event.preventDefault();
if (isLoading) {
return;
}
let prompt;
try {
prompt = validatePrompt(input.value);
} catch (error) {
setStatus(error.message, "error");
input.focus();
return;
}
const userMessage = {
role: "user",
content: prompt
};
conversation.push(userMessage);
appendMessage("user", prompt);
input.value = "";
setLoading(true);
const loadingMessage = appendMessage(
"assistant",
"در حال آمادهسازی پاسخ..."
);
try {
const answer = await requestAssistant(
conversation
);
loadingMessage.remove();
conversation.push({
role: "assistant",
content: answer
});
appendMessage("assistant", answer);
} catch (error) {
loadingMessage.remove();
if (error.name === "AbortError") {
appendMessage(
"assistant",
"درخواست توسط کاربر متوقف شد."
);
return;
}
appendMessage(
"assistant",
`خطا: ${error.message}`,
{
isError: true
}
);
setStatus("خطا در دریافت پاسخ", "error");
} finally {
activeController = null;
setLoading(false);
}
}
form.addEventListener("submit", handleSubmit);
stopButton.addEventListener("click", () => {
activeController?.abort();
});
input.addEventListener("keydown", (event) => {
if (
event.key === "Enter" &&
!event.shiftKey &&
!event.isComposing
) {
event.preventDefault();
form.requestSubmit();
}
});
این فایل چند مفهوم اصلی JavaScript را در یک پروژه واقعی ترکیب میکند:
- انتخاب عناصر DOM
- تعریف State
- تابع
- Object و Array
- Event Listener
- اعتبارسنجی ورودی
- Async/Await
- Fetch API
- AbortController
- Try/Catch/Finally
- بررسی پاسخ HTTP
- ساخت عناصر DOM
- استفاده از
textContent - Optional Chaining
مرحله ششم: ساخت Backend با Node.js
فایل server.js:
import "dotenv/config";
import express from "express";
const app = express();
const port = Number(
process.env.PORT || 3000
);
const apiKey =
process.env.DARVAREH_API_KEY;
const modelId =
process.env.DARVAREH_MODEL_ID;
const darvarehChatUrl =
"https://api.darvareh.ir/v1/chat/completions";
app.disable("x-powered-by");
app.use(
express.json({
limit: "64kb"
})
);
app.use(
express.static("public")
);
function isValidMessage(message) {
return (
message !== null &&
typeof message === "object" &&
["user", "assistant"].includes(message.role) &&
typeof message.content === "string" &&
message.content.trim().length > 0 &&
message.content.length <= 10000
);
}
function validateMessages(messages) {
if (!Array.isArray(messages)) {
return false;
}
if (
messages.length === 0 ||
messages.length > 30
) {
return false;
}
return messages.every(isValidMessage);
}
function extractAnswer(data) {
const answer =
data?.choices?.[0]?.message?.content;
if (
typeof answer !== "string" ||
!answer.trim()
) {
throw new Error(
"ساختار پاسخ مدل معتبر نیست."
);
}
return answer;
}
app.get("/api/status", (request, response) => {
response.json({
status: "ok"
});
});
app.post("/api/chat", async (request, response) => {
if (!apiKey || !modelId) {
return response.status(500).json({
error:
"تنظیمات API در سرور کامل نیست."
});
}
const { messages } = request.body ?? {};
if (!validateMessages(messages)) {
return response.status(400).json({
error:
"ساختار یا تعداد پیامها معتبر نیست."
});
}
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, 60000);
try {
const upstreamResponse = await fetch(
darvarehChatUrl,
{
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: modelId,
messages: [
{
role: "system",
content:
"شما یک دستیار فارسی، دقیق و مفید هستید. " +
"پاسخها را واضح و ساختاریافته ارائه کنید."
},
...messages
],
temperature: 0.4,
max_tokens: 1200
}),
signal: controller.signal
}
);
const rawBody =
await upstreamResponse.text();
let data;
try {
data = JSON.parse(rawBody);
} catch {
return response.status(502).json({
error:
"پاسخ سرویس مدل قابل پردازش نبود."
});
}
if (!upstreamResponse.ok) {
console.error(
"Darvareh API error:",
upstreamResponse.status
);
return response.status(502).json({
error:
"سرویس مدل پاسخ موفقی ارسال نکرد."
});
}
const answer = extractAnswer(data);
return response.json({
answer
});
} catch (error) {
if (error.name === "AbortError") {
return response.status(504).json({
error:
"زمان انتظار برای پاسخ به پایان رسید."
});
}
console.error(
"Chat request failed:",
error.message
);
return response.status(502).json({
error:
"ارتباط با سرویس مدل برقرار نشد."
});
} finally {
clearTimeout(timeoutId);
}
});
app.use((request, response) => {
response.status(404).json({
error: "مسیر درخواستی پیدا نشد."
});
});
app.listen(port, () => {
console.log(
`Server is running on http://localhost:${port}`
);
});
مرحله هفتم: تنظیم متغیرهای محیطی
فایل .env:
PORT=3000
DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL_ID=YOUR_MODEL_ID
برای دریافت کلید API میتوانید در درواره ثبتنام کنید.
برای انتخاب مدل، مشاهده Model ID و بررسی اطلاعات قیمت به صفحه مدلهای درواره مراجعه کنید.
کلید واقعی API را در آموزش، Repository عمومی، JavaScript مرورگر یا پیامهای پشتیبانی منتشر نکنید.
مرحله هشتم: ساخت gitignore
فایل .gitignore:
node_modules/
.env
npm-debug.log*
.DS_Store
فایل package-lock.json را معمولاً وارد Git کنید تا نسخه وابستگیها در نصبهای مختلف قابلپیشبینیتر باشد.
مرحله نهم: اجرای پروژه
حالت عادی:
npm start
حالت توسعه:
npm run dev
سپس آدرس زیر را در مرورگر باز کنید:
http://localhost:3000
مسیر کامل یک پیام در پروژه
وقتی کاربر پیام را ارسال میکند:
- رویداد
submitفرم اجرا میشود. - رفتار پیشفرض فرم متوقف میشود.
- متن با
trim()پاکسازی اولیه میشود. - طول و خالینبودن پیام بررسی میشود.
- پیام در DOM نمایش داده میشود.
- پیام به Array تاریخچه افزوده میشود.
- حالت Loading فعال میشود.
- Frontend به
/api/chatدرخواست POST میفرستد. - Backend ساختار پیامها را اعتبارسنجی میکند.
- Backend کلید API را از متغیر محیطی میخواند.
- درخواست به API درواره ارسال میشود.
- پاسخ مدل در Backend بررسی میشود.
- فقط متن موردنیاز به Frontend بازگردانده میشود.
- Frontend پاسخ را با
textContentنمایش میدهد. - حالت Loading غیرفعال میشود.
چرا مستقیماً از مرورگر به API درواره درخواست نمیفرستیم؟
برای ارسال مستقیم باید API Key داخل کد مرورگر قرار گیرد:
// این کار را انجام ندهید
const apiKey = "YOUR_DARVAREH_API_KEY";
کد HTML و JavaScript مرورگر برای کاربر قابلمشاهده است. Minify یا Obfuscate کردن کد نیز کلید را به Secret واقعی تبدیل نمیکند.
استفاده از Backend مزایای دیگری نیز دارد:
- محدودکردن طول ورودی
- کنترل تعداد پیامها
- Rate Limiting
- ثبت میزان مصرف
- کنترل دسترسی کاربران
- انتخاب Model ID
- مدیریت Timeout
- نگهداری Secret
- یکسانسازی پیامهای خطا
- جلوگیری از نمایش جزئیات سرویس داخلی
محدودکردن تاریخچه گفتوگو
اگر تمام پیامهای یک مکالمه طولانی در هر درخواست ارسال شوند، مصرف Token و هزینه افزایش پیدا میکند.
یک راه ساده در Frontend:
function getRecentMessages(messages, limit = 12) {
return messages.slice(-limit);
}
هنگام درخواست:
const answer = await requestAssistant(
getRecentMessages(conversation)
);
در سیستم حرفهایتر میتوان:
- پیامهای قدیمی را خلاصه کرد.
- تعداد Tokenها را تخمین زد.
- System Prompt را جداگانه مدیریت کرد.
- مکالمه را در دیتابیس نگهداری کرد.
- Context مرتبط را انتخاب کرد.
- محدودیت هر مدل را در نظر گرفت.
جلوگیری از درخواست همزمان تکراری
در پروژه، متغیر isLoading مانع ارسال چندباره فرم میشود:
if (isLoading) {
return;
}
دکمه نیز غیرفعال میشود:
sendButton.disabled = true;
بااینحال، کنترل Frontend کافی نیست. در پروژه Production باید محدودیتهای لازم در Backend نیز اعمال شوند؛ زیرا کاربر میتواند خارج از رابط مستقیماً به Endpoint درخواست بفرستد.
مدیریت خطای بهتر
پیام خطا برای کاربر باید قابلفهم باشد، اما جزئیات حساس داخلی را نمایش ندهد.
نمونه مناسب برای کاربر:
{
"error": "سرویس مدل موقتاً در دسترس نیست."
}
Log سرور میتواند جزئیات فنی بیشتری داشته باشد:
console.error({
event: "upstream_request_failed",
status: upstreamResponse.status
});
API Key، Authorization Header و متن حساس کاربر را بدون ضرورت در Log ذخیره نکنید.
اضافهکردن Retry کنترلشده
Retry باید فقط برای خطاهای موقت و با تعداد محدود انجام شود. درخواست نامعتبر یا خطای احراز هویت معمولاً با تکرار حل نمیشود.
function wait(milliseconds) {
return new Promise((resolve) => {
setTimeout(resolve, milliseconds);
});
}
async function fetchWithRetry(
url,
options,
maxAttempts = 3
) {
let lastError;
for (
let attempt = 1;
attempt <= maxAttempts;
attempt += 1
) {
try {
const response = await fetch(url, options);
if (
response.ok ||
![429, 502, 503, 504].includes(
response.status
)
) {
return response;
}
lastError = new Error(
`Temporary HTTP error: ${response.status}`
);
} catch (error) {
lastError = error;
}
if (attempt < maxAttempts) {
const delay =
500 * 2 ** (attempt - 1);
await wait(delay);
}
}
throw lastError;
}
در Production باید موارد زیر نیز بررسی شوند:
Retry-After- Idempotency
- Timeout کلی
- Jitter
- حداکثر تعداد Retry
- بودجه درخواست
- لغو درخواست کاربر
Streaming پاسخ چیست؟
در حالت عادی، Frontend تا کاملشدن پاسخ صبر میکند. در Streaming، بخشهای پاسخ بهتدریج نمایش داده میشوند.
معماری مفهومی:
مدل
↓ بخشهای پاسخ
Backend
↓ Stream
مرورگر
↓ نمایش تدریجی
رابط کاربری
Streaming تجربه کاربری را بهتر میکند، اما پیادهسازی آن به مدیریت موارد زیر نیاز دارد:
- خواندن Stream
- Decode کردن Chunkها
- تشخیص پایان پاسخ
- مدیریت قطع اتصال
- لغو درخواست
- ذخیره پاسخ نهایی
- مدیریت خطای میانه جریان
برای اولین پروژه بهتر است ابتدا نسخه غیرStreaming را کامل و پایدار کنید و سپس Streaming را اضافه کنید.
تست واحد برای تابع اعتبارسنجی
Node.js دارای ابزار تست داخلی است. منطق اعتبارسنجی را در فایل جداگانه قرار دهید.
فایل validation.js:
export function validatePrompt(value) {
if (typeof value !== "string") {
throw new TypeError(
"Prompt must be a string"
);
}
const prompt = value.trim();
if (!prompt) {
throw new Error(
"Prompt cannot be empty"
);
}
if (prompt.length > 4000) {
throw new Error(
"Prompt is too long"
);
}
return prompt;
}
فایل validation.test.js:
import test from "node:test";
import assert from "node:assert/strict";
import {
validatePrompt
} from "./validation.js";
test("trims a valid prompt", () => {
assert.equal(
validatePrompt(" سلام "),
"سلام"
);
});
test("rejects an empty prompt", () => {
assert.throws(
() => validatePrompt(" "),
/cannot be empty/
);
});
test("rejects non-string input", () => {
assert.throws(
() => validatePrompt(null),
TypeError
);
});
اجرا:
node --test
اشتباههای رایج JavaScript
فراموشکردن await
const response = fetch("/api/chat");
console.log(response);
در اینجا response یک Promise است.
نسخه صحیح داخل تابع Async:
const response = await fetch("/api/chat");
بررسینکردن response.ok
const response = await fetch("/api/chat");
const data = await response.json();
نسخه بهتر:
const response = await fetch("/api/chat");
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
استفاده از forEach با await
این الگو منتظر پایان همه عملیات نمیماند:
items.forEach(async (item) => {
await processItem(item);
});
اجرای ترتیبی:
for (const item of items) {
await processItem(item);
}
اجرای همزمان:
await Promise.all(
items.map((item) => processItem(item))
);
مقایسه Objectها
console.log({} === {}); // false
هر Object Reference متفاوتی دارد:
const object = {};
console.log(object === object); // true
تغییر ناخواسته Object
const settings = {
temperature: 0.4
};
const copy = settings;
copy.temperature = 0.8;
console.log(settings.temperature); // 0.8
کپی سطحی:
const copy = {
...settings
};
استفاده اشتباه از innerHTML
output.innerHTML = userInput;
برای متن کنترلنشده:
output.textContent = userInput;
قراردادن API Key در Frontend
کلید API را در این مکانها قرار ندهید:
- فایل JavaScript مرورگر
- HTML
- CSS
- Local Storage
- Repository عمومی
- URL
- Query String
- کد نمونه منتشرشده
Debug کردن JavaScript
console.log
console.log("messages:", conversation);
برای خطا:
console.error("request failed:", error);
برای نمایش جدولی:
console.table(conversation);
قبل از انتشار، Logهای اضافی یا حاوی اطلاعات حساس را حذف کنید.
Breakpoint
در DevTools مرورگر:
- بخش Sources را باز کنید.
- فایل JavaScript را انتخاب کنید.
- روی شماره خط کلیک کنید.
- عملیات را در صفحه تکرار کنید.
- مقدار متغیرها و Call Stack را بررسی کنید.
همچنین میتوان موقتاً از دستور زیر استفاده کرد:
debugger;
Network Panel
برای بررسی درخواست API:
- URL صحیح است؟
- Method درست است؟
- Request Body ارسال شده؟
- Content-Type چیست؟
- Status Code چند است؟
- پاسخ JSON معتبر است؟
- مدت درخواست چقدر بوده؟
- آیا درخواست لغو شده است؟
Authorization درخواست Backend به درواره در Network Panel مرورگر دیده نمیشود؛ زیرا آن درخواست از سرور انجام میشود.
JavaScript یا TypeScript؟
| معیار | JavaScript | TypeScript |
|---|---|---|
| اجرای مستقیم | بله | معمولاً پس از تبدیل |
| یادگیری اولیه | سادهتر | نیازمند شناخت Type |
| بررسی نوع | در Runtime | عمدتاً هنگام توسعه |
| پروژه کوچک | مناسب | بسته به نیاز |
| پروژه بزرگ | نیازمند انضباط بیشتر | معمولاً قابلنگهداریتر |
| کتابخانه وب | پایه اکوسیستم | تبدیلشونده به JavaScript |
مسیر پیشنهادی:
- JavaScript پایه را یاد بگیرید.
- DOM، Fetch و Async/Await را تمرین کنید.
- Module و مدیریت خطا را یاد بگیرید.
- سپس سراغ TypeScript بروید.
TypeScript جای JavaScript را حذف نمیکند؛ در نهایت کد آن برای اجرا به JavaScript تبدیل میشود.
JavaScript یا Python برای هوش مصنوعی؟
Python برای علم داده، Machine Learning و آموزش مدلها اکوسیستم بسیار بزرگی دارد. JavaScript برای ساخت محصول وب، رابط کاربری و سرویسهای Full Stack گزینه مهمی است.
| کاربرد | انتخاب رایج |
|---|---|
| آموزش مدل Machine Learning | Python |
| تحلیل داده | Python |
| رابط وب | JavaScript یا TypeScript |
| Backend وب | هر دو |
| اپلیکیشن Full Stack | JavaScript یا TypeScript |
| اتصال ساده به API مدل | هر دو |
| ابزار مرورگر | JavaScript |
| افزونه Chrome | JavaScript یا TypeScript |
اگر هدف شما ساخت محصول وب مبتنی بر API هوش مصنوعی است، JavaScript میتواند Frontend و Backend را با یک زبان پوشش دهد.
استفاده حرفهای از JavaScript در پروژه هوش مصنوعی
در پروژه Production بهتر است این اجزا را از یکدیگر جدا کنید:
src/
├── config/
│ └── env.js
├── routes/
│ └── chat.js
├── services/
│ └── darvareh.js
├── validators/
│ └── chat.js
├── middleware/
│ └── error-handler.js
├── public/
│ ├── index.html
│ ├── styles.css
│ └── app.js
└── server.js
مسئولیت هر بخش:
config: تنظیمات و متغیرهای محیطیroutes: Endpointهای HTTPservices: ارتباط با سرویس خارجیvalidators: بررسی ورودیmiddleware: مدیریت خطا و درخواستpublic: رابط مرورگرserver.js: راهاندازی برنامه
این ساختار برای پروژه بسیار کوچک ضروری نیست، اما با رشد محصول از بزرگشدن یک فایل جلوگیری میکند.
چکلیست پروژه JavaScript
پیش از انتشار بررسی کنید:
- از
constوletبهدرستی استفاده شده است. - مقایسهها با
===و!==انجام میشوند. - ورودی کاربر در Frontend و Backend اعتبارسنجی میشود.
- پاسخ HTTP با
response.okبررسی میشود. - خطاها با
try/catchمدیریت میشوند. - حالت Loading و Disabled وجود دارد.
- کاربر میتواند درخواست طولانی را لغو کند.
- Timeout در Backend تعریف شده است.
- API Key فقط در Backend نگهداری میشود.
- فایل
.envوارد Git نمیشود. - پاسخ مدل با
textContentنمایش داده میشود. - اطلاعات حساس در Log ثبت نمیشوند.
- تعداد پیامهای تاریخچه محدود است.
- خطای سرویس خارجی مستقیماً به کاربر نمایش داده نمیشود.
- کد به Moduleهای قابلفهم تقسیم شده است.
- توابع اصلی تست دارند.
- وابستگیها بهروز و موردنیاز هستند.
- Model ID از تنظیمات خوانده میشود.
- قیمتها بهصورت ثابت و غیرقابلبهروزرسانی در کد قرار نگرفتهاند.
نقشه راه یادگیری JavaScript
مرحله اول: مبانی
- متغیر
- انواع داده
- Operator
- شرط
- حلقه
- تابع
مرحله دوم: ساختار داده
- Array
- Object
- Map
- Set
- JSON
- Destructuring
- Spread
مرحله سوم: مرورگر
- DOM
- Event
- Form
- Class
- Attribute
- Local Storage
مرحله چهارم: برنامهنویسی غیرهمزمان
- Event Loop
- Promise
- Async/Await
- Fetch API
- AbortController
- Error Handling
مرحله پنجم: معماری
- Module
- Scope
- Closure
- Class
- جداسازی مسئولیتها
- اعتبارسنجی
- تست
مرحله ششم: توسعه محصول
- Node.js
- Express
- REST API
- دیتابیس
- احراز هویت
- Logging
- Deployment
- TypeScript
سؤالهای متداول
JavaScript چیست؟
JavaScript زبان برنامهنویسی اصلی ایجاد تعامل در وب است. این زبان در مرورگر و محیطهایی مانند Node.js اجرا میشود و برای Frontend، Backend، افزونه مرورگر و اپلیکیشنهای مختلف کاربرد دارد.
آیا JavaScript همان Java است؟
خیر. این دو زبان از نظر نوعدهی، Runtime، Syntax و کاربرد تفاوتهای اساسی دارند.
آیا یادگیری JavaScript سخت است؟
شروع آن نسبتاً ساده است، اما تسلط بر Scope، Closure، Async Programming، Event Loop، Module و معماری پروژه به تمرین نیاز دارد.
برای یادگیری React ابتدا باید JavaScript بلد باشیم؟
بله. قبل از React بهتر است تابع، Array، Object، Destructuring، Module، Promise، Async/Await و DOM را یاد بگیرید.
JavaScript در Backend هم استفاده میشود؟
بله. با Node.js میتوان Web Server، REST API، Worker، ابزار خط فرمان و Backend اپلیکیشنهای هوش مصنوعی را ساخت.
تفاوت let و const چیست؟
متغیر const دوباره مقداردهی نمیشود، اما let میتواند مقدار جدید دریافت کند. بهتر است پیشفرض شما const باشد و فقط در صورت نیاز از let استفاده کنید.
تفاوت null و undefined چیست؟
undefined معمولاً یعنی مقداری تعریف یا اختصاص داده نشده است. null معمولاً نبودن آگاهانه مقدار را نشان میدهد.
Promise چیست؟
Promise نماینده نتیجه آینده یک عملیات غیرهمزمان است و میتواند Pending، Fulfilled یا Rejected باشد.
Async/Await چیست؟
async و await Syntax خواناتری برای کار با Promiseها فراهم میکنند. تابع Async همیشه Promise برمیگرداند.
Fetch API چیست؟
Fetch API رابطی برای ارسال درخواست HTTP است. با آن میتوان اطلاعات را از Backend دریافت یا داده JSON ارسال کرد.
چرا fetch برای خطای ۴۰۴ وارد catch نمیشود؟
Fetch معمولاً خطاهای HTTP را بهتنهایی Reject نمیکند. باید response.ok یا response.status را بررسی کنید. خطاهای شبکه یا لغو درخواست معمولاً باعث Reject شدن Promise میشوند.
آیا میتوان API هوش مصنوعی را مستقیماً از JavaScript مرورگر فراخوانی کرد؟
از نظر فنی ممکن است، اما اگر API به Secret Key نیاز دارد نباید کلید را در مرورگر قرار دهید. درخواست باید از Backend شما عبور کند.
آیا باید JavaScript یا TypeScript یاد بگیریم؟
ابتدا JavaScript را یاد بگیرید. سپس برای پروژههای بزرگتر و بررسی نوع بهتر به TypeScript بروید.
JavaScript برای ساخت محصول هوش مصنوعی مناسب است؟
بله. JavaScript برای ساخت رابط وب، Backend، داشبورد، افزونه مرورگر و اپلیکیشن Full Stack متصل به API مدلها مناسب است.
آیا JavaScript برای آموزش مدل هوش مصنوعی مناسب است؟
برای آموزش مدل و محاسبات علمی، Python معمولاً اکوسیستم گستردهتری دارد. JavaScript بیشتر برای ساخت محصول، رابط و اتصال به مدلهای آماده استفاده میشود.
جمعبندی
JavaScript فقط زبانی برای نمایش Alert یا تغییر رنگ دکمه نیست. این زبان یکی از پایههای اصلی توسعه نرمافزار مدرن و ابزار مهم ساخت محصولات مبتنی بر هوش مصنوعی است.
برای کار حرفهای با JavaScript باید این مفاهیم را یاد بگیرید:
- متغیرها و انواع داده
- شرط و حلقه
- تابع و Scope
- Array و Object
- DOM و Event
- JSON
- Promise
- Async/Await
- Fetch API
- مدیریت خطا
- Module
- Node.js و Backend
در پروژه عملی این مقاله، یک دستیار هوش مصنوعی واقعی ساختیم. رابط برنامه در مرورگر با JavaScript اجرا شد، Backend با Node.js و Express ساخته شد و درخواست مدل از طریق API درواره ارسال شد.
مهمترین اصل معماری پروژه این بود که API Key در Frontend قرار نگیرد. مرورگر فقط با Backend خود برنامه ارتباط برقرار میکند و Backend مسئول نگهداری کلید، اعتبارسنجی ورودی، Timeout و اتصال به درواره است.
برای شروع، دریافت کلید API و اتصال پروژه JavaScript خود میتوانید در درواره ثبتنام کنید. برای مشاهده مدلها، انتخاب Model ID و بررسی اطلاعات بهروز قیمت نیز به صفحه مدلهای درواره مراجعه کنید.
منابع تکمیلی
- راهنمای کامل JavaScript در MDN
- مرجع JavaScript در MDN
- راهنمای Fetch API در MDN
- مستندات Async Function در MDN
- راهنمای JavaScript Modules در MDN
مقالات مرتبط
- ساخت اپلیکیشن دسکتاپ هوش مصنوعی با Electron و JavaScript
- آموزش Node.js و Express؛ ساخت API هوش مصنوعی با درواره
- ساخت افزونه Chrome هوش مصنوعی با JavaScript و API درواره
- ساخت چتبات هوش مصنوعی با Next.js، React و API درواره
- آموزش ساخت چتبات با API هوش مصنوعی درواره
- JSON چیست؟ آموزش کامل JSON در Python، JavaScript و API
- HTTP چیست؟ آموزش Request، Response، Method و Status Code
- REST API چیست؟ راهنمای کامل طراحی RESTful API
- آموزش Playwright و تست End-to-End با هوش مصنوعی
- ساخت اپلیکیشن هوش مصنوعی با Angular و TypeScript
برای مطالعه شرایط استفاده و محدودیتهای مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.