راهنمای استفاده از Getterها و Setterها در JavaScript برای توسعه دهندگان

13 خرداد 1398, خواندن در 8 دقیقه

Getterها و Setterها، توابع یا متدهایی هستند که برای دریافت کردن و تنظیم کردن مقادیر متغیرها استفاده می‌شوند. مفهوم getter-setter در برنامه‌نویسی کامپیوتر رایج است. تقریبا تمام زبان‌های برنامه‌نویسی سطح بالا شامل JavaScript، یک سینتکس برای پیاده‌سازی getterها و setterها دارند.

در این پست، ما خواهیم دید که getterها و setterها چه هستند، و چگونه می‌توان در JavaScript آن‌ها را ساخته و یا از آن‌ها استفاده کرد. جای اشاره دارد که با توجه به محور این مقاله و در صورت تمایل، می‌توانید دوره مربوط به JavaScript را بر روی راکت بگذرانید.

Getterها و Setterها و کپسوله‌سازی

ایده getterها و setterها همیشه در پیوستگی کپسوله‌سازی بیان می‌شود. کپسوله‌سازی می‌تواند به دو روش درک شود.

اول، کپسوله‌سازی در واقع راه‌اندازی سه‌گانه داده - getterها - setterها، برای دسترسی به و تغییر دادن آن داده است. این تعریف وقتی که برخی عملیات‌ها مانند اعتبارسنجی باید قبل از ذخیره کردن یا دیدن داده‌ها بر روی آن‌ها انجام شوند، کاربردی است. getterها و setterها یک محل عالی برای آن‌ها ایجاد می‌کنند.

دوم، یک تعریف سخت‌تر هم وجود دارد که طبق آن کپسوله‌سازی برای مخی کردن داده‌ها، خارج از دسترس قرار دادن آن از کدهای دیگر، به جز با استفاده از getterها و setterها است. به این صورت، شما به طور اتفاقی داده‌های مهم را با مقداری کد دیگر در برنامه بازنویسی نخواهید کرد.

Getterها و Setterها را بسازید

۱. با استفاده از متدها

با توجه به این که getterها و setterها اساسا توابعی هستند که یک مقدار را دریافت می‌کنند / تغییر می‌دهد، بیش از یک راه برای ساخت آن‌ها وجود دارد. اولین راه این است که:

var obj = {
  foo:    'this is the value of foo',
  getFoo: function() {
            return this.foo;
        },
  setFoo: function(val) {
            this.foo = val;
        }
}
 
console.log(obj.getFoo());
// "this is the value of foo"
 
obj.setFoo('hello');
 
console.log(obj.getFoo());
// "hello"

ین ساده‌ترین راه برای ساخت getterها و setterها است. یک ویژگی به نام foo، و دو متد با نام‌های getFoo و setFoo برای برگرداندن و اختصاص دادن یک مقدار به آن ویژگی وجود دارند.

۲. با استفاده از کلیدواژه‌ها

یک راه «رسمی‌تر» و قدرتمندتر برای ساخت getterها و setterها، استفاده از کلیدواژه‌های get و set است.

برای ساخت یک getter، کلیدواژه get را در مقابل یک تعریف تابع که قرار است به عنوان متد getter عمل کند، و از کلیدواژه set هم به همین روش برای ساخت یک setter استفاده کنید. سینتکس مربوطه به این صورت است:

var obj = {

  fooVal: 'this is the value of foo',

  get foo() {

      return this.fooVal;

  },

  set foo(val) {

      this.fooVal = val;

  }

}



console.log(obj.foo);

// "this is the value of foo"



obj.foo = 'hello';



console.log(obj.foo);

// "hello"

دقت کنید که داده‌ها فقط می‌توانند تحت یک نام ویژگی (fooVal) که با نام متدهای getter-setter تفاوت داشته باشد ذخیره شوند؛ زیرا ویژگی‌ای که متدهای getter-setter را نگه می‌دارد (foo)، نمی‌تواند داده‌ها را هم به همراه آن‌ها نگه دارد.

کدام روش بهتر است؟

اگر انتخاب کنید که getterها و setterها را با استفاده از کلیدواژه‌ها بسازید، می‌توانید از عملگر اختصاص‌دهی برای تنظیم داده‌ها و عملگر نقطه برای دریافت داده‌ها استفاده کنید. به همان روشی که می‌توانید به مقدار یک ویژگی معمولی دسترسی داشته باشید، یا آن را تنظیم کنید.

