JavaScript మరియు TypeScript ఆబ్జెక్ట్ రిఫరెన్స్‌లను (object references) పారవేసేవిగా భావించడం ప్రమాదకరంగా సులభం చేస్తుంది. మీరు ఒక ఆబ్జెక్ట్‌ను సృష్టించి, దానిని ఒక ఫంక్షన్‌కు పంపి, ఒక క్యాష్‌లో నిల్వ చేసి, ఆ తర్వాత ఆ వేరియబుల్‌ను కొత్త ఇన్‌స్టన్స్‌తో భర్తీ చేస్తారు. భాష (language) దీనిపై ఎటువంటి ఫిర్యాదు చేయదు. పాత రిఫరెన్స్ మీ ప్రోగ్రామ్‌లో మరెక్కడో ఇంకా ఉంటూనే ఉంటుంది, అది ఇప్పుడు ప్రస్తుతమైనది కాని డేటాను సూచిస్తుంది. ఇది ప్రోగ్రామ్ క్రాష్ అవ్వడం కాదు. ఇది అంతకంటే దారుణమైనది: మీ కోడ్‌బేస్‌లోని రెండు భాగాలు తామే నిజమైన డేటాను కలిగి ఉన్నామని అనుకుంటూ, వాటి మధ్య నిశ్శబ్దంగా ఏర్పడే వైరుధ్యం.

దీనిని నివారించే పద్ధతిని Hard Object References అంటారు. ఇది ఏదో లైబ్రరీ లేదా కంపైలర్ ఫీచర్ కాదు. ఇది మీరు మీ కోడ్‌ అంతటా అమలు చేయాల్సిన ఒక నియమం (contract).

The Stale Alias సమస్య

ఒక మాడ్యూల్ ఒక ఆబ్జెక్ట్‌కు రిఫరెన్స్‌ను కలిగి ఉన్నప్పుడు, మరొక మాడ్యూల్ ఆ ఆబ్జెక్ట్‌ను కొత్త దానితో భర్తీ చేస్తే 'stale alias' ఏర్పడుతుంది. మొదటి రిఫరెన్స్ ఇప్పటికీ సరైన కోడ్‌గానే ఉంటుంది, కానీ అది ఇకపై ప్రస్తుత డేటాను సూచించదు.

ఒక సాధారణ వెబ్ అప్లికేషన్‌లో యూజర్ రికార్డును ఊహించుకోండి:

const user = {
  name: "Alice",
  address: {
    city: "Seoul",
    country: "KR"
  }
};

ఒక షిప్పింగ్ కాంపోనెంట్ (shipping component) ప్రారంభంలోనే అడ్రస్‌ను తీసుకుంటుంది:

const shippingAddress = user.address;

ఆ తర్వాత, ప్రొఫైల్ అప్‌డేట్ వస్తుంది. ఒక reducer లేదా service handler మొత్తం ఆబ్జెక్ట్‌ను భర్తీ చేయాలని నిర్ణయించుకుంటుంది:

user.address = { city: "Tokyo", country: "JP" };

ఈ సమయంలో, user.address టోక్యోను సూచిస్తుంది. కానీ shippingAddress ఇంకా సియోల్‌లోని పాత ఆబ్జెక్ట్‌నే సూచిస్తుంది. ఎటువంటి ఎక్సెప్షన్ (exception) రాదు. టైప్స్ (types) సరిపోతున్నందున TypeScript కూడా సంతోషంగా ఉంటుంది. ప్రొఫైల్ పేజీలో UI అప్‌డేట్ చేయబడిన నగరాన్ని చూపిస్తుంది, కానీ షిప్పింగ్ లేబుల్ మాత్రం నిశ్శబ్దంగా పాత నగరాన్నే ప్రింట్ చేస్తుంది. యూజర్ తమ ప్యాకేజీ తప్పు దేశానికి వెళ్ళిందని ఫిర్యాదు చేసినప్పుడు మాత్రమే ఈ బగ్ బయటపడుతుంది.

JavaScript ఐడెంటిటీని (identity) మరియు విలువను (value) వేరుగా చూడటం వల్ల ఇది జరుగుతుంది. మీరు ఒక ఆబ్జెక్ట్ ప్రాపర్టీని కొత్త ఆబ్జెక్ట్ లిటరల్‌తో భర్తీ చేసినప్పుడు, మీరు ఆ గొలుసును (chain) తెంచుతారు. పాత ఆబ్జెక్ట్ నాశనం చేయబడదు; అది కేవలం అనాథగా (orphaned) మిగిలిపోతుంది. దానిని ఇంకా కలిగి ఉన్న ఎవరైనా ఒక 'ఘోస్ట్' (ghost) తోనే పని చేస్తున్నట్లు అవుతుంది.

Hard Object References అంటే ఏమిటి

నియమం సరళమైనది: ప్రిమిటివ్ విలువలను (primitive values) భర్తీ చేయండి, కానీ ఆబ్జెక్ట్ లేదా అర్రే (array) రిఫరెన్స్‌లను ఎప్పుడూ భర్తీ చేయకండి. కొత్త డేటా వచ్చినప్పుడు, కంటైనర్‌ను మార్చడానికి బదులుగా, అందులోకి డేటాను కాపీ చేయండి.

