நீங்கள் மூன்று கோப்புகளைத் தேடி ஒரு பிழையைக் (bug) கண்டறிய முயலும்போது, ஒரு சாதாரண உதவியாளர் செயல்பாடு (helper function) பயனரின் பெயரை மாற்றியிருப்பதை மட்டும் காண்கிறீர்கள். நீங்கள் அசல் தரவை மாற்றவே இல்லை. அல்லது அப்படி நீங்கள் நினைத்திருக்கலாம். JavaScript-இல், ஒரு மாறியை (variable) மற்றொரு மாறிக்கு ஒதுக்குவது (assigning) நீங்கள் எதிர்பார்ப்பது போலவே எப்போதும் இருக்காது. இந்த மொழி தனது தரவை இரண்டு சேமிப்பு உத்திகளாகப் பிரிக்கிறது, மேலும் நீங்கள் எதைப் பயன்படுத்துகிறீர்கள் என்பதை மறந்துவிடுவதுதான் உற்பத்தி குறியீட்டில் (production code) அமைதியான மாற்றங்கள் (silent mutations) உருவாகக் காரணமாகிறது.

இதைத் தவிர்க்க, ஒரு என்ஜின் (engine) எவ்வாறு பார்க்கிறதோ, அதேபோல primitive மதிப்புகளுக்கும் (primitive values) ஆப்ஜெக்ட்களுக்கும் (objects) இடையிலான வித்தியாசத்தை நீங்கள் பார்க்க வேண்டும்.

Primitives: உண்மையான நகல்கள்

ஒரு primitive என்பது ஒரு ஒற்றை, பிரிக்க முடியாத தரவு (datum) ஆகும். அதைச் சிறிய JavaScript மதிப்புகளாகப் பிரிக்க முடியாது. இந்த மொழி ஏழு primitive வகைகளை வரையறுக்கிறது: String, Number, Boolean, Undefined, Null, Symbol, மற்றும் BigInt.

Primitives அணுக்களாக (atomic) இருப்பதால், அவை பொதுவாக மாறி பிணைப்பிற்குள் (variable binding) நேரடியாக இருக்கும். நீங்கள் ஒரு primitive மாறியை மற்றொரு மாறிக்கு நகலெடுக்கும்போது, என்ஜின் உண்மையான தரவை நகலெடுக்கிறது. ஒவ்வொரு மாறியும் நினைவகத்தில் (memory) தனக்கென ஒரு தனிப்பட்ட இடத்தைப் பெறுகிறது.

let a = "Alina";
let b = a;
b = "Ali";

console.log(a); // "Alina"
console.log(b); // "Ali"

இங்கே, a மாற்றப்படாமல் அப்படியே உள்ளது. b-ஐ மீண்டும் ஒதுக்குவது (reassigning) ஒரு புதிய மதிப்பை உருவாக்கி b-ஐ அதனுடன் இணைக்கிறது, அதே நேரத்தில் a அதன் அசல் சரத்தை (string) அப்படியே வைத்திருக்கிறது. இந்தச் செயல்பாடு 'copy by value' எனப்படும். இது எண்கள் (numbers), பூலியன்கள் (booleans), சிம்பல்கள் (symbols) மற்றும் பிற primitive வகைகளுக்கும் இதேபோலவே செயல்படுகிறது. நீங்கள் அவற்றைச் செயல்பாடுகளுக்குள் (functions) அனுப்பலாம், மீண்டும் ஒதுக்கலாம் அல்லது பக்க விளைவுகள் (side effects) குறித்த கவலை இன்றி அவற்றை மீண்டும் பெறலாம் (return).

Objects: பகிரப்பட்ட முகவரிகள்

