تعداد بازدید: 666

Proxy Standard Build-in Object

Proxy یک شئ پیش فرض در جاوا اسکریپت است که بر اساس Proxy Design Pattern در جاوا اسکریپت تعریف شده است و قابلیت رهگیری (intercept) و به دام انداختن Property ها و Descriptor های یک Object را واقع می شود . به این ترتیب می توان برخی عملیات های یک شئ را فیلتر گذاری کرد

proxy-in-javascript

Proxy خود یک شئ است که روی یک شئ دیگر سوار می شود و می تواند به ویژگی های Public آن دسترسی داشته باشید و آن ها را رهگیری کند و همچنین روی برخی متد های این شئ Middleware اعمال کند (یا همان به دام انداختن پیش از ارسال به سمت بلوک کدی که درخواست اجرای این متد را داشته).

 

کاربرد های معمول این Build-in Object می توان به موارد زیر ارشاره کرد:

  • logs Property access
  • Setters , Getters
    • validate
    • format
    • sanitize inputs

در زمان ساخت Proxy دو مورد زیر را باید تعیین کنید:

  • target : همان شئ (JS Native Object , …) است که قرار است Proxy روی آن سوار شود و رهگیری روی آن انجام می شود.
  • handler : این یک شئ است که متد های پیش فرض Proxy روی آن پیاده سازی می شود و رهگیری توسط این متد ها اتفاق می افتد .
let target = {
  name: 'ali',
}
let handler = {}
let p = new Proxy(target,handler)
console.log(p.name)

به طور مثال می توان با متد Get Proxy Handler Method یک فیلتر گذاری در زمان مقدار دهی Target properties انجام داد:

let target = {
  name: 'ali',
  age: 20,
}

let handler = {
  get(target,prop,reciever){
    console.log(target,prop,reciever)
    return target[prop]
  }
}

let p = new Proxy(target,handler)
console.log(p.name)

در مثال بالا می توان پیش از برگشت دادن مقدار درخواست شده مقادیر پارامتر های Get Proxy Handler Method را پرینت گرفت.عملیات بالا می تواند رهگیری دریافت یک مقدار از یک شئ است.

Proxy Handler Methods

مهم ترین متد های رهگیری یک شئ در Handler را در جدول زیر به همراه کاربرد آن مشاهده می کنید. برای مشاهده لیست کامل این متد ها می توان به وب سایت developer.mozilla رجوع کنید.

get در زمان دریافت مقدار یکی از property های شئ target می توان این مقدار را دریافت کرد یا به دام انداخت .
set در زمان set کردن مقادیر در property های شئ target می توان این عملیات را به دام انداخت و مقدار ورودی این property را فیلتر کرد.
has در زمان چک کردن با عملگر in می توان مقادیر را رهگیری کرد.

let target = {
  name: 'ali',
  age: 27,
  _id: 20014242,
}

let handler = {
  has(target,key){
    if('_' == key[0]){
      return false
    }
    return true
  }
}

let p = new Proxy(target,handler)
console.log('_id' in p)
constructor در زمان اجرای constructor می توان شئ برگشتی از این متد سازنده را فیلتر کرد.
apply در زمان اجرای متد apply می توان عملیات را فیلتر کرد .
defineProperty به دام انداختن متد استاتیک Object.defineProperty . اگر شما قرار است یک property برای یک Native Object تعریف کنید می توانید لیست داده ها و عملیات را رهگیری کنید.
deleteProperty رهگیری در زمان اجرای عملگر delete
getOwnPropertyDescriptor به دام انداختن متد استاتیک Object.getOwnPropertyDescriptor .

Proxy Common Usage Examples

در این بخش کاربردهای Proxy را در قالب یک سری مثال تعیین می کنیم که برای اجرای این امکانات از متدهای Proxy Handlers استفاده می کنیم:

Property Default Value

در این مثال اگر یک Property در target object وجود نداشت مقدار پیش فرض Defualt Value را بر می گرداند.

let target = {
  name: 'ali',
  age: 27
}

let handler = {
  get(target,prop,reciever){
    if(prop in target){
      return target[prop]
    }
    return 'Default value'
  }
}

let p = new Proxy(target,handler)
console.log(p.job)

Extending Constructor

در این مثال ما constructor را در یک function object در تله می اندازیم . روال کار در این مثال به این شکل است که یک تابع به نام extendingConstructor داریم که این تابع پارامتر اول آن یک تابع یا function object است و پارامتر دوم تابعی دیگر که قرار است constructor این دو تابع را به هم متصل کنیم.

