সরাসরি পর্বে যান
পর্ব ৫৬ · ৫৯-এর মধ্যে · মডিউল ৮

আউটপুট কী হবে? — JavaScript কোড পড়ার অনুশীলন

What Will This Print? — JavaScript Output-Prediction Practice

Taught in Bangla, this page offers 109 verified JavaScript output-prediction questions across modules one to seven: a dry-run method with trace tables, call stack and microtask and task columns, coercion, hoisting, closures, this, prototypes, generators, Promise and async/await ordering, DOM events and Node.js, plus how to tell SyntaxError, runtime error and environment-dependent answers apart.

এই পর্বে যা পাবেন

  • কাগজে-কলমে JavaScript কোড চালানোর (dry run) পদ্ধতি — scope তৈরির ধাপ, call stack আর event loop-এর লাইন একসাথে
  • coercion, hoisting, this আর Promise-এর ক্রম — যে নিয়মগুলো সবচেয়ে বেশি ভুল উত্তর তৈরি করে, এক জায়গায়
  • মডিউল ১ থেকে ৭ পর্যন্ত ১০৯টি প্রশ্ন — সহজ থেকে কঠিন, প্রতিটির পূর্ণ ব্যাখ্যা আর কোন পর্ব ঝালাবেন তার লিংক
  • SyntaxError, runtime error আর “পরিবেশ-নির্ভর” — তিনটি “অদ্ভুত” উত্তর আলাদা করে চেনা ও কারণসহ লেখা
  • AI-কে দিয়ে নতুন প্রশ্ন বানানো, আর তার উত্তর ব্রাউজার ও Node-এ চালিয়ে যাচাইয়ের নিয়ম

১ · মডিউল ৮ শুরু — পরীক্ষা ও ইন্টারভিউর প্রস্তুতি

অভিনন্দন! ৫৫টি পর্বে আপনি JavaScript-এর মান আর টাইপ থেকে শুরু করে closure, prototype, class, generator, event loop, Promise, DOM, Web Worker, নিরাপত্তা, module, TypeScript, Node.js, Express, ডেটাবেস আর একটি পূর্ণ ক্যাপস্টোন প্রজেক্ট পর্যন্ত এসেছেন। এবার শেখা জিনিস এমনভাবে ঝালিয়ে নেওয়ার পালা, যাতে পরীক্ষার হলে বা ইন্টারভিউর টেবিলে চাপের মধ্যেও ঠিক উত্তর মাথায় আসে।

এই শেষ মডিউলে নতুন কোনো বিষয় নেই। আছে চারটি অনুশীলনের পর্ব, প্রতিটি আলাদা ধরনের প্রশ্নের জন্য:

পর্ব ৫৬ · আউটপুট কী হবে?

এই পর্ব। ছোট কোড দেখে বলতে হবে কী ছাপা হবে, কোন ক্রমে ছাপা হবে, নাকি কোনো error উঠবে। লিখিত পরীক্ষা, MCQ আর ফ্রন্ট-এন্ড/ব্যাক-এন্ড ইন্টারভিউ — তিন জায়গাতেই আসে।

কৌশল: trace table

পর্ব ৫৭ · MCQ প্রশ্নব্যাংক

ধারণা, সংজ্ঞা আর তথ্যের দ্রুত প্রশ্ন — টাইপ, scope, event loop, DOM, HTTP, module. চাকরির প্রাথমিক বাছাই পরীক্ষায় এ ধরনের প্রশ্ন প্রায়ই আসে।

কৌশল: সময় ধরে চর্চা

পর্ব ৫৮ · লিখিত ও ভাইভা

“পার্থক্য লিখুন”, “উদাহরণসহ ব্যাখ্যা করুন”, “function লিখুন” আর মুখোমুখি প্রশ্নের উত্তর কীভাবে সাজাবেন।

কৌশল: উত্তরের কাঠামো

পর্ব ৫৯ · কোডিং ইন্টারভিউ

সমস্যা শুনে প্রশ্ন করা, পরিকল্পনা বলা, JavaScript-এ কোড লেখা, টেস্ট করা — সফটওয়্যার কোম্পানির ইন্টারভিউর পুরো ধারা।

কৌশল: জোরে ভাবা

“আউটপুট কী হবে?” প্রশ্ন এত বেশি আসে কেন

প্রশ্নকর্তা আসলে জানতে চান: আপনি কি JavaScript engine-এর মতো করে কোড পড়তে পারেন? JavaScript-এ অনেক কাজ চোখের আড়ালে ঘটে — কোড চলার আগেই var আর function declaration তৈরি হয়ে যায় (hoisting), + নিজে থেকে টাইপ বদলায় (coercion), this ঠিক হয় ডাকার ধরন দেখে, আর then-এর callback চলে বাকি সব sync কোড শেষ হওয়ার পরে। যিনি এই অদৃশ্য কাজগুলো দেখতে পান, তিনিই বাগ খুঁজে পান, AI-এর লেখা কোড যাচাই করতে পারেন, আর code review-তে কাজের মন্তব্য দিতে পারেন।

আপনার লক্ষ্যশুরু করুনতারপর
বিশ্ববিদ্যালয়ের লিখিত পরীক্ষাপর্ব ৫৬ (আউটপুট) ও ৫৮ (লিখিত)পর্ব ৫৭ দিয়ে দ্রুত ঝালাই
সরকারি বা ব্যাংকের IT/প্রোগ্রামার পদের পরীক্ষাপর্ব ৫৭ (MCQ) ও ৫৬পর্ব ৫৮-এর লিখিত ও ভাইভা অংশ
ফ্রন্ট-এন্ড, ব্যাক-এন্ড বা full-stack ইন্টারভিউপর্ব ৫৯ (কোডিং)এই পর্বের ৭ ও ৮ নম্বর ভাগ — event loop আর DOM event-এর ক্রমের প্রশ্ন টেকনিক্যাল রাউন্ডে প্রায়ই আসে
শুধু নিজের শেখা যাচাইএই পর্ব, প্রথম থেকে শেষযে ভাগে বেশি ভুল, সেই পর্বে ফিরে যান

এই পাতা কীভাবে ব্যবহার করবেন

  1. প্রথম দফা — অপশন ঢেকে রাখুন। প্রতিটি কোড পড়ে খাতায় নিজের উত্তর লিখুন, তারপর অপশনে চাপুন। প্রথম চেষ্টায় কতগুলো ঠিক হলো, ৮ নম্বর ভাগের শেষে স্কোর দেখাবে।
  2. দ্বিতীয় দফা — ভুলগুলোর কারণ খুঁজুন। প্রতিটি ভুল অপশনের ব্যাখ্যা পড়ুন। প্রতিটি ভাগের শুরুতে লেখা আছে কোন পর্বগুলো ঝালাতে হবে।
  3. তৃতীয় দফা — নিজে চালান। প্রতিটি ভাগের “নিজে চালিয়ে দেখুন” কোড চালান। একটি লাইন বদলে আগে ফল অনুমান করুন, তারপর চালিয়ে মেলান। ব্রাউজারের DevTools console বা node-ও একই কাজে লাগে।
এই পাতার ধরে নেওয়া পরিবেশ

আলাদা কিছু বলা না থাকলে প্রতিটি কোড একটি আধুনিক ব্রাউজারে (Chrome বা Edge) একটি নতুন, আলাদা classic script হিসেবে চলছে — "use strict" নেই, module নয়। যেখানে strict mode, ES module বা Node.js দরকার, প্রশ্নেই তা লেখা আছে; Node.js মানে Node 20, CommonJS ফাইল। আউটপুট দেখানো হয়েছে এই পাতার রানারের মতো, Node-এর ধরনে: [ 1, 2 ], { x: 1 }, array-র ভেতরের string একক উদ্ধৃতিতে 'a'. ব্রাউজারের DevTools একই মান একটু অন্যভাবে দেখায় (যেমন (2) [1, 2]), কিন্তু মান একই। Error-এর বার্তা V8-এর (Chrome, Edge, Node); Firefox-এর SpiderMonkey একই error type দেয়, কিন্তু বার্তার ভাষা আলাদা। অপশনে একাধিক লাইনের আউটপুট “ · ” চিহ্ন দিয়ে আলাদা করা।

C/C++/Python/Java জানলে

C ও C++-এ অনেক আউটপুট প্রশ্নের উত্তর “undefined behavior”; JavaScript-এ তা নেই — ভাষার নিয়ম (ECMAScript) প্রায় সবকিছু নির্দিষ্ট করে, তাই i++ + ++i-এরও একটিই উত্তর। Java-র মতো compile-এর সময় টাইপ যাচাই নেই, তাই Java-তে যা compile error, JavaScript-এ তা প্রায়ই চুপচাপ একটি অদ্ভুত মান ("5" - 2 হলো 3) বা চলার সময়ের TypeError. Python থেকে এলে সাবধান: Python-এ "1" + 2 TypeError, JavaScript-এ "12"; আর Python-এর asyncio-র মতো একটি event loop থাকলেও JavaScript-এ সেটি সবসময় চলছে। তুলনার জন্য দেখুন Java-র আউটপুট প্রশ্ন, Python-এর আউটপুট প্রশ্ন ও C-এর আউটপুট প্রশ্ন।

পরীক্ষা ও ইন্টারভিউতে এ ধরনের প্রশ্ন সাধারণত এই চেহারায় আসে:

  • “নিচের কোডের আউটপুট লিখুন” — খাতায় trace table এঁকে দেখান; শুধু উত্তর নয়, ধাপগুলোও নম্বর পায়।
  • “console.log-গুলো কোন ক্রমে ছাপা হবে?” — sync, microtask আর task আলাদা কলামে লিখে উত্তর দিন; কোনটি কেন আগে, এক লাইনে কারণ বলুন।
  • MCQ-তে চারটি আউটপুট থেকে বেছে নেওয়া — একটি অপশনে প্রায়ই “TypeError” বা “ReferenceError” থাকে; আগে নিজে হিসাব করুন, তারপর অপশন দেখুন।
  • ইন্টারভিউতে “এই কোডে কী সমস্যা?” — var দিয়ে লুপে timer, হারানো this, forEach-এর ভেতরে await, shallow copy — এগুলো আগে বলুন; এটাই অভিজ্ঞতার লক্ষণ।

২ · JavaScript কোড মাথায় চালানোর পদ্ধতি

আউটপুট বলার একটাই নির্ভরযোগ্য উপায়: engine যেভাবে চালায়, ঠিক সেভাবে এক লাইন এক লাইন করে চালানো, আর প্রতিটি পরিবর্তন লিখে রাখা। একে বলে dry run, আর যে টেবিলে লেখেন সেটি trace table. JavaScript-এ টেবিলে চারটি জিনিস রাখতে হয়: ভ্যারিয়েবলের মান (TDZ-সহ), কোন function কাকে ডেকেছে (call stack), this কী, আর কোন callback কোন লাইনে অপেক্ষা করছে।

একটি বিয়েবাড়ির রান্নাঘরের কথা ভাবুন। রান্না শুরুর আগে বাবুর্চি তালিকা দেখে সব হাঁড়ি সাজিয়ে রাখেন — কোনোটায় এখনো কিছু নেই (var → undefined), কোনোটা তালাবদ্ধ, সময় হলে খুলবে (let/const → TDZ), আর মসলার বাটা আগেই তৈরি (function declaration). তারপর রান্না চলে একটির পর একটি। মাঝে কেউ “চা দাও” বললে সেটি লিখে রাখা হয় — হাতের তরকারি শেষ না করে বাবুর্চি চা বানান না (task). কিন্তু তরকারি নামানোর ঠিক পরে লবণ চেখে দেখা — সেটি পরের কাজের আগেই (microtask).

JavaScript-এর trace table-এর ছয়টি নিয়ম

  1. আগে দেখুন কোডটি parse হবে কি না। একই scope-এ একই নামে দুটি let, ভুল জায়গায় await, ভাঙা বন্ধনী — এগুলো early error: পুরো script-এর একটি লাইনও চলে না, প্রথম console.log-ও না।
  2. প্রতিটি scope দুই ধাপে চলে। প্রথম ধাপে (creation) engine সব ঘোষণা খুঁজে নেয়: var পায় undefined, function declaration পায় পুরো function, let/const/class থাকে TDZ-এ। দ্বিতীয় ধাপে কোড ওপর থেকে নিচে চলে।
  3. প্রতিটি এক্সপ্রেশন বাঁ থেকে ডানে। Operand আর function-এর argument বাঁ থেকে ডানে মূল্যায়ন হয়; precedence শুধু বলে কোন অপারেটর কোন operand নেবে। প্রতিটি +-এ জিজ্ঞেস করুন: একটি দিক কি string? হলে জোড়া, নইলে সংখ্যায় যোগ।
  4. প্রতিটি function ডাকে this লিখুন। new দিয়ে? → নতুন object. call/apply/bind? → দেওয়া object. obj.method()? → obj. খালি fn()? → strict mode-এ undefined, sloppy-তে global object. Arrow function? → নিজের this নেই, বাইরের scope-এরটা।
  5. Async কোডে তিনটি কলাম: আউটপুট, microtask (then/catch/finally, await-এর পরের অংশ, queueMicrotask) আর task (setTimeout, event). Script শেষে microtask কলাম পুরো খালি করুন, তারপর একটি task, আবার সব microtask.
  6. Error উঠলে থামুন। try না থাকলে বর্তমান script বা callback সেখানেই থামে — আগে যা ছাপা হয়েছে থাকে, পরের লাইন চলে না। কিন্তু আগে নিবন্ধিত timer বা Promise-এর callback পরে ঠিকই চলবে।

নিচের ডায়াগ্রামে দ্বিতীয় নিয়মটি একটি পূর্ণ প্রোগ্রামে চালানো হয়েছে — পরীক্ষায় সবচেয়ে বেশি আসা “hoisting আর TDZ” প্রশ্ন, তিন রকম ঘোষণা একসাথে। “পরে” চাপার আগে নিজে পুরো আউটপুট লিখে নিন — লাইন ২-এ later-এর টাইপ কী আসবে?

