রহস্যময়ী জাভাস্ক্রিপ্টঃ {} + [] আবার শূন্য(0) কেনো?

আপনারা হয়তো অনেকসময় আবার এটাও খেয়াল করেছেন আমরা যদি এরকম খালি একটা {} এর সাথে খালি অ্যারে [] কে + অপারেটর দিয়ে যুক্ত করি তাহলে আবার সম্পূর্ন ভিন্ন একটা ফলাফল দেখায়ঃ

{} + []
// 0

আমরা জাস্ট আগের পর্বেই এটার আরেকটা ভ্যারিয়েন্ট দেখেছিলাম যেখানে খালি অ্যারে [] টা আগে ছিলো, আর খালি {} টা পরেঃ

[] + {}
// '[object Object]'

এই সমস্যাটি নোড জেএস এর REPL এ ট্রাই করলে ঠিক এরকম দেখতে পাবেন না, তাই গুগল ক্রোমের ডেভটুল বা অন্যকোনো ব্রাউজারের ডেভটুল ইউজ করার পরামর্শ থাকলো।

এখানে আসলে কি এমন ঘটনা ঘটলো যে আগেপিছে করাতে ফলাফল একদম সম্পূর্ণ উল্টিয়ে গেলো? হ্যাঁ, এখানে আসলে ঘটনাটাই বদলে গেছে, দেখতে একরকম হলেও এখানে আসলে ঘটছে সম্পূর্ন ভিন্ন ঘটনা। তার আগে আমাদের নতুন দুইটা জিনিসের সাথে একটু পরিচিত হতে হবে।

জাভাস্ক্রিপ্টে ব্লক(Block)

জাভাস্ক্রিপ্টে সাধারণত আমরা ব্লক ইউজ করে থাকি if, else, for, while লুপসহ বিভিন্ন জায়গায়। ব্লকের জন্য এই কার্লি ব্রেসের বা ব্র্যাকেট {} ইউজ করা হয়, ব্লক শুরু হয় ওপেনিং ব্র্যাকেট { দিয়া আর শেষ হয় ক্লোজিং } ব্র্যাকেট দিয়েঃ

for(...) { 
	// ব্লকের ভিতরে
}  

while(...) {
	// ব্লকের ভিতরে
}

if(...) {
	// if এর ব্লক 
} else if(...) {
	// else if এর ব্লক
} else {
	// else এর ব্লক
}

আপনি হয়তো জেনে থাকবেন এদের সাথে ছাড়াও আপনি খালি ব্লকও কিন্তু তৈরি করতে পারবেন এরকমভাবেঃ

{
	...
	// ব্লক এর ভিতরে
	...
}

এখন এই ব্লকটাই যদি আবার খালি হয় তাহলে দেখবেন যে এটা খালি অবজেক্ট {} এর মতোই দেখাচ্ছেঃ

{} 