گرچه اگر اولین راه برای کدنویسی getterها و setterها را انتخاب کنید، باید متدهای getter‌ و setter را با استفاده از سینتکس فراخوانی تابع فراخوانی کنید؛ زیرا این توابع، توابع معمولی هستند (به مانند مواردی که با استفاده از کلیدواژه‌های get و set ساخته شده‌اند، خاص نیستند).

همچنین احتمال دارد به طور اتفاقی یک مقدار دیگر را به ویژگی‌ای که این متدهای getter-setter را نگه می‌دارد اختصاص دهید و آن‌ها را به طور کامل از دست بدهید! گاهی اوقات نیازی نیست در متد دوم نگران آن باشید.

پس حال می‌توانید ببینید که چرا من گفتم تکنیک دوم قدرتمندتر است.

جلوگیری‌ها را بازنویسی کنید

اگر به هر دلیلی اولین تکنیک را ترجیح می‌دهید، با استفاده از Object.defineProperties ویژگی‌هایی که متدهای getter-setter را نگه می‌دارند، بسازید و آن‌ها را read-only کنید. ویژگی‌هایی که با استفاده از Object.defineProperties، Object.defineProperty و Reflect.defineProperty ساخته شده‌اند، به طور خودکار به صورت writable: false پیکربندی می‌شوند که یعنی read-only:

/* Overwrite prevention */
var obj = {
  foo: 'this is the value of foo'
};
 
Object.defineProperties(obj, {
  'getFoo': {
      value: function () {
          return this.foo;
      }
  },
  'setFoo': {
      value: function (val) {
          this.foo = val;
      }
  }
});
 
obj.getFoo = 66;
// getFoo is not going to be overwritten!
 
console.log(obj.getFoo());
// "this is the value of foo"

عملیات‌های داخل getterها و setterها

پس از این که getterها و setterها را معرفی کردید، می‌توانید ادامه دهید و عملیات‌هایی را قبل از تغییر دادن یا برگرداندن داده‌ها روی آن‌ها انجام دهید.

در کد زیر و در تابع getter، داده‌ها قبل از برگردانده شدن در پیوستگی با یک رشته هستند و در تابع setter و قبل از بروزرسانی n، یک اعتبارسنجی درباره این که آیا مقدار مورد نظر یک عدد است یا نه، انجام می‌ شود.

var obj = {
  n: 67,
  get id() {
      return 'The ID is: ' + this.n;
  },
  set id(val) {
      if (typeof val === 'number')
          this.n = val;
  }
}
 
console.log(obj.id);
// "The ID is: 67"
 
obj.id = 893;
 
console.log(obj.id);
// "The ID is: 893"
 
obj.id = 'hello';
 
console.log(obj.id);
// "The ID is: 893"

با استفاده از getterها و setterها، از داده‌ها حفاظت کنید

تا به اینجا، ما استفاده از getterها و setterها را در اولین زمینه کپسوله‌سازی توضیح داده‌ایم. بیایید به دومین زمینه، یا به عبارتی نحوه مخفی کردن داده‌ها از خارج کد، بدون کمک getterها و setterها برویم.

داده‌های محافظت نشده

راه‌اندازی getterها و setterها به این معنی نیست که داده‌ها می‌توانند مورد دسترسی قرار گیرند و از طریق آن متدها تغییر یابند. در مثال زیر، این داده‌ها به طور مستقیم و بدون دست زدن به متدهای getter و setter تغییر می‌یابند:

var obj = {
  fooVal: 'this is the value of foo',
  get foo() {
      return this.fooVal;
  },
  set foo(val) {
      this.fooVal = val;
  }
}
 
obj.fooVal = 'hello';
 
console.log(obj.foo);
// "hello"

ما از setter استفاده نکردیم، اما به طور مستقیم داده مورد نظر (fooVal) را تغییر دادیم. داده‌هایی که ما به طور اولیه در داخل obj قرار دادیم، حال از بین رفته است. برای جلوگیری از این اتفاق، شما به نوعی محافظت برای داده‌های خود نیاز دارید. شما می‌توانید با محدود کردن محدوده جایی که داده‌های شما قابل دسترسی هستند، این نوع محافظت را اضافه کنید. شما می‌توانید این کار را با استفاده از scope کردن بلوک یا scope‌ کردن تابع انجام دهید.

۱. scope کردن بلوک