1console.log(typeof greet, typeof later);2console.log(v);3var v = "var";4function greet() { return "hi " + v; }5var later = () => "later";6console.log(greet());7try { console.log(k); }8catch (e) { console.log(e.name); }9let k = "let";10console.log(k, later());
Trace table
v
greet
later
k
  1. Creation ধাপ — কোনো লাইন চলার আগে। Engine পুরো script দেখে ঘোষণা জোগাড় করে। var v আর var later পায় undefined; function greet পুরোপুরি তৈরি; let k তৈরি কিন্তু TDZ-এ — ছুঁলেই error. মান বসানো (= "var") এখনো হয়নি।
  2. লাইন ১. greet আগেই পুরো function, তাই function. later একটি var, তাতে arrow function বসবে লাইন ৫-এ — এখন undefined. ফাঁদ: “function expression-ও hoist হয়” — শুধু নামটি hoist হয়, মান নয়।
  3. লাইন ২. v আছে, কিন্তু মান এখনো বসেনি: undefined. ReferenceError নয় — এটাই var-এর hoisting.
  4. লাইন ৩–৫. এবার মান বসানো: v = "var", later = arrow function. লাইন ৪-এর declaration আগেই কাজ সেরেছে, এখানে নতুন কিছু ঘটে না।
  5. লাইন ৬. greet() চলে, ভেতরে v এখন "var": hi var.
  6. লাইন ৭–৮. k এখনো TDZ-এ, কারণ লাইন ৯ চলেনি। ছুঁতেই ReferenceError: Cannot access 'k' before initialization. try থাকায় প্রোগ্রাম থামেনি, catch ছাপল e.name.
  7. লাইন ৯–১০. let k = "let" চলতেই TDZ শেষ। ছাপা হয় let later. পুরো আউটপুট পাঁচ লাইন। নিয়মটি এক লাইনে: var → undefined, function declaration → পুরো, let/const → তালাবদ্ধ।

নিচের কোডটি হুবহু ডায়াগ্রামের প্রোগ্রাম। চালানোর আগে একটি পরিবর্তনের ফল অনুমান করুন: লাইন ১০-এর পরে const later2 = 1; লিখে লাইন ১-এ typeof later2 যোগ করলে কী হবে? (উত্তর: typeof-ও TDZ পেরোতে পারে না — ReferenceError, আর লাইন ১ থেকেই প্রোগ্রাম থামবে।)

console.log(typeof greet, typeof later);
console.log(v);
var v = "var";
function greet() { return "hi " + v; }
var later = () => "later";
console.log(greet());
try { console.log(k); }
catch (e) { console.log(e.name); }
let k = "let";
console.log(k, later());

আউটপুট পাঁচ লাইন: function undefined, undefined, hi var, ReferenceError, let later. এখানে একটি সূক্ষ্মতা আছে: অঘোষিত নামে typeof নিরাপদ (typeof notDeclared হলো "undefined"), কিন্তু TDZ-এ থাকা নামে নয় — engine জানে নামটি আছে, শুধু এখনো খোলেনি। ঝালিয়ে নিন: পর্ব ০৭ · let, const, var — scope, hoisting আর TDZ, পর্ব ০৫ · JavaScript কীভাবে চলে, পর্ব ১৩ · function.

চার রকম উত্তর হতে পারে

পরীক্ষার্থীরা ধরে নেন প্রতিটি কোড একটি নির্দিষ্ট লেখা ছাপবে। বাস্তবে উত্তর চার রকম, আর ভালো প্রশ্নকর্তা ঠিক এটাই যাচাই করেন।

✅ নির্দিষ্ট আউটপুট

ECMAScript-এর নিয়ম ফল পুরোপুরি ঠিক করে দেয় — যেকোনো আধুনিক engine-এ একই লেখা, একই ক্রম। বেশিরভাগ প্রশ্ন এই ধরনের, async ক্রমের প্রশ্নও।

যেমন: "3" - 1 হলো 2

⛔ SyntaxError — কিছুই চলে না

Parse করার সময়েই ধরা পড়ে (early error), তাই script-এর একটি লাইনও চলে না — প্রথম লাইনের console.log-ও না। কারণটাও বলুন: একই নামে দুবার let, function-এর বাইরে return, ভুল জায়গায় await.

যেমন: let x; let x;

⚠️ কিছু আউটপুট, তারপর runtime error

চলতে চলতে একটি লাইনে থামে। আগের ছাপা থাকে, পরেরটা আসে না। Error-এর ঠিক নাম লিখুন: TypeError, ReferenceError, RangeError…

যেমন: null.name

🔀 পরিবেশ-নির্ভর বা নিশ্চয়তাহীন

Strict বনাম sloppy mode, ব্রাউজার বনাম Node, CommonJS বনাম ES module — এগুলো this আর কিছু ক্রম বদলায়। আর Node-এর মূল script-এ setTimeout(fn, 0) বনাম setImmediate-এর ক্রম নিশ্চিতই নয়। “নির্ভর করে, কারণ…” লেখাই সঠিক উত্তর।

যেমন: খালি fn()-এ this

(মাঝারি) একটি classic script-এ এই চার লাইন আছে। কী ছাপা হবে?

console.log("শুরু");
let x = 1;
let x = 2;
console.log(x);
  • শুরু, তারপর SyntaxError
  • শুরু · 2
  • কিছুই ছাপা হয় না — SyntaxError: Identifier 'x' has already been declared
  • শুরু, তারপর ReferenceError

(মাঝারি) একই function দুই পরিবেশে: (ক) ব্রাউজারের classic script, "use strict" ছাড়া; (খ) একই কোড, কিন্তু ফাইলের প্রথম লাইনে "use strict";. দুই ক্ষেত্রে কী ছাপা হবে?

function who() {
  return this === undefined ? "undefined" : typeof this;
}
console.log(who());
  • object · object
  • object · undefined
  • undefined · object
  • function · undefined

৩ · ভিত্তি — মান, টাইপ, অপারেটর, string আর number

এই ভাগের কোড এক-দুই লাইনের, কিন্তু ফাঁদ গভীর। প্রতিটি এক্সপ্রেশনে তিনটি প্রশ্ন করুন: কোন টাইপ কোন টাইপে বদলাচ্ছে (type conversion)? + কি যোগ, নাকি string জোড়া? আর তুলনাটি == (টাইপ বদলে তুলনা) নাকি === (টাইপসহ তুলনা)? সংখ্যার প্রশ্নে মনে রাখুন, JavaScript-এর সব সাধারণ সংখ্যা একটিই টাইপ — ৬৪-বিটের floating-point (IEEE 754).

(সহজ) চারটি typeof — কী ছাপা হবে?

console.log(typeof null, typeof [], typeof function () {}, typeof undefined);
  • null array function undefined
  • object object function undefined
  • object object object undefined
  • undefined object function object

(সহজ) + বনাম অন্য অপারেটর — কী ছাপা হবে?

console.log(1 + "2", "3" - 1, "3" * "4", true + 1);
  • 3 2 12 2
  • 12 NaN 12 2
  • 12 2 12 true1
  • 12 2 12 2

(সহজ) বাঁ থেকে ডানে যোগ আর জোড়া — কী ছাপা হবে?

console.log(1 + 2 + "3", "1" + 2 + 3);
  • 33 123
  • 123 123
  • 33 15
  • 6 6

(মাঝারি) null, undefined আর NaN-এর তুলনা — কী ছাপা হবে?

console.log(null == 0, null >= 0, undefined == null, NaN == NaN);
  • true true true false
  • false false true false
  • false true true false
  • false true true true

(মাঝারি) array আর object-এ + — কী ছাপা হবে?

console.log(JSON.stringify([[] + [], [] + {}, [1, 2] + [3]]));
  • [[],{},[1,2,3]]
  • ["","[object Object]","1,23"]
  • [0,"[object Object]","1,23"]
  • ["","[object Object]","1,2,3"]

(সহজ) || বনাম ?? — কী ছাপা হবে?

console.log(0 || 5, 0 ?? 5, null ?? 5, undefined || null);
  • 5 0 5 null
  • 5 5 5 null
  • true 0 5 false
  • 5 0 5 undefined

(মাঝারি) optional chaining — কী ছাপা হবে?

const u = { profile: null };
console.log(u.profile?.name, u.settings?.theme ?? "light");
console.log(u.profile.name);
  • undefined light · undefined
  • প্রথম লাইনেই TypeError, কিছুই ছাপা হয় না
  • undefined null · TypeError
  • undefined light, তারপর TypeError: Cannot read properties of null (reading 'name')

(কঠিন) ঋণাত্মক শূন্য — কী ছাপা হবে?

const z = -0;
console.log(z === 0, Object.is(z, 0), 1 / z, String(z));
  • false false -Infinity -0
  • true false Infinity 0
  • true false -Infinity 0
  • true true -Infinity -0

(সহজ) দশমিক যোগ — কী ছাপা হবে?

console.log(0.1 + 0.2 === 0.3, (0.1 + 0.2).toFixed(2));
  • true 0.30
  • false 0.30
  • false 0.30000000000000004
  • false 0.3

(কঠিন) গোল করার তিনটি উপায় — কী ছাপা হবে?

console.log((1.005).toFixed(2), (2.5).toFixed(0), Math.round(-2.5));
  • 1.00 3 -2
  • 1.01 3 -3
  • 1.00 2 -2
  • 1.01 3 -2

(মাঝারি) parseInt বনাম Number — কী ছাপা হবে?

console.log(parseInt("08"), parseInt("12px"), parseInt("px12"), Number("12px"), parseInt(0.0000005));
  • 0 12 NaN NaN 0
  • 8 12 NaN 12 0
  • 8 12 NaN NaN 5
  • 8 12 12 NaN 0

(মাঝারি) BigInt — কী ছাপা হবে?

console.log(10n / 3n, typeof 10n);
console.log(10n + 1);
  • 3.3333333333333335 bigint · 11
  • 3n bigint · 11n
  • 3n number · TypeError
  • 3n bigint, তারপর TypeError: Cannot mix BigInt and other types, use explicit conversions

(মাঝারি) বাংলা আর emoji-র দৈর্ঘ্য — কী ছাপা হবে?

const s = "বাংলা";
console.log(s.length, "😀".length, [..."😀"].length);
  • 3 1 1
  • 5 2 1
  • 5 2 2
  • 15 4 1

(মাঝারি) slice, substring আর at — কী ছাপা হবে?

const t = "JavaScript";
console.log(t.slice(-6), t.substring(4, 0), t.at(-1));
  • Script Java t
  • Script (খালি) t
  • JavaSc Java t
  • Script Java undefined

(কঠিন) নিরাপদ পূর্ণসংখ্যার সীমা — কী ছাপা হবে?

console.log(2 ** 53 === 2 ** 53 + 1, 9007199254740993);
  • false 9007199254740993
  • true Infinity
  • true 9007199254740992
  • true 9007199254740993

(মাঝারি) NaN চেনা — কী ছাপা হবে?

console.log(typeof NaN, NaN === NaN, Number.isNaN("abc"), isNaN("abc"));
  • NaN false false true
  • number false true true
  • number true false true
  • number false false true

(মাঝারি) একটি বিখ্যাত ধাঁধা — কী ছাপা হবে?

console.log("b" + "a" + +"a" + "a");
  • baaa
  • baNaNa
  • SyntaxError
  • NaN

(মাঝারি) একই এক্সপ্রেশনে দুটি increment — কী ছাপা হবে?

let i = 1;
const r = i++ + ++i;
console.log(r, i);
  • 4 3
  • 5 3
  • 4 2
  • ফল নির্দিষ্ট নয় (engine-ভেদে আলাদা)

(সহজ) তুলনার শিকল — কী ছাপা হবে?

console.log(1 < 2 < 3, 3 > 2 > 1);
  • true true
  • false false
  • true false
  • TypeError

(মাঝারি) বাংলা অঙ্ক — কী ছাপা হবে?

console.log("৫" * 2, "5" * 2, Number("১২"));
  • 10 10 12
  • ৫৫ 10 ১২
  • NaN NaN NaN
  • NaN 10 NaN

নিজে চালিয়ে দেখুন — coercion আর সংখ্যা

এই ভাগের অনেকগুলো প্রশ্ন এক জায়গায়। চালানোর আগে প্রতিটি লাইনের উত্তর খাতায় লিখুন; তারপর একটি করে মান বদলে দেখুন — যেমন 0 ?? "ডিফল্ট"-এ 0-এর জায়গায় "" দিলে কী আসে?

console.log(1 + "2", "3" - 1, true + 1, [] + {});
console.log(null == 0, null >= 0, undefined == null, NaN === NaN);
console.log(0 || "ডিফল্ট", 0 ?? "ডিফল্ট", "" || null || "শেষ");
console.log(0.1 + 0.2, (1.005).toFixed(2), Math.round(-2.5));
console.log(parseInt("12px"), Number("12px"), Number(""), Number(null), Number(undefined));
console.log(Object.is(-0, 0), 1 / -0, 2 ** 53 + 1);
let i = 1;
console.log(i++ + ++i, i);
try {
  console.log(10n + 1);
} catch (e) {
  console.log(e.name + ": " + e.message);
}

আটটি লাইন: 12 2 2 [object Object], false true true false, ডিফল্ট 0 শেষ, 0.30000000000000004 1.00 -2, 12 NaN 0 0 NaN, false -Infinity 9007199254740992, 4 3, আর TypeError: Cannot mix BigInt and other types, use explicit conversions. পঞ্চম লাইনটি আলাদা করে মনে রাখুন: খালি string আর null সংখ্যায় 0, কিন্তু undefined সংখ্যায় NaN — তাই ফর্মের খালি ঘর Number() দিয়ে বদলালে 0 আসে, NaN নয়; যাচাই আলাদা করে করতে হয়। তৃতীয় লাইনে "" || null || "শেষ": প্রথম দুটি falsy, তাই শেষেরটা।

নিজে চালিয়ে দেখুন — string আর বাংলা Unicode

বাংলা লেখা নিয়ে কাজ করলে length আর চোখে দেখা অক্ষরের সংখ্যা প্রায়ই মেলে না। চালানোর আগে অনুমান করুন: বাংলাদেশের পতাকার emoji-র length কত?

const word = "বাংলা";
const seg = new Intl.Segmenter("bn", { granularity: "grapheme" });
console.log(word.length, [...word].length, [...seg.segment(word)].length);
console.log([...seg.segment(word)].map((s) => s.segment).join(" | "));
const smile = "😀";
console.log(smile.length, [...smile].length, smile.codePointAt(0).toString(16));
const flag = "🇧🇩";
console.log(flag.length, [...flag].length, [...seg.segment(flag)].length);
const lang = "JavaScript";
console.log(lang.slice(-6), lang.substring(4, 0), lang.at(-1), lang.slice(4, 0) === "");
console.log("৫" * 2, "5" * 2, `${1 + 1}টি`);
console.log("é" === "é", "é".normalize() === "é".normalize());