এটা নিয়েই মূলত আমাদের কনফিউশনটা কাজ করে যে এটাকে আমরা কখন অবজেক্ট হিসেবে গণ্য করবো, আর কখন খালি ব্লক হিসেবে সেটা নিয়ে। এটা যদি এক্সপ্রেশন(Expression) কন্টেক্সটে এ থাকে তাহলে অবজেক্ট হিসেবে, আর স্টেটমেন্ট(Statement) কন্টেক্সট এ থাকলে ব্লক হিসেবে গণ্য হবে। যেমন if, else, for, while ইত্যাদির পরে স্টার্টিং ব্র্যাকেট { শুরু হলে সেটা ব্লকের শুরু হিসেবেই গণ্য হবে। এছাড়াও যদি একদম স্টেটমেন্ট এর শুরুতেই কোন এক্সপ্রেশন বা কোনকিছু ভিতরে ছাড়া যদি স্টার্টিং ব্র্যাকেট { শুরু হয়ে তাহলে সেটাকে জাভাস্ক্রিপ্ট ব্লকের স্টার্ট বলেই গণ্য করবে, এবং পরবর্তি ক্লোজিং ব্র্যাকেট } কে ব্লক শেষ বলে ধরে নিবে।

কিন্তু আবার যদি অন্য কোথায় থাকে যেমনঃ [] + {} তো সেক্ষেত্রে এখানে এক্সপ্রেশনের ভিতরে থাকায় ব্লক হিসেবে না বরং খালি অবজেক্ট {} হিসেবেই গণ্য হবে। কারণ এখানে শুরু হয়েছে খালি অ্যারে [] দিয়ে তারপর অ্যাডিশন অপারেটর + দিয়ে খালি {} কে যুক্ত করা হচ্ছে যেটা স্টেটমেন্ট হচ্ছে না, তাই ব্লকের সিনট্যাক্সের মধ্যে পড়ছে না।

তবে আপনারা যারা নোড জেএস এর REPL এ বা ক্রোমের ডেভেলপার কন্সোলে ট্রাই করতে যাবেন তখন কিছুটা ভিন্ন এবং প্রত্যাশিত ফলাফল দেখতে পারেন। ক্রোমের ডেভটুলে বা নোড জেএস এর REPL এ যদি খালি দুইটা ব্র্যাকেট {} এন্টার করান তাহলে এটাকে অবজেক্ট হিসেবেই গণ্য করা হবে। কারন এদের REPL এটাকে এক্সপ্রেশন হিসেবেই কাউন্ট করেঃ

{}
// {}

REPL মানে হচ্ছে Read, Evaluate, Print এবং Loop। এটা প্রোগ্রামিং ল্যাংগুয়েজের জন্য একটা এনভায়োরেনমেন্ট বা কন্সোল যেখান একটা সিঙ্গেল এক্সপ্রেশনকে ইনপুট হিসেবে নেওয়া হয় এবং সেটার ফলাফল দেখানো হয়। এসব REPL সাধারণত কুইকলি কোড টেস্ট করার জন্য ইউজ করা হয়। তবে এরা যেভাবে কোড এক্সকিউট করে সেটা আর কোন ফাইল/স্ক্রিপ্টের মধ্যে কোড এক্সকিউট করার মধ্যে তফাৎ থাকতে পারে। তবে বেশীরভাগ ক্ষেত্রেই এদেরকে ডেভেলপ বা কন্টিনিউয়াসলি ইম্প্রুভ করা হয়ে থাকে যথাসম্ভব কাছাকাছি ও নির্ভুল ফলাফল দেখানোর জন্য।

এটা হওয়ার মূল কারণ হচ্ছে এদের REPL একটু ডিফারেন্টলি কোড এক্সকিউট করে। একটা ফাইলে কোড করে জাভাস্ক্রিপ্ট রান করা আর এদের REPL এ রান করার মধ্যে কিছুটা পার্থক্য থাকতে পারে। এরা যখন শুধুমাত্র {} দেখে তখন এটাকে এক্সপ্রেশন হিসেবে খালি অবজেক্ট হিসেবেই ধরে নেয়। কিন্তু এটার পরে যদি কিছু অ্যাড করেন তখন এটা স্টেটমেন্ট হয়ে আবার ব্লক হিসেবেই গণ্য হবেঃ

{};
// undefined

{ let world = 'earth'; };
//undefined

{ 
	let world = 'earth'; 
}
//undefined

ইনফ্যাক্ট একেক জায়গার REPL একেকরকম আচরণ করতে পারে, এগুলো সবই উক্ত REPL এ কিভাবে কোড এক্সিকিউট হচ্ছে সেটার উপর নির্ভর করছে।

ইউনারি(Unary) প্লাস + অপারেটর

জাভাস্ক্রিপ্টসহ অন্যান্য প্রোগ্রামিং ল্যাংগুয়েজ এই + জিনিসটার অনেক কাজ থাকতে পারে। যেমন দুই অপারেন্ডের মাঝে বসলে অ্যাডিশন অপারেটর হিসেবে কাজ করে, আবার একটা অপারেন্ডের আগে পিছে বসলে ইউনারি অপারেটর হিসেবে কাজ করে, যেমনঃ ইউনারি প্লাস(+x), ইউনারি মাইনাস(-x), ইউনারি ইনক্রিমেন্ট(++x, x++), ইউনারি ডিক্রিমেন্ট(--x, x--) অপারেটর হিসেব কাজ করতে পারে। তবে আমরা এখানে আমাদের টপিক বুঝার জন্য জাস্ট ইউনারি প্লাস(+x) এর দিকেই ফোকাস করবো।

ইউনারি প্লাস + অপারেটর অপারেন্ডের আগে বসে অপারেন্ডকে নাম্বারে কনভার্ট করার চেষ্টা করবে যদি নাম্বার না হয়ে থাকেঃ

+ 10
// 1

+ '10'
// 10

দেখেন এখানে কিভাবে অপারেন্ডের আগে বসে সেটাকে নাম্বারে কনভার্ট করে ফেলছে! তবে এখানে অবশ্যই লক্ষণীয় বিষয় হচ্ছে আপনাকে এটা একটা অপারেন্ডের আগেই বসাতে হবে, অপারেন্ড দুইটা হলে কিন্তু সেই অ্যাডিশন অপারেটর হিসেবেই কাজ করা শুরু করবে। তো ইউনারি প্লাস + অপারেটর কিনা সেটা যাচাই করার আগে ভালো করে খেয়াল করতে হবে যে এটা কিভাবে ইউজ হচ্ছে, কোথায় বসেছে এবং আগে কিছু আছে কিনা।

ব্লক আর ইউনারি অপারেটর + সম্পর্কে ধারনা হয়ে গেলে এবার আপনি আমাদের আসল সমস্যাটা এবং সেটার এমন উইয়ার্ড ফলাফলের আসল কারণ খুব সহজেই খুঁজে বের করতে পারবেনঃ

{} + [] 
// 0

এখানে আসলে {} শুরুতেই থাকায় উপরের আলোচনা অনুযায়ী জাভাস্ক্রিপট ইঞ্জিন এটাকে ব্লক হিসেবেই গণ্য করে, তাই এটাকে মূলত খালি ব্লক {} স্টেটমেন্ট হিসেবে গণ্য করেছে যেটার আসলে কোন ফলাফল নেই। তারপর + খালি অ্যারে হওয়াতে এখানে + টা আসলে ইউনারি প্লাস + অপারেটর হিসেবে গণ্য করবে, আর এর পরের অপারেন্ড, আমাদের ক্ষেত্রে খালি অ্যারেটাকে [] নাম্বারে কনভার্ট করার চেষ্টা করবেঃ

Number([])
// 0

তো দিনশেষে এটাকেই ফলাফল হিসেবে দেখানো হয়ঃ

{} + []  
// 0

 + []
// 0

তারপর ব্লকের ভিতরে যাই থাকুক না কেন ব্লক থেকে যেহেতু কিছু রিটার্ন হয় না, তাই ফলাফল কোন পরিবর্তন দেখবেন নাঃ

{
    let name = 'Code with Zonayed';
} + []
// 0

তারপর আমরা যদি আবার কোন ট্রিক্স খাটাই যে জাভাস্ক্রিপ্ট ইঞ্জিন আর খালি {} কে ব্লক হিসেবে গণ্য না করে, ফোর্স এক্সপ্রেশনে নিয়ে যাই, তাহলে ঠিক আগের পর্বের মতই সব হবেঃ

// force expression
({} + [])
'[object Object]'

// force {} to be an object
Object({}) + []
// '[object Object]'

আর নোড জেএস এ একটু অন্যরকম আচরণ দেখতে পারেন যদিও ক্রোমের বা অন্যান্য মেজর কিছু ব্রাউজারের ডেভেলপার কন্সোলে {} + [] সমান 0 ই দেখাবে। বাট নোড জেএস এর REPL এ যদি {} + [] লিখেন, তাহলে দেখবেন এটা আউপুট দেখাচ্ছে '[object Object]’

{} + []
// '[object Object]'

নোড জেএসসহ আরো বেশ কিছু জায়গায় এমন আচরণ দেখা যেতে পারে, এটা সম্পূর্ণই নির্ভর করছে তাদের REPL এর কোড এক্সিকিউট করার প্রসেস এর সাথে যেটা আগে উল্লেখ করেছি। নোড জেএস এর REPL এখানে {} + [] কে এক্সপ্রেশন হিসেবেই গণ্য করছে, তাই এমন ফলাফল দেখতে পাচ্ছেন। এমন সময়ে সময়ে এটা পরিবর্তনও হতে পারে। খুব বেশী কমপ্লেক্স অথবা যেহেতু এই জিনিসটা পরিবর্তনশীল তাই এটা নিয়ে ডিটেইলস আলোচনা করাও সম্ভব নয়। তবে জাস্ট এটুকু মনে রাখবেন যে এই REPL গুলো যেভাবে কোড এক্সকিউট করে, একটা ফাইলে জাভাস্ক্রিপ্ট রাখলে সেগুলো সেভাবে করে না। তাই এরকম দুই একটা ডিফারেন্স চোখে পড়তে পারে মাঝেমধ্যে।

শেয়ার করুন

লেখাটি ভাল লাগলে সোশ্যাল মিডিয়ায় শেয়ার করুন। আপনার কলিগ, বন্ধু কিংবা প্রিয় কারও কাজে লাগতে পারে। জানেন তো, শেয়ারিং ইজ কেয়ারিং!

সাবস্ক্রিপশন সেন্টার

প্রতিদিন ওয়েবসাইটে আসা আপনার জন্য কষ্টকর হতে পারে। তাই যখনই আমি নতুন ব্লগ পোস্ট, সিরিজ, বই বা ভিডিও পাবলিশ করব,
তখনই তা আপনার ইমেইলে পেতে সাবস্ক্রাইব করুন। নো স্প্যামিং প্রমিজ!