آپ ایک بگ (bug) کا سراغ تین فائلوں میں ڈھونڈتے ہیں، صرف یہ جاننے کے لیے کہ ایک بے ضرر ہیلپر فنکشن نے صارف کا نام تبدیل کر دیا ہے۔ آپ نے اصل فائل کو کبھی ہاتھ نہیں لگایا۔ یا کم از کم آپ کو ایسا ہی لگا۔ JavaScript میں، ایک ویری ایبل کو دوسرے کو تفویض (assign) کرنے کا مطلب ہمیشہ وہ نہیں ہوتا جو آپ توقع کرتے ہیں۔ یہ زبان اپنے ڈیٹا کو اسٹوریج کی دو حکمت عملیوں میں تقسیم کرتی ہے، اور یہ بھول جانا کہ آپ کون سی استعمال کر رہے ہیں، خاموش تبدیلیوں (mutations) کے پروڈکشن کوڈ میں داخل ہونے کا سبب بنتا ہے۔
اس سے بچنے کے لیے، آپ کو primitive values اور objects کے درمیان فرق کو بالکل اسی طرح دیکھنے کی ضرورت ہے جیسے انجن دیکھتا ہے۔
Primitives: اصل کاپیاں
ایک primitive ایک واحد، ناقابل تقسیم ڈیٹا (datum) ہے۔ اسے مزید چھوٹے JavaScript ویلیوز میں تقسیم نہیں کیا جا سکتا۔ یہ زبان سات primitive اقسام کی تعریف کرتی ہے: String، Number، Boolean، Undefined، Null، Symbol، اور BigInt۔
چونکہ primitives ایٹمی (atomic) ہوتے ہیں، اس لیے وہ عام طور پر براہ راست ویری ایبل بائنڈنگ کے اندر رہتے ہیں۔ جب آپ ایک primitive ویری ایبل کو دوسرے میں کاپی کرتے ہیں، تو انجن اصل ڈیٹا کو ڈپلیکٹ کر دیتا ہے۔ ہر ویری ایبل میموری میں اپنا آزاد سلاٹ (slot) حاصل کرتا ہے۔
let a = "Alina";
let b = a;
b = "Ali";
console.log(a); // "Alina"
console.log(b); // "Ali"
یہاں، a کو ہاتھ نہیں لگایا گیا۔ b کو دوبارہ تفویض کرنے سے ایک بالکل نئی ویلیو تخلیق ہوئی اور b اس کی طرف اشارہ کرنے لگا، جبکہ a اب بھی اپنی اصل اسٹرنگ (string) برقرار رکھے ہوئے ہے۔ یہ رویہ copy by value کہلاتا ہے۔ یہ نمبرز، بولینز، سمبلز اور پرائمٹیو فیملی کے باقی تمام عناصر کے لیے بالکل اسی طرح کام کرتا ہے۔ آپ انہیں فنکشنز میں پاس کر سکتے ہیں، دوبارہ تفویض کر سکتے ہیں، یا متعلقہ ویری ایبلز پر اثرات (side effects) کی فکر کیے بغیر واپس (return) کر سکتے ہیں۔
Objects: مشترکہ ایڈریسز
Objects مختلف ہوتے ہیں۔ ایک آبجیکٹ ایک مرکب کنٹینر (composite container) ہے جو ڈیٹا کے متعدد ٹکڑوں کو ایک ساتھ گروپ کرتا ہے۔ اس زمرے میں سادہ آبجیکٹس، arrays، functions، dates، اور ہر دوسری non-primitive قسم شامل ہے۔ چونکہ یہ ڈھانچے بڑے اور نیسٹڈ (nested) ہو سکتے ہیں، اس لیے JavaScript پورے آبجیکٹ کو ویری ایبل کے اندر اسٹور نہیں کرتا۔ اس کے بجائے، ویری ایبل ایک ریفرنس (reference) رکھتا ہے، جو بنیادی طور پر ایک میموری ایڈریس ہے جو کہیں اور اسٹور شدہ اصل ڈیٹا کی طرف اشارہ کرتا ہے۔
جب آپ کسی آبجیکٹ کو نئے ویری ایبل کو تفویض کرتے ہیں، تو انجن ایڈریس کو کاپی کرتا ہے، آبجیکٹ کو نہیں۔ اب دو ویری ایبلز بالکل ایک ہی گھر کی طرف اشارہ کر رہے ہیں۔
const user = { name: "Alina" };
const copy = user;
copy.name = "Ali";
console.log(user.name); // "Ali"
copy.name کو تبدیل کرنے سے user.name بھی تبدیل ہو گیا کیونکہ دونوں نام ایک ہی بنیادی آبجیکٹ کی طرف اشارہ کرتے ہیں۔ یہ copy by reference ہے۔ یہی حیرت arrays کے ساتھ بھی نظر آتی ہے:
const scores = [82, 91, 74];
const backup = scores;
backup.push(88);
console.log(scores); // [82, 91, 74, 88]
میموری میں صرف ایک ہی array موجود ہے۔ scores اور backup محض دو اشارے ہیں جو اس کی طرف اشارہ کر رہے ہیں۔
const کی ورڈ الجھن کی ایک اور تہہ پیدا کرتا ہے۔ const کے ساتھ کسی آبجیکٹ کا اعلان کرنا ویری ایبل بائنڈنگ کو لاک کر دیتا ہے تاکہ آپ اسے کسی نئے ایڈریس کی طرف اشارہ نہ کر سکیں۔ یہ خود آبجیکٹ کو لاک نہیں کرتا۔
const settings = { theme: "dark" };
settings.theme = "light"; // Works perfectly.
settings = { theme: "dark" }; // TypeError
ڈویلپرز اکثر توقع کرتے ہیں کہ const غیر تبدیلی (immutability) کی ضمانت دیتا ہے۔ ایسا نہیں ہے۔ یہ صرف ریفرنس کی دوبارہ تفویض (reassignment) کو روکتا ہے۔ اگر آپ مواد کو محفوظ (sealed) رکھنا چاہتے ہیں، تو آپ کو ارادتاً کاپی کرنا ہوگا۔
اصل بگ کہاں چھپے ہوتے ہیں
ریفرنس بگ (Reference bugs) شاذ و نادر ہی ویری ایبل کی واضح دوبارہ تفویض کے طور پر سامنے آتے ہیں۔ وہ فنکشن کالز کے اندر چھپے ہوتے ہیں۔
function addTimestamp(record) {
record.timestamp = Date.now();
return record;
}
const original = { id: 1 };
addTimestamp(original);
console.log(original.timestamp); // A number now exists here. Oops.
پیرامیٹر record کو ریفرنس کی ایک کاپی موصول ہوئی۔ فنکشن کے اندر کسی بھی پراپرٹی کی تبدیلی (mutation) نے براہ راست کالر (caller) کے آبجیکٹ میں لکھ دیا۔ فنکشن ایک سادہ تبدیلی (transformation) کی طرح لگ رہا تھا، پھر بھی اس نے اسکوپ کی حدود کے پار اسٹیٹ (state) کو خارج (leak) کر دیا۔
یہ پیٹرن خاص طور پر React جیسے UI فریم ورکس میں تکلیف دہ ہوتا ہے، جہاں اسٹیٹ اپ ڈیٹس انحصار کرتی ہیں