আউটপুট: 5 5 2, বাং | লা, 2 1 1f600, 4 2 1, Script Java t true, NaN 10 2টি, false true. “বাংলা”-য় পাঁচটি code point, কিন্তু চোখে দেখা অক্ষরগুচ্ছ (grapheme cluster) দুটি — “বাং” আর “লা”। পতাকা দুটি “regional indicator” অক্ষর (B আর D-এর প্রতীক), প্রতিটি surrogate pair: length 4, code point 2, grapheme 1. শেষ লাইনে একই দেখতে “é” দুইভাবে লেখা যায় — একটি অক্ষর, অথবা e আর একটি যুক্ত-চিহ্ন; normalize() করলে তবেই সমান। নামের তুলনা বা খোঁজায় এটা জরুরি। Grapheme ভাঙার নিয়ম Unicode সংস্করণের সাথে একটু বদলায়, তাই খুব পুরোনো ব্রাউজারে সংখ্যা আলাদা হতে পারে।

৪ · নিয়ন্ত্রণ, scope আর function

এই ভাগে প্রতিটি প্রশ্নের আগে নিজেকে জিজ্ঞেস করুন: নামটি কোন scope-এ ঘোষিত, আর লাইনটি চলার মুহূর্তে সেটি কোন অবস্থায় — undefined, TDZ, নাকি মান বসানো? Function-এর প্রশ্নে চারটি জিনিস দেখুন: default parameter কখন বসে, rest parameter কী জমায়, arrow function-এ { মানে block নাকি object, আর return-এর পরে লাইন ভাঙলে কী হয় (ASI).

ঝালিয়ে নিন: পর্ব ০৫ · JavaScript কীভাবে চলে, পর্ব ০৭ · let, const, var, পর্ব ১১ · if, switch আর ternary, পর্ব ১২ · লুপ, পর্ব ১৩ · function. মডিউল ১-এর পর্ব ০৪ · প্রথম প্রোগ্রাম-ও একবার দেখে নিন — console.log-এর একাধিক argument space দিয়ে জোড়া লাগে।

(সহজ) ঘোষণার আগে var — কী ছাপা হবে?

console.log(a);
var a = 5;
console.log(a);
  • ReferenceError
  • undefined · 5
  • 5 · 5
  • null · 5

(মাঝারি) block-এর ভেতরে একই নাম — কী ছাপা হবে?

let b = 1;
{
  console.log(b);
  let b = 2;
}
  • 1
  • undefined
  • SyntaxError: Identifier 'b' has already been declared
  • ReferenceError: Cannot access 'b' before initialization

(মাঝারি) declaration বনাম expression — কী ছাপা হবে?

console.log(typeof f, typeof g);
function f() {}
var g = function () {};
  • function undefined
  • function function
  • undefined undefined
  • ReferenceError

(সহজ) break ছাড়া switch — কী ছাপা হবে?

const x = 2;
switch (x) {
  case 1: console.log("এক");
  case 2: console.log("দুই");
  case 3: console.log("তিন"); break;
  default: console.log("অন্য");
}
  • দুই
  • এক · দুই · তিন
  • দুই · তিন
  • দুই · তিন · অন্য

(মাঝারি) switch-এর তুলনা — কী ছাপা হবে?

switch ("1") {
  case 1: console.log("সংখ্যা ১"); break;
  default: console.log("default");
}
  • সংখ্যা ১
  • default
  • TypeError
  • কিছুই ছাপা হয় না

(মাঝারি) array-তে for…in আর for…of — কী ছাপা হবে?

const arr = [10, 20];
arr.extra = "x";
for (const k in arr) console.log(k);
for (const v of arr) console.log(v);
  • 10 · 20 · 0 · 1
  • 0 · 1 · 10 · 20
  • 0 · 1 · extra · 10 · 20 · x
  • 0 · 1 · extra · 10 · 20

(সহজ) const object — কী ছাপা হবে?

const o = { n: 1 };
o.n = 2;
console.log(o.n);
o = {};
  • 2, তারপর TypeError: Assignment to constant variable.
  • o.n = 2 লাইনেই TypeError, কিছুই ছাপা হয় না
  • কিছুই ছাপা হয় না — SyntaxError
  • 2, শেষ লাইনটি চুপচাপ উপেক্ষিত

(মাঝারি) default parameter কখন বসে — কী ছাপা হবে?

function f(a, b = a * 2) { return [a, b]; }
console.log(JSON.stringify([f(3), f(3, undefined), f(3, null)]));
  • [[3,6],[3,null],[3,null]]
  • [[3,6],[3,6],[3,6]]
  • [[3,6],[3,6],[3,null]]
  • ReferenceError: a is not defined

(কঠিন) function-এর length আর arguments — কী ছাপা হবে?

function g(a, b = 2, ...rest) {}
function h(a, b) { return arguments.length; }
console.log(g.length, h(1, 2, 3), h());
  • 3 2 2
  • 1 3 0
  • 1 2 2
  • 2 3 0

(মাঝারি) arrow function থেকে object — কী ছাপা হবে?

const mk = () => { name: "রিনা" };
console.log(mk());
  • undefined
  • { name: 'রিনা' }
  • SyntaxError
  • রিনা

(মাঝারি) return-এর পরে নতুন লাইন — কী ছাপা হবে?

function get() {
  return
  { ok: true };
}
console.log(get());
  • { ok: true }
  • SyntaxError
  • null
  • undefined

(কঠিন) function-এর ভেতরে একই নামের var — কী ছাপা হবে?

var n = 1;
function show() {
  console.log(n);
  var n = 2;
}
show();
  • 1
  • 2
  • undefined
  • ReferenceError

(মাঝারি) ঘোষণা ছাড়া মান বসানো — ব্রাউজারের classic script, "use strict" নেই। কী ছাপা হবে?

function setY() { y = 10; }
setY();
console.log(y, window.y);
  • ReferenceError: y is not defined
  • 10 10
  • undefined undefined
  • 10 undefined

(সহজ) continue আর break — কী ছাপা হবে?

for (let i = 0; i < 5; i++) {
  if (i === 1) continue;
  if (i === 3) break;
  console.log(i);
}
  • 0 · 2
  • 0 · 2 · 4
  • 0 · 2 · 3
  • 0

নিজে চালিয়ে দেখুন — function, scope আর ASI

এই ভাগের ফাঁদগুলো একসাথে। চালানোর আগে অনুমান করুন: f(1, undefined, 3, 4)-এ rest parameter-এ কয়টি মান যাবে, আর arguments.length কত?

function f(a, b = a * 2, ...rest) {
  return `${a}-${b}-${rest.length}-${arguments.length}`;
}
console.log(f(1), f(1, undefined, 3, 4), f(1, null), f.length);
var n = 1;
function show() {
  console.log("show:", n);
  var n = 2;
}
show();
const mk = () => ({ title: "রিনা" });
const bad = () => { title: "রিনা" };
console.log(mk(), bad());
function get() {
  return
  42;
}
console.log(get());
switch ("2") {
  case 2: console.log("সংখ্যা 2"); break;
  default: console.log("default — switch === দিয়ে মেলায়");
}
for (let i = 0; i < 5; i++) {
  if (i === 1) continue;
  if (i === 3) break;
  console.log("i =", i);
}

আউটপুট: 1-2-0-1 1-2-2-4 1-null-0-2 1, show: undefined, { title: 'রিনা' } undefined, undefined, default — switch === দিয়ে মেলায়, i = 0, i = 2. প্রথম লাইনের দ্বিতীয় ডাকে লক্ষ করুন: স্পষ্ট undefined দিলেও default বসে (b = 2), rest পায় 3 আর 4, আর arguments চারটিই গোনে। arguments arrow function-এ নেই — সেখানে rest parameter-ই একমাত্র উপায়। return-এর পরের 42; কখনো চলে না; ESLint-এর no-unreachable নিয়ম এমন লাইন ধরিয়ে দেয়।

৫ · ডেটা স্ট্রাকচার — object, array, destructuring, Map, JSON আর Date

এই ভাগের প্রায় প্রতিটি ফাঁদ একটি প্রশ্নে নেমে আসে: নতুন কিছু তৈরি হলো, নাকি একই জিনিস বদলাল? Object আর array-র ভ্যারিয়েবলে থাকে reference — দুটি নাম একই object দেখাতে পারে। কোন method মূল array বদলায় (in-place: sort, splice, reverse, push) আর কোনটি নতুন array দেয় (map, slice, toSorted), তা মুখস্থ রাখুন। আর spread বা Object.assign শুধু এক স্তরের কপি (shallow copy) বানায়।

(কঠিন) map-এ সরাসরি parseInt — কী ছাপা হবে?

console.log(["1", "2", "3"].map(parseInt));
  • [ 1, 2, 3 ]
  • [ NaN, NaN, NaN ]
  • [ 1, NaN, NaN ]
  • [ 1, NaN, 11 ]

(সহজ) সংখ্যার array-তে খালি sort — কী ছাপা হবে?

console.log([10, 9, 1, 100].sort());
  • [ 1, 10, 100, 9 ]
  • [ 1, 9, 10, 100 ]
  • [ 100, 10, 9, 1 ]
  • [ 10, 9, 1, 100 ]

(মাঝারি) slice বনাম splice — কী ছাপা হবে?

const a = [1, 2, 3, 4, 5];
const s1 = a.slice(1, 3);
const s2 = a.splice(1, 3);
console.log(s1, s2, a);
  • [ 2, 3 ] [ 2, 3 ] [ 1, 4, 5 ]
  • [ 2, 3 ] [ 2, 3, 4 ] [ 1, 2, 3, 4, 5 ]
  • [ 2, 3, 4 ] [ 2, 3, 4 ] [ 1, 5 ]
  • [ 2, 3 ] [ 2, 3, 4 ] [ 1, 5 ]

(কঠিন) ফাঁকা ঘরওয়ালা array — কী ছাপা হবে?

const h = [1, , 3];
console.log(h.length, 1 in h, h.indexOf(undefined), h.includes(undefined));
  • 2 false -1 false
  • 3 false -1 true
  • 3 true 1 true
  • 3 false -1 false

(মাঝারি) reference আর spread কপি — কী ছাপা হবে?

const a = { n: 1 };
const b = a;
b.n = 2;
const c = { ...a };
c.n = 3;
console.log(a.n, b.n, c.n, a === b, a === c);
  • 2 2 3 true false
  • 1 2 3 false false
  • 3 3 3 true true
  • 2 2 3 true true

(মাঝারি) nested array-সহ spread কপি — কী ছাপা হবে?

const orig = { name: "রিনা", tags: ["js"] };
const copy = { ...orig };
copy.name = "মিনা";
copy.tags.push("node");
console.log(orig.name, orig.tags.length);
  • মিনা 2
  • রিনা 1
  • রিনা 2
  • মিনা 1

(মাঝারি) destructuring-এর default — কী ছাপা হবে?

const { a = 1, b = 2 } = { a: undefined, b: null };
console.log(a, b);
  • 1 2
  • 1 null
  • undefined null
  • 1 2 — দুটিই default, কারণ null আর undefined সমান

(মাঝারি) spread-এর ক্রম — কী ছাপা হবে?

const o = { x: 1, ...{ x: 2, y: 3 }, y: 4 };
console.log(o);
  • { x: 1, y: 4 }
  • { x: 2, y: 3 }
  • { x: 1, x: 2, y: 3, y: 4 }
  • { x: 2, y: 4 }

(মাঝারি) swap আর string-এ rest — কী ছাপা হবে?

let p = 1, q = 2;
[p, q] = [q, p];
const [first, ...others] = "abc";
console.log(p, q, first, others);
  • 2 1 a [ 'b', 'c' ]
  • 2 2 a [ 'b', 'c' ]
  • 2 1 a bc
  • TypeError: "abc" is not iterable

(মাঝারি) Map বনাম object-এর key — কী ছাপা হবে?

const m = new Map();
m.set(1, "সংখ্যা");
m.set("1", "লেখা");
const o = {};
o[1] = "সংখ্যা";
o["1"] = "লেখা";
console.log(m.size, Object.keys(o).length, m.get(1));
  • 1 1 লেখা
  • 2 2 সংখ্যা
  • 2 1 সংখ্যা
  • 2 1 লেখা

(মাঝারি) Set-এর আকার — কী ছাপা হবে?

const s = new Set([1, "1", 1, NaN, NaN, {}, {}]);
console.log(s.size);
  • 6
  • 5
  • 4
  • 3

(কঠিন) JSON.stringify কী বাদ দেয় — কী ছাপা হবে?

console.log(JSON.stringify({ a: undefined, b: () => 1, c: NaN, d: new Date(0), e: [undefined] }));
  • {"a":undefined,"b":"() => 1","c":NaN,"d":"1970-01-01T00:00:00.000Z","e":[undefined]}
  • {"c":null,"e":[null]}
  • {"c":null,"d":"1970-01-01T00:00:00.000Z","e":[]}
  • {"c":null,"d":"1970-01-01T00:00:00.000Z","e":[null]}

(কঠিন) মাস শূন্য থেকে — কী ছাপা হবে?

const d = new Date(2024, 0, 31);
d.setMonth(1);
console.log(d.getMonth(), d.getDate());
  • 2 2
  • 1 29
  • 1 31
  • Invalid Date

(মাঝারি) object-এর key-এর ক্রম — কী ছাপা হবে?

const o = { b: 1, 2: "x", a: 2, 1: "y" };
console.log(Object.keys(o));
  • [ 'b', '2', 'a', '1' ]
  • [ '1', '2', 'a', 'b' ]
  • [ '1', '2', 'b', 'a' ]
  • [ 1, 2, 'b', 'a' ]

(মাঝারি) length বদলানো — কী ছাপা হবে?

const a = [1, 2, 3];
a.length = 1;
a[3] = 4;
console.log(a.length, JSON.stringify(a));
  • 4 [1,2,3,4]
  • 4 [1,null,null,4]
  • 2 [1,4]
  • 4 [1,4]

(মাঝারি) reduce আর খালি array — কী ছাপা হবে?

console.log([1, 2, 3].reduce((s, x) => s + x));
console.log([].reduce((s, x) => s + x));
  • 6 · 0
  • 6 · undefined
  • প্রথম লাইনেই TypeError
  • 6, তারপর TypeError: Reduce of empty array with no initial value

(মাঝারি) toSorted বনাম sort — কী ছাপা হবে?

const a = [3, 1, 2];
const b = a.toSorted();
const c = a.sort();
console.log(a === b, a === c);
  • false true
  • false false
  • true true
  • true false

(মাঝারি) structuredClone কী কপি করতে পারে না — কী ছাপা হবে?

try {
  structuredClone({ f() {} });
} catch (e) {
  console.log(e.name, e instanceof DOMException);
}
const c = structuredClone({ d: new Date(0), m: new Map([[1, 2]]) });
console.log(c.d instanceof Date, c.m.get(1));
  • true 2 (প্রথম অংশে কিছু ছাপা হয় না)
  • TypeError false · true 2
  • DataCloneError true · true 2
  • DataCloneError true · false undefined

নিজে চালিয়ে দেখুন — array-র ফাঁদ

প্রতিটি লাইনে একটি জনপ্রিয় ভুল। চালানোর আগে লিখে ফেলুন: তৃতীয় লাইনে শেষ argument a ছাপার সময় কোন অবস্থায় থাকবে — মনে রাখবেন, argument বাঁ থেকে ডানে হিসাব হয়।

console.log(["1", "2", "3"].map(parseInt), ["1", "2", "3"].map(Number));
console.log([10, 9, 1, 100].sort(), [10, 9, 1, 100].sort((a, b) => a - b));
const a = [1, 2, 3, 4, 5];
console.log(a.slice(1, 3), a.splice(1, 3), a);
const h = [1, , 3];
console.log(h.length, 1 in h, h.indexOf(undefined), h.includes(undefined));
const nums = [3, 1, 2];
console.log(nums.toSorted() === nums, nums.sort() === nums, nums);
console.log([1, 2, 3].reduce((s, x) => s + x, ""), [1, [2, [3]]].flat().length);
const arr = [1, 2, 3];
arr.length = 1;
arr[3] = 4;
console.log(arr.length, JSON.stringify(arr));

আউটপুট: [ 1, NaN, NaN ] [ 1, 2, 3 ], [ 1, 10, 100, 9 ] [ 1, 9, 10, 100 ], [ 2, 3 ] [ 2, 3, 4 ] [ 1, 5 ], 3 false -1 true, false true [ 1, 2, 3 ], 123 3, 4 [1,null,null,4]. ষষ্ঠ লাইনে দুটি নতুন ফাঁদ: initial value "" দেওয়ায় reduce যোগ নয়, জোড়া করেছে — “123”; আর flat() ডিফল্টে মাত্র এক স্তর খোলে: [1, 2, [3]], তাই length 3 (সব স্তর খুলতে flat(Infinity)). তৃতীয় লাইনের a ছাপা হয়েছে splice-এর পরের অবস্থায়, কারণ argument বাঁ থেকে ডানে হিসাব হয়।

নিজে চালিয়ে দেখুন — কপি, JSON, Map আর Date

একটি object-এর তিন রকম কপি পাশাপাশি। চালানোর আগে অনুমান করুন: JSON দিয়ে কপি করার পরে born-এর টাইপ কী হবে?

const orig = { title: "রিনা", tags: ["js"], born: new Date(2000, 0, 1) };
const shallow = { ...orig };
const deep = structuredClone(orig);
shallow.tags.push("node");
console.log(orig.tags.length, deep.tags.length, deep.born instanceof Date);
const viaJson = JSON.parse(JSON.stringify(orig));
console.log(typeof viaJson.born, viaJson.tags.length);
console.log(JSON.stringify({ a: undefined, b: () => 1, c: NaN, e: [undefined] }));
const { x = 1, y = 2 } = { x: undefined, y: null };
console.log(x, y, { a: 1, ...{ a: 2, b: 3 }, b: 4 });
console.log(Object.keys({ b: 1, 2: "x", a: 2, 1: "y" }));
const m = new Map([[1, "সংখ্যা"], ["1", "লেখা"]]);
console.log(m.size, m.get(1), new Set([1, "1", NaN, NaN, {}, {}]).size);
const d = new Date(2024, 0, 31);
d.setMonth(1);
console.log(d.getMonth(), d.getDate());

আউটপুট: 2 1 true, string 2, {"c":null,"e":[null]}, 1 null { a: 2, b: 4 }, [ '1', '2', 'b', 'a' ], 2 সংখ্যা 5, 2 2. প্রথম লাইন: shallow কপির push মূল object-এর array-তেই গেছে (2), কিন্তু structuredClone-এর গভীর কপি আলাদা (1), আর Date-কে Date রেখেছে। দ্বিতীয় লাইন: JSON দিয়ে কপি করলে Date হয়ে যায় string — এবং viaJson তৈরি হয়েছে push-এর পরে, তাই tags-এ 2টি। শেষ লাইনের তারিখ স্থানীয় সময় দিয়ে তৈরি আর পড়া, তাই যেকোনো time zone-এ একই ফল।

৬ · ফাংশন ও অবজেক্ট গভীরে — closure, this, prototype, class, generator, error আর regex

মডিউল ৪-এর প্রশ্নে তিনটি জিনিস আলাদা করে লিখুন। Closure: function কোন ভ্যারিয়েবলটি মনে রেখেছে — একটি ভাগ করা var, নাকি প্রতি ঘুরে নতুন let (per-iteration binding)? this: function কীভাবে ডাকা হলো — ঘোষণা কোথায় হলো তা নয় (arrow function বাদে)। Prototype: property নিজের, নাকি prototype chain ধরে ওপর থেকে আসছে? class-এর ভেতরের কোড সবসময় strict mode-এ চলে — এটা মনে না রাখলে অর্ধেক প্রশ্নে ভুল হয়।

(মাঝারি) লুপে var আর setTimeout — কী ছাপা হবে?

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
  • 0 · 1 · 2
  • 3 · 3 · 3
  • 2 · 2 · 2
  • 0 · 1 · 2, কারণ delay 0

(সহজ) দুটি counter — কী ছাপা হবে?

function makeCounter() {
  let c = 0;
  return () => ++c;
}
const a = makeCounter(), b = makeCounter();
a(); a();
console.log(a(), b());
  • 3 4
  • 2 0
  • 1 1
  • 3 1

(কঠিন) class-এর method আলাদা করে ডাকা — কী ছাপা হবে?

class Btn {
  constructor() { this.label = "OK"; }
  show() { return this.label; }
}
const btn = new Btn();
const fn = btn.show;
console.log(btn.show(), fn());
  • কিছুই ছাপা হয় না — TypeError: Cannot read properties of undefined (reading 'label')
  • OK, তারপর TypeError
  • OK OK
  • OK undefined

(মাঝারি) method-এর ভেতরে arrow আর সাধারণ function — ফাইলের শুরুতে "use strict"; আছে। কী ছাপা হবে?

"use strict";
const user = {
  name: "রাফি",
  greet() {
    const inner = () => this.name;
    function plain() { return this === undefined ? "undefined" : "অন্য কিছু"; }
    return inner() + " " + plain();
  },
};
console.log(user.greet());
  • রাফি রাফি
  • undefined undefined
  • রাফি undefined
  • রাফি অন্য কিছু

(কঠিন) bind করা function-এ call আর আবার bind — কী ছাপা হবে?

function who() { return this.n; }
const a = { n: "a" }, b = { n: "b" };
const bound = who.bind(a);
console.log(bound.call(b), bound.bind(b)(), who.call(b));
  • b b b
  • a a b
  • a b b
  • a a a

(কঠিন) bind করা constructor-এ new — কী ছাপা হবে?

function P(x) { this.x = x; }
const BP = P.bind({ x: "bound" }, 1);
const o = new BP();
console.log(o.x, o instanceof P);
  • bound true
  • undefined true
  • 1 false
  • 1 true

(মাঝারি) prototype থেকে পাওয়া property — কী ছাপা হবে?

const base = { greet: "হ্যালো", list: [] };
const child = Object.create(base);
child.greet = "সালাম";
child.list.push(1);
console.log(base.greet, base.list.length, Object.hasOwn(child, "list"));
  • হ্যালো 1 false
  • সালাম 1 false
  • হ্যালো 0 true
  • হ্যালো 1 true

(মাঝারি) subclass constructor-এ super-এর আগে ও পরে — কী ছাপা হবে?

class A {
  constructor() { console.log("A", this.constructor.name); }
}
class B extends A {
  constructor() {
    console.log("B শুরু");
    super();
    console.log("B শেষ");
  }
}
new B();
  • A A · B শুরু · B শেষ
  • B শুরু · A A · B শেষ
  • B শুরু · A B · B শেষ
  • ReferenceError — super()-এর আগে কোনো কোড চলতে পারে না

(কঠিন) base constructor থেকে override করা method — কী ছাপা হবে?

class Base {
  constructor() { console.log(this.describe()); }
  describe() { return "base"; }
}
class Child extends Base {
  name = "child";
  describe() { return "I am " + this.name; }
}
new Child();
  • base
  • I am undefined
  • I am child
  • ReferenceError

(মাঝারি) private field — কী ছাপা হবে?

class Wallet {
  #taka = 100;
  static has(o) { return #taka in o; }
  get taka() { return this.#taka; }
}
const w = new Wallet();
console.log(w.taka, w["#taka"], Wallet.has(w), Wallet.has({}));
  • 100 100 true false
  • 100 undefined true, তারপর TypeError
  • undefined undefined true false
  • 100 undefined true false

(কঠিন) static field আর argument-এর ক্রম — কী ছাপা হবে?

class C {
  static count = 0;
  constructor() { C.count++; }
}
new C(); new C();
console.log(C.count, new C().count, C.count);
  • 2 undefined 3
  • 3 undefined 3
  • 2 3 3
  • 2 0 3

(কঠিন) generator-এ next(value) — কী ছাপা হবে?

function* g() {
  const x = yield 1;
  console.log("x =", x);
  yield x * 2;
  return 99;
}
const it = g();
console.log(it.next("প্রথম").value);
console.log(it.next(5).value);
console.log(it.next().value, it.next().done);
  • 1 · x = প্রথম · NaN · 99 true
  • 1 · x = 5 · 10 · undefined true
  • 1 · x = 5 · 10 · 99 true
  • x = undefined · 1 · NaN · 99 true

(মাঝারি) generator-এ spread আর return — কী ছাপা হবে?

function* g() { yield 1; yield 2; return 3; }
console.log([...g()]);
  • [ 1, 2, 3 ]
  • [ 1, 2 ]
  • [ Object [Generator] {} ]
  • [ 3 ]

(মাঝারি) Symbol key — কী ছাপা হবে?

const id = Symbol("id");
const o = { [id]: 1, name: "x" };
console.log(Object.keys(o).length, JSON.stringify(o), o[id], Symbol("a") === Symbol("a"), Symbol.for("a") === Symbol.for("a"));
  • 2 {"name":"x","id":1} 1 false true
  • 1 {"name":"x"} 1 true true
  • 1 {"name":"x"} undefined false true
  • 1 {"name":"x"} 1 false true

(কঠিন) Proxy-র get trap — কী ছাপা হবে?

const p = new Proxy({ a: 1 }, {
  get: (t, k) => (k in t ? t[k] : `নেই: ${String(k)}`),
});
console.log(p.a, p.b, "b" in p);
  • 1 নেই: b false
  • 1 নেই: b true
  • 1 undefined false
  • TypeError — proxy invariant ভাঙা

(কঠিন) finally-তে return — কী ছাপা হবে?

function f() {
  try { return "try"; } finally { console.log("finally চলল"); }
}
function g() {
  try { throw new Error("x"); }
  catch (e) { return "catch"; }
  finally { return "finally"; }
}
console.log(f());
console.log(g());
  • try · finally চলল · catch
  • finally চলল · try · catch
  • finally চলল · try · finally
  • try · finally

(কঠিন) g flag-ওয়ালা regex দিয়ে বারবার test — কী ছাপা হবে?

const re = /a/g;
console.log(re.test("a"), re.test("a"), re.test("a"));
  • true true true
  • true false true
  • true false false
  • false true false

(মাঝারি) custom error — কী ছাপা হবে?

class AppError extends Error {
  constructor(m) { super(m); this.name = "AppError"; }
}
try {
  throw new AppError("ভুল");
} catch (e) {
  console.log(e.name, e.message, e instanceof Error, String(e));
}
  • Error ভুল true Error: ভুল
  • AppError ভুল false AppError: ভুল
  • AppError ভুল true ভুল
  • AppError ভুল true AppError: ভুল

(মাঝারি) replace, replaceAll আর capture group — কী ছাপা হবে?

console.log("a-b-c".replace("-", "+"), "a-b-c".replaceAll("-", "+"), "2024-01-31".match(/(\d+)-(\d+)/)[2]);
  • a+b-c a+b+c 01
  • a+b+c a+b+c 01
  • a+b-c a+b+c 1
  • a+b-c a+b+c 2024-01

(মাঝারি) compose আর reduceRight — কী ছাপা হবে?

const compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);
const inc = (x) => x + 1;
const dbl = (x) => x * 2;
console.log(compose(inc, dbl)(5), compose(dbl, inc)(5));
  • 12 11
  • 11 11
  • 11 12
  • NaN NaN

নিজে চালিয়ে দেখুন — closure আর this

এই কোড ব্রাউজারের classic script হিসেবে চলে, strict mode ছাড়া — তাই খালি ডাকে this হলো window. চালানোর আগে অনুমান করুন: lost() error দেবে, নাকি কিছু একটা ফেরত দেবে?

for (var i = 0; i < 3; i++) setTimeout(() => console.log("var:", i), 0);
for (let j = 0; j < 3; j++) setTimeout(() => console.log("let:", j), 0);
function whoAmI() { return this === window ? "window" : String(this); }
console.log(whoAmI());
const user = {
  label: "রাফি",
  regular() { return this.label; },
  arrow: () => this === window,
  later() { return [1].map(function () { return this === window; })[0]; },
};
const lost = user.regular;
console.log(user.regular(), lost(), user.arrow(), user.later());
function who() { return this.n; }
const bound = who.bind({ n: "a" });
console.log(bound.call({ n: "b" }), bound.bind({ n: "c" })(), who.call({ n: "d" }));

আউটপুট: window, রাফি undefined true true, a a d, তারপর timer থেকে var: 3 তিনবার আর let: 0, let: 1, let: 2. Timer-এর লেখাগুলো সবার শেষে, যদিও কোডে সবার আগে — সেগুলো task, চলে পুরো script শেষ হওয়ার পরে। lost() sloppy mode-এ error দেয়নি: this হলো window, আর window.label নেই — undefined. এটাই বিপজ্জনক দিক: বাগ চুপচাপ লুকিয়ে থাকে। প্রথম লাইনে "use strict"; যোগ করে আবার চালান — whoAmI() দেবে "undefined", আর lost() TypeError দিয়ে থামবে। arrow top-level-এর this নেয় (classic script-এ window); later-এর ভেতরের সাধারণ function callback খালি ডাকা হয়, তাই সেটিও window.

নিজে চালিয়ে দেখুন — class, generator, finally, regex আর Proxy

মডিউল ৪-এর পাঁচটি ফাঁদ এক জায়গায়। চালানোর আগে লিখুন: প্রথম দুই লাইনের কোনটি আগে ছাপা হবে — “Base:” নাকি “পরে:”?

class Base {
  constructor() { console.log("Base:", this.describe()); }
  describe() { return "base"; }
}
class Child extends Base {
  title = "child";
  describe() { return "I am " + this.title; }
}
console.log("পরে:", new Child().describe());
function* g() { const x = yield 1; yield x * 2; return 99; }
const it = g();
console.log(it.next("হারিয়ে যাবে").value, it.next(5).value, it.next().value, it.next().done);
function f() {
  try { return "try"; } finally { console.log("finally আগে চলে"); }
}
console.log(f());
const re = /a/g;
console.log(re.test("a"), re.test("a"), re.lastIndex);
const p = new Proxy({}, { get: (t, k) => `চাওয়া হলো ${String(k)}` });
console.log(p.foo, "foo" in p);

আউটপুট: Base: I am undefined, পরে: I am child, 1 10 99 true, finally আগে চলে, try, true false 0, চাওয়া হলো foo false. প্রথম লাইন আগে আসে, কারণ console.log("পরে:", …)-এর argument হিসাব করতে গিয়েই new Child() চলে — আর তার ভেতরে Base-এর constructor ছাপে, যখন title এখনো বসেনি। Object তৈরি শেষ হলে একই method ঠিক মান দেয়। Regex-এর লাইনে দ্বিতীয় test ব্যর্থ হয়ে lastIndex-কে 0-তে ফিরিয়েছে। Proxy-র লাইনে "foo" in p get trap ছোঁয় না — has trap নেই, তাই খালি target-এ যাচাই হয়ে false.

৭ · Asynchronous JavaScript — event loop, Promise আর async/await

ইন্টারভিউতে সবচেয়ে প্রিয় প্রশ্ন: “এই console.log-গুলো কোন ক্রমে ছাপা হবে?” উত্তর বের করার নিয়ম একটাই, আর সেটি কখনো বদলায় না: আগে বর্তমান script-এর সব sync কোড; তারপর microtask queue পুরো খালি; তারপর task queue থেকে একটি task; আবার সব microtask — এভাবে চলতে থাকে (event loop). তিনটি জিনিস মনে রাখুন: Promise-এর executor আর async function-এর প্রথম await পর্যন্ত অংশ sync চলে; await-এর পরের অংশ একটি microtask; আর setTimeout(fn, 0) মানে “এখনই” নয়, “পরের কোনো task-এ”।

নিচের ডায়াগ্রামে একটি পূর্ণ ক্রমের ধাঁধা — async function-এর ভেতরে আরেকটি async function, একটি Promise executor আর একটি timer. “পরে” চাপার আগে নিজে তিন কলামের টেবিল এঁকে পুরো আউটপুট লিখুন — “a2” কি “script শেষ”-এর আগে আসবে?

1async function a1() {2  console.log("a1 শুরু");3  await a2();4  console.log("a1 শেষ");5}6async function a2() { console.log("a2"); }7console.log("script শুরু");8setTimeout(() => console.log("timeout"), 0);9a1();10new Promise((r) => { console.log("executor"); r(); })11  .then(() => console.log("then"));12console.log("script শেষ");
কে কোথায় অপেক্ষা করছে
Call stack
Microtask
Task queue
  1. লাইন ৭ — script নিজেই প্রথম task. দুটি async function শুধু ঘোষিত হয়েছে, চলেনি। প্রথম লেখা: script শুরু.
  2. লাইন ৮ — timer. callback-টি timer-এর হাতে গেল, ০ ms পরে দাঁড়াল task queue-তে। Script এখনো চলছে, তাই সেটি অপেক্ষা করবে।
  3. লাইন ৯ — a1() ডাকা. async function ডাকলে তার শরীর সাথে সাথে, sync চলতে শুরু করে: a1 শুরু. “async মানে পরে চলে” — এই ভুল ধারণাটিই এখানে ধরা পড়ে।
  4. লাইন ৩ — await a2(). আগে a2() পুরো sync চলে: a2; সে একটি fulfilled Promise ফেরত দেয়। তারপর await a1-কে থামায়, আর a1-এর বাকি অংশ (লাইন ৪) microtask হিসেবে লাইনে দাঁড়ায়। নিয়ন্ত্রণ ফেরে script-এ।
  5. লাইন ১০–১১ — Promise executor. Executor-ও sync চলে: executor. r() Promise-কে fulfilled করে, তাই then-এর callback microtask queue-তে a1-এর বাকি অংশের পেছনে দাঁড়ায়।
  6. লাইন ১২. script শেষ. প্রথম task শেষ, call stack খালি। এখন event loop দেখে: microtask আছে? হ্যাঁ, দুটি।
  7. প্রথম microtask — a1-এর বাকি অংশ. যে আগে লাইনে দাঁড়িয়েছে, সে আগে: a1 শেষ.
  8. দ্বিতীয় microtask — then. then. Microtask queue এখন খালি। ব্রাউজার চাইলে এখানে পাতা আঁকতে পারে।
  9. তারপর task — timeout. সবচেয়ে আগে নিবন্ধিত হলেও timer চলে সবার শেষে: timeout. মোট আটটি লেখা। এক লাইনে নিয়ম: sync (async function-এর প্রথম await পর্যন্ত আর executor-সহ) → সব microtask → একটি task.

নিচের কোডটি হুবহু ডায়াগ্রামের প্রোগ্রাম। চালানোর আগে একটি পরিবর্তনের ফল অনুমান করুন: লাইন ৩-এ await a2();-এর বদলে শুধু a2(); লিখলে (await ছাড়া) “a1 শেষ” কোথায় সরে যাবে?

async function a1() {
  console.log("a1 শুরু");
  await a2();
  console.log("a1 শেষ");
}
async function a2() { console.log("a2"); }
console.log("script শুরু");
setTimeout(() => console.log("timeout"), 0);
a1();
new Promise((r) => { console.log("executor"); r(); })
  .then(() => console.log("then"));
console.log("script শেষ");

আউটপুট: script শুরু, a1 শুরু, a2, executor, script শেষ, a1 শেষ, then, timeout. await সরিয়ে দিলে a1-এর কোথাও থামা নেই, তাই “a1 শেষ” সাথে সাথে ছাপা হবে — “a2”-এর ঠিক পরে, “executor”-এর আগে। একটি ঐতিহাসিক টীকা: ২০১৯ সালের আগের অনেক engine-এ await একটি native Promise-এর জন্য বাড়তি দুটি microtask-ধাপ নিত, তখন “then” আগে আসত। ES2019-এ নিয়মটি সরল করা হয় (V8 7.2-এ তা চালু হয়) — আধুনিক সব engine-এ উত্তর ওপরের মতো।

ক্রমব্রাউজারে একটি script চলছে, তাতে sync কোড, কয়েকটি then আর একটি setTimeout আছে। Event loop কোন ক্রমে কাজ করে? (▲▼ চেপে সাজান)

  1. Script-এর সব sync কোড চলে, call stack খালি হয়
  2. Microtask queue পুরো খালি হয় (মাঝে নতুন microtask এলে সেটিও)
  3. দরকার হলে ব্রাউজার পাতা নতুন করে আঁকে
  4. Task queue থেকে সবচেয়ে পুরোনো একটি task চলে (যেমন setTimeout-এর callback)
  5. আবার microtask queue পুরো খালি হয়

(সহজ) সবচেয়ে পরিচিত ক্রমের প্রশ্ন — কী ছাপা হবে?

console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
  • 1 · 2 · 3 · 4
  • 1 · 4 · 2 · 3
  • 1 · 4 · 3 · 2
  • 1 · 3 · 4 · 2

(মাঝারি) async function-এর sync অংশ — কী ছাপা হবে?

async function f() {
  console.log("B");
  await null;
  console.log("D");
}
console.log("A");
f();
console.log("C");
  • A · B · C · D
  • A · C · B · D
  • A · B · D · C
  • TypeError: await-এর জন্য Promise লাগবে

(কঠিন) দুটি then-শিকল পাশাপাশি — কী ছাপা হবে?

Promise.resolve().then(() => console.log(1)).then(() => console.log(2));
Promise.resolve().then(() => console.log(3)).then(() => console.log(4));
  • 1 · 2 · 3 · 4
  • 1 · 3 · 2 · 4
  • 3 · 1 · 4 · 2
  • ক্রম নিশ্চিত নয়

(কঠিন) then থেকে Promise ফেরত দেওয়া — কী ছাপা হবে?

Promise.resolve()
  .then(() => { console.log("a"); return Promise.resolve("b"); })
  .then((v) => console.log(v));
Promise.resolve()
  .then(() => console.log(1)).then(() => console.log(2))
  .then(() => console.log(3)).then(() => console.log(4));
  • a · b · 1 · 2 · 3 · 4
  • a · 1 · b · 2 · 3 · 4
  • 1 · a · 2 · 3 · 4 · b
  • a · 1 · 2 · 3 · b · 4

(মাঝারি) task-এর ভেতরে microtask — কী ছাপা হবে?

setTimeout(() => {
  console.log("T1");
  Promise.resolve().then(() => console.log("M1"));
}, 0);
setTimeout(() => console.log("T2"), 0);
  • T1 · M1 · T2
  • T1 · T2 · M1
  • M1 · T1 · T2
  • T1 আর T2-এর ক্রম নিশ্চিত নয়

(মাঝারি) executor আর একাধিক resolve — কী ছাপা হবে?

const p = new Promise((res, rej) => {
  console.log("x");
  res(1);
  res(2);
  rej(new Error("no"));
  console.log("y");
});
p.then((v) => console.log(v));
console.log("z");
  • x · z · 1
  • x · y · z · 2
  • x · y · z · 1
  • x · y · z, তারপর Uncaught Error: no

(সহজ) Promise.all-এর ফলের ক্রম — কী ছাপা হবে?

const wait = (ms, v) => new Promise((res) => setTimeout(() => res(v), ms));
Promise.all([wait(30, "ক"), wait(10, "খ")]).then((v) => console.log(v));
  • [ 'খ', 'ক' ]
  • [ 'ক', 'খ' ]
  • খ
  • খ · ক

(কঠিন) race বনাম any — কী ছাপা হবে?

const wait = (ms, v, fail) =>
  new Promise((res, rej) => setTimeout(() => (fail ? rej(new Error(v)) : res(v)), ms));
Promise.race([wait(20, "ধীর"), wait(10, "ভুল", true)])
  .then((v) => console.log("race ok:", v), (e) => console.log("race:", e.message));
Promise.any([wait(20, "ধীর"), wait(10, "ভুল", true)])
  .then((v) => console.log("any:", v));
  • race ok: ধীর · any: ধীর
  • race: ভুল, তারপর AggregateError
  • any: ধীর · race: ভুল
  • race: ভুল · any: ধীর

(মাঝারি) allSettled-এর ফল — কী ছাপা হবে?

Promise.allSettled([Promise.resolve(1), Promise.reject(new Error("x"))])
  .then((r) => console.log(r.map((s) => s.status).join(), r[1].reason.message));
  • fulfilled,rejected x
  • resolved,rejected x
  • Uncaught (in promise) Error: x
  • fulfilled,rejected undefined

(মাঝারি) try/catch আর timer-এর ভেতরের error — কী ঘটবে?

try {
  setTimeout(() => { throw new Error("পরে"); }, 0);
} catch (e) {
  console.log("ধরা পড়েছে");
}
console.log("শেষ");
  • ধরা পড়েছে · শেষ
  • ধরা পড়েছে, তারপর শেষ আর error
  • শেষ, তারপর Uncaught Error: পরে
  • শুধু Uncaught Error: পরে — শেষ ছাপা হয় না

(মাঝারি) forEach-এর ভেতরে await — কী ছাপা হবে?

[1, 2, 3].forEach(async (n) => {
  await null;
  console.log(n);
});
console.log("লুপ শেষ");
  • 1 · 2 · 3 · লুপ শেষ
  • লুপ শেষ · 1 · 2 · 3
  • লুপ শেষ · 3 · 2 · 1
  • TypeError

(মাঝারি) async function কী ফেরত দেয় — কী ছাপা হবে?

async function f() { return 1; }
const r = f();
console.log(r instanceof Promise, typeof r);
r.then((v) => console.log(v));
  • false number · 1
  • true promise · 1
  • true object · Promise { 1 }
  • true object · 1

(মাঝারি) catch, finally আর then — কী ছাপা হবে?

Promise.reject(new Error("x"))
  .catch(() => "সামলানো")
  .finally(() => "ফেলে দেওয়া")
  .then((v) => console.log(v));
  • সামলানো
  • ফেলে দেওয়া
  • কিছুই ছাপা হয় না, শিকল এখনো rejected
  • Error: x

(মাঝারি) Response আর text() — কী ছাপা হবে?

const res = new Response("abc");
console.log(res.ok, res.status);
res.text().then((t) => console.log(t));
console.log("আগে");
  • true 200 · abc · আগে
  • false 0 · আগে · abc
  • true 200 · আগে · abc
  • ReferenceError: Response is not defined

(মাঝারি) debounce — কী ছাপা হবে?

function debounce(fn, ms) {
  let t;
  return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms); };
}
const log = debounce((v) => console.log(v), 50);
log(1); log(2);
setTimeout(() => log(3), 10);
  • 1 · 2 · 3
  • 3
  • 1
  • কিছুই ছাপা হয় না

