২৪ জানুয়ারী, ২০২৩
পড়তে মিনিট লাগতে পারে

রহস্যময়ী জাভাস্ক্রিপ্টঃ {} + [] আবার শূন্য(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 গুলো যেভাবে কোড এক্সকিউট করে, একটা ফাইলে জাভাস্ক্রিপ্ট রাখলে সেগুলো সেভাবে করে না। তাই এরকম দুই একটা ডিফারেন্স চোখে পড়তে পারে মাঝেমধ্যে।

ট্যাগঃ
শেয়ার করুনঃ

ক্রমানুসারে এই জাভাস্ক্রিপ্ট সিরিজঃ

জাভাস্ক্রিপ্ট ব্যাসিক
জাভাস্ক্রিপ্টঃ কি, কেন, কখন? জাভাস্ক্রিপ্টঃ কোথায় এবং কিভাবে ইউজ করা হয়? জাভাস্ক্রিপ্টঃ ব্যাসিক ক্রোম ডেভেলপার কন্সোল জাভাস্ক্রিপ্টঃ ভ্যারিয়েবল এবং ডাটা টাইপ জাভাস্ক্রিপ্টঃ অপারেটর নিয়ে সবকিছু জাভাস্ক্রিপ্টঃ কন্ডিশনাল স্টেটমেন্ট নিয়ে সবকিছু জাভাস্ক্রিপ্টঃ লুপ নিয়ে সবকিছু জাভাস্ক্রিপ্টঃ অ্যারে নিয়ে সবকিছু জাভাস্ক্রিপ্টঃ ব্যাসিক অবজেক্ট জাভাস্ক্রিপ্টঃ ব্যাসিক ফাংশন জাভাস্ক্রিপ্টঃ স্টেটমেন্ট আর এক্সপ্রেশন জাভাস্ক্রিপ্টঃ ড্রাই প্রিন্সিপ্যাল জাভাস্ক্রিপ্টঃ নাকি ইকমাস্ক্রিপ্ট? জাভাস্ক্রিপ্টঃ বিহ্যাইন্ড দ্যা সীন জাভাস্ক্রিপ্টঃ হোইস্টিং(Hoisting) নিয়ে ধারণা জাভাস্ক্রিপ্টঃ স্কোপ(Scope) নিয়ে ধারণা জাভাস্ক্রিপ্টঃ ক্লোজারস(Closures) নিয়ে ধারণা জাভাস্ক্রিপ্টঃ ইফি, Immediately Invoked Function Expressions (IIFE)
জাভাস্ক্রিপ্ট অ্যাডভান্স
জাভাস্ক্রিপ্ট অ্যাডভান্সঃ ‘this’ কীওয়ার্ড জাভাস্ক্রিপ্ট অ্যাডভান্সঃ call(), bind() এবং apply() মেথড জাভাস্ক্রিপ্ট অ্যাডভান্সঃ অবজেক্ট ওরিয়েন্টেড জাভাস্ক্রিপ্ট জাভাস্ক্রিপ্ট অ্যাডভান্সঃ ফাংশন কন্সট্রাকটর(Constructor) এবং ‘new’ কীওয়ার্ড জাভাস্ক্রিপ্ট অ্যাডভান্সঃ ইনহেরিট্যান্স (Inheritance) জাভাস্ক্রিপ্ট অ্যাডভান্সঃ স্ট্রিক্ট (Strict) মোড
জাভাস্ক্রিপ্ট ইএস ৬(ES6)
জাভাস্ক্রিপ্ট ইএস ৬(ES6): নতুন কি আছে? জাভাস্ক্রিপ্ট ইএস ৬(ES6): let এবং const দিয়ে ভ্যারিয়েবল ডিক্লেয়ার করা জাভাস্ক্রিপ্ট ইএস ৬(ES6):ইফি (Immediately Invoked Function Expressions) জাভাস্ক্রিপ্ট ইএস ৬(ES6): টেমপ্লেট লিটারেল(Template Literal) ও স্ট্রিং মেথড জাভাস্ক্রিপ্ট ইএস ৬(ES6): অ্যারো (Arrow) ফাংশন ও লেক্সিক্যাল(Lexical) ‘this’ কীওয়ার্ড জাভাস্ক্রিপ্ট ইএস ৬(ES6): অ্যারে জাভাস্ক্রিপ্ট ইএস ৬(ES6): স্প্রেড(Spread) অপারেটর জাভাস্ক্রিপ্ট ইএস ৬(ES6): ডি-স্ট্রাকচারিং (Destructuring) জাভাস্ক্রিপ্ট ইএস ৬(ES6): রেস্ট(Rest) প্যারামিটার জাভাস্ক্রিপ্ট ইএস ৬(ES6): ডিফল্ট(Default) প্যারামিটার জাভাস্ক্রিপ্ট ইএস ৬(ES6): ম্যাপ (Map) জাভাস্ক্রিপ্ট ইএস ৬(ES6): ক্লাস (Class) জাভাস্ক্রিপ্ট ইএস ৬(ES6): ক্লাস ও ইনহেরিট্যান্স (Inheritance) জাভাস্ক্রিপ্ট ইএস ৬(ES6): সব ইনভারোমেন্ট এ ইএস৬
নিত্যদিনের জাভাস্ক্রিপ্ট
নিত্যদিনের জাভাস্ক্রিপ্টঃ ফার্স্ট ক্লাস ফাংশন ও হাইয়ার অর্ডার ফাংশন নিত্যদিনের জাভাস্ক্রিপ্টঃ map(), filter() ও reduce() নিত্যদিনের জাভাস্ক্রিপ্টঃ some() ও every() মেথড নিত্যদিনের জাভাস্ক্রিপ্টঃ অবজেক্ট এ লুপ চালানো নিত্যদিনের জাভাস্ক্রিপ্টঃ ডট নোটেশন এবং ব্র্যাকেট নোটেশন নিত্যদিনের জাভাস্ক্রিপ্টঃ ম্যাথ (Math) অবজেক্ট নিত্যদিনের জাভাস্ক্রিপ্টঃ ডেট (Date) অবজেক্ট নিত্যদিনের জাভাস্ক্রিপ্টঃ লজিক্যাল অপারেটর অর (।।) এবং অ্যান্ড (&&) নিত্যদিনের জাভাস্ক্রিপ্টঃ কন্ডিশনাল/টার্নারি (Ternary) অপারেটর নিত্যদিনের জাভাস্ক্রিপ্টঃ রেগুলার এক্সপ্রেশন (Regular Expression) নিত্যদিনের জাভাস্ক্রিপ্টঃ এরর হ্যান্ডলিং (Error Handling) নিত্যদিনের জাভাস্ক্রিপ্টঃ ডেভেলপার কন্সোল (Console) নিত্যদিনের জাভাস্ক্রিপ্টঃ অ্যাসিনক্রোনাস (Asynchronous) নিত্যদিনের জাভাস্ক্রিপ্টঃ কলব্যাক(Callback) ফাংশন নিত্যদিনের জাভাস্ক্রিপ্টঃ প্রমিস (Promise) নিত্যদিনের জাভাস্ক্রিপ্টঃ async এবং await
রহস্যময়ী জাভাস্ক্রিপ্ট
রহস্যময়ী জাভাস্ক্রিপ্টঃ type of NaN === “number” কেনো? রহস্যময়ী জাভাস্ক্রিপ্টঃ 9 + '1’ সমান ‘91’, আবার 91 - ‘1’ সমান 90 হচ্ছে কিভাবে? রহস্যময়ী জাভাস্ক্রিপ্টঃ true + true + true === 3, true - true === 0 হচ্ছে কিভাবে? রহস্যময়ী জাভাস্ক্রিপ্টঃ [] === 0 মিথ্যা(false) হলেও [] == 0 আবার সত্য(true) হয় কিভাবে? রহস্যময়ী জাভাস্ক্রিপ্টঃ [] === ‘’ মিথ্যা(false) হলেও [] == ‘’ আবার সত্য(true) হয় কিভাবে? রহস্যময়ী জাভাস্ক্রিপ্টঃ [] + [] খালি স্ট্রিং(’’) হচ্ছে কেনো? রহস্যময়ী জাভাস্ক্রিপ্টঃ [] + {} আবার কেনো '[object Object]’ দেখাচ্ছে? রহস্যময়ী জাভাস্ক্রিপ্টঃ {} + [] আবার শূন্য(0) কেনো? রহস্যময়ী জাভাস্ক্রিপ্টঃ [] + {} === {} + [] কিভাবে সত্য true হচ্ছে?