2.11 কল বাই ভ্যালু এবং কল বাই রেফারেন্স

 

আজকে আমরা আলোচনা করতে যাচ্ছি Primitive এবং Reference টাইপ ডাটার মাঝে কি পার্থক্য এবং এই ডাটা টাইপগুলো কিভাবে কাজ করে। Primitive এবং Reference টাইপকে pass by value এবং pass by reference-ও বলা হয়ে থাকে। একজন জাভাস্ক্রিপ্ট প্রোগ্রামার হিসাবে এই ডাটা টাইপগুলো সম্পর্কে পরিষ্কার জ্ঞান রাখা আবশ্যক।

জাভাস্ক্রিপ্টে দুই টাইপের ডাটা টাইপ আছে।

  • Primitive ডাটা টাইপ
  • Non-primitive বা reference ডাটা টাইপ

জাভাস্ক্রিপ্টে Strings, Numbers, Boolean, Null, undefined, bigint এবং symbol  এই ডাটা টাইপগুলো প্রিমিটিভ ডাটা টাইপ হিসাবে পরিচিত এবং Arrays, Objects, Function নন-প্রিমিটিভ বা রেফারেন্স ডাটা টাইপ হিসাবে পরিচিত। এদের মাঝে মৌলিক পার্থক্য হচ্ছে যে প্রিমিটিভ ডাটা immutable বা অপরিবর্তনীয় এবং নন-প্রিমিটিভ ডাটা mutable বা পরিবর্তনীয়।

প্রিমিটিভ ডাটা টাইপ

প্রমিটিভ ডাটা immutable বা অপরিবর্তনীয় ডাটা টাইপ হিসাবে পরিচিত। কারণ এই ডাটা একবার তৈরি হয়ে গেলে এটি পরিবর্তন করার কোন পথ নেই। তাহলে চলেন আপনাদের প্রমাণ করে দেখাই।

let str1 = "Hi there, I am a string!";
console.log(str1[1]); // "i"

str1[1] = "e";
console.log(str1); // "Hi there, I am a string!"

উপরের কোডটা রান করলে জাভাস্ক্রিপ্টের কারিশমা দেখতে পারবেন। সব কিছু মাথার উপর দিয়ে গেল? আচ্ছা, চলেন ব্যাপারটা ব্যাখ্যা করি। আপনি হাজার বার চাইলেও স্ট্রিং এর ভ্যালু পরিবর্তন করতে পারবেন না। কারণ স্ট্রিং একটি immutable বা অপরিবর্তনীয় ডাটা। একটি কথা মনে রাখবেন যদি স্ট্রিংকে কোন ভেরিয়েবলে অ্যাসাইন করে ফেলেন এবং অ্যাসাইন করার পর স্ট্রিংকে মডিফাই করতে চান, তাহলে আপনি একটি নতুন স্ট্রিং পাবেন। যেমন- .toUpperCase(), .slice(), .trim() ইত্যাদি।

let str1 = "Hi there, I am a string!";
    let newStr = str1.toUpperCase();
    
    console.log(newStr); // HI THERE, I AM A STRING!
    console.log(str1); Hi there, I am a string!

প্রিমিটিভ ডাটা টাইপগুলো একে অপরের সাথে তাদের ভ্যালু দ্বারা তুলনা করে।

let str1 = "Hi there, I am a string!";
let str2 = "Hi there, I am a string!";

console.log(str1 == str2); // true

let num1 = 7;
let num2 = 7;

console.log(num1 == num2); // true

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

let num1 = 7;
let num2 = num1;

console.log(num1); // 7
console.log(num2); // 7

num2 = 8;

console.log(num1); // 7
console.log(num2); // 8

 

নন-প্রিমিটিভ ডাটা টাইপ