(মাঝারি) AbortController দুবার abort — কী ছাপা হবে?

const c = new AbortController();
c.signal.addEventListener("abort", () => console.log("abort ঘটনা"));
c.abort();
c.abort();
console.log(c.signal.aborted, c.signal.reason.name);
  • abort ঘটনা · abort ঘটনা · true AbortError
  • true AbortError · abort ঘটনা
  • abort ঘটনা · true undefined
  • abort ঘটনা · true AbortError

(কঠিন) সব কিছু একসাথে — কী ছাপা হবে?

console.log(1);
setTimeout(() => console.log(2));
queueMicrotask(() => console.log(3));
(async () => { console.log(4); await undefined; console.log(5); })();
Promise.resolve().then(() => console.log(6));
console.log(7);
  • 1 · 7 · 4 · 3 · 5 · 6 · 2
  • 1 · 4 · 7 · 6 · 3 · 5 · 2
  • 1 · 4 · 7 · 3 · 5 · 6 · 2
  • 1 · 4 · 7 · 2 · 3 · 5 · 6

নিজে চালিয়ে দেখুন — Promise combinator

একটি নকল wait function দিয়ে চারটি combinator পাশাপাশি — সময় ধরে ধরে। চালানোর আগে প্রতিটি লাইনের পাশে লিখুন কত ms-এ ছাপা হবে, তারপর সেই ক্রমে সাজান।