یکی از روش‌ها، استفاده از یک scope بلوک است که داخل آن، داده‌ها با استفاده از کلیدواژه let تعریف خواهند شد، که scope آن را به آن بلوک محدود می‌کند.

یک scope بلوک می‌تواند با قرار دادن کد خود داخل یک جفت براکت انجام شود. هر زمان که شما یک scope بلوک را می‌سازید، مطمئن شوید که یک کامنت درباره آن قرار می‌دهید، تا بعدا کاری به براکت‌ها نداشته باشید و کسی به طور اتفاقی آن‌ها را حذف نکند.

/* BLOCK SCOPE, leave the braces alone! */
{
let fooVal = 'this is the value of foo';
var obj = {
    get foo() {
        return fooVal;
    },
    set foo(val) {
        fooVal = val
    }
  }
}
 
fooVal = 'hello';
// not going to affect the fooVal inside the block
 
console.log(obj.foo);
// "this is the value of foo"

تغییر دادن / ساختن fooVal در خارج از بلوک، fooVal که داخل getter-setterها به آن‌ها اشاره شده است را تحت تاثیر قرار نمی‌دهد.

۲. scope کردن تابع

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

function myobj(){
  var fooVal = 'this is the value of foo';
  return {
      get foo() {
          return fooVal;
      },
      set foo(val) {
          fooVal = val
      }
  }
}
 
fooVal = 'hello';
// not going to affect our original fooVal
 
var obj = myobj();
 
console.log(obj.foo);
// "this is the value of foo"

آبجکت برگردانده شده توسط تابع myobj (که getter-setter مربوط به foo() را داخل خود دارد)، در obj ذخیره شده است و سپس obj برای فراخوانی getter و setter استفاده می‌شود.

۳. حفاظت داده بدون scope کردن

همچنین یک راه دیگر هم برای حفاظت از داده‌های خود در مقابل بازنویسی، بدون محدود کردن scope آن وجود دارد. منطق پشت آن به این صورت است: اگر نمی‌دانید یک داده چه نام دارد، چگونه می‌توانید آن را تغییر دهید؟

اگر داده مورد نظر یک نام متغیر / ویژگی‌ دارد که به سادگی قابل تجدید نیست، احتمالا هیچ کس (حتی خود شما) قرار نیست با اختصاص دادن یک مقدار دیگر به آن نام، آن را بازنویسی کند.

var obj = {
  s89274934764: 'this is the value of foo',
  get foo() {
    return this.s89274934764;
  },
  set foo(val) {
    this.s89274934764 = val;
  }
}
 
console.log(obj.foo);
// "this is the value of foo"

می‌بینید؟ این یک راه برای رفع این مشکل است. با این که نام منتخب من خیلی نام خوبی نیست، اما شما همچنین می‌توانید از مقادیر یا نمادهای تصادفی برای ساخت نام‌های ویژگی استفاده کنید. هدف اصلی، مخفی نگه داشتن داده‌ها از کد دیگر و اجازه دادن به یک getter-setter برای دسترسی / بروزرسانی آن است.

چرا باید از getterها و setterها استفاده کنید؟

حال به سوال اصلی می‌رسیم: آیا حال شما شروع به اختصاص‌دهی getterها و setterها به تمام داده‌های خود می‌کنید؟

اگر شما در حال مخفی کردن داده‌ها هستید، انتخاب دیگری وجود ندارد.

اما اگر داده‌های شما توسط کد دیگری هم دیده می‌شود، آیا هنوز باید از getter-setterها استفاده کنید، فقط هم برای این که آن را با کدی که عملیات مشابهی را انجام می‌دهد ادغام کنید؟ به نظر من، بله. کد شما به زودی عقلانی به نظر خواهد رسید. ساخت واحدهای کوچک از داده‌های تکی به همراه getter-setter مختص خود، یک استقلال خاص برای کار کردن بر روی داده مورد نظر، و بدون تاثیر گذاشتن روی بخش‌های دیگر کد برای شما فراهم می‌کند.

منبع

چه امتیازی به این مقاله می دید؟
خیلی بد
بد
متوسط
خوب
عالی

دیدگاه‌ها و پرسش‌ها

برای ارسال دیدگاه لازم است، ابتدا وارد سایت شوید.

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

آفلاین
user-avatar
عرفان کاکایی @er79ka
دنبال کردن

گفتگو‌ برنامه نویسان

بخشی برای حل مشکلات برنامه‌نویسی و مباحث پیرامون آن وارد شو