function extendingContructor(parent,base){
  let exn = new Function
  exn.prototype.constructor = new Proxy(base,{
    construct(target,args,newTarget){
      let object = new Object
      this.apply(target,object,args)
      return object
    },	
    apply(target,object,args){
      parent.apply(object,args)
      base.apply(object,args)
    }
  })
  return exn.prototype.constructor
}

function Parent (name){
  this.name = name
  this.getName = function(){
    return this.name	
  }
}

function Children(name,age){
  this.age = age,
    this.getAge = function(){
    return this.age	
  }
}

let Person = extendingContructor(Parent,Children)
let ali = new Person('ali',29)
console.log(ali.getAge())

بدنه این تابع به این شکل است که دو تابع (function object or object constructor) تعریف می کنیم و در نهایت یک تابع هم بر میگردانیم که constructor آن رفتار هر دو تابع را دارد . پیش از بررسی بدنه این تابع باید Fact های زیر را بدانیم:

  • یک تابع یک شئ است که قابلیت constructor دارد. یعنی در زمان ساختن می توان Property های آن را مقدار دهی کنید.
  • در یک تابع یک شئ جریان دارد . این شئ در درون تابع پس از ساخته شدن this می باشد.
  • در زمان new شدن یک تابع constructor آن فراخوانی می شود
  • constructor این تابع یک شئ برمی گرداند.
  • Proxy handler constructor روی constructor این تابع که یکی از توابع ورودی است دسترسی دارد.
  • هر تابع یک ویژگی به نام prototype دارد که متدهایی از جمله constructor از آن ارث بری می شود.
  • با استفاده از apply می توانیم یک شئ را روی یک تابع اجرا کنیم .

در این تابع ما دو تابع و آرگومان های آن ها را داریم . توابع که با نام های super , base تعریف شده اند که مقدار پارامتر های این تابع هستند. پارامتر های این تابع را با فیلتر گذاری روی constructor می توانیم به دست بیاوریم . در نهایت با استفاده از apply یک شئ را می سازیم و هر دو متد super , base را با استفاده از آرگومان های آن اجرا می کنیم و در نهایت همان شئ را بر می گردانیم.

Mount/Day Validation

در این مثال ما مقادیر ماه و روز اگر به اشتباه وارد شود خطا Range اجرا می کنیم. مقادیر یک ماه باید از ۱۲ کوچکتر و مقادیر روز باید از ۳۱ کوچکتر باشند.

let handler = {
  set(target,prop,value,proxy){
    if('mount' == prop && value > 12){
      throw new RangeError('invalid value for Mount')
    } else if('day' == prop && value > 31){
      throw new RangeError('invalid value for Day')
    }
    target[prop] = value
  }
}

let target = {
  year: 0,
  mount: 0,
  day: 0,
}

let p = new Proxy(target,handler)
p.mount=20
p.day = 33
p.year = 2022
console.log(p)

Operation Forward

اگر در یک Proxy پس از initialize یک مقدار set کنیم در target آن نیز این operator وجود دارد (operation forwarding)

let target = {}
let handler = {}

let p = new Proxy(target,handler)
p.a = 'hello world'
console.log(target,p)

Proxy Access Private property

در Proxy ما به مقادیر Private چه Method/Property دسترسی نداریم.

class Template{
  #secret
}

let target = new Template
let handler = {}