const wait = (ms, v, fail) =>
  new Promise((res, rej) => setTimeout(() => (fail ? rej(new Error(v)) : res(v)), ms));
Promise.all([wait(30, "ক"), wait(10, "খ")]).then((v) => console.log("all:", v));
Promise.all([wait(30, "ক"), wait(10, "ভুল", true)])
  .catch((e) => console.log("all ব্যর্থ:", e.message));
Promise.race([wait(20, "ধীর"), wait(10, "দ্রুত ভুল", true)])
  .catch((e) => console.log("race:", e.message));
Promise.any([wait(20, "ধীর"), wait(10, "দ্রুত ভুল", true)])
  .then((v) => console.log("any:", v));
Promise.any([wait(5, "একমাত্র", true)])
  .catch((e) => console.log(e.name, e.errors.length));
Promise.allSettled([wait(15, "ঠিক"), wait(5, "ভুল", true)]).then((r) =>
  console.log("allSettled:", r.map((x) => x.status + "=" + (x.value ?? x.reason.message)).join(", ")));

আউটপুট সময়ের ক্রমে: AggregateError 1 (৫ ms — any-এর সব Promise ব্যর্থ), all ব্যর্থ: ভুল (১০ ms — all প্রথম rejection-এই ব্যর্থ, ৩০ ms-এর অপেক্ষা করে না), race: দ্রুত ভুল (১০ ms), allSettled: fulfilled=ঠিক, rejected=ভুল (১৫ ms — সব শেষ হলে, input-এর ক্রমে), any: ধীর (২০ ms), all: [ 'ক', 'খ' ] (৩০ ms). মনে রাখার ছক: all — সব সফল চাই, একটি ব্যর্থ হলেই ব্যর্থ; allSettled — সবার ফল চাই, কখনো ব্যর্থ নয়; race — প্রথম যে শেষ হয়, সফল বা ব্যর্থ; any — প্রথম সফল, সব ব্যর্থ হলে AggregateError. লক্ষ করুন, Promise.all ব্যর্থ হওয়ার পরেও ৩০ ms-এর “ক” timer চলতেই থাকে — all অন্য কাজ বাতিল করে না; বাতিলের জন্য AbortController লাগে।

৮ · ব্রাউজার, DOM, module আর Node.js

DOM event-এর প্রশ্ন আসলে ক্রমের প্রশ্ন। একটি ক্লিক তিন ধাপে চলে (event propagation): capture — window থেকে নিচে target পর্যন্ত; target; তারপর bubble — target থেকে ওপরে। addEventListener-এর তৃতীয় argument true হলে listener capture ধাপে চলে, নইলে bubble ধাপে (event bubbling). e.target হলো যেখানে ঘটনা শুরু, e.currentTarget হলো যার listener এখন চলছে। Module-এর প্রশ্নে মনে রাখুন: ES module-এর import হলো live binding, আর সব import কোড চলার আগেই depth-first ক্রমে একবার করে চলে। Node.js-এ process.nextTick Promise-এর আগে, আর I/O callback-এর ভেতর থেকে setImmediate timer-এর আগে।

ঝালিয়ে নিন: পর্ব ৩৭ · DOM, পর্ব ৩৮ · event, bubbling আর delegation, পর্ব ৩৯ · form আর validation, পর্ব ৪০ · localStorage আর cookie, পর্ব ৪২ · Web Worker, পর্ব ৪৩ · নিরাপদ JavaScript, পর্ব ৪৫ · module — ESM বনাম CommonJS, পর্ব ৪৭ · TypeScript-এর শুরু, পর্ব ৪৯ · Node.js. DOM-এর প্রশ্নে HTML দেওয়া আছে কোডের ওপরে মন্তব্য হিসেবে; btn.click() একটি সত্যিকারের ক্লিকের মতোই পুরো propagation চালায়, আর sync চলে।

(সহজ) bubbling-এর ক্রম — কী ছাপা হবে?

// <div id="outer"><button id="btn">চাপুন</button></div>
const outer = document.querySelector("#outer");
const btn = document.querySelector("#btn");
outer.addEventListener("click", () => console.log("outer"));
btn.addEventListener("click", () => console.log("btn"));
document.addEventListener("click", () => console.log("document"));
btn.click();
  • outer · btn · document
  • btn · outer · document
  • document · outer · btn
  • btn

(মাঝারি) capture আর bubble একসাথে — কী ছাপা হবে?

// <div id="outer"><button id="btn">চাপুন</button></div>
document.addEventListener("click", () => console.log("document"));
outer.addEventListener("click", () => console.log("outer bubble"));
outer.addEventListener("click", () => console.log("outer capture"), true);
btn.addEventListener("click", () => console.log("btn"));
btn.click();
  • document · outer bubble · outer capture · btn
  • btn · outer capture · outer bubble · document
  • document · outer capture · btn · outer bubble
  • outer capture · btn · outer bubble · document

(মাঝারি) target বনাম currentTarget — কী ছাপা হবে?

// <div id="outer"><button id="btn">চাপুন</button></div>
outer.addEventListener("click", (e) => {
  console.log(e.target.id, e.currentTarget.id, e.eventPhase);
});
btn.click();
  • btn outer 3
  • btn btn 2
  • outer outer 3
  • btn outer 1

(মাঝারি) stopPropagation — কী ছাপা হবে?

// <div id="outer"><button id="btn">চাপুন</button></div>
btn.addEventListener("click", (e) => { console.log("১"); e.stopPropagation(); });
btn.addEventListener("click", () => console.log("২"));
outer.addEventListener("click", () => console.log("৩"));
btn.click();
  • ১
  • ১ · ২ · ৩
  • ১ · ২
  • ২ · ১

(মাঝারি) event delegation আর closest — কী ছাপা হবে?

// <ul id="list"><li data-id="7"><span>আম</span></li></ul>
const list = document.querySelector("#list");
list.addEventListener("click", (e) => {
  const li = e.target.closest("li");
  console.log(e.target.tagName, li.dataset.id);
});
list.querySelector("span").click();
  • LI 7
  • SPAN 7
  • span 7
  • SPAN undefined

(মাঝারি) dispatchEvent কি sync? — কী ছাপা হবে?

// <button id="btn">চাপুন</button>
btn.addEventListener("ping", () => console.log("ping পেল"));
console.log("আগে");
btn.dispatchEvent(new Event("ping"));
console.log("পরে");
  • আগে · পরে · ping পেল
  • আগে · পরে
  • ping পেল · আগে · পরে
  • আগে · ping পেল · পরে

(কঠিন) CustomEvent কি bubble করে? — কী ছাপা হবে?

// <div id="outer"><button id="btn">চাপুন</button></div>
outer.addEventListener("hello", () => console.log("outer শুনল"));
btn.dispatchEvent(new CustomEvent("hello", { detail: 1 }));
btn.dispatchEvent(new CustomEvent("hello", { bubbles: true }));
  • outer শুনল (একবার)
  • outer শুনল · outer শুনল
  • কিছুই ছাপা হয় না
  • TypeError — detail ছাড়া CustomEvent তৈরি করা যায় না

(মাঝারি) live বনাম static তালিকা — কী ছাপা হবে?

// <ul id="list"><li>এক</li></ul>
const live = document.getElementsByTagName("li");
const fixed = document.querySelectorAll("li");
list.append(document.createElement("li"));
console.log(live.length, fixed.length);
  • 2 2
  • 1 1
  • 2 1
  • 1 2

(মাঝারি) textContent-এ HTML — কী ছাপা হবে?

// <p id="msg"></p>
msg.textContent = "<b>হাই</b>";
console.log(msg.children.length, msg.innerHTML);
  • 1 <b>হাই</b>
  • 0 &lt;b&gt;হাই&lt;/b&gt;
  • 1 হাই
  • 0 (খালি)

(মাঝারি) handler-এর ভেতরে this — classic script, strict mode নেই। কী ছাপা হবে?

// <div id="outer"><button id="btn">চাপুন</button></div>
outer.addEventListener("click", function (e) { console.log(this.id, this === e.currentTarget); });
outer.addEventListener("click", (e) => console.log(this === window));
btn.click();
  • btn false · true
  • outer true · false
  • undefined false · true
  • outer true · true

(মাঝারি) number input-এর মান — কী ছাপা হবে?

// <input id="qty" type="number" value="5">
const qty = document.querySelector("#qty");
console.log(typeof qty.value, qty.value + 1, qty.valueAsNumber + 1);
  • string 51 6
  • number 6 6
  • string 51 51
  • string 1 NaN

(মাঝারি) localStorage-এ সংখ্যা আর object — একটি সাধারণ ওয়েবসাইটের পাতায় চালানো হলো। কী ছাপা হবে?

localStorage.setItem("n", 5);
localStorage.setItem("o", { a: 1 });
const n = localStorage.getItem("n");
console.log(typeof n, n + 1, localStorage.getItem("o"));
  • number 6 { a: 1 }
  • string 51 {"a":1}
  • string 51 [object Object]
  • TypeError — শুধু string রাখা যায়

(কঠিন) ES module বনাম CommonJS-এর export — কী ছাপা হবে?

// counter.mjs
export let count = 0;
export function inc() { count++; }
// main.mjs
import { count, inc } from "./counter.mjs";
inc(); inc();
console.log(count);

// counter.cjs
let count = 0;
function inc() { count++; }
module.exports = { count, inc };
// main.cjs
const { count, inc } = require("./counter.cjs");
inc(); inc();
console.log(count);
  • 2 (ESM) · 2 (CommonJS)
  • 2 (ESM) · 0 (CommonJS)
  • 0 (ESM) · 0 (CommonJS)
  • ESM-এ TypeError: Assignment to constant variable.

(কঠিন) module চলার ক্রম — node main.mjs চালালে কী ছাপা হবে?

// a.mjs
console.log("a");
export const A = 1;
// b.mjs
import "./a.mjs";
console.log("b");
// main.mjs
console.log("main");
import "./b.mjs";
import "./a.mjs";
  • main · b · a · a
  • a · b · a · main
  • main · a · b
  • a · b · main

(কঠিন) Node.js-এ I/O callback-এর ভেতরে চার রকম কাজ — কী ছাপা হবে?

const fs = require("fs");
fs.readFile(__filename, () => {
  setTimeout(() => console.log("timeout"), 0);
  setImmediate(() => console.log("immediate"));
  Promise.resolve().then(() => console.log("promise"));
  process.nextTick(() => console.log("nextTick"));
});
  • nextTick · promise · immediate · timeout
  • promise · nextTick · immediate · timeout
  • nextTick · promise · timeout · immediate
  • timeout আর immediate-এর ক্রম নিশ্চিত নয়

(মাঝারি) Node.js-এর মূল CommonJS script-এ nextTick আর Promise — কী ছাপা হবে?

Promise.resolve().then(() => console.log("P"));
process.nextTick(() => console.log("N"));
console.log("S");
  • S · P · N
  • N · S · P
  • S · N · P
  • ক্রম নিশ্চিত নয়

(মাঝারি) Node.js-এর মূল script-এ timer বনাম setImmediate — একই ফাইল কয়েকবার চালালে কী দেখবেন?

setTimeout(() => console.log("timeout"), 0);
setImmediate(() => console.log("immediate"));
  • সবসময় immediate · timeout
  • দুটিই ছাপা হবে, কিন্তু ক্রম রান থেকে রানে বদলাতে পারে
  • সবসময় timeout · immediate
  • শুধু একটি ছাপা হবে

(মাঝারি) TypeScript-এর টাইপ চলার সময় — tsc দিয়ে compile করে চালালে কী ছাপা হবে?

const n: number = "5" as any;
console.log(typeof n, n + 1);
  • number 6
  • Compile error: Type 'string' is not assignable to type 'number'
  • TypeError
  • string 51

সব প্রশ্নের উত্তর দেওয়া শেষ হলে এখানে আপনার প্রথম চেষ্টার স্কোর দেখাবে:

নিজে চালিয়ে দেখুন — event-এর পথ, delegation আর live তালিকা

এই কোড চলে একটি ছোট HTML-এর ওপর: div#outer-এর ভেতরে ul#list, তার ভেতরে একটি li, তার ভেতরে button#btn. চালানোর আগে অনুমান করুন: stopPropagation যোগ করার পরের দ্বিতীয় ক্লিকে কোন লেখাটি তবু ছাপা হবে?

const [outer, list, btn] = ["#outer", "#list", "#btn"].map((s) => document.querySelector(s));
document.addEventListener("click", () => console.log("৪. document (bubble)"));
outer.addEventListener("click", () => console.log("১. outer (capture)"), true);
outer.addEventListener("click", (e) => console.log("৩. outer (bubble):", e.target.id, e.currentTarget.id));
list.addEventListener("click", (e) => console.log("২. list — delegation, li =", e.target.closest("li").dataset.id));
btn.click();
btn.addEventListener("click", (e) => e.stopPropagation());
console.log("— stopPropagation যোগ করার পরে —");
btn.click();
outer.addEventListener("hi", () => console.log("hi শুনল outer"));
btn.dispatchEvent(new CustomEvent("hi"));
btn.dispatchEvent(new CustomEvent("hi", { bubbles: true }));
const live = list.getElementsByTagName("li");
const fixed = list.querySelectorAll("li");
list.append(document.createElement("li"));
console.log(live.length, fixed.length);

আউটপুট: ১. outer (capture), ২. list — delegation, li = 7, ৩. outer (bubble): btn outer, ৪. document (bubble), — stopPropagation যোগ করার পরে —, ১. outer (capture), hi শুনল outer, 2 1. দ্বিতীয় ক্লিকে capture listener তবু চলেছে — capture ধাপ target-এর আগেই শেষ, আর stopPropagation থামায় শুধু পরের ধাপগুলো। “hi” একবারই শোনা গেছে — শুধু bubbles: true দেওয়া ঘটনাটি। আর সব লেখা sync এসেছে, কারণ click() আর dispatchEvent listener-গুলো সাথে সাথে চালায়।

নিজে চালিয়ে দেখুন — Node.js-এ module আর loop-এর phase

এই প্রোগ্রাম Node 20-এ চলে। এটি প্রথমে দুটি ছোট module ফাইল লিখে নেয় — একটি ES module, একটি CommonJS — তারপর দুটিই ব্যবহার করে, আর শেষে একটি I/O callback-এর ভেতরে চার রকম কাজ নিবন্ধন করে। চালানোর আগে অনুমান করুন: “counter.mjs চলল” কোন লাইনের পরে আসবে?

const fs = require("fs");
fs.writeFileSync("counter.mjs",
  'console.log("counter.mjs চলল");\nexport let count = 0;\nexport function inc() { count++; }\n');
