ارسال متغیرها به روش با مقدار و با ارجاع در جاوا اسکریپت — به زبان ساده

۳۷۹ بازدید
آخرین به‌روزرسانی: ۰۷ شهریور ۱۴۰۲
زمان مطالعه: ۴ دقیقه
ارسال متغیرها به روش با مقدار و با ارجاع در جاوا اسکریپت — به زبان ساده

اگر با زبان‌های سطح پایین از قبیل ++C/C آشنا باشید، احتمالاً می‌دانید که متغیرها می‌توانند به صورت «با مقدار» و «با ارجاع» ارسال شوند. همچنین امکان ارسال مقادیر به وسیله اشاره‌گرها وجود دارد، که البته بررسی آن خارج از حیطه این مقاله است. روش ارسال متغیر در زبان ++C باید به صراحت تعیین شود، اما در جاوا اسکریپت همه چیز در پس‌زمینه انجام می‌شود. اغلب کسانی که به تازگی شروع به کار با جاوا اسکریپت کرده‌اند، ‌ممکن است با این تفاوت بنیادی آشنا نباشند و در نتیجه موجب بروز باگ‌های زیادی شوند. در این راهنما با تفاوت ارسال متغیرها به روش با مقدار و با ارجاع در جاوا اسکریپت آشنا خواهیم شد.

معنی ارسال با مقدار و با ارجاع چیست؟

معنی ارسال با مقدار این است که متغیر جدید، یک مقدار واقعی از متغیر اولیه می‌گیرد. یعنی متغیرها به همدیگر وابسته نیستند و تغییر در یک متغیر، موجب ایجاد تغییر در متغیر دیگر نمی‌شود.

ارسال با ارجاع به این معنی است که متغیر جدید، نشانی حافظه متغیر اولیه را می‌گیرد. از آنجا که هر دو متغیر به نشانی حافظه یکسانی اشاره می‌کنند، تغییر در یکی موجب ایجاد همان تغییر در متغیر دیگر می‌شود.

ارسال متغیرها به روش با مقدار و با ارجاع

در جاوا اسکریپت، همه انواع «مقدماتی» (primitive) داده‌ها از قبیل رشته، عدد، بولی به روش با مقدار ارسال می‌شوند و انواع داده پیچیده مانند آرایه و شیء به روش با ارجاع ارسال می‌شوند.

بررسی برخی مثال‌های ساده

در این بخش متغیر bar را با استفاده از متغیر foo دیگری مقداردهی می‌کنیم. از آنجا که انواع داده مقدماتی (در این مورد، رشته) به صورت با مقدار ارسال می‌شوند، تغییر در متغیر جدید، تأثیری بر متغیر اولیه نخواهد داشت.

1var foo = 'iPhone';
2var bar = foo;
3bar = 'One Plus';
4
5console.log(foo); // outputs 'iPhone'
6console.log(bar); // outputs 'One Plus'

این کد همانند روش ارسال مقادیر به تابع‌ها عمل می‌کند.

1var foo = 'linux';
2
3function bar(baz) {
4  baz = 'windows';
5  
6  console.log(foo); // outputs 'linux'
7  console.log(baz); // outputs 'windows'
8}
9
10bar(foo);

اما زمانی که تلاش می‌کنیم یک متغیر پیچیده مانند شیء (Object) ‌را ارسال کنیم، رفتار کاملاً متفاوتی نشان می‌دهد. در ادامه می‌بینید که یک شیء را به متغیر یا تابع دیگری ارسال می‌کنیم و با ارجاع ارسال شده است. در این حالت تغییر در یکی، بر متغیرهای دیگری که به نشانی حافظه آن ارجاع می‌دهند تأثیر خواهد گذاشت.

1var phone = {OS: 'iOS'};
2
3function changeOS(telephone) {
4  telephone.OS = 'Android';
5}
6
7console.log(phone.OS); // outputs 'iOS'
8
9changeOS(phone);
10
11console.log(phone.OS); // outputs 'Android'
12
13var newPhone = phone;
14
15newPhone.OS = 'Windows Phone'
16
17console.log(phone.OS); // outputs 'Windows Phone'

تغییر رفتار پیش‌فرض جاوا اسکریپت

جاوا اسکریپت این امکان را به ما نمی‌دهد که به صراحت شیوه ارسال یک متغیر را تعیین کنیم، بنابراین باید راهکاری برای تغییر رفتار پیش‌فرض جاوا اسکریپت پیدا کنیم.

ارسال متغیرهای مقدماتی به صورت شیوه با ارجاع

برای ارسال متغیرهای مقدماتی به صورت با ارجاع، هیچ روشی به جز قرار دادن آن‌ها درون یک شیء یا یک آرایه و سپس تجزیه کردن آن‌ها در مقصد وجود ندارد.