let p = new Proxy(target,handler)
console.log(p.#secret)
/* Private field '#secret' must be declared in an enclosing class */

نکته : در Proxy در برخی اشیا به طور مثال map هم به ویژگی های آن دسترسی نداریم (Proxy internal slot)

let target = new Map
let handler = target

let p = new Proxy(target,handler)
console.log(target.size) // 0
console.log(p.size) //undefined

Proxy Manipulating Dom

در proxy ما می توانیم روی عملیات های خاص یک شئ فیلتر هایی بگذاریم که قرار است یک سری عملیات روی یک dom اعمال کند. به طول مثال ما می خواهیم اگر یک کلاس روی یک node قرار گرفت به صورت toggle یک کلاس روی node قبلی تغییر کند.

let target = {
  current: false
}

let handler = {
  set(target,prop,newValue){
    let oldElement = target[prop]
    if(oldElement){
      oldElement.classList.add('disable')
      oldElement.classList.remove('selected')
    }
    newValue.classList.add('selected')
    newValue.classList.remove('disable')
    target[prop] = newValue
    return true
  }
}

let toggler = new Proxy(target,handler)

function clickElement(event){
  let element = event.target
  toggler.current = element
}

Value Correction Extra Value

در این مثال ما یک لیست عدد داریم و اگر مقدار اشتباه برای آن set کنیم تبدیل به NaN می شود . همچنین یک سری متد که در شئ وجود ندارد با استفاده از Proxy Handler Method مدیریت می کنیم.مثلا اگر آخرین عدد از این لیست را خواستیم این متد که در آن نوشته شده است را می توان با استفاده از مقدار NumberList و طول آن برگرداند در صورتی که که در target ویژگی با نام latest وجود ندارد.

let target = {
  numberList: []
}

let handler = {
  set(target,prop,value){
    if('numberList' != prop){
      return undefined
    }
    value = parseInt(value)
    target.numberList.push(value)
  },
  get(target,prop,proxy){
    if('latest' == prop){
      return target['numberList'][target.numberList.length - 1]
    }
    return undefined
  }
}

let proxyNumberList = new Proxy(target,handler)

proxyNumberList.numberList = '3'
proxyNumberList.numberList = 'ali'
proxyNumberList.numberList = '54'
console.log(proxyNumberList)
console.log(proxyNumberList.latest)

Finding Array Item Object

با استفاده از یک Proxy که روی get Proxy handler method اعمال شده است حالت های زیر را روی یک collection پیاده سازی می کنیم.

  • اگر عدد index از این آرایه را برگرداند ما مقدار متناظر آن را بر می گردانیم .
  • اگر مقدار number را درخواست کرد تعداد کل این collection را بر می گردانیم.
  • اگر نام – name – یکی از این آیتم های collection را درخواست کرد آن آیتم از collection را بر می گردانیم.
  • اگر نوع – type – را درخواست داد ما آیتم هایی از این نوع را در collection موجود است را در قالب یک آرایه برمی گردانیم.
  • اگر types را درخواست داد لیست انواع آیتم های این collection را در قالب یک آرایه بر می گردانیم.
let target = [
  {name: 'firefox',type: 'browser'},
  {name: 'seaMonkey', type: 'browser'},
  {name: 'thunderbird', type: 'mailer'}
]

let handler = {
  get(target,prop,proxy){

    if(target[prop]){
      return target[prop]
    }

    if('number' == prop){
      return target.length
    }

    let result = new Array 
    let types = new Array

    for(const product of target){
      if(product.name == prop || product.type == prop){
        result.push(product)
      }
      if('types' == prop){
        if(!types.includes(product.type)){
          types.push(product.type)
          result = types
        }
      }
    }

    if(result.length > 0){
      return result
    }

    return undefined
  }
}

let p = new Proxy(target,handler)
console.log('Finding By index------------------------')
console.log(p[1])
console.log('Retrive Product Count-------------------')
console.log(p.number)
console.log('Find By Product Name--------------------')
console.log(p.seaMonkey)
console.log('Find By Product Type--------------------')
console.log(p.browser)
console.log(p.mailer)
console.log('Find Types------------------------------')
console.log(p.types)
console.log('Not Found!------------------------------')
console.log(p.chrome)

 

مطالب مشابه

Set Standard Build-in Object

Set یک موجودیت با رفتاری شبیه به آرایه است و می تواند در مواقع خاص جایگزین آن شود . این شرایط خاص با توجه به محتوای...

Map Standard Build-in Object

در جاوا اسکریپت همه چیز شئ‌ است . هر زمان که لازم باشد یک امکان جدید در اختیار شما شما قرار بدهد یک شئ استاندارد برای...

Date Time in JavaScript

زمان یک میلی ثانیه از تاریخ در جریان است که مستقل از هر platform (این که روی چه سروری است و چه مرورگری در چه منطقه...

اشتراک گذاری :

مدیر وب سایت گنوتک . برنامه نویسی رو با زبان C در هفده سالگی شروع کردم . در حال حاضر به برنامه نویسی php برپایه معماری MVC , HMVC و همچنین سیستم مدیریت محتوای WordPress و فریم ورک محبوب لاراول علاقه مند هستم و دوست دارم اطلاعاتم رو با شما به اشتراک بگذارم.

۰ دیدگاه برای Proxy Standard Build-in Object

دیدگاهتان را بنویسید

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