fs.writeFileSync("counter.cjs",
  "let count = 0;\nfunction inc() { count++; }\nmodule.exports = { count, inc };\n");

const cjs = require("./counter.cjs");
cjs.inc(); cjs.inc();
console.log("CommonJS count:", cjs.count);

import("./counter.mjs").then((esm) => {
  esm.inc(); esm.inc();
  console.log("ESM count:", esm.count);
  fs.readFile(__filename, () => {
    setTimeout(() => console.log("৪. setTimeout"), 0);
    setImmediate(() => console.log("৩. setImmediate"));
    Promise.resolve().then(() => console.log("২. promise"));
    process.nextTick(() => console.log("১. nextTick"));
  });
});
console.log("script শেষ — import এখনো চলছে");

আউটপুট: CommonJS count: 0, script শেষ — import এখনো চলছে, counter.mjs চলল, ESM count: 2, তারপর ১. nextTick, ২. promise, ৩. setImmediate, ৪. setTimeout. require sync — ফাইল সাথে সাথে পড়ে চালায়; dynamic import() একটি Promise দেয়, তাই module চলে script শেষ হওয়ার পরে। CommonJS-এর count কপি হওয়া মান (0), ESM-এর count live binding (2). I/O callback-এর ভেতরে ক্রম চারটি নিয়মে বাঁধা: nextTick → promise → check phase (setImmediate) → পরের ঘুরের timers (setTimeout).

৯ · AI-এর যুগে — চালানো আউটপুটই শেষ বিচারক

আজ অনেকে AI-কে কোড দেখিয়ে জিজ্ঞেস করেন “এটা কী ছাপবে?” সাধারণ কোডে উত্তর প্রায়ই ঠিক আসে। কিন্তু এই পাতায় যে জায়গাগুলো অনুশীলন করলেন — then থেকে Promise ফেরত দেওয়ার বাড়তি ধাপ, indexOf বনাম includes-এর hole, base constructor থেকে override করা method, finally-র return, Node-এর nextTick আর ESM-এর ক্রম — সেখানে ভাষা-মডেল প্রায়ই আত্মবিশ্বাসী সুরে ভুল বলে। কারণ সে কোড চালায় না; সে অনুরূপ লেখা দেখে সম্ভাব্য উত্তর অনুমান করে। আর ইন্টারনেটের পুরোনো লেখায় পুরোনো নিয়মই বেশি — ২০১৯-এর আগের await-এর ক্রম, Node 11-এর আগের timer-এর আচরণ, var-যুগের উদাহরণ।

AI-এর উত্তর একটি অনুমান, চালানো আউটপুট একটি পর্যবেক্ষণ।
আর পরিবেশ-নির্ভর কোডে একটি পরিবেশের আউটপুটও পুরো উত্তর নয় — উত্তর হলো নিয়ম আর তার শর্ত।

AI সাধারণত যেখানে JavaScript-এর আউটপুট ভুল বলে

যে ধরনের কোডAI-এর সাধারণ ভুলকীভাবে ধরবেন
Promise-শিকল আর await-এর ক্রমthen-শিকল পালা করে চলে তা ভুলে যায়; thenable ফেরত দেওয়ার বাড়তি ধাপ জানে নাব্রাউজার console-এ বা node-এ হুবহু চালান; প্রতিটি লাইনে একটি অনন্য লেখা রাখুন
thisstrict আর sloppy মিশিয়ে ফেলে; class-এর ভেতরে যে সবসময় strict, তা ভুলে যায়একবার "use strict" ছাড়া, একবার সহ চালান; ফল আলাদা হলে প্রশ্নে পরিবেশ লিখুন
Node.js বনাম ব্রাউজারnextTick, setImmediate আর ESM/CommonJS-এর পার্থক্য উপেক্ষা করে একটি ক্রম লিখে দেয়Node-এ .js আর .mjs — দুইভাবে চালান; মূল script-এর timer বনাম immediate কয়েকবার চালান
Error-এর বার্তাপুরোনো বা অন্য engine-এর বার্তা দেয় (যেমন “Cannot read property 'x' of undefined” — V8-এর পুরোনো রূপ)বার্তা নয়, আগে error type মেলান; বার্তা উদ্ধৃত করলে নিজের চালানো থেকে কপি করুন
console-এর ছাপার ধরনNode-এর [ 1, 2 ] আর DevTools-এর (2) [1, 2] মিশিয়ে ফেলে; MCQ-তে “ভুল” অপশন বানায় শুধু ফরম্যাট বদলেভাবুন মান কী, ফরম্যাট নয়; দরকার হলে JSON.stringify দিয়ে ছাপুন

AI দিয়ে নতুন অনুশীলনের প্রশ্ন বানাতে পারেন — শর্ত একটাই, উত্তর যাচাইয়ের দায়িত্ব আপনার। নিচের prompt-টি AI-কে এমনভাবে বাঁধে, যাতে সে আগে প্রশ্ন দেয়, উত্তর পরে, পরিবেশ স্পষ্ট লেখে, আর “error” বা “পরিবেশ-নির্ভর” উত্তর লুকাতে না পারে:

আমি JavaScript-এর “আউটপুট কী হবে?” ধরনের প্রশ্নের অনুশীলন করছি। বিষয়: [যেমন: Promise-শিকল, async/await আর setTimeout-এর ক্রম]. পরিবেশ ধরে নাও: সর্বশেষ Chrome-এর classic script (strict mode ছাড়া); কোনো প্রশ্ন Node.js-এর হলে স্পষ্ট লেখো "Node 20, CommonJS". ১. ১০টি ছোট সম্পূর্ণ কোড দাও (প্রতিটি ১২ লাইনের মধ্যে, কোনো লাইব্রেরি ছাড়া, যেন হুবহু কপি করে চালানো যায়)। কঠিনতা মেশাও: ৪টি সহজ, ৪টি মাঝারি, ২টি কঠিন। ২. অন্তত ১টির উত্তর SyntaxError (কিছুই চলে না), ১টির উত্তর কিছু আউটপুটের পরে একটি নির্দিষ্ট runtime error (ঠিক type সহ), আর ১টির উত্তর "পরিবেশ-নির্ভর" (কোন শর্তে কী আসে সহ) রাখো। ৩. প্রতিটি console.log-এ আলাদা লেখা রাখো, যাতে ক্রম পরিষ্কার বোঝা যায়। Math.random, Date.now বা নেটওয়ার্ক ব্যবহার কোরো না। ৪. প্রতিটির জন্য ৪টি অপশন দাও, কিন্তু উত্তর এখন বলো না। ৫. শেষে আলাদা অংশে প্রতিটির উত্তর দাও: লাইন ধরে trace, async হলে microtask আর task queue-র অবস্থা ধাপে ধাপে, আর কোন নিয়ম কাজ করছে। ৬. কোনো আচরণ engine বা সংস্করণভেদে বদলালে স্পষ্ট লেখো। নিশ্চিত না হলে "নিশ্চিত নই" বলো, অনুমান করো না। আমি প্রতিটি কোড ব্রাউজারে আর node-এ চালিয়ে তোমার উত্তর মিলিয়ে দেখব।

AI-এর দেওয়া প্রশ্ন বা উত্তর হাতে পেলে — যাচাইয়ের চেকলিস্ট

ভাবনার প্রশ্ন · যে ফাঁদে সবাই পড়ে

JavaScript-এর “আউটপুট কী হবে?” প্রশ্নের পেছনের ছয়টি বড় প্রশ্ন। প্রতিটি নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১JavaScript-এ typeof null “object”, [] + {} “[object Object]” — এই অদ্ভুত নিয়মগুলো কেন আছে, আর কেন ঠিক করা হয় না?

১৯৯৫ সালে Netscape-এ Brendan Eich খুব অল্প সময়ে — প্রচলিত গল্পে দশ দিনে — ভাষাটির প্রথম রূপ বানান। লক্ষ্য ছিল ওয়েব পাতায় ছোট ছোট কাজ, যেমন ফর্ম যাচাই, যা ডিজাইনার আর শখের প্রোগ্রামাররাও লিখতে পারবেন। তাই সিদ্ধান্ত হলো ভাষাটি “ক্ষমাশীল” হবে: টাইপ না মিললে error না দিয়ে নিজেই বদলে নেবে (coercion). ফর্মের ঘর থেকে আসা “5” আর সংখ্যা 2 গুণ করলে 10 — সুবিধাজনক। কিন্তু + একসাথে যোগ আর জোড়া দুটোই করে, তাই “5” + 2 হলো “52” — বিভ্রান্তির জন্ম সেখানেই।

  • typeof null: প্রথম বাস্তবায়নে প্রতিটি মানের সাথে একটি ছোট টাইপ-ট্যাগ রাখা হতো; object-এর ট্যাগ ছিল 0, আর null ছিল শূন্য pointer — তাই typeof তাকে object পড়ত। প্রায় ২০১০ সালে ES Harmony-র আলোচনায় এটি ঠিক করার (typeof null হবে “null”) একটি প্রস্তাব এসেছিল, কিন্তু বাতিল হয়, কারণ অসংখ্য পুরোনো কোড এই ফলের ওপর নির্ভর করে।
  • ==-এর জটিল নিয়ম: null শুধু undefined-এর সমান, কিন্তু >= null-কে 0 বানায় — দুটি আলাদা অ্যালগরিদম। এজন্যই আধুনিক style guide আর ESLint-এর eqeqeq নিয়ম === চায়।
  • “Don't break the web”: ব্রাউজার নির্মাতারা এমন কোনো বদল আনেন না যাতে পুরোনো ওয়েবসাইট ভেঙে যায়। কোটি কোটি পাতা আর কখনো আপডেট হবে না। ভাষার কমিটি TC39 তাই পুরোনো আচরণ না বদলে নতুন, নিরাপদ বিকল্প যোগ করে: ===, Number.isNaN, Object.is, ??, let/const, strict mode.

একটি বিখ্যাত উদাহরণ: ২০১৮ সালে Array.prototype.flatten নামের একটি নতুন method যোগ করতে গিয়ে দেখা গেল, পুরোনো MooTools লাইব্রেরি ব্যবহার করা ওয়েবসাইট ভেঙে যাচ্ছে। সমাধান হিসেবে নামটিই বদলে flat রাখা হয় — মজা করে ঘটনাটিকে “SmooshGate” বলা হয়। অর্থাৎ নতুন নিয়ম আনতেও পুরোনো ওয়েবের সাথে মানিয়ে চলতে হয়।

মূল উপলব্ধি: JavaScript-এর অদ্ভুত আউটপুটগুলো ভুল নয়, ইতিহাসের স্তর। পরীক্ষায় এগুলো চিনতে হবে, কিন্তু নিজের কোডে নতুন, স্পষ্ট বিকল্পগুলো (===, ??, Number(), Array.isArray) ব্যবহার করুন — তাহলে ফাঁদগুলো আপনার কোডে কখনো ঢুকবে না।

প্র ০২একই কোড ব্রাউজারে আর Node.js-এ আলাদা আউটপুট দেয় কেন — পরীক্ষায় উত্তর লেখার সময় কী কী শর্ত উল্লেখ করবেন?

JavaScript ভাষাটি (ECMAScript) একটি, কিন্তু সে চলে কোনো না কোনো host-এর ভেতরে — ব্রাউজার বা Node.js. ভাষা ঠিক করে +, this-এর নিয়ম, Promise আর microtask. আর host ঠিক করে global object কী, timer কীভাবে চলে, event loop-এ কোন কোন phase আছে, console কীভাবে ছাপে। আউটপুট আলাদা হয় সাধারণত পাঁচটি জায়গায়:

  • Top-level this আর global: ব্রাউজারের classic script-এ top-level this হলো window, আর var x window-এর property হয়। Node-এর CommonJS ফাইলে প্রতিটি ফাইল একটি function-এর ভেতরে মোড়ানো, তাই top-level this হলো module.exports (শুরুতে খালি object), আর var global-এ যায় না। ES module-এ (দুই জায়গাতেই) top-level this undefined. সব জায়গায় একই global পেতে globalThis (ES2020).
  • Strict mode: class আর ES module নিজে থেকে strict. খালি fn()-এ this — sloppy-তে global, strict-এ undefined.
  • Event loop: Node-এ libuv-এর phase (timers, poll, check), process.nextTick আর setImmediate আছে, ব্রাউজারে নেই। আবার ব্রাউজারে rendering আর requestAnimationFrame আছে। Node 11 (২০১৮)-এর আগে timer-এর microtask চলত পুরো timers phase শেষে, এখন প্রতিটি timer-এর পরে — ব্রাউজারের মতো।
  • console-এর ছাপা: Node দেখায় [ 1, 2 ], DevTools দেখায় (2) [1, 2], যা চাপলে খোলে — আর DevTools অনেক সময় object-কে খোলার মুহূর্তের অবস্থায় দেখায়, log-এর মুহূর্তের নয়।
  • Error-এর বার্তা: V8 (Chrome, Edge, Node) বলে “Cannot read properties of null (reading 'x')”; Firefox-এর SpiderMonkey বলে অন্যভাবে। Error type (TypeError) সবখানে একই, কারণ সেটি ভাষার নিয়ম।

তাই পরীক্ষায় পরিবেশ-নির্ভর প্রশ্নে এক লাইনের শর্ত লিখে দিন: “ব্রাউজারের classic script, non-strict ধরে নিয়ে — window; strict mode বা module হলে undefined.” এতে উত্তর যেভাবেই মূল্যায়ন হোক, আপনার যুক্তি পরিষ্কার থাকে। আর MCQ-তে চারটি অপশনের মধ্যে যেটি প্রশ্নের দেওয়া পরিবেশের সাথে মেলে, সেটিই বেছে নিন; কোনো পরিবেশ বলা না থাকলে সাধারণত ব্রাউজার আর non-strict ধরা হয় — কিন্তু সন্দেহ থাকলে শর্তটি লিখে দেওয়াই নিরাপদ।

মূল উপলব্ধি: ভাষা আর host আলাদা করে ভাবুন। +, closure, Promise-এর ক্রম সব জায়গায় এক; global, timer-এর phase, console আর error-বার্তা host-ভেদে আলাদা। পরিবেশ-নির্ভর উত্তরে শর্ত লেখা দুর্বলতা নয় — সেটাই বিশেষজ্ঞের উত্তর।

প্র ০৩পরীক্ষার চাপের মধ্যে অচেনা কোড পেলে কোন ক্রমে পড়বেন, যাতে তাড়াহুড়োয় ভুল না হয়?

পরীক্ষার হলে সবচেয়ে বড় শত্রু তাড়াহুড়া — চোখ কোডের ওপর দিয়ে দৌড়ায়, আর মাথা “স্বাভাবিক” উত্তর বসিয়ে দেয়। [10, 9, 1, 100].sort() দেখে মাথা বলে “1, 9, 10, 100”, কারণ জীবনে আমরা সংখ্যা এভাবেই সাজাই। প্রতিকার একটি নির্দিষ্ট ক্রম, যা প্রতিবার একইভাবে মানবেন:

  • ১. আগে parse হবে কি না দেখুন। একই নামে দুটি let/const? function-এর বাইরে return? module ছাড়া top-level await? এগুলো SyntaxError — উত্তর “কিছুই ছাপা হবে না”। এক মিনিট এখানে খরচ করলে বাকি হিসাব বাঁচে।
  • ২. পরিবেশ চিহ্নিত করুন। "use strict" আছে? class-এর ভেতরে? Node-এর জিনিস (require, process)? ES module (import/export)? এগুলো this আর ক্রম বদলায়।
  • ৩. ঘোষণাগুলো আগে তুলে আনুন। প্রতিটি scope-এর var (undefined), function declaration (পুরো), let/const/class (TDZ) — খাতার একপাশে লিখুন। hoisting-এর প্রশ্ন এখানেই অর্ধেক মিটে যায়।
  • ৪. সন্দেহজনক অপারেটরে থামুন। প্রতিটি +, ==, ||/??, ++ আর argument-এর ভেতরে লুকানো কাজ (যেমন console.log(a.splice(1), a)) — বাঁ থেকে ডানে।
  • ৫. প্রতিটি function ডাকে this লিখুন — new, bind/call, obj.method, খালি ডাক, arrow — এই ক্রমে।
  • ৬. Async থাকলে তিন কলাম। আউটপুট, microtask, task. Script শেষে microtask খালি, তারপর একটি task, আবার microtask. নতুন microtask এলে কলামের নিচে যোগ করুন।
  • ৭. শেষে উত্তর আবার মেলান। লেখার আগে প্রশ্ন করুন: space, নতুন লাইন, উদ্ধৃতিচিহ্ন (array-র ভেতরে 'a') ঠিক আছে তো? error-এর পরের লাইন বাদ দিয়েছি তো?

কম্পিউটারের সামনে থাকলে আরও দুটি সাহায্য আছে। DevTools-এর debugger-এ debugger; লিখে বা breakpoint দিয়ে এক লাইন এক লাইন এগোনো যায় — Scope প্যানেলে var-এর undefined আর let-এর TDZ নিজের চোখে দেখা যায়; এটাই আসলে স্বয়ংক্রিয় trace table. আর async ক্রম বুঝতে প্রতিটি callback-এ একটি অনন্য লেখা দিয়ে console.log — সবচেয়ে সস্তা, সবচেয়ে নির্ভরযোগ্য পরীক্ষা।

মূল উপলব্ধি: ভালো trace মানে ধীর trace নয়, নিয়মিত trace. একই সাত ধাপ প্রতিবার মানলে কঠিন প্রশ্নও কয়েকটি সহজ প্রশ্নে ভেঙে যায় — আর ভুল হলে ঠিক কোন ধাপে হলো, তা খুঁজে পাওয়া যায়।

প্র ০৪Task আর microtask — দুটি আলাদা লাইন কেন বানানো হলো? সব callback একই লাইনে রাখলে সমস্যা কী ছিল?

Promise আসার আগে ব্রাউজারে মূলত একটাই ধরনের অপেক্ষার লাইন ছিল — task queue: timer, ক্লিক, নেটওয়ার্কের ঘটনা। ২০১০-এর দশকের শুরুতে Promise জনপ্রিয় হলো (প্রথমে লাইব্রেরি হিসেবে, পরে Promises/A+ নামের একটি সাধারণ নিয়ম অনুযায়ী), আর ES2015-এ ভাষার অংশ হলো। প্রশ্ন উঠল: একটি fulfilled Promise-এর then কখন চলবে?

  • সাথে সাথে (sync) চালানো বিপজ্জনক: তাহলে একই then কখনো sync, কখনো async চলত — Promise আগে থেকে fulfilled কি না তার ওপর। এই অনিশ্চয়তার নাম Zalgo; Promises/A+ তাই নিয়ম করল, then-এর callback সবসময় async চলবে।
  • Task queue-তে পাঠানো ধীর: তাহলে প্রতিটি then-এর মাঝে timer, ক্লিক আর পাতা আঁকা ঢুকে পড়ত; দশ ধাপের একটি শিকল দশটি আলাদা task হয়ে অনেক দেরিতে শেষ হতো, আর মাঝপথের অর্ধেক-বদলানো অবস্থায় পাতা আঁকা হয়ে যেত।
  • সমাধান — microtask: বর্তমান কাজ শেষ হওয়ার ঠিক পরে, পরের task বা পাতা আঁকার আগে চলে এমন একটি আলাদা লাইন। ECMAScript-এ এর নাম “job queue”, HTML spec-এ “microtask queue”. DOM-এর MutationObserver-ও একই লাইন ব্যবহার করে, যাতে অনেকগুলো DOM বদল একসাথে জানানো যায়।

এর একটি দামও আছে: microtask নিজে নতুন microtask যোগ করতে থাকলে task আর rendering কখনো সুযোগ পায় না — পাতা জমে যায় (microtask starvation). তাই বড় হিসাব ভাঙতে microtask নয়, task (setTimeout, scheduler.yield()) ব্যবহার করা হয়। async/await (ES2017) এই ব্যবস্থার ওপরেই বসানো: প্রতিটি await-এর পরের অংশ একটি microtask. প্রথম দিকে প্রতিটি await-এ কয়েকটি বাড়তি microtask লাগত; ২০১৮-১৯-এ নিয়মটি সরল করা হয়, তাই পুরোনো ব্লগের কিছু ক্রমের উত্তর আজ আর মেলে না।

Node.js-এ আরেকটি স্তর আছে: process.nextTick, যা Promise আসার অনেক আগে থেকেই ছিল, আর Promise-এর microtask-এরও আগে চলে। ঐতিহাসিক কারণে এটি রয়ে গেছে; Node-এর ডকুমেন্টেশন নতুন কোডে সাধারণত queueMicrotask পছন্দ করতে বলে।

মূল উপলব্ধি: microtask মানে “এই কাজের অংশ, কিন্তু এখনই নয়”; task মানে “পরের কোনো এক সময়ের আলাদা কাজ”। এই একটি পার্থক্য বুঝলে ক্রমের প্রায় সব প্রশ্ন মিলে যায়।

প্র ০৫0.1 + 0.2 কেন 0.3 নয়, আর টাকা-পয়সার হিসাবে JavaScript-এ কী করবেন?

JavaScript-এর সাধারণ সংখ্যা একটিই টাইপ: ৬৪-বিটের IEEE 754 double — ১৯৮৫ সালে প্রকাশিত একটি মান, যা প্রায় সব CPU আর ভাষা ব্যবহার করে। এতে ১ বিট চিহ্ন, ১১ বিট exponent, আর ৫২ বিট mantissa (সাথে একটি লুকানো বিট, মোট ৫৩ বিট নির্ভুলতা)। সংখ্যা রাখা হয় দুই-ভিত্তিতে। দশমিকে ১/৩ যেমন 0.3333… — কখনো শেষ হয় না, তেমনি দুই-ভিত্তিতে ১/১০ (0.1) শেষ হয় না। তাই 0.1 আসলে রাখা হয় তার খুব কাছের একটি মানে, 0.2-ও তাই। যোগফল 0.3-এর কাছের মানটির সাথে মেলে না: 0.30000000000000004.

  • এটি JavaScript-এর বাগ নয়: C-এর double, Java-র double, Python-এর float — সবখানে একই ফল। পার্থক্য শুধু এই যে JavaScript-এ আলাদা integer টাইপ ছিল না (BigInt আসে ES2020-এ)।
  • নিরাপদ পূর্ণসংখ্যার সীমা: 253 − 1 (Number.MAX_SAFE_INTEGER) পর্যন্ত প্রতিটি পূর্ণসংখ্যা নির্ভুল। এর বেশি হলে পাশাপাশি সংখ্যা আলাদা রাখা যায় না — তাই বড় ID (যেমন কিছু ডেটাবেস বা সোশ্যাল মিডিয়ার ID) JSON-এ string হিসেবে পাঠানো হয়।
  • গোল করার ফাঁদ: (1.005).toFixed(2) “1.00”, কারণ 1.005 মেমরিতে একটু কম। Math.round(-2.5) -2, কারণ Math.round সমান দূরত্বে +∞-এর দিকে যায়।

টাকা-পয়সার নিয়ম সহজ: সবচেয়ে ছোট একক পূর্ণসংখ্যায় রাখুন — ১২৩.৪৫ টাকা নয়, ১২৩৪৫ পয়সা। যোগ-বিয়োগ পূর্ণসংখ্যায় নির্ভুল; শুধু দেখানোর সময় ১০০ দিয়ে ভাগ করে Intl.NumberFormat("bn-BD", { style: "currency", currency: "BDT" }) দিয়ে ফরম্যাট করুন। খুব বড় অঙ্ক বা সুদের মতো হিসাবে BigInt বা একটি decimal লাইব্রেরি। তুলনার সময় সরাসরি === না করে একটি ছোট সহনশীলতা (Math.abs(a - b) < Number.EPSILON) — তবে এটি ছোট মানের জন্যই কাজ করে, বড় মানে আপেক্ষিক সহনশীলতা লাগে।

মূল উপলব্ধি: floating-point মানে “কাছাকাছি”, “ঠিক” নয়। যেখানে ঠিক মান দরকার — টাকা, গণনা, ID — সেখানে পূর্ণসংখ্যা, BigInt বা string ব্যবহার করুন; double রাখুন মাপজোখ আর বিজ্ঞানের হিসাবের জন্য।

প্র ০৬“use strict” কেন এল, আর class ও module-এ কেন নিজে থেকেই strict — এটা আউটপুট প্রশ্নে কতটা বদল আনে?

২০০৯ সালে ES5 প্রকাশের সময় TC39-এর সামনে একটি সমস্যা ছিল: ভাষার কিছু পুরোনো আচরণ স্পষ্টতই ক্ষতিকর — টাইপো করা নামে মান বসালে নীরবে global ভ্যারিয়েবল তৈরি হওয়া, খালি ডাকে this-এর window হয়ে যাওয়া, with statement, read-only property-তে লিখলে চুপচাপ ব্যর্থ হওয়া। কিন্তু এগুলো সরালে পুরোনো ওয়েব ভাঙে। সমাধান: একটি ঐচ্ছিক মোড, যা ফাইল বা function-এর শুরুতে "use strict"; লিখে চালু করতে হয়। লেখাটি একটি সাধারণ string literal, তাই পুরোনো ব্রাউজার একে উপেক্ষা করে — সামনের দিকে মানানসই একটি কৌশল।

  • অঘোষিত নামে মান বসানো: sloppy-তে implicit global, strict-এ ReferenceError.
  • খালি ডাকে this: sloppy-তে global object, strict-এ undefined — তাই হারানো this-এর বাগ strict-এ সাথে সাথে TypeError দিয়ে ধরা পড়ে, sloppy-তে চুপচাপ undefined.
  • নীরব ব্যর্থতা থেকে error: Object.freeze করা object-এ লেখা, read-only property বা getter-ছাড়া property-তে লেখা — strict-এ TypeError.
  • অন্যান্য: function-এ একই নামে দুটি parameter, with, octal literal 010 — strict-এ SyntaxError.

ES2015-এ যখন class আর module এল, তখন নতুন কোডের পুরোনো ভার বইবার কোনো কারণ ছিল না, তাই সিদ্ধান্ত হলো: class-এর শরীর আর ES module-এর পুরো কোড নিজে থেকেই strict. আজকের প্রায় সব প্রজেক্ট (Vite, React, Node-এর .mjs) module ব্যবহার করে, তাই বাস্তবে আপনি প্রায় সবসময় strict mode-এ আছেন — "use strict" না লিখেও।

আউটপুট প্রশ্নে এর প্রভাব বড়: এই পাতার বেশ কয়েকটি প্রশ্নের উত্তর strict বনাম sloppy-তে আলাদা। পরীক্ষার পুরোনো ধাঁধা প্রায়ই sloppy classic script ধরে লেখা (this === window), অথচ বাস্তব কোড strict. তাই উত্তরে শর্তটি লিখে দিন, আর নিজের কোডে কখনো sloppy-নির্ভর আচরণ ব্যবহার করবেন না।

মূল উপলব্ধি: strict mode হলো “চুপচাপ ভুল”-কে “স্পষ্ট error”-এ বদলানোর ব্যবস্থা। প্রশ্নে class, module বা “use strict” দেখলেই this আর অঘোষিত নামের নিয়ম বদলে নিন।

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বে ব্যবহৃত সব শব্দ এখানে একসাথে:

মনে রাখুন

  1. আউটপুট অনুমান নয় — trace table এঁকে লাইন ধরে চালান। আগে দেখুন parse হবে কি না (SyntaxError হলে কিছুই চলে না), তারপর ঘোষণাগুলো তুলে আনুন: var → undefined, function → পুরো, let/const/class → TDZ.
  2. বাঁ থেকে ডানে আর টাইপ বদল: + একটি string পেলেই জোড়া, অন্য অপারেটর সংখ্যায় বদলায়; == টাইপ বদলায়, === বদলায় না; ?? শুধু null/undefined দেখে; সংখ্যা মানে IEEE 754 double.
  3. this ঠিক হয় ডাকার ধরনে: new → bind/call → obj.method → খালি ডাক (strict-এ undefined, sloppy-তে global); arrow function-এর নিজের this নেই। class আর module সবসময় strict.
  4. Async-এর এক নিয়ম: sync (executor আর async function-এর প্রথম await পর্যন্ত) → সব microtask → একটি task → আবার সব microtask. Node-এ nextTick Promise-এর আগে, আর I/O callback-এর ভেতরে setImmediate timer-এর আগে।
  5. উত্তর চার রকম — নির্দিষ্ট আউটপুট, SyntaxError, কিছু আউটপুটের পরে runtime error, আর পরিবেশ-নির্ভর; AI-এর অনুমান নিজে চালিয়ে যাচাই করুন, আর পরিবেশ-নির্ভর হলে শর্ত লিখুন।

পরের পর্বে: কোড পড়ার পরে এবার দ্রুত ধারণার যাচাই — পুরো JavaScript কোর্স থেকে বিষয়ভিত্তিক MCQ প্রশ্নব্যাংক, সময় ধরে অনুশীলনের কৌশলসহ।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান