अन्य जानकारी

प्रोटोटाइपल इनहेरिटेंस

null और undefined को छोड़कर, हर प्राइमिटिव डेटा टाइप का एक प्रोटोटाइप होता है. यह एक ऑब्जेक्ट रैपर होता है, जो वैल्यू के साथ काम करने के तरीके उपलब्ध कराता है. जब किसी प्राइमिटिव पर कोई मेथड या प्रॉपर्टी लुकअप लागू किया जाता है, तो JavaScript प्राइमिटिव को बैकग्राउंड में रैप कर देता है. इसके बाद, वह मेथड को कॉल करता है या रैपर ऑब्जेक्ट पर प्रॉपर्टी लुकअप करता है.

उदाहरण के लिए, स्ट्रिंग लिटरल में कोई मेथड नहीं होता. हालांकि, उस पर .toUpperCase() मेथड को कॉल किया जा सकता है. ऐसा, उससे जुड़े String ऑब्जेक्ट रैपर की मदद से किया जा सकता है:

"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL

इसे प्रोटोटाइपल इनहेरिटेंस कहा जाता है. इसमें, वैल्यू के कॉन्स्ट्रक्टर से प्रॉपर्टी और तरीकों को इनहेरिट किया जाता है.

Number.prototype
> Number { 0 }
>  constructor: function Number()
>  toExponential: function toExponential()
>  toFixed: function toFixed()
>  toLocaleString: function toLocaleString()
>  toPrecision: function toPrecision()
>  toString: function toString()
>  valueOf: function valueOf()
>  <prototype>: Object {  }

इन कन्स्ट्रक्टर का इस्तेमाल करके प्राइमिटिव बनाए जा सकते हैं. इसके लिए, उन्हें सिर्फ़ उनकी वैल्यू के हिसाब से तय करने की ज़रूरत नहीं होती. उदाहरण के लिए, String कन्स्ट्रक्टर का इस्तेमाल करने पर, स्ट्रिंग ऑब्जेक्ट बनता है, न कि स्ट्रिंग लिटरल: यह ऐसा ऑब्जेक्ट होता है जिसमें न सिर्फ़ हमारी स्ट्रिंग वैल्यू होती है, बल्कि कन्स्ट्रक्टर की इनहेरिट की गई सभी प्रॉपर्टी और तरीके भी होते हैं.

const myString = new String( "I'm a string." );

myString;
> String { "I'm a string." }

typeof myString;
> "object"

myString.valueOf();
> "I'm a string."

ज़्यादातर मामलों में, नतीजे के तौर पर मिलने वाले ऑब्जेक्ट, उन वैल्यू की तरह काम करते हैं जिनका इस्तेमाल हमने उन्हें तय करने के लिए किया है. उदाहरण के लिए, new Number कन्स्ट्रक्टर का इस्तेमाल करके संख्या की वैल्यू तय करने पर, एक ऑब्जेक्ट बनता है. इसमें Number प्रोटोटाइप के सभी तरीके और प्रॉपर्टी होती हैं. हालांकि, उन ऑब्जेक्ट पर मैथमैटिकल ऑपरेटर का इस्तेमाल ठीक वैसे ही किया जा सकता है जैसे संख्या के लिटरल पर किया जाता है:

const numberOne = new Number(1);
const numberTwo = new Number(2);

numberOne;
> Number { 1 }

typeof numberOne;
> "object"

numberTwo;
> Number { 2 }

typeof numberTwo;
> "object"

numberOne + numberTwo;
> 3

आपको इन कन्स्ट्रक्टर का इस्तेमाल बहुत कम करना पड़ेगा, क्योंकि JavaScript में पहले से मौजूद प्रोटोटाइपल इनहेरिटेंस का मतलब है कि इनसे कोई फ़ायदा नहीं मिलता. कंस्ट्रक्टर का इस्तेमाल करके प्राइमिटिव बनाने पर भी अनचाहे नतीजे मिल सकते हैं, क्योंकि नतीजा एक ऑब्जेक्ट होता है, न कि कोई सामान्य लिटरल:

let stringLiteral = "String literal."

typeof stringLiteral;
> "string"

let stringObject = new String( "String object." );

stringObject
> "object"

इससे, सटीक तुलना करने वाले ऑपरेटर का इस्तेमाल मुश्किल हो सकता है:

const myStringLiteral = "My string";
const myStringObject = new String( "My string" );

myStringLiteral === "My string";
> true

myStringObject === "My string";
> false

सेमीकोलन अपने-आप डालने की सुविधा (एएसआई)

