জাভাস্ক্রিপ্ট অনেক জনপ্রিয় হলেও এর কিছু weird ব্যবহারের জন্য অনেকের কাছেই জাভাস্ক্রিপ্ট বেশ confusing programming language হিসাবে পরিচিত। আমরা যদি জাভাস্ক্রিপ্ট এর confusing parts গুলো ব্যবহারে সচেতন থাকি তাহলে আমরা জাভাস্ক্রিপ্টকে ভালভাবে ব্যবহার করতে পারব।

এই ব্লগ সিরিজের প্রথম পার্ট আগে পড়তে চাইলে এইখানে পাওয়া যাবে ।

"This"

জাভাস্ক্রিপ্টে this keyword রেফার করে আমরা জাভাস্ক্রিপ্ট কোডের যে অংশকে রান করার ট্রাই করছি তার execuation state কোনটি। যেটা নানা ভাবে অনেক সময় খুবই কনফিউজিং। নিচের উদারনটি খেয়াল করি ।

function helloworld() { 
    console.log(this);

}

helloworld(); // Here console.log will log this as a window object

এমন কি আমরা যদি ফাংশনকে কোন ভ্যারিয়েবল এ আস্যাইন করে কল করি তাহলে দেখা যাবে যে এটা window object কেই রেফার করে আছে।

let helloworld = function() { 

    console.log(this);
}

helloworld(); // console.log will log this as a window object 

কিন্তু আমরা যদি কোন অবজেক্ট এর প্রপার্টি হিসাবে ফাংশন নেই এবং তার ভিতরে আমরা লগ করি তাহলে দেখব this এই ক্ষেত্রে এই অবজেক্টেই রেফার করে ।


let obj = { 
    name: "I am function",
    func: function() { 
       console.log(this);  // console.log: {name: "I am function", func: ƒ} 
       this.name = "I am function and I am updating"
       console.log(this); // console.log: {name: "I am function and I am updating", func: ƒ}

    }

}

obj.func();

এটাও ঠিক আছে। যে কোন ফাংশন global namespace এ থাকলে তার execution context হল global namespace যেখানে this রেফার হচ্ছে window variable কে । কিন্তু যদি ফাংশন কোন অবজেক্ট এর ভিতর থাকে তার জন্য আলাদা execution context তৈরি হয় এবং তা অবশ্যই ফাংশনের execution context। এই অবস্থায় this অবজেক্টকে রেফার করছে। কিন্তু যদি আমরা নিচের উদাহরণ দেখিঃ

let obj = { 

name: "I am object",
func: function() { 
  console.log(this); // log: {name: "I am object", func: ƒ}
  this.name = "I am object and I am updated"
  let newFunc = function() { 
     console.log(this); // log: window object
     this.name = "hello world" // this will also update window name variable
  }
     newFunc(); // {name: "I am object and I am updated", func: ƒ}

    console.log(this);
}

}
obj.func();

এই রকম কেন হল তা ব্যাখ্যা করার আগে আমরা নিচের আরও একটা উদাহরণ দেখে নেই,

let obj = { 

name: "I am object",
func: function() { 
  console.log(this); // log: {name: "I am object", func: ƒ}
  this.name = "I am object and I am updated"
  let newFunc = function() { 
     console.log(this); // log: window object
     this.name = "hello world" // this will also update window name variable
     let anotherObj = {
         name: "I am anotherObj",
         func: function() {
            console.log(this); // log: {name: "I am anotherObj", func: f}
         }
     }
     anotherObj.func();
  }
     newFunc(); // {name: "I am object and I am updated", func: ƒ}

    console.log(this);
}

}
obj.func();

this সব সময় যেখানে ব্যবহার করা হয় তার execution state কোনটি তাকে রেফার করে । সহজ ভাষায় this তার owner কে রেফার করে। যদি আমরা ফাংশনের ভিতর this ব্যবহার করি by default যদি ঐ ফাংশনের owner কোন অবজেক্ট হয়, তাহলে this ঐ অবজেক্টকে রেফার করে, যদি কোন ফাংশন হয় তাহলে তা global namespace এর window কে রেফার করে। এই জন্য প্রথম newFunc এর owner func যা নিজে একটি ফাংশন তাই newFunc এর ভিতরের this window কে রেফার করে কিন্তু newFunc এর ভিতরের একটি অবজেক্ট anotherObj এর ভিতর যখন নতুন func ফাংশনকে ডিক্লেয়ার করা হয় এবং তার owner একজন অবজেক্ট তাই func এর ভিতরের this তার owner অবজেক্ট anotherObj কে রেফার করে । এই ব্যাপারগুলো অনেক কনফিউজিং। এর ধরনের ক্ষেত্রে Javascript এর কিছু best practices আছে। আমরা পরবর্তী সিরিজগুলোতে তা দেখব।

Event Loop

জাভাস্ক্রিপ্ট সিঙ্গেল থ্রেডেড প্রোগ্রামিং লাঙ্গুয়েঞ্জ, এর মানে হইল এর একটা মাত্র Call Stack এবং একটি মাত্র Memory Heap আছে । জাভাস্ক্রিপ্ট তার কোডগুলাকে অডারে একের পর এক চালায় এবং আউটপুট বের করে । কোন একটা কিছুর আউটপুট বের করার আগে পরের অডারের কাজগুলা হল্ট অবস্থায় থাকে মানে কোন কাজ হয় না যতক্ষণ না আগের কাজটি শেষ হচ্ছে । সাধারণ, বেশিরভাগ ব্রাউজারগুলিতে প্রতিটি ব্রাউজারের ট্যাবটির জন্য একটি ইভেন্ট লুপ থাকে যাতে প্রতিটি প্রক্রিয়া আলাদা হয়ে যায় এবং যাতে কখনও অসীম লুপ বা সময় সাপেক্ষ প্রসেসিং এর জন্য সম্পূর্ণ ব্রাউজারটি ব্লক হয়ে যাওয়া এড়ানো যায়। Call Stack আসলে কিভাবে কাজ করে জিনিসটা একটা উদাহরণের মাধ্যমে দেখা যাকঃ