దీని కోసం మూడు నిర్దిష్టమైన అలవాట్లు అవసరం.

మొదటిది, ఆబ్జెక్ట్‌లు మరియు అర్రేలను const తో డిక్లేర్ చేయండి. ఇది టాప్-లెవల్ వేరియబుల్‌ను కొత్త ఇన్‌స్టన్స్‌కు మళ్ళీ బైండ్ చేయాలనే కోరికను తగ్గిస్తుంది. ఆ స్కోప్ (scope) కాలపరిమితి అంతా వేరియబుల్ స్థిరంగా ఉండాలి.

రెండవది, ఆబ్జెక్ట్ లేదా అర్రేని కలిగి ఉన్న ప్రాపర్టీని కొత్త దానితో ఎప్పుడూ భర్తీ చేయకండి. మీరు ఒక అడ్రస్‌ను అప్‌డేట్ చేయాలనుకుంటే, దాని లోపల ఉన్న ప్రాపర్టీలను మ్యుటేట్ (mutate) చేయండి.

మూడవది, మీరు స్టేట్‌ను క్లియర్ చేయాలన్నా లేదా రీసెట్ చేయాలన్నా, కొత్త ఖాళీ ఆబ్జెక్ట్ లేదా అర్రే కోసం పాతదాన్ని పారేయకుండా, ఉన్న స్ట్రక్చర్‌నే ఖాళీ చేయండి.

అడ్రస్ ఉదాహరణను మళ్ళీ చూస్తే, సరైన అప్‌డేట్ ఇలా ఉంటుంది:

user.address.city = "Tokyo";
user.address.country = "JP";

వచ్చే డేటా పాక్షికంగా (partial) లేదా డైనమిక్‌గా ఉంటే, ఉన్న టార్గెట్‌లోకి రాయడానికి Object.assign ఉపయోగించండి:

Object.assign(user.address, incomingAddressData);

మెమరీలో సరిగ్గా అదే ఆబ్జెక్ట్‌ను సూచించే shippingAddress వేరియబుల్, ఇప్పుడు కొత్త ఫీల్డ్‌లను వెంటనే చూస్తుంది. లైవ్ సోర్స్ ఆఫ్ ట్రూత్ (live source of truth) గా పనిచేసేది కేవలం ఒకే ఒక కానానికల్ (canonical) ఆబ్జెక్ట్ మాత్రమే.

ఇది ఎక్కడ అత్యంత ముఖ్యమైనది

ఒక ఫ్లాట్ కాన్ఫిగరేషన్ ఆబ్జెక్ట్ కోసం ఈ పద్ధతి అవసరమనిపించకపోవచ్చు. కానీ మీ స్టేట్ ఒక గ్రాఫ్‌లాగా పెరిగి, బహుళ సబ్‌సిస్టమ్స్ (subsystems) ఒకదానితో ఒకటి సంబంధం ఉన్న నోడ్స్‌కు పాయింటర్లను కలిగి ఉన్నప్పుడు ఇది చాలా అవసరమవుతుంది.

ఒక రిచ్ టెక్స్ట్ ఎడిటర్‌ను తీసుకోండి. డాక్యుమెంట్ మోడల్ అనేది నోడ్స్ యొక్క ఒక ట్రీ (tree). సెలెక్షన్ మోడల్ ప్రారంభ మరియు ముగింపు నోడ్స్‌కు రిఫరెన్స్‌లను కలిగి ఉంటుంది. హిస్టరీ బఫర్ చివరి ఆపరేషన్‌లో మారిన నోడ్స్‌కు రిఫరెన్స్‌లను కలిగి ఉంటుంది. రెండరింగ్ లేయర్ లేఅవుట్ కోసం కొలిచిన నోడ్స్‌కు రిఫరెన్స్‌లను కలిగి ఉంటుంది. ఒకవేళ టెక్స్ట్ మారిన కారణంగా స్టేట్ మేనేజర్ ఒక పారాగ్రాఫ్ నోడ్‌ను కొత్త ఆబ్జెక్ట్‌తో భర్తీ చేస్తే, ఆ సబ్‌సిస్టమ్స్ అన్నీ ఇప్పుడు ఒక 'stale alias'ను కలిగి ఉంటాయి. దీనివల్ల సెలెక్షన్ తప్పు ప్రాంతాన్ని హైలైట్ చేస్తుంది. హిస్టరీ సిస్టమ్ సరిగ్గా వెనక్కి వెళ్లలేదు (revert). రెండరర్ క్రాష్ అవుతుంది లేదా దారుణంగా, ఫాంటమ్ కర్సర్‌లను (phantom cursors) చూపిస్తుంది.

UI, యాక్సెస్-కంట్రోల్ లేయర్ మరియు ఆటోసేవ్ రూటీన్ ద్వారా రిఫరెన్స్ చేయబడే నెస్టెడ్ సెట్టింగ్‌లు మరియు పర్మిషన్లు ఉన్న యూజర్ ప్రొఫైల్స్‌లో కూడా ఇదే ప్రమాదం కనిపిస్తుంది. పేరెంట్ కంటైనర్లు చైల్డ్ నోడ్స్ కొలతలను క్యాష్ చేసే లేఅవుట్ ఇంజన్లలో కూడా ఇది కనిపిస్తుంది. రన్‌టైమ్ కంట్రోలర్ యాక్టివ్ ఎంటిటీలను రిఫరెన్స్ ద్వారా ట్రాక్ చేసే విజువల్ ఎడిటర్లు మరియు కాన్వాస్ టూల్స్‌లో కూడా ఇది కనిపిస్తుంది. ఈ అన్ని రంగాలలో, కాంపోనెంట్లు ఒక ఆబ్జెక్ట్‌ను పట్టుకుంటాయి మరియు ఆ హ్యాండిల్ (handle) ఎల్లప్పుడూ నిజమైన డేటాను చూపిస్తుందని ఆశిస్తాయి.

Hard Object References ఆబ్జెక్ట్‌ను ఒక స్థిరమైన చిరునామాగా (stable address) పరిగణిస్తుంది. లోపల ఉన్న ఫర్నిచర్ మారవచ్చు, కానీ తలుపు మాత్రం అదే చోట ఉంటుంది. ఆ అడ్రస్ ఉన్న ఎవరైనా లోపలికి వెళ్లి ప్రస్తుత లేఅవుట్‌ను చూడవచ్చు.

Replacement కు బదులుగా Reactivity

మీరు Redux లేదా ఇటువంటి immutable state libraries తో పనిచేసి ఉంటే, ఈ మోడల్ మీకు విరుద్ధంగా అనిపించవచ్చు. ఆ వ్యవస్థలలో, కొత్త ఆబ్జెక్ట్‌ను సృష్టించడం ద్వారా మార్పును సూచిస్తారు. రిఫరెన్స్ మార్పుయే ఆ సంకేతం. రీ-రెండర్ అవ్వాలా వద్దా అని తెలుసుకోవడానికి కాంపోనెంట్స్ prevProps.data === nextProps.data ను పోల్చి చూస్తాయి.

Hard Object References ఆ ఊహను మార్చుకోవాలని కోరుతుంది. రిఫరెన్స్ స్థిరంగా ఉండటం వల్ల, డేటా మారిందో లేదో రిఫరెన్స్ ఈక్వాలిటీ (reference equality) ద్వారా తెలియదు. అప్‌డేట్‌లను బ్రాడ్‌కాస్ట్ చేయడానికి మీకు వేరే మార్గం కావాలి.

ఆచరణలో, దీని అర్థం reactivity systems, explicit observers లేదా dirty flags పై ఆధారపడటం. user.address.city ను మ్యుటేట్ (mutate) చేయడం ద్వారా సబ్‌స్క్రైబర్లకు తెలియజేసే ఒక సెట్టర్‌ను (setter) ట్రిగ్గర్ చేయవచ్చు. ఒక ఆబ్జెక్ట్ ఈవెంట్ బస్ (event bus) ద్వారా మార్పు ఈవెంట్‌ను పంపవచ్చు. ఒక గేమ్ లూప్ లేదా కాన్వాస్ టూల్ గ్లోబల్ డిర్టీ ఫ్లాగ్‌ను (global dirty flag) సెట్ చేసి, ఫ్రేమ్ చివరలో గ్రాఫ్‌ను మళ్ళీ స్కాన్ చేయవచ్చు. రిఫరెన్స్ స్థిరంగా ఉంటుంది, కాబట్టి మీరు ఇతర పద్ధతుల ద్వారా డేటా ఫ్లోను కనిపించేలా చేయాలి.

ఈ ఆర్కిటెక్చరల్ మార్పు కారణంగానే, ఈ విధానం సంక్లిష్టమైన frontend state, పెద్ద component-local state, visual editors, canvas tools మరియు runtime controllers లలో బాగా సరిపోతుంది. ఈ వ్యవస్థలు ఇప్పటికే granular updates, direct mutations లేదా imperative APIs పై ఆధారపడి ఉంటాయి. వీటిపై బలవంతంగా immutabilityని రుద్దడం వల్ల, తగినంత స్పష్టత రాకపోగా, అనవసరమైన allocation pressure మరియు reference churn ఏర్పడుతుంది. ప్రతి ఫ్రేమ్ ముఖ్యం అయినప్పుడు, కేవలం ఒక slider ని కదిలించడానికి కొత్త object graph ని కేటాయించడం వృధా. రిఫరెన్స్‌ను hard గా ఉంచి, లోపల ఉన్న డేటాను మ్యుటేట్ చేయడం అనేది సమస్య యొక్క అసలు మెకానిక్స్‌కు సరిపోతుంది.

దీనిని అమలు చేయడం

ఈ నియమం యొక్క విస్మరించబడిన ప్రయోజనాలలో ఒకటి