فکر میکنم همه ماها با این مشکل برخورد کردیم، وقتایی که آرایه یا آبجکت را کپی میکنیم و تغییر میدیم، میبینیم که آرایه اصلی هم تغییر کرده و آپدیت شده. خب توی این موارد ما آرایه یا آبجکت را کپی نمیکردیم بلکه بهش رفرنس میدادیم و توی اصول برنامه نویسی وقتی یه رفرنس از یک متغیر به یک متغیر دیگه میدیم، درصورت تغییر هر کدوم، اون یکی هم تغییر میکنه.
حالا باید توی JavaScript چیکار کنیم تا بدون رفرنس کپی کنیم؟؟؟ خب راه های زیادی داره که موردی میگم.
فرض کنیم ما یه آرایه با سه تا متغیر داریم
let array_1 = [ "Dornica", "Mohammad", "Ali"];
خب یک آرایه دوم از این آرایه اول کپی میکنیم
let array_2 = array_1; console.log( array _ 2 ); // [ "Dornica", "Mohammad", "Ali"]
میبینیم که مقدار آرایه اول در آرایه دوم کپی شد. الان به آرایه دوم یه مقدار دیگه هم اضافه میکنیم
array_2[3] = 'Ahmad'; console.log( array_2 ); // [ "Dornica", "Mohammad", "Ali", "Ahmad"]
این همه ماجرا نیست، شاید تا اینجا کار مشکلی ندیده باشیم ولی مشکل اصلی اینه که آرایه اول هم مثل آرایه دوم تغییر کرده
console.log( array_1); // [ "Dornica", "Mohammad", "Ali", "Ahmad"]
با وجود اینکه ما فقط مقدار آرایه دوم را تغییر دادیم ولی آرایه اول هم تغییر کرد، خب باید بگم ما از آرایه اول کپی نگرفتیم بلکه به آرایه دوم یه رفرنس از آرایه اول دادیم، یعنی هر تغییری که توی آرایه دوم انجام بدیم توی ارایه اول هم اعمال میشه. خب باید چیکار کنیم که اینطوری نشه؟
let array_2 = [...array_1];
خب این دستور داره از ویژگی itterable بودن آرایه استفاده میکنه که این خودش میتونه یه بحث جدا برای پارس کرده آرایه ها باشه که شاید در آینده براتون گفتم. شاید هر کدوم از این تابع هایی که معرفی میکنم خودش یه مطلب جدا باشه ولی برای اینکه از بحص اصلیمون دور نشیم فقط یه توضیح مختصر ازش میدم.
let array_1 = [ "Dornica", "Mohammad", "Ali"]; let array_2 = [...array_1]; array_2[3] = 'Ahmad'; console.log( array_2 ); // [ "Dornica", "Mohammad", "Ali", "Ahmad"] console.log( array_1); // [ "Dornica", "Mohammad", "Ali"]
let array_2 = array_1.slice( );
با این خط کد یک کپی از آرایه اول توی آرایه دوم میریزه
let array_1 = [ "Dornica", "Mohammad", "Ali"]; let array_2 = array_1.slice( ); array_2[3] = 'Ahmad'; console.log( array_2 ); // [ "Dornica", "Mohammad", "Ali", "Ahmad"] console.log( array_1); // [ "Dornica", "Mohammad", "Ali"]
این تابع کار ما را راه میندازه ولی اصلا کار این تابع چیه؟ یه برش از آرایه اول میگیره و توی آرایه دوم میریزه، این تابع دو تا متغیر میگیره و متغیر اول میگه "از خونه چندم آرایه" و متغیر دوم میگه "تا خونه چندم آرایه"، یعنی اگر (1,4) بدیم بهمون از خونه اول تا خونه چهارم کپی میکنه و توی آرایه دوم میریزه.
خب توی این مثال که ما اصلا ورودی وارد نکردیم؟
متغیر توی این تابع اختیاری، یعنی بصورت دیفالت متغیر اول خونه صفر آرایه و متغیر دوم اندازه طول آرایه منهای یکه، یعنی کل آرایه را برامون کپی میکنه
let array_2 = [ ].concat( array_1 );
تابع concat آرایه دوم را به آرایه اول نسبت میده و توی متغیری که میخوایم میریزه، توی مثال ما array_1 را به یک آرایه خالی اضافه میکنه و توی array_2 میریزه ( دقت کنین قبل کانکت یه آرایه خالی گذاشتم، عملا دو تا ارایه را ادغام میکنه ).
let array_1 = [ "Dornica", "Mohammad", "Ali"]; let array_2 = array_1.slice( ); array_2[3] = 'Ahmad'; console.log( array_2 ); // [ "Dornica", "Mohammad", "Ali", "Ahmad"] console.log( array_1); // [ "Dornica", "Mohammad", "Ali"]
با این روش ها میتونیم یه کپی بدون رفرنس از آرایه بگیریم.
خب درباره کپی ابجکت ها هم بگیم
فرض کنین یه object داریم
let object_1 = { name: 'Dornica', work: 'Web Developer' };
خب مثل آرایه ها یک کپی از object اول توی یک object جدید بریزیم
let object_2 = object_1; console.log( object_2 ); // { name: 'Dornica', work: 'Web Developer' };
اگر object_2 تغییر بدیم اون تغییر توی object_1 هم اعمال میشه
object_2.work = c console.log( object_2 ); // { name: 'Dornica', work: ''Web & Mobile Developer';}; console.log( object_1 ); // { name: 'Dornica', work: ''Web & Mobile Developer';};
یعنی همون رفرنس میشه، خب برای فقط کپی باید چیکار کنیم؟
let object_2 = Object.assign( {}, object_1 );
این تابع تقریبا مثل کانکت عمل میکنه و متغیر دوم را به متغیر اول اضافه میکنه و اگر فیلد مشترکی داشته باشن از متغیر دوم استفاده میکنه، توی این مثال هم ما object_1 را با یک object خالی ادغام میکنیم و توی object_2 میریزیم. این مثال برای توضیحات بیشتر این تابع اوردم.
const target = { a: 1, b: 2 }; const source = { b: 4, c: 5 }; const returnedTarget = Object.assign(target, source); console.log(target); // expected output: Object { a: 1, b: 4, c: 5 } console.log(returnedTarget); // expected output: Object { a: 1, b: 4, c: 5 }
البته با این روش اگر object تو در تو یا چند سطحی داشته باشیم دوباره مثل رفرنس عمل میکنه و فقط برای object های یک سطحی استفاده میشه، پس الان باید چیکار کنیم؟ میتونیم از راه زیر استفاده کنیم که هر نوع object را کپی کنیم.
let object_2 = JSON.parse ( JSON.stringify( object_1 ) ) ;
این تابع حتما باهاش آشنایی دارین ( منظورم اینه با این دو تا تابع که با هم داریم استفاده میکنیم )، اینجا اول میایم object را به یه رشته تبدیل میکنیم و دوباره اون رشته را به object تبدیل میکنیم.
با این روش object کپی بدون رفرنس میشه.
****************************************
یکی از دوستان توی کامنت مطلبی گفت بعد از بررسی گفتم به مطلب اضافه کنم
استفاده از Spread
let object_2 = {...object_1};
این حالت هم مثل assign فقط در یک سطح ویرایش کار میکنه و توی آبجکت های تو در تو جواب نمیده.
****************************************
امیدوارم این مطلب بدردتون خورده باشه، شاید راه های دیگه ای هم باشه که بشه اینکارو انجام داد ولی من بیشتر از این ها استفاده کردم اگر شما موردی استفاده میکنین به استراک بزارین تا استفاده کنیم. اگر سوال یا مطلبی بود خوشحال میشم بتونم کمک کوچیکی کنم یا اشکالات منو بهم یادآوری کنین.
با تشکر