JavaScript program-এ কোনো ভুলের কারণে code প্রত্যাশিতভাবে কাজ না করলে সেই ভুল খুঁজে বের করে ঠিক করার প্রক্রিয়াকে Debugging বলা হয়।
সহজভাবে বললে, program কীভাবে চলছে তা পর্যবেক্ষণ করে কোথায় সমস্যা হচ্ছে তা শনাক্ত করা এবং প্রয়োজনীয় পরিবর্তন করে program-কে সঠিকভাবে চালানোই Debugging-এর মূল উদ্দেশ্য।
Debugging = Error খুঁজে বের করা, কারণ বোঝা এবং code ঠিক করা।Browser-এর Developer Tools ব্যবহার করে JavaScript code debug করা যায়।
let number = 10; let result = number * 2; console.log(result);
JavaScript debugging-এর সবচেয়ে সহজ পদ্ধতিগুলোর একটি হলো console.log() ব্যবহার করা। কোনো variable বা expression-এর বর্তমান value console-এ দেখার জন্য এটি ব্যবহার করা যায়।
Code-এর কোন অংশ পর্যন্ত execution হয়েছে এবং কোনো variable-এ কী value রয়েছে তা বোঝার জন্য console.log() বিশেষভাবে উপকারী।
console.log() = Program-এর তথ্য Console-এ দেখার সহজ উপায়।নিচের উদাহরণে number এবং result-এর value Console-এ দেখা হচ্ছে।
let number = 20; console.log("number =", number); let result = number + 10; console.log("result =", result);
JavaScript code-এ এমন কোনো সমস্যা থাকলে যার কারণে program সঠিকভাবে execute করতে পারে না বা প্রত্যাশিত result পাওয়া যায় না, তাকে সাধারণভাবে Error বলা হয়।
Syntax ভুল, Reference ভুল বা program-এর logic-এর ভুল—বিভিন্ন কারণে সমস্যা তৈরি হতে পারে। Error message এবং Console-এর তথ্য দেখে সমস্যার কারণ বোঝা সহজ হয়।
Error message debugging-এর সময় সমস্যার জায়গা ও কারণ বুঝতে সাহায্য করে।নিচের code-এ undefined variable ব্যবহার করার কারণে ReferenceError তৈরি হবে।
console.log(userName);
JavaScript-এর syntax বা লেখার নিয়ম সঠিক না হলে SyntaxError হতে পারে। যেমন bracket, quote বা statement-এর structure ভুল হলে browser code parse করতে সমস্যা করতে পারে।
এই ধরনের সমস্যা সমাধানের জন্য Console-এ দেখানো error message এবং line number লক্ষ্য করা যায়।
Syntax Error = JavaScript-এর নির্ধারিত syntax অনুসরণ না করার কারণে তৈরি হওয়া সমস্যা।নিচের উদাহরণে closing parenthesis না থাকায় syntax সমস্যা তৈরি হবে।
function add(a, b { return a + b; }
যে variable বা function-এর reference পাওয়া যাচ্ছে না, সেটি ব্যবহার করলে ReferenceError হতে পারে। সাধারণত variable declaration-এর আগে বা ভুল নাম ব্যবহার করলে এই ধরনের সমস্যা দেখা যায়।
Variable-এর নাম declaration-এর সঙ্গে মিলিয়ে দেখা এবং variable কোথায় তৈরি হয়েছে তা পরীক্ষা করা ReferenceError খুঁজে বের করার সহজ উপায়।
ReferenceError = প্রয়োজনীয় variable বা function-এর reference না পাওয়া।নিচের উদাহরণে total নামের variable ব্যবহার করা হলেও সেটি declare করা হয়নি।
let price = 500; console.log(total);
JavaScript-এর debugger statement ব্যবহার করে browser-এর Developer Tools-এর debugger-এ program-এর execution থামানো যায়। এটি code-এর নির্দিষ্ট জায়গা থেকে execution পর্যবেক্ষণ করতে সাহায্য করে।
Developer Tools খোলা থাকলে debugger statement execution-কে সেই জায়গায় pause করতে পারে। এরপর variable এবং execution flow পরীক্ষা করা যায়।
debugger; = Debugging-এর সময় code execution pause করার একটি statement।নিচের উদাহরণে debugger statement-এর কারণে execution নির্দিষ্ট জায়গায় থামতে পারে।
let price = 1000; let quantity = 2; debugger; let total = price * quantity; console.log(total);
Browser Developer Tools-এর Sources বা Debugger panel-এ breakpoint সেট করা যায়। Breakpoint হলো code-এর এমন একটি জায়গা যেখানে execution সাময়িকভাবে থেমে যায়।
Breakpoint ব্যবহার করে code line-by-line পর্যবেক্ষণ করা এবং variable-এর value পরীক্ষা করা যায়।
Breakpoint = নির্দিষ্ট code line-এ execution সাময়িকভাবে থামানোর স্থান।নিচের function-এর execution পর্যবেক্ষণ করতে debugger statement ব্যবহার করে breakpoint-এর ধারণা বোঝা যায়।
function calculateTotal(price, quantity) { let total = price * quantity; debugger; return total; } console.log(calculateTotal(500, 3));
Debugger-এ code pause করার পর Step Over, Step Into এবং Step Out-এর মতো control ব্যবহার করে program-এর execution পর্যবেক্ষণ করা যায়। এগুলো function call এবং পরবর্তী statement কীভাবে execute হচ্ছে তা বুঝতে সাহায্য করে।
বিশেষ করে জটিল function বা একাধিক function call থাকা code-এর ক্ষেত্রে ধাপে ধাপে execution দেখা debugging সহজ করতে পারে।
Step-by-step debugging = Code-এর execution ধাপে ধাপে অনুসরণ করা।নিচের উদাহরণে function call-এর execution পর্যবেক্ষণ করা যায়।
function multiply(a, b) { return a * b; } function calculate() { const result = multiply(10, 5); return result; } debugger; console.log(calculate());
সব debugging problem syntax বা runtime error-এর কারণে হয় না। অনেক সময় code কোনো error না দেখিয়েও ভুল result দিতে পারে। এই ধরনের সমস্যাকে সাধারণভাবে Logic Error বলা হয়।
Logic Error খুঁজে বের করতে expected result এবং actual result তুলনা করা, variable-এর value পরীক্ষা করা এবং code-এর প্রতিটি ধাপ অনুসরণ করা প্রয়োজন।
Logic Error = Code চলে, কিন্তু প্রত্যাশিত ফলাফল পাওয়া যায় না।নিচের উদাহরণে total হিসাবের formula ভুল হওয়ায় result প্রত্যাশিত হবে না।
let price = 500; let quantity = 2; let total = price + quantity; console.log(total);
Effective debugging-এর জন্য প্রথমে problem-টি নির্দিষ্টভাবে শনাক্ত করা, তারপর error message বা result পরীক্ষা করা এবং code-এর ছোট ছোট অংশ যাচাই করা উপকারী। একসঙ্গে অনেক পরিবর্তন না করে ধাপে ধাপে পরিবর্তন করলে সমস্যার কারণ বোঝা সহজ হয়।
Console, debugger statement, breakpoint এবং browser Developer Tools একসঙ্গে ব্যবহার করলে JavaScript code-এর execution সম্পর্কে পরিষ্কার ধারণা পাওয়া যায়।
মনে রাখুন: Debugging হলো অনুমান করে code বদলানো নয়; evidence দেখে সমস্যার কারণ শনাক্ত করার প্রক্রিয়া।নিচের উদাহরণে console.log() ব্যবহার করে প্রতিটি ধাপে variable-এর value পরীক্ষা করা হয়েছে।
function calculateTotal(price, quantity) { console.log("price =", price); console.log("quantity =", quantity); const total = price * quantity; console.log("total =", total); return total; } console.log(calculateTotal(500, 2));
Inheritance ব্যবহার করার সময় child class তার parent class-এর কোনো method নিজের প্রয়োজন অনুযায়ী নতুনভাবে define করলে তাকে Method Overriding বলা হয়।
Polymorphism বোঝার জন্য method overriding একটি গুরুত্বপূর্ণ ধারণা। Parent class-এ একটি common method রাখা যায় এবং child class সেই method-এর আলাদা implementation দিতে পারে।
Method Overriding = Parent method-কে Child class-এ নতুন implementation দেওয়া।নিচের উদাহরণে Animal class-এর sound() method Dog class-এ override করা হয়েছে।
class Animal { sound() { return "Animal sound"; } } class Dog extends Animal { sound() { return "Woof!"; } } const dog = new Dog(); console.log(dog.sound());
একটি parent class-এর common method বিভিন্ন child class-এ আলাদাভাবে implement করা যায়। ফলে একই method call বিভিন্ন object-এর জন্য ভিন্ন result দিতে পারে।
এভাবে application-এ common structure বজায় রেখেও object-এর নিজস্ব behavior তৈরি করা যায়।
একটি common method + বিভিন্ন implementation = Polymorphic Behaviorনিচের উদাহরণে Dog, Cat এবং Cow একই sound() method ব্যবহার করলেও তাদের output আলাদা।
class Animal { sound() { return "Animal sound"; } } class Dog extends Animal { sound() { return "Woof!"; } } class Cat extends Animal { sound() { return "Meow!"; } } class Cow extends Animal { sound() { return "Moo!"; } } console.log(new Dog().sound()); console.log(new Cat().sound()); console.log(new Cow().sound());
JavaScript-এ Java বা C#-এর মতো strict interface keyword নেই। তবে একটি common method structure অনুসরণ করে বিভিন্ন class-কে একই ধরনের interface-এর মতো ব্যবহার করা যায়।
এর ফলে calling code জানে কোন method ব্যবহার করতে হবে, কিন্তু প্রতিটি class সেই method কীভাবে কাজ করবে তা নিজের মতো নির্ধারণ করতে পারে।
Common Interface = একই ধরনের method contract, কিন্তু implementation ভিন্ন হতে পারে।নিচের উদাহরণে Payment-এর বিভিন্ন class pay() method প্রকাশ করছে।
class CardPayment { pay(amount) { return "Paid " + amount + " by card"; } } class CashPayment { pay(amount) { return "Paid " + amount + " by cash"; } } console.log(new CardPayment().pay(1000)); console.log(new CashPayment().pay(1000));
Inheritance parent class-এর property ও method child class-এ reuse করার সুযোগ দেয়। Polymorphism সেই common method-কে child class অনুযায়ী ভিন্ন behavior দিতে সাহায্য করে।
অর্থাৎ inheritance structure তৈরি করতে সাহায্য করে এবং overriding-এর মাধ্যমে polymorphic behavior প্রকাশ করা যায়।
Inheritance তৈরি করে সম্পর্ক; Polymorphism প্রকাশ করে ভিন্ন behavior।নিচের উদাহরণে Shape parent class এবং Rectangle ও Circle child class।
class Shape { area() { return 0; } } class Rectangle extends Shape { constructor(width, height) { super(); this.width = width; this.height = height; } area() { return this.width * this.height; } } class Circle extends Shape { constructor(radius) { super(); this.radius = radius; } area() { return Math.PI * this.radius * this.radius; } } console.log(new Rectangle(10, 5).area()); console.log(new Circle(5).area());
একটি function এমনভাবে লেখা যায় যাতে সেটি নির্দিষ্ট একটি class-এর উপর নির্ভর না করে একটি common method ব্যবহার করে। তখন বিভিন্ন object একই function-এর মাধ্যমে নিজের behavior প্রকাশ করতে পারে।
এটি code reuse এবং flexible design তৈরিতে সাহায্য করে।
একটি function + common method = বিভিন্ন object-এর সঙ্গে কাজ করার সুযোগ।নিচের উদাহরণে printSound() function যেকোনো object-এর sound() method ব্যবহার করছে।
class Dog { sound() { return "Woof!"; } } class Cat { sound() { return "Meow!"; } } function printSound(animal) { console.log(animal.sound()); } printSound(new Dog()); printSound(new Cat());
Child class method override করার পরেও parent class-এর একই method-এর functionality প্রয়োজন হলে super ব্যবহার করা যায়। এতে parent behavior-এর সঙ্গে child-এর অতিরিক্ত behavior যোগ করা সম্ভব।
এটি বিশেষভাবে useful যখন child class পুরোপুরি নতুন behavior না দিয়ে parent behavior-এর উপর ভিত্তি করে কাজ বাড়াতে চায়।
super.methodName() = Parent class-এর method call করার একটি উপায়।নিচের উদাহরণে Dog class parent sound() method-এর result-এর সঙ্গে নিজের text যোগ করছে।
class Animal { sound() { return "Animal sound"; } } class Dog extends Animal { sound() { return super.sound() + " and Dog barks"; } } const dog = new Dog(); console.log(dog.sound());
বাস্তব application-এ একই কাজের বিভিন্ন implementation থাকলে Polymorphism ব্যবহার করা যেতে পারে। যেমন payment, notification, storage বা report system-এ একই method name-এর মাধ্যমে বিভিন্ন service ব্যবহার করা যায়।
ফলে মূল application logic-কে প্রতিটি implementation-এর internal detail জানার প্রয়োজন কমে যায়।
Common behavior-এর জন্য common method এবং service অনুযায়ী আলাদা implementation ব্যবহার করা যায়।নিচের উদাহরণে Email ও SMS notification একই send() method প্রকাশ করছে।
class EmailNotification { send(message) { return "Email: " + message; } } class SMSNotification { send(message) { return "SMS: " + message; } } function notify(service, message) { console.log(service.send(message)); } notify(new EmailNotification(), "Hello"); notify(new SMSNotification(), "Hello");
Polymorphism বড় application-এ flexible এবং reusable code structure তৈরি করতে সাহায্য করতে পারে। একই interface বা method ব্যবহার করে বিভিন্ন implementation পরিচালনা করা সম্ভব হয়।
নতুন implementation যোগ করার সময় existing calling code অপরিবর্তিত রাখা সম্ভব হলে application maintain করা সহজ হতে পারে।
Polymorphism code-কে flexible, reusable এবং extensible করতে সাহায্য করতে পারে।নিচের উদাহরণে বিভিন্ন payment object একই pay() method-এর মাধ্যমে ব্যবহার করা হচ্ছে।
class CardPayment { pay(amount) { return "Card payment: " + amount; } } class MobilePayment { pay(amount) { return "Mobile payment: " + amount; } } function makePayment(payment, amount) { console.log(payment.pay(amount)); } makePayment(new CardPayment(), 2000); makePayment(new MobilePayment(), 2000);
JavaScript-এ Polymorphism বুঝতে হলে common method, inheritance এবং method overriding-এর সম্পর্ক বোঝা গুরুত্বপূর্ণ। একই method name বিভিন্ন object-এ ভিন্ন behavior প্রকাশ করতে পারে।
সব Polymorphism inheritance-এর মাধ্যমে হতে হবে এমন নয়। JavaScript-এর flexible object model-এর কারণে common method থাকা আলাদা object-ও একই function-এর সঙ্গে কাজ করতে পারে।
মনে রাখুন: Polymorphism-এর মূল ধারণা হলো একই ধরনের operation-কে বিভিন্ন object তাদের নিজস্বভাবে সম্পাদন করতে পারে।নিচের উদাহরণে Dog ও Cat একই sound() method প্রকাশ করছে এবং printSound() function উভয়ের সঙ্গেই কাজ করছে।
class Dog { sound() { return "Woof!"; } } class Cat { sound() { return "Meow!"; } } function printSound(animal) { console.log(animal.sound()); } printSound(new Dog()); printSound(new Cat());