নন-প্রিমিটিভ ডাটা mutable বা পরিবর্তনীয়। কারণ একটি নন-প্রিমিটিভ ডাটা তৈরি হয়ে যাওয়ার পরেও তার ভ্যালু পরিবর্তন হতে পারে। আমরা যখন কোন নন-প্রিমিটিভ ডাটা তৈরি করি, তখন সেই ডাটার জন্যে মেমোরিতে একটা অ্যাড্রেস তৈরি হয় এবং সেই অ্যাড্রেসটাকে মনে রেখে কোন এক জায়গায় ভ্যালুগুলোকে স্ট্রোর করে রাখে। তারপর আমাদের যখন দরকার পরে তখন সে ঐ অ্যাড্রেসকে কল করে এবং আমাদের ডাটা প্রদান করে। এটা বুঝতে হলে আপনাকে স্ট্যাক এবং হীপ মেমোরি সম্পর্কে জানতে হবে। তবে আমি যতটুকু বললাম এখন এতটুকু মনে রাখলেই হবে।

let arr1 = ["JavaScript", "React", "Redux", "React-Redux"];
let arr2 = arr1;

console.log(arr1); // ["JavaScript", "React", "Redux", "React-Redux"]
console.log(arr2); // ["JavaScript", "React", "Redux", "React-Redux"]

arr2[3] = "Redux-Toolkit";
console.log(arr1); // ["JavaScript", "React", "Redux", "Redux-Toolkit"]
console.log(arr2); // ["JavaScript", "React", "Redux", "Redux-Toolkit"]

 

নন-প্রিমিটিভ বা রেফারেন্স ডাটাগুলো সব সময় তাদের রেফারেন্স পাস করে। যখন আমরা কোন রেফারেন্স ডাটাকে অন্য কোন ভেরিয়েবলে অ্যাসাইন করি, তখন তার রেফারেন্স কপি হয়। মানে arr1 কে যখন আমরা arr2 তে অ্যাসাইন করি তখন তার রেফারেন্স বা অ্যাড্রেসটাকে কপি করে বা মনে রাখে তার ভ্যালুকে না। তাই দুইটা ভেরিয়েবলের অ্যাড্রেস একই থাকে। তাই যখন আমরা কোন একটি ভেরিয়েবলের ভ্যালু পরিবর্তন করি, তখন দুইটা ভেরিয়েবলেরই ভ্যালু পরিবর্তন হয়ে যায়।

   let obj1 = {
	name: 'JavaScript'
   };
   let obj2 = obj1;
   
   console.log(`${obj1.name}`); // JavaScript
   console.log(`${obj2.name}`); // JavaScript
   
   obj2.name = "React";
   
   console.log(`${obj1.name}`); // React
   console.log(`${obj2.name}`); // React

 

আশা করি, উপরের কোডে কি হচ্ছে সেটা এখন খুব ভাল ভাবেই বুঝতে পারছেন। একটি কথা নন-প্রিমিটিভ ডাটা তাদের রেফারেন্স দ্বারা তুলনা করে।

   let obj1 = {
	name: 'JavaScript'
   };
   
   let obj2 = {
	name: 'React'
   };
   
   console.log(obj1 === obj2); // false

এখানে দুইটা অবজেক্টের একই ভ্যালু কিন্তু যখন আমরা দুইটা অবজেক্টকে একে-অপরের সাথে তুলনা করছি, তখন তারা false রিটার্ন করছে। কারণ তাদের ভ্যালু একই হলেও তাদের অ্যাড্রেস এক না।

   let obj1 = {
	name: 'JavaScript'
   };
   
   let obj2 = obj1;
   
   console.log(obj1 === obj2); // true

 

উদাহরন
  • Call By Value or primitives types এর উদাহরণ
    function multiplyByTen(value) {
      value = value * 10;
    }
    
    var number = 7;
    
    console.log("Before call: number = " + number); // 7
    
    multiplyByTen(number);
    
    console.log("After call: number = " + number); // 7

     

  • Call By Reference of non-primitives types এর উদাহরণ
    function passByReference(person) {
      person.name = "Alex";
    }
    
    var alam = { name: "Alam" };
    
    console.log(alam.name);
    // Alam
    
    passByReference(alam);
    
    console.log(alam.name);
    // Alex
    

     

  • call by value
    function printGreeting(greetName) {  
        greetName = "Hello, " + greetName;  
        console.log("New value: " + greetName);  // Output: Hello, Anik
    }  
    var greetName = 'Anik';
    printGreeting(greetName);  
    console.log("Old value: " + greetName);  // Output: Anik 

     

  • call by reference
    function modifyObj(person) {  
        person.name = 'Fahim';  
        person.age = 26;  
    }  
    
    var person = {  
        name: 'Anik',  
        age: 25
    };   
    
    console.log("Initial name value: " + person.name); // Initial name value: Anik
    modifyObj(person);  
    console.log("Updated name value: " + person.name); // Udated name value: Fahim
    
    
  • মনে করুন, আমি Google Doc এ একটি ফাইল খুলেছি এবং সেটাতে আপনাকেও এডিট করার পারমিশন দিয়েছি। এখন ফাইলটিতে আপনি বা আমি যেই কোন কিছু লিখি বা পরিবর্তন করি না কেন সেটা আমাদের দুজনের দিক থেকেই পরিবর্তন হবে। বিষয়টি এমন হবে না যে আপনি দেখলে এক রকম হবে আর আমি দেখলে অন্য রকম হবে। এই বিষয়টি ঠিক Call by reference এর মতো। আবার অন্যদিকে, আমার কাছে থাকা একটি ফাইলের কপি আমি আপনাকে দিলাম অর্থাৎ কপি মূলত দুটি, একটি আপনার আর অন্যটি আমার। সুতরাং আমি যদি আমার ফাইলে কোন পরিবর্তন করি তবে সেটি শুধুমাত্র আমার ফাইলেই পরিবর্তন হবে, আপনারটা যেমন ছিল বা আপনি যেভাবে পরিবর্তন করেছেন সেভাবেই থাকবে। আমাদের কারো পরিবর্তন একে অন্যের উপর কোন প্রভাব ফেলবে না। এই বিষয়টি Call by value এর মতো। আশাকরি এখন বিষয় দুটি বুঝতে কোন সমস্যা হবে না। চলুন একটা উদাহরণ দেখিঃ
    // Call By Value Part 
    let primitiveData = 200;
    
    (function callByValue(primitiveData) {
        primitiveData = 404;
    })(primitiveData);
    
    console.log(primitiveData); // Output: 200
    
    
    // Call By Reference Part 
    let nonPrimitiveData = ['Abid', 'Shahan', 'Imrul', 'Biplob'];
    
    (function callByReference(nonPrimitiveData) {
        nonPrimitiveData[0] = 'Tareq';
        nonPrimitiveData.push('Tajnur');
    })(nonPrimitiveData);
    
    console.log(nonPrimitiveData); // Output: [ 'Tareq', 'Shahan', 'Imrul', 'Biplob', 'Tajnur' ]

     

এসো নিজে করি
  • জাভাস্ক্রিপ্টে pass by value এবং pass by reference মধ্যে পার্থক্য কী?
  • নিচের কোডের আউটপুট কি হবে?
    var myObject = {
      price: 20.99,
      get_price: function () {
        return this.price;
      },
    };
    var customObject = Object.create(myObject);
    customObject.price = 19.99;
    delete customObject.price;
    console.log(customObject.get_price());
  • নিচের কোডের আউটপুট কি হবে?
    (function (a) {
      arguments[0] = 10;
      return a;
    })(5);

     

  • নিচের কোডের আউটপুট কি হবে?
    function Car(color) {
      this.color = color;
    }
    var lada = new Car('Black');
    Car.prototype.currentGear = 1;
    console.log(++lada.currentGear);
    console.log(Car.prototype.currentGear);

     

  • নিচের কোডের আউটপুট কি হবে?
    var User = function () {};
    
    User.prototype.attributes = {
      isAdmin: false,
    };
    
    var admin = new User('Sam'),
      guest = new User('Bob');
    
    admin.attributes.isAdmin = true;
    
    console.log(admin.attributes.isAdmin);
    console.log(guest.attributes.isAdmin);