1var car = 'Tesla';
2
3function changeCar(carObj) {
4  carObj.car = 'ZAZ';
5}
6
7var carWrapper = {car};
8
9changeCar(carWrapper);
10
11var {car} = carWrapper;
12
13console.log(car); // outputs 'ZAZ'

اما این کار از نظر عملکردی بسیار پرهزینه است، زیرا باید یک متغیر پیچیده را ساخته و تخریب کنیم.

ارسال متغیرهای پیچیده به شیوه با مقدار

برای ارسال یک متغیر به شیوه با مقدار کافی است آن را کپی کنیم. چندین روش برای انجام این کار وجود دارند. در مورد اشیا می‌توانید از روش‌های زیر استفاده کنید:

  • ()Object.create – یک شیء جدید با استفاده از شیء موجود می‌سازد.
  • ()Object.assign – همه مشخصه‌ها را از اشیای مبدأ به یک شیء مقصد کپی کرده و شیء حاصل را بازگشت می‌دهد.
  • ()Object.entries()+Object.fromEntries – متد entries یک شیء را به یک آرایه از جفت‌های کلید و مقدار تبدیل می‌کند و متد fromEntries آن را دوباره به یک شیء جدید تبدیل می‌کند.
  • عملگر Spread – یک کپی از شیء ایجاد می‌کند.
1var obj1 = {a: 'a'};
2
3var obj2 = Object.assign({}, obj1);
4
5obj2.b = 'b';
6
7console.log(obj2); // outputs {a: 'a', b: 'b'}
8console.log(obj1); // outputs {a: 'a'}
9
10var obj3 = {...obj1};
11
12obj3.a = 'z';
13
14console.log(obj3); // outputs {a: 'z'}
15console.log(obj1); // outputs {a: 'a'}

در مورد آرایه‌ها می‌توانید از روش‌های زیر استفاده کنید:

  • ()Array.map – یک کپی با تغییرهای تعریف‌شده (یا بدون آن‌ها) ایجاد می‌کند.
  • ()Array.slice – یک کپی از بخشی از یک آرایه (یا در صورت فراخوانی با آرگومان از یک مدخل آرایه) ایجاد می‌کند.
  • ()Array.from – یک کپی از یک آرایه می‌سازد.
  • عملگر Spread – یک کپی از یک آرایه ایجاد می‌کند.
1var arr1 = ['a', 'b', 'c'];
2
3var arr2 = arr1.slice();
4
5arr2.push('d');
6
7console.log(arr2); // outputs ['a', 'b', 'c', 'd']
8console.log(arr1); // outputs ['a', 'b', 'c']
9
10var arr3 = arr1.map((item) => item);
11
12arr3.pop();
13
14console.log(arr3); // outputs ['a', 'b']
15console.log(arr1); // outputs ['a', 'b', 'c']

باید اشاره کنیم که همه این متدها یک نکته مهم دارند و آن این است که این متدها تنها یک کپی «سطحی» (shallow) از یک متغیر اولیه ایجاد می‌کنند. معنی این حرف آن است که اگر درون آرایه یا شیء، متغیرهای پیچیده تودرتویی وجود داشته باشند، همچنان به صورت با ارجاع کپی خواهند شد. به مثال زیر توجه کنید:

1var obj1 = {
2  a: 'a',
3  b: ['1', '2', '3'],
4};
5
6var obj2 = {...obj1};
7
8obj2.b.push('4');
9
10console.log(obj1.b); // outputs ['1', '2', '3', '4']

برای ایجاد یک کپی «عمیق» (deep) چند روش وجود دارد:

  • ()JSON.stringify()+JSON.parse – متد stringify متغیر را به یک رشته تبدیل می‌کند و متد parse مجدداً رشته را به یک شیء تبدیل خواهد کرد.
  • شما می‌توانید متد خاص خود را نیز برای کپی کردن عمیق با استفاده از یک متد کتابخانه بسازید. به عنوان نمونه می‌توان از متد ()cloneDeep در کتابخانه Lodash استفاده کرد.
1var obj1 = {
2  a: 'a',
3  b: ['1', '2', '3'],
4};
5
6var obj2 = JSON.parse(JSON.stringify(obj1));
7
8obj2.b.push('4');
9
10console.log(obj1.b); // outputs ['1', '2', '3']

سخن پایانی

بدین ترتیب به پایان این راهنما می‌رسیم. نکته مهمی که باید در خاتمه اشاره کنیم، این است که موقع استفاده از متدهای داخلی یا کتابخانه‌های که با آرایه‌ها یا اشیا کار می‌کنند، باید مراقب باشید و همواره بررسی کنید که آیا متغیر اولیه را تغییر می‌دهند یا متغیر جدیدی تولید می‌کنند. برای نمونه متد ()Array.pop آخرین عنصر را از یک آرایه اولیه حذف می‌کند، در حالی که متد ()array.filter آرایه جدیدی ایجاد می‌کند.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۳ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
better-programming
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *