|
| 1 | +# অবজেক্ট রেফারেন্স এবং কপি করা |
| 2 | + |
| 3 | +অবজেক্ট এবং প্রিমিটিভ দের মধ্যে অন্যতম পার্থক্য হলো যে অবজেক্ট গুলি কপি ও সংরখ্যন হয় রেফারেন্স এর মাধ্যমে, যেখানে প্রিমিটিভ মানঃ স্ট্রিং, বুলিয়ান, ইত্যাদি -- যেগুলো সবসময় "সম্পুর্ন মান" হিসেবে কপি হয়। |
| 4 | + |
| 5 | +একটি মান কপি করলে কি হয় তা একটু গভীরভাবে দেখলেই আমরা এটি আরো ভালোভাবে বুঝতে পারব। |
| 6 | + |
| 7 | +স্ট্রিং এর মত একটি প্রিমিটিভ নিয়েই শুরু করা যাক। |
| 8 | + |
| 9 | +এখানে আমরা `message` এর একটি কপি কে `phrase` এ রাখলামঃ |
| 10 | + |
| 11 | +```js |
| 12 | +let message = "Hello!"; |
| 13 | +let phrase = message; |
| 14 | +``` |
| 15 | + |
| 16 | +এর ফলে আমরা দুটি স্বাধীন ভেরিয়েবল আছে, প্রতিটি "হেলো" স্ট্রিংটি সংরক্ষণ করছে । |
| 17 | + |
| 18 | + |
| 19 | + |
| 20 | +খুব স্বাবাভিক ই মনে হচ্ছে, তাই না? |
| 21 | + |
| 22 | +অবজেক্ট রা এমন নয়। |
| 23 | + |
| 24 | +**একটি অবজেক্ট এর জন্য নির্ধারিত ভেরিয়েবল সেই অবজেক্ট কে সংরক্ষণ করে না, বরং এর ঠিকানা সংরক্ষণ করে, অন্য কথায় এটির একটি "রেফারেন্স"।** |
| 25 | + |
| 26 | +এমন একটি ভেরিয়েবল এর উদাহরণ দেখা যাকঃ |
| 27 | + |
| 28 | +```js |
| 29 | +let user = { |
| 30 | + name: "John" |
| 31 | +}; |
| 32 | +``` |
| 33 | + |
| 34 | +এটি স্মৃতি তে কিভাবে সংরক্ষণ করা হয় তা নিচের ছবিতে দেখানো হলোঃ |
| 35 | + |
| 36 | + |
| 37 | + |
| 38 | +অবজেক্ট টি স্মৃতির কোথাও সংরক্ষণ করা আছে (ডানে), আর `user` ভেরিয়েবল এর কাছে এর একটি রেফারেন্স আছে। |
| 39 | + |
| 40 | +আমরা `user` এর মতো অবজেক্ট কে একটি কাগজের টুকরো হিসেবে ভাবতে পারি, যাতে ঠিকানা লেখা আছে । |
| 41 | + |
| 42 | +যখন আমরা অবজেক্ট এর উপরে কোন কাজ করি, যেমন `user.name` প্রপার্টি কে নেয়া, জাভাস্ক্রিপ্ট ইঞ্জিন ঠিকানা থেকে অবজেক্ট টি বের তার উপরে কাজ টি সম্পাদন করে । |
| 43 | + |
| 44 | +এটি গুরুতপুর্ন কারণ |
| 45 | + |
| 46 | +**যখন কোনও বস্তুর ভেরিয়েবল কপি করা হয় - রেফারেন্সটি কপি হয়, বস্তুটি নকল হয় না ।** |
| 47 | + |
| 48 | +যেমন : |
| 49 | + |
| 50 | +```js no-beautify |
| 51 | +let user = { name: "John" }; |
| 52 | + |
| 53 | +let admin = user; // রেফারেন্স কপি হলো |
| 54 | +``` |
| 55 | + |
| 56 | +এখন আমাদের দুটি ভেরিয়েবল রয়েছে, প্রত্যেকেই একই বস্তুর রেফারেন্স: |
| 57 | + |
| 58 | + |
| 59 | + |
| 60 | +আমরা দেখতে পাচ্ছি যে, অবজেক্ট একটাই আছে কিন্তু এখন একে দুটি ভেরিয়েবল রেফারেন্স করছে । |
| 61 | + |
| 62 | +আমরা এই দুটি ভেরিয়েবল এর যেকোনো টি ব্যাবহার করে অবজেক্ট টি এক্সেস করতে পারি ও এর ভেতরের কন্টেন্ট বা ডেটা গুলি পরিবর্তন করতে পারি। |
| 63 | + |
| 64 | +```js run |
| 65 | +let user = { name: 'John' }; |
| 66 | + |
| 67 | +let admin = user; |
| 68 | + |
| 69 | +*!* |
| 70 | +admin.name = 'Pete'; // এডমিন রেফারেন্সের এর মাধ্যমে পরিবর্তন হলো |
| 71 | +*/!* |
| 72 | + |
| 73 | +alert(*!*user.name*/!*); // 'Pete', পরিবর্তন টি "user" রেফারেন্স থেকে দেখা যাচ্ছে |
| 74 | +``` |
| 75 | +
|
| 76 | +
|
| 77 | +উপরের উদাহরণটি প্রমাণ করে যে এখানে কেবল একটি অবজেক্ট রয়েছে। যেন আমাদের একি কক্ষের দুটি চাবি আছে আর আমরা একটি চাবি (`admin`) দিয়ে কক্ষে প্রবেশ করেছি। পরে অন্যটি (`user`) দিয়ে কক্ষের ভেতরে দেখেছি। |
| 78 | +
|
| 79 | +## রেফারেন্স এর মাধ্যমে তুলনা |
| 80 | +
|
| 81 | +যদি দুটি অবজেক্ট একই বস্তু হয়, শুধুমাত্র তাহলেই তারা "ইকুয়াল"। |
| 82 | +
|
| 83 | +যেমন, এখানে `a` এবং `b` একই অবজেক্ট কে রেফারেন্স করে, সুতরাং তারা ইকুয়াল: |
| 84 | +
|
| 85 | +```js run |
| 86 | +let a = {}; |
| 87 | +let b = a; // রেফারেন্স কপি হোলো |
| 88 | + |
| 89 | +alert( a == b ); // true, দুটি অবজেক্ট ই সমান |
| 90 | +alert( a === b ); // true |
| 91 | +``` |
| 92 | +
|
| 93 | +আর এখানে দুটি অবজেক্ট সমান নয়, যদিও তারা দেখতে একই (দুজনই খালি) : |
| 94 | +
|
| 95 | +```js run |
| 96 | +let a = {}; |
| 97 | +let b = {}; // দুটি স্বাধীন অবজেক্ট |
| 98 | + |
| 99 | +alert( a == b ); // false |
| 100 | +``` |
| 101 | +
|
| 102 | +`obj1 > obj2` এর মত তুলনা এর জন্য অথবা কোন প্রিমিটিভ এর সাথে তুলনা করার জন্য `obj == 5`, অবজেক্ট কে প্রিমিটিভ এ রূপান্তর করা হয়। অবজেক্ট গুলোকে কিভাবে তুলনা করা হয় তা সম্পর্কে আমরা শিগ্রই জানব, কিন্তু সত্যি বলতে এই ধরনের তুলনা খুব কমই প্রয়োজন হয়, সাধারণত এগুলি ভুলক্রমে চলে আসে। |
| 103 | +
|
| 104 | +## ক্লোন করা ও মিলিত করা, Object.assign |
| 105 | +
|
| 106 | +তো আমরা জানলাম অবজেক্ট ভেরিএবল কে কপি করলে তা শুধু একটি নতুন রেফারেন্স তৈরি করে। |
| 107 | +
|
| 108 | +কিন্তু আমাদের যদি অবজেক্ট এর স্বাধীন নকল বা ক্লোন তৈরি করতে হয় তাহলে আমরা কি করব? |
| 109 | +
|
| 110 | +তাও সম্ভব কিন্তু একটু কঠিন, কারণ এই কাজ করার জন্য জাভাস্ক্রিপ্ট এর কোন অন্তর্নির্মিত মেথড নেই। |
| 111 | +আসলে এটি খুব কমই প্রয়োজন হয়। রেফারেন্সে কপি করাই বেশিরভাগ সময় যথেষ্ট। |
| 112 | +
|
| 113 | +কিন্তু আমরা যদি আসলেই এটি চাই তাহলে আমাদের নতুন একটি অবজেক্ট বানাতে হবে, ও মুল অবজেক্ট টির সম্পূর্ণ কাঠামো কে নকল করে এর সকল প্রপার্টির প্রিমিটিভ স্তরে প্রতিলিতি তৈরি করতে হবে। |
| 114 | +
|
| 115 | +যেমন: |
| 116 | +
|
| 117 | +```js run |
| 118 | +let user = { |
| 119 | + name: "John", |
| 120 | + age: 30 |
| 121 | +}; |
| 122 | + |
| 123 | +*!* |
| 124 | +let clone = {}; // নতুন খালি অবজেক্ট |
| 125 | + |
| 126 | +// এখন user অবজেক্ট এর সকল প্রপার্টি কে clone অবজেক্ট এ কপি করি |
| 127 | +for (let key in user) { |
| 128 | + clone[key] = user[key]; |
| 129 | +} |
| 130 | +*/!* |
| 131 | + |
| 132 | + |
| 133 | +// এখন ক্লোন হচ্ছে একটি স্বাধীন অবজেক্ট যার কন্টেন্ট ইউজার এর সমান |
| 134 | +clone.name = "Pete"; // ডাটা পরিবর্তন করলাম |
| 135 | + |
| 136 | +alert( user.name ); // মুল অবজেক্ট এ এখনো John |
| 137 | +``` |
| 138 | +
|
| 139 | +আমরা এর জন্য [Object.assign](mdn:js/Object/assign) মেথড টিও ব্যাবহার করতে পারি। |
| 140 | +
|
| 141 | +এর সিনট্যাক্স হলো: |
| 142 | +
|
| 143 | +```js |
| 144 | +Object.assign(dest, [src1, src2, src3...]) |
| 145 | +``` |
| 146 | +
|
| 147 | +- প্রথম আর্গুমেন্ট `dest` হলো টার্গেট অবজেক্ট । |
| 148 | +- বাকি আর্গুমেন্ট গুলো `src1, ..., srcN` (যতো প্রয়োজন দেয়া যাবে) হলো মুল অবজেক্ট। |
| 149 | +- এটি মুল অবজেক্ট এর সকল প্রপার্টি `src1, ..., srcN` টার্গেট `dest` এ কপি করে। অন্য কথায়, দ্বিতীয় আর্গুমেন্ট থেকে বাকি সকল আর্গুমেন্ট এর প্রপার্টি গুলো প্রথম অবজেক্ট এ কপি হয়। |
| 150 | +- এই কল টি `dest` কে রিটার্ন করে। |
| 151 | +
|
| 152 | +আমরা এটি ব্যাবহার করে একাধিক অবজেক্টকে একটি অবজেক্ট এ মিলিত করতে পারি: |
| 153 | +```js |
| 154 | +let user = { name: "John" }; |
| 155 | + |
| 156 | +let permissions1 = { canView: true }; |
| 157 | +let permissions2 = { canEdit: true }; |
| 158 | + |
| 159 | +*!* |
| 160 | +// permissions1 ও permissions2 এর সকল প্রপার্টি কে user এ কপি করে |
| 161 | +Object.assign(user, permissions1, permissions2); |
| 162 | +*/!* |
| 163 | + |
| 164 | +// এখন user = { name: "John", canView: true, canEdit: true } |
| 165 | +``` |
| 166 | +
|
| 167 | +কপি করা প্রপার্টি যদি ইতিমধ্যেই থেকে থাকে থাকলে এটি ওভাররাইট হয়ে যাবে: |
| 168 | +
|
| 169 | +```js run |
| 170 | +let user = { name: "John" }; |
| 171 | + |
| 172 | +Object.assign(user, { name: "Pete" }); |
| 173 | + |
| 174 | +alert(user.name); // এখন user = { name: "Pete" } |
| 175 | +``` |
| 176 | +
|
| 177 | +আমরা `for..in` এর জায়গায় `Object.assign` ব্যাবহার করে সাধারণ ক্লোনিং করতে পারি : |
| 178 | +
|
| 179 | +```js |
| 180 | +let user = { |
| 181 | + name: "John", |
| 182 | + age: 30 |
| 183 | +}; |
| 184 | + |
| 185 | +*!* |
| 186 | +let clone = Object.assign({}, user); |
| 187 | +*/!* |
| 188 | +``` |
| 189 | +
|
| 190 | +এটি `user` এর সকল প্রপার্টি কে খালি অবজেক্ট এ কপি করে তাকে রিটার্ন করে । |
| 191 | +
|
| 192 | +## অভ্যন্তরীণ ক্লোনিং (Nested cloning) |
| 193 | +
|
| 194 | +এতক্ষণ পর্যন্ত আমরা ধরে নিয়েছিলাম যে `user` এর সকল প্রপার্টি ই প্রিমিটিভ । কিন্তু প্রপার্টি গুলো তো অন্যান্য অবজেক্ট এর রেফারেন্স ও হতে পারে । সেক্ষেত্রে আমরা কি করবো? |
| 195 | +
|
| 196 | +যেমন: |
| 197 | +```js run |
| 198 | +let user = { |
| 199 | + name: "John", |
| 200 | + sizes: { |
| 201 | + height: 182, |
| 202 | + width: 50 |
| 203 | + } |
| 204 | +}; |
| 205 | + |
| 206 | +alert( user.sizes.height ); // 182 |
| 207 | +``` |
| 208 | +
|
| 209 | +এখন এটি `clone.sizes = user.sizes` কে কপি করার জন্য যথেষ্ট নয়, কারণ `user.sizes` হলো একটি অবজেক্ট, এটি রেফারেন্সের মাধ্যমে কপি হবে। সুতরাং `clone` ও `user` একই size শেয়ার করবে: |
| 210 | +
|
| 211 | +এমন: |
| 212 | +
|
| 213 | +```js run |
| 214 | +let user = { |
| 215 | + name: "John", |
| 216 | + sizes: { |
| 217 | + height: 182, |
| 218 | + width: 50 |
| 219 | + } |
| 220 | +}; |
| 221 | + |
| 222 | +let clone = Object.assign({}, user); |
| 223 | + |
| 224 | +alert( user.sizes === clone.sizes ); // true, একই অবজেক্ট |
| 225 | + |
| 226 | +// user এবং clone size কে শেয়ার করে |
| 227 | +user.sizes.width++; // একটি জায়গা থেকে প্রপার্টি পরিবর্তন |
| 228 | +alert(clone.sizes.width); // 51, অন্য জায়গায় রেসাল্ট দেখা |
| 229 | +``` |
| 230 | +
|
| 231 | +এটি সমাধান করার জন্য আমাদের একটি ক্লোনিং লুপ ব্যাবহার করা লাগবে যা `user[key]` এর প্রত্যেক মান কে পরীক্ষা করবে, এবং যদি এটি অবজেক্ট হয়, তাহলে এর স্ট্রাকচার কেও কপি করবে। একে বলে "ডিপ ক্লোনিং"। |
| 232 | +
|
| 233 | +আমরা রিকার্সন ব্যাবহার করে এটি তৈরি করতে পারি অথবা ইতিমধ্যেই বাস্তবায়িত একটি ব্যাবহার করতে পারি, যেমন [lodash](https://lodash.com) এর [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) ফাংশন। |
| 234 | +
|
| 235 | +## সংক্ষিপ্ত |
| 236 | +
|
| 237 | +অবজেক্ট গুলো রেফারেন্স এর মাধ্যমে কপি হয়। অন্য কথায়, একটি ভেরিয়েবল অবজেক্ট এর মান সংরক্ষণ করে না , বরং একটি রেফারেন্স (মেমোরি এড্রেস) সংরক্ষণ করে। সুতরাং এই ধরনের ভেরিয়েবল কে কপি করলে অবজেক্ট কপি হয় না বরং রেফারেন্স কপি হয়। |
| 238 | +
|
| 239 | +কপি করা রেফারেন্সে এর মাধ্যমে করে সকল কাজ (যেমন প্রপার্টি যোগ করা/মোছা) একই অবজেক্ট এ সম্পাদিত হয়। |
| 240 | +
|
| 241 | +একটি "বাস্তব কপি" (ক্লোন) তৈরি করতে আমরা ব্যাবহার করতে পারি `Object.assign` যাকে "শ্যালো কপি"(অভ্যন্তরীণ অবজেক্ট রেফারেন্সের মাধ্যমে কপি হয়) বলা হয় অথবা আমরা ব্যাবহার করতে পারি [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) এর মত "ডিপ ক্লোনিং" ফাংশন। |
0 commit comments