const boo = () => console.log('boo')

const thoo = () => console.log('thoo')

const foo = () => {
  console.log('foo')
  boo();
  thoo();
}

foo();

এই কোডের আউটপুট আসবে

foo
boo
thoo

Call Stack এ কোড চালানোর সময় ডাটাস্ট্রাকচারের Stack এর LIFO ( Last In First Out ) যথাযথ অনুসরণ করা হয়। এইখানে প্রথমে Call Stack Empty ছিল, প্রথমে ফাংশন foo insert হইছে এরপর console.log('foo') Stack এ insert হইছে, তারপর console.log('foo') এর জন্য foo আউটপুট এ আসছে, তারপর যথাক্রমে boo এবং console.log('boo') insert হয়েছে এবং পর্যায়ক্রমে console.log('boo') এবং boo ফাংশন কল Pop হয়েছে । সর্বশেষ thoo ফাংশন কল এবং console.log('thoo') একই জিনিস অনুসরণ করে সর্বশেষ Call Stack থেকে ফাংশন foo বের হয়েছে । সবকিছু যা হবার তাই হইছে, কোন প্রবলেম নাই । যদি আমরা উপরের উদাহরণকে একটু পরিবর্তন করি এভাবে

const boo = () => console.log('boo')

const thoo = () => console.log('thoo')

const foo = () => {
  console.log('foo')
  setTimeout(boo, 0)
  thoo();
}

তাহলে এই কোডের আউটপুট আসবে


foo
thoo
undefined
boo

কেন এমন হয়? এর উত্তর হল Javascript Message QueueCall Stack যখন sequentially একটার পর একটা কাজ করতে থাকে এবং javascript Engine যদি দেখে এখানে WebApi কল প্রয়োজন আছে তখন তা ঐ কাজটাকে একটি Message Queue এর মধ্যে দিয়ে দেয়, যখন Call Stack এর সব গুলো কাজ Pop করা শেষ হয় তখন Message Queue থেকে একটার পর একটা event নিয়ে চেক করা হয় এর আউটপুট আসছে কিনা বা অন্য কিছু করতে হবে কিনা । এইখানে setTimeout পাওয়া মাত্র জাভাস্ক্রিপ্ট ইঞ্জিন তাকে মেসেজ কিউতে পুশ করে দেয় এবং যখন boo ফাংশনের console.log('boo') কে প্রিন্ট করে মেসেস কিউ চেক করতে যায় প্রথম টার্মে setTimeout তখনও কাজ শেষ না করার জন্য প্রথমে undefiend রিটান করছে এবং পরের টার্মে console.log('boo')। প্রথমের undefiend ব্রাউজার ভেদে নাও আসতে পারে ।

JavaScriptMessageQueue

Null, Undefined and NaN

Null, Undefiend and NaN জাভাস্ক্রিপ্টের বহুল ব্যবহৃত এবং নানা কারণে অনেক বেশি কনফিউজিং জিনিস। আমরা যদি তাদের টাইপ দেখি

typeof null; // object
typeof undefiend; // undefiend
typeof NaN; // number 

অর্থাৎ তারা সবাই আলাদা, কিন্তু নিজেদের মধ্যে কম্পারিজন যদি দেখি


null == NaN ; // false
null == undefined; // true
undefined == NaN; // false

null === undefined ; // false
!null === !undefined; // true

আবার তাদের যদি ভ্যালু এর সাথে কম্পারিজন করে দেখি


!null; // true
null == false; // false
null === false; // false
null == true; // false
null === true; // false

!undefined; // true;
undefined == false; // false
undefined === false; // false
undefined == true; // false
undefined === true; // false

!NaN; // true;
NaN == false; // false
NaN === false; // false
NaN == true; // false
NaN === true; // false

একটি null ভ্যালু রিপ্রেসেন্টস একটি রেফারেন্স যা সাধারণত ইচ্ছাকৃতভাবে একটি অস্তিত্বহীন বা অবৈধ অবজেক্ট বা ঠিকানার দিকে নির্দেশ করে। আর undefined গ্লোবাল প্রিমিটিভ টাইপ undefined কে নির্দেশ করে এর মানে হচ্ছে এমন কিছু যা এখনও নির্দিষ্ট করা হয়নি। NaN এর ফুল ফর্ম হচ্ছে Not a Number (যদিও তার টাইপ আবার number)। যদিও NaN এর ব্যাপারটা জাভাস্ক্রিপ্ট ইঞ্জিনের কাছে থেকে লজিকাল রেসপন্স পাওয়া যায়।
যখন নাম্বার না এমন কোন টাইপকে নাম্বার টাইপের কিছুর সাথে arithmetic কোন অপারেশন করার চেষ্টা করা হয় তখন জাভাস্ক্রিপ্ট ইঞ্জিন NaN রিটার্ন করে ।

"hello" / 12; 
// NaN
20 * "hi" 
// NaN