Objects வேறுபட்டவை. ஒரு ஆப்ஜெக்ட் என்பது பல தரவுகளை ஒன்றாகக் குழுவாக்கும் ஒரு கூட்டுப் பெட்டி (composite container) போன்றது. இந்த வகையின் கீழ் சாதாரண ஆப்ஜெக்ட்கள், அணிகள் (arrays), செயல்பாடுகள் (functions), தேதிகள் (dates) மற்றும் பிற அனைத்து non-primitive வகைகளும் அடங்கும். இந்த அமைப்புகள் பெரியதாகவும் ஒன்றிற்குள் ஒன்று (nested) அமைந்ததாகவும் இருக்கலாம் என்பதால், JavaScript முழு ஆப்ஜெக்ட்டையும் மாறியினுள் சேமிப்பதில்லை. அதற்குப் பதிலாக, மாறி ஒரு குறிப்பை (reference) மட்டுமே வைத்திருக்கும்; இது அடிப்படையில் வேறு எங்கோ சேமிக்கப்பட்டுள்ள உண்மையான தரவைக் குறிக்கும் ஒரு நினைவக முகவரி (memory address) ஆகும்.

நீங்கள் ஒரு ஆப்ஜெக்ட்டை ஒரு புதிய மாறிக்கு ஒதுக்கும்போது, என்ஜின் முகவரியை மட்டுமே நகலெடுக்கிறது, ஆப்ஜெக்ட்டை அல்ல. இப்போது இரண்டு மாறிகளும் ஒரே வீட்டைக் குறிக்கின்றன.

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]

நினைவகத்தில் ஒரே ஒரு அணி மட்டுமே உள்ளது. scores மற்றும் backup என்பவை அதைச் சுட்டிக்காட்டும் இரண்டு அடையாளச் சின்னங்கள் மட்டுமே.

const என்ற முக்கிய சொல் (keyword) குழப்பத்தை மேலும் கூட்டுகிறது. const-ஐப் பயன்படுத்தி ஒரு ஆப்ஜெக்ட்டை அறிவிப்பது (declaring), மாறி பிணைப்பைத் (variable binding) பூட்டுகிறது, இதனால் நீங்கள் அதை ஒரு புதிய முகவரிக்கு மாற்ற முடியாது. ஆனால் அது ஆப்ஜெக்ட்டைத் தானே பூட்டுவதில்லை.

const settings = { theme: "dark" };
settings.theme = "light";        // Works perfectly.
settings = { theme: "dark" };    // TypeError

டெவலப்பர்கள் பெரும்பாலும் const என்பது மாற்ற முடியாத தன்மையை (immutability) வழங்கும் என்று எதிர்பார்க்கிறார்கள். ஆனால் அது அவ்வாறு செய்யாது. அது குறிப்பை (reference) மீண்டும் ஒதுக்குவதைத் மட்டுமே தடுக்கிறது. நீங்கள் உள்ளடக்கங்களை மாற்ற முடியாதபடி மாற்ற விரும்பினால், நீங்கள் திட்டமிட்டு நகலெடுக்க வேண்டும்.

உண்மையான பிழைகள் எங்கே ஒளிந்துள்ளன

குறிப்புப் பிழைகள் (Reference bugs) அரிதாகவே தெளிவான மாறி மறுஒதுக்கீடுகளாகத் தெரியவருகின்றன. அவை செயல்பாடுகளின் அழைப்புகளுக்குள் (function calls) ஒளிந்து கொள்கின்றன.

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 என்ற அளவுரு (parameter), குறிப்பின் நகலைப் பெற்றது. செயல்பாட்டிற்குள் (function) செய்யப்படும் எந்தவொரு பண்பு மாற்றமும் (property mutation), அழைத்தவரின் (caller) ஆப்ஜெக்ட்டிலேயே நேரடியாக எழுதப்பட்டது. அந்தச் செயல்பாடு ஒரு எளிய மாற்றமாகத் தெரிந்தாலும், அது ஸ்கோப் எல்லைகளைத் (scope boundaries) தாண்டி நிலையை (state) கசியவிட்டது.

இந்த முறை React போன்ற UI கட்டமைப்புகளில் (frameworks) குறிப்பாகப் Painful ஆக இருக்கும், அங்கு நிலை மாற்றங்கள் (state updates) சார்ந்திருக்கும்