स्क्रिप्ट को पार्स करते समय, JavaScript इंटरप्रिटर, ऑटोमैटिक सेमीकोलन इंसर्शन (एएसआई) नाम की सुविधा का इस्तेमाल कर सकते हैं. इससे, वे उन जगहों पर सेमीकोलन जोड़ सकते हैं जहां वे मौजूद नहीं हैं. अगर JavaScript पार्सर को कोई ऐसा टोकन मिलता है जिसकी अनुमति नहीं है, तो वह सिंटैक्स से जुड़ी संभावित गड़बड़ी को ठीक करने के लिए, उस टोकन से पहले सेमीकोलन जोड़ने की कोशिश करता है. ऐसा तब तक किया जाता है, जब तक इनमें से एक या उससे ज़्यादा शर्तें पूरी होती हैं:

  • उस टोकन को पिछले टोकन से लाइन ब्रेक से अलग किया जाता है.
  • वह टोकन } है.
  • पिछला टोकन ) है और डाला गया सेमीकोलन, dowhile स्टेटमेंट के आखिर में मौजूद सेमीकोलन होगा.

ज़्यादा जानकारी के लिए, एएसआई के नियम देखें.

उदाहरण के लिए, नीचे दिए गए स्टेटमेंट के बाद सेमीकोलन हटाने पर, एएसआई की वजह से सिंटैक्स से जुड़ी गड़बड़ी नहीं होगी:

const myVariable = 2
myVariable + 3
> 5

हालांकि, एक ही लाइन में कई स्टेटमेंट के लिए, एएसआई का इस्तेमाल नहीं किया जा सकता. अगर एक ही लाइन में एक से ज़्यादा स्टेटमेंट लिखे जाते हैं, तो उन्हें सेमीकोलन लगाकर अलग करें:

const myVariable = 2 myVariable + 3
> Uncaught SyntaxError: unexpected token: identifier

const myVariable = 2; myVariable + 3;
> 5

एएसआई, गड़बड़ी को ठीक करने की कोशिश है, न कि JavaScript में सिंटैक्स से जुड़ी कोई सुविधा. जहां ज़रूरी हो वहां सेमीकोलन का इस्तेमाल करना न भूलें, ताकि सही कोड बनाने के लिए आपको उस पर निर्भर न होना पड़े.

स्ट्रिक्ट मोड

JavaScript को लिखने के तरीके को कंट्रोल करने वाले स्टैंडर्ड, भाषा के शुरुआती डिज़ाइन के दौरान तय किए गए किसी भी स्टैंडर्ड से काफ़ी आगे बढ़ चुके हैं. JavaScript के काम करने के तरीके में होने वाले हर नए बदलाव से, पुरानी वेबसाइटों में गड़बड़ियां नहीं होनी चाहिए.

ES5, "स्ट्रिक्ट मोड" को शामिल करके, JavaScript के सेमेंटेक्स से जुड़ी कुछ पुरानी समस्याओं को हल करता है. इससे, मौजूदा लागू किए गए बदलावों में कोई बदलाव किए बिना, पूरी स्क्रिप्ट या किसी फ़ंक्शन के लिए, भाषा के नियमों के ज़्यादा पाबंदी वाले सेट में ऑप्ट इन करने का तरीका मिलता है. स्ट्रिक्ट मोड चालू करने के लिए, स्क्रिप्ट या फ़ंक्शन की पहली लाइन में स्ट्रिंग लिटरल "use strict" के बाद सेमीकोलन का इस्तेमाल करें:

"use strict";
function myFunction() {
  "use strict";
}

स्ट्रिक्ट मोड, कुछ "असुरक्षित" कार्रवाइयों या बंद की गई सुविधाओं को रोकता है. साथ ही, सामान्य "साइलेंट" गड़बड़ियों के बजाय साफ़ तौर पर गड़बड़ियों की जानकारी देता है. साथ ही, ऐसे सिंटैक्स के इस्तेमाल पर पाबंदी लगाता है जो आने वाले समय में भाषा की सुविधाओं के साथ काम न कर पाएं. उदाहरण के लिए, वैरिएबल के दायरे के लिए डिज़ाइन से जुड़े शुरुआती फ़ैसलों की वजह से, डेवलपर को वैरिएबल का एलान करते समय ग्लोबल स्कोप को गलती से "दूषित" करने की संभावना ज़्यादा होती है. भले ही, उसमें कोई भी कॉन्टेक्स्ट हो, var कीवर्ड को हटाकर ऐसा किया जा सकता है:

(function() {
  mySloppyGlobal = true;
}());

mySloppyGlobal;
> true

आधुनिक JavaScript रनटाइम, इस व्यवहार को ठीक नहीं कर सकते. ऐसा करने पर, उस पर निर्भर किसी भी वेबसाइट को गलत तरीके से या जान-बूझकर नुकसान पहुंच सकता है. इसके बजाय, आधुनिक JavaScript, डेवलपर को नए काम के लिए स्ट्रिक्ट मोड में ऑप्ट इन करने की सुविधा देता है. साथ ही, भाषा की नई सुविधाओं के लिए डिफ़ॉल्ट रूप से स्ट्रिक्ट मोड को चालू करता है, ताकि ये सुविधाएं लेगसी लागू करने में रुकावट न डालें:

(function() {
    "use strict";
    mySloppyGlobal = true;
}());
> Uncaught ReferenceError: assignment to undeclared variable mySloppyGlobal

आपको "use strict" को स्ट्रिंग लिटरल के तौर पर लिखना होगा. टेंप्लेट लिटरल (use strict) काम नहीं करेगा. आपको अपने काम के संदर्भ में, किसी भी कोड को चलाने से पहले "use strict" भी शामिल करना होगा. ऐसा न करने पर, अनुवादक उसे अनदेखा कर देता है.

(function() {
    "use strict";
    let myVariable = "String.";
    console.log( myVariable );
    sloppyGlobal = true;
}());
> "String."
> Uncaught ReferenceError: assignment to undeclared variable sloppyGlobal

(function() {
    let myVariable = "String.";
    "use strict";
    console.log( myVariable );
    sloppyGlobal = true;
}());
> "String." // Because there was code prior to "use strict", this variable still pollutes the global scope

रेफ़रंस के हिसाब से, वैल्यू के हिसाब से

किसी भी वैरिएबल में, प्राइमटिव वैल्यू या रेफ़रंस वैल्यू हो सकती है. जैसे, किसी ऑब्जेक्ट की प्रॉपर्टी, फ़ंक्शन पैरामीटर, और ऐरे, सेट या मैप में मौजूद एलिमेंट.

जब किसी प्राइमटिव वैल्यू को एक वैरिएबल से दूसरे वैरिएबल में असाइन किया जाता है, तो JavaScript इंजन उस वैल्यू की कॉपी बनाता है और उसे वैरिएबल को असाइन करता है.

जब किसी वैरिएबल में कोई ऑब्जेक्ट (क्लास इंस्टेंस, ऐरे, और फ़ंक्शन) असाइन किया जाता है, तो उस ऑब्जेक्ट की नई कॉपी बनाने के बजाय, वैरिएबल में मेमोरी में ऑब्जेक्ट की सेव की गई पोज़िशन का रेफ़रंस होता है. इस वजह से, किसी वैरिएबल से रेफ़र किए गए ऑब्जेक्ट में बदलाव करने पर, उस ऑब्जेक्ट में बदलाव होता है, न कि सिर्फ़ उस वैरिएबल में मौजूद वैल्यू में. उदाहरण के लिए, अगर किसी ऑब्जेक्ट के रेफ़रंस वाले वैरिएबल की मदद से, नए वैरिएबल को शुरू किया जाता है, तो उस ऑब्जेक्ट में प्रॉपर्टी जोड़ने के लिए, नए वैरिएबल का इस्तेमाल किया जाता है. इससे, प्रॉपर्टी और उसकी वैल्यू, मूल ऑब्जेक्ट में जुड़ जाती है:

const myObject = {};
const myObjectReference = myObject;

myObjectReference.myProperty = true;

myObject;
> Object { myProperty: true }

यह ऑब्जेक्ट में बदलाव करने के लिए ही नहीं, बल्कि सटीक तुलना करने के लिए भी ज़रूरी है. ऐसा इसलिए है, क्योंकि ऑब्जेक्ट के बीच सटीक बराबरी के लिए, दोनों वैरिएबल को true का आकलन करने के लिए, एक ही ऑब्जेक्ट का रेफ़रंस देना ज़रूरी है. ये अलग-अलग ऑब्जेक्ट का रेफ़रंस नहीं दे सकते, भले ही वे ऑब्जेक्ट एक जैसे हों:

const myObject = {};
const myReferencedObject = myObject;
const myNewObject = {};

myObject === myNewObject;
> false

myObject === myReferencedObject;
> true

मेमोरी का बंटवारा

