در زبانهای برنامهنویسی مانند سیشارپ و یا جاوا میتوانیم از
Reflection جهت خواندن متادیتاها استفاده کنیم. به عنوان مثال امکان تعریف پراپرتی و یا متدها و حتی تایپهایی در زمان اجرا را در اختیارمان قرار میدهد. اما از آنجائیکه جاوا اسکریپت یک زبان داینامیک است، این قابلیت کمتر مورد توجه قرار گرفته است. در جاوا اسکریپت حین کار با کلاسها و اشیاء، ممکن است نیاز داشته باشید تا از اعضای یک کلاس
کوئری بگیرید و یا اینکه یک سری پراپرتی و متدهایی را در زمان اجرا به اشیاءتان اضافه کنید و یا مواردی از این دست.
تا قبل از
ES 6 میتوانستیم به این صورت به اعضای یک کلاس دسترسی داشته باشیم:
var person = {
name: 'Sirwan',
family: 'Afifi',
doWork: function () {
//....
}
};
for (var prop in person) {
console.log(prop);
}
همانطور که مشاهده میکنید، توسط سینتکس for...in میتوانیم به اعضای person دسترسی داشته باشیم. همچنین برای دسترسی به مقادیر هر کدام از اعضای آن میتوانستیم از bracket syntax استفاده کنیم:
for (var prop in person) {
console.log(person[prop]);
}
لازم به ذکر است میتوانستیم از متدهایی همانند Array.isArray, Object.getOwnPropertyDescriptor و حتی Object.keys نیز جهت دسترسی به اعضای پنهان یک شیء استفاده کنیم. اکنون قابلیت توکاری با نام Reflect این امکان را به
ES 6 اضافه کرده است تا تمام اعمال فوق را به راحتی انجام داد.
Reflect
همانطور که عنوان شد توسط API جدیدی با نام Reflect، میتوانیم به سادگی اعمال مربوط به Reflection را انجام دهیم. به عنوان مثال برای دسترسی به اعضای شیء person با استفاده از این API میتوانیم به این صورت عمل کنیم:
var person = {
name: 'Sirwan',
family: 'Afifi',
doWork: function () {
//....
}
};
for (let prop of Reflect.enumerate(person)) {
console.log(`the value for ${prop} is ${person[prop]}`);
}
در کد فوق از متد enumerate استفاده شده است، این متد یک پارامتر target را از ورودی میپذیرد. در واقع target همان شیءایی است که میخواهید پراپرتیهای آن را پیمایش کنید. همچنین مقدار بازگشتی این متد یک iterator میباشد. مفهوم iterator نیز خیلی ساده است. به زبان ساده، امکان پیمایش درون یک کالکشن را در اختیارمان قرار میدهد. نکتهی دیگری که در کد فوق وجود دارد، استفاده از سینتکس for..of است، این سینتکس شبیه به for...in عمل میکند، با این تفاوت که در اینجا به جای پیمایش درون یکسری keys، درون values پیمایش میکنیم. در نتیجه برای پیمایش iterators باید از این سینتکس استفاده شود.
متدهای شیء Reflect
در ادامه تعدادی از متدهای شیء Reflect را مشاهده میکنید:
Reflect.get(target, name, [receiver])
Reflect.set(target, name, value, [receiver])
Reflect.has(target, name)
Reflect.apply(target, receiver, args)
Reflect.construct(target, args)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.defineProperty(target, name, desc)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, newProto)
Reflect.deleteProperty(target, name)
Reflect.enumerate(target)
Reflect.preventExtensions(target)
Reflect.isExtensible(target)
Reflect.ownKeys(target)
تعدادی از متدهای فوق خیلی مشابه متدهای Object هستند؛ با این تفاوت که متدهای فوق اطلاعات بهتری را بر میگردانند. به عنوان مثال متد Reflect.defineProperty در صورت ایجاد شدن یک پراپرتی جدید، مقدار true را برمیگرداند:
var yay = Reflect.defineProperty(target, 'foo', { value: 'bar' })
if (yay) {
// yay!
} else {
// oops.
}
اگر همین کار را با استفاده از متد Object.defineProperty انجام میدادیم میبایست کد را درون بلاک try/catch مینوشتیم:
try {
Object.defineProperty(target, 'foo', { value: 'bar' })
// yay!
} catch (e) {
// oops.
}
به عنوان یک مثال دیگر، قبلاً برای حذف یک پراپرتی از یک شیء از کلمه کلیدی delete به اینصورت استفاده میکردیم:
var target = { foo: 'bar', baz: 'wat' }
delete target.foo
console.log(target)
// <- { baz: 'wat' }
اما با کمک متد Reflect.deleteProperty به راحتی میتوانیم یک پراپرتی را حذف کنیم:
var target = { foo: 'bar', baz: 'wat' }
let isDeleted = Reflect.deleteProperty(target, 'foo')
console.log(isDeleted );
// true
در اینحالت همانند متد Reflect.defineProperty، در صورت موفقیتآمیز بودن عمل حذف، مقدار true برگردانده میشود.
برای مشاهدهی لیست کامل متدهای فوق میتوانید به
اینجا مراجعه کنید. همچنین میتوان با مراجعه به قسمت
Browser compatibility وضعیت پشتیبانی هر کدام را در مرورگرهای مختلف مشاهده کرد.
نکاتی که در حین کار کردن با Reflect باید در نظر بگیرید:
- این شیء فاقد متد [[Construct]] میباشد. یعنی نمیتواند همراه با کلمهی کلیدی new مورد استفاده قرار گیرد.
- همچنین نمیتوان آن را همانند یک تابع فراخوانی کرد.
- تمام متدهای آن به صورت استاتیک تعریف شدهاند (همانند شیء Math).