JavaScript, मेमोरी को अपने-आप मैनेज करने की सुविधा का इस्तेमाल करता है. इसका मतलब है कि डेवलपमेंट के दौरान, मेमोरी को साफ़ तौर पर ऐलोकेट या डिऐलोकेट करने की ज़रूरत नहीं होती. हालांकि, इस मॉड्यूल में, मेमोरी मैनेजमेंट के लिए JavaScript इंजन के तरीकों के बारे में जानकारी नहीं दी गई है, लेकिन यह समझना कि मेमोरी कैसे असाइन की जाती है, रेफ़रंस वैल्यू के साथ काम करने के लिए काम का कॉन्टेक्स्ट देता है.

मेमोरी में दो "एरिया" होते हैं: "स्टैक" और "हीप". स्टैक में स्टैटिक डेटा सेव होता है. जैसे, प्राइमटिव वैल्यू और ऑब्जेक्ट के रेफ़रंस. ऐसा इसलिए होता है, क्योंकि स्क्रिप्ट के लागू होने से पहले, इस डेटा को सेव करने के लिए ज़रूरी जगह को तय किया जा सकता है. ढेर में ऐसे ऑब्जेक्ट सेव किए जाते हैं जिन्हें डाइनैमिक तौर पर जगह चाहिए, क्योंकि उनके साइज़ में प्रोग्राम के चलने के दौरान बदलाव हो सकता है. मेमोरी को "गार्बेज कलेक्शन" नाम की प्रोसेस से खाली किया जाता है. यह प्रोसेस, मेमोरी से ऐसे आइटम हटा देती है जिनका कोई रेफ़रंस नहीं होता.

मुख्य थ्रेड

JavaScript, "सिंक्रोनस" एक्ज़ीक्यूशन मॉडल वाली एक सिंगल-थ्रेड भाषा है. इसका मतलब है कि यह एक बार में सिर्फ़ एक टास्क को एक्ज़ीक्यूट कर सकती है. क्रम से चलने वाले इस एक्ज़ीक्यूशन कॉन्टेक्स्ट को मुख्य थ्रेड कहा जाता है.

मुख्य थ्रेड को ब्राउज़र के अन्य टास्क शेयर करते हैं. जैसे, एचटीएमएल पार्स करना, पेज के कुछ हिस्सों को रेंडर करना और फिर से रेंडर करना, सीएसएस ऐनिमेशन चलाना, और उपयोगकर्ता इंटरैक्शन को मैनेज करना. इन इंटरैक्शन में, टेक्स्ट को हाइलाइट करने जैसे आसान से लेकर फ़ॉर्म एलिमेंट के साथ इंटरैक्ट करने जैसे मुश्किल इंटरैक्शन शामिल हैं. ब्राउज़र वेंडर ने मुख्य थ्रेड के ज़रिए किए जाने वाले टास्क को ऑप्टिमाइज़ करने के तरीके ढूंढ लिए हैं. हालांकि, ज़्यादा जटिल स्क्रिप्ट अब भी मुख्य थ्रेड के संसाधनों का ज़्यादा इस्तेमाल कर सकती हैं और पेज की परफ़ॉर्मेंस पर असर डाल सकती हैं.

कुछ टास्क, वेब वर्कर्स नाम की बैकग्राउंड थ्रेड में पूरे किए जा सकते हैं. हालांकि, इन पर कुछ सीमाएं लागू होती हैं:

  • वर्कर्स थ्रेड, सिर्फ़ स्टैंडअलोन JavaScript फ़ाइलों पर काम कर सकती हैं.
  • उनके पास ब्राउज़र विंडो और यूज़र इंटरफ़ेस (यूआई) का ऐक्सेस बहुत कम है या बिलकुल नहीं है.
  • वे मुख्य थ्रेड के साथ सीमित तरीके से बातचीत कर सकते हैं.

इन सीमाओं की वजह से, ये फ़ंक्शन ज़्यादा संसाधनों वाले उन टास्क के लिए सबसे सही होते हैं जिन पर फ़ोकस करना ज़रूरी होता है. ऐसा न करने पर, ये टास्क मुख्य थ्रेड पर असर डाल सकते हैं.

कॉल स्टैक

"एक्सीक्यूशन कॉन्टेक्स्ट" को मैनेज करने के लिए इस्तेमाल किया जाने वाला डेटा स्ट्रक्चर, एक सूची होती है. इसे कॉल स्टैक कहा जाता है. इसे अक्सर सिर्फ़ "स्टैक" भी कहा जाता है. जब कोई स्क्रिप्ट पहली बार लागू की जाती है, तो JavaScript इंटरप्रेटर एक "ग्लोबल एक्सीक्यूशन कॉन्टेक्स्ट" बनाता है और उसे कॉल स्टैक में डालता है. साथ ही, उस ग्लोबल कॉन्टेक्स्ट में मौजूद स्टेटमेंट को एक बार में एक करके, ऊपर से नीचे की ओर लागू करता है. जब इंटरप्रेटर, ग्लोबल कॉन्टेक्स्ट को लागू करते समय किसी फ़ंक्शन कॉल का पता लगाता है, तो वह उस कॉल के लिए "फ़ंक्शन एक्सीक्यूशन कॉन्टेक्स्ट" को स्टैक के सबसे ऊपर पुश करता है. साथ ही, ग्लोबल एक्सीक्यूशन कॉन्टेक्स्ट को रोककर, फ़ंक्शन एक्सीक्यूशन कॉन्टेक्स्ट को लागू करता है.

जब भी किसी फ़ंक्शन को कॉल किया जाता है, तो उस कॉल के लिए फ़ंक्शन के एक्सीक्यूशन कॉन्टेक्स्ट को स्टैक के सबसे ऊपर, मौजूदा एक्सीक्यूशन कॉन्टेक्स्ट के ठीक ऊपर पुश किया जाता है. कॉल स्टैक, "आखिर में जोड़ा गया, पहले हटाया गया" के आधार पर काम करता है. इसका मतलब है कि स्टैक में सबसे ऊपर मौजूद, सबसे हाल ही में जोड़ा गया फ़ंक्शन कॉल, तब तक चलता रहेगा, जब तक वह पूरा नहीं हो जाता. जब वह फ़ंक्शन पूरा हो जाता है, तो इंटरप्रेटर उसे कॉल स्टैक से हटा देता है. साथ ही, उस फ़ंक्शन कॉल वाले एक्सीक्यूशन कॉन्टेक्स्ट को फिर से स्टैक में सबसे ऊपर ले जाता है और उसे फिर से एक्सीक्यूट करना शुरू कर देता है.

ये एक्सीक्यूशन कॉन्टेक्स्ट, अपने एक्सीक्यूशन के लिए ज़रूरी सभी वैल्यू कैप्चर करते हैं. ये फ़ंक्शन, अपने पैरंट कॉन्टेक्स्ट के आधार पर, फ़ंक्शन के दायरे में उपलब्ध वैरिएबल और फ़ंक्शन भी तय करते हैं. साथ ही, फ़ंक्शन के कॉन्टेक्स्ट में this कीवर्ड की वैल्यू तय करते हैं और उसे सेट करते हैं.

इवेंट लूप और कॉलबैक कतार

क्रम से होने वाले इस तरीके का मतलब है कि ऐसे असाइनमेंट जो कॉलबैक फ़ंक्शन के साथ होते हैं, जैसे कि किसी सर्वर से डेटा फ़ेच करना, उपयोगकर्ता के इंटरैक्शन का जवाब देना या setTimeout या setInterval से सेट किए गए टाइमर का इंतज़ार करना, वे मुख्य थ्रेड को तब तक ब्लॉक कर देंगे, जब तक वह असाइनमेंट पूरा नहीं हो जाता. इसके अलावा, कॉलबैक फ़ंक्शन के एक्ज़ीक्यूशन कॉन्टेक्स्ट को स्टैक में जोड़ने के बाद, मौजूदा एक्ज़ीक्यूशन कॉन्टेक्स्ट में अचानक रुकावट आ सकती है. इस समस्या को हल करने के लिए, JavaScript, "इवेंट लूप" और "कॉलबैक कतार" (इसे कभी-कभी "मैसेज कतार" भी कहा जाता है) से बने इवेंट-ड्रिवन "एक साथ कई काम करने की सुविधा वाले मॉडल" का इस्तेमाल करके, असाइनमेंट को मैनेज करता है.

जब मुख्य थ्रेड पर कोई असाइनमेंट एक साथ नहीं किया जाता, तो कॉलबैक फ़ंक्शन के एक्ज़ीक्यूशन कॉन्टेक्स्ट को कॉलबैक कतार में रखा जाता है, न कि कॉल स्टैक में. इवेंट लूप एक पैटर्न है, जिसे कभी-कभी रिएक्टर कहा जाता है. यह कॉल स्टैक और कॉलबैक कतार की स्थिति को लगातार पोल करता रहता है. अगर कॉलबैक सूची में टास्क हैं और इवेंट लूप यह तय करता है कि कॉल स्टैक खाली है, तो कॉलबैक सूची के टास्क को एक बार में एक करके स्टैक में डाला जाता है, ताकि उन्हें लागू किया जा सके.