φυβλαςのβλογ
บล็อกของ phyblas



javascript เบื้องต้น บทที่ ๔๐: พร็อกซี
เขียนเมื่อ 2020/02/08 07:01
แก้ไขล่าสุด 2020/04/18 14:12


ในบทนี้จะพูดถึงสิ่งที่เรียกว่าพร็อกซี (proxy) ในจาวาสคริปต์




การสร้างพร็อกซี

พร็อกซีเป็นออบเจ็กต์ชนิดหนึ่งที่เอาไว้ควบคุมพฤติกรรมต่างๆของออบเจ็กต์ในขณะที่ทำอะไรบางอย่าง เช่น ดูค่าพรอเพอร์ตี, ตั้งค่าพรอเพอร์ตี, ลบพรอเพอร์ตี, ฯลฯ

เนื่องจากว่าพร็อกซีก็เป็นออบเจ็กต์ชนิดหนึ่ง วิธีการสร้างก็ใช้ new
let proxy = new Proxy(เป้าหมาย, ตัวรับมือ)

ค่าที่ต้องใส่ในวงเล็บมี ๒ อย่าง

  • เป้าหมาย (target) คือออบเจ็กต์ที่ต้องการจะให้ออบเจ็กต์พร็อกซีตัวนี้ควบคุม

  • ตัวรับมือ (handler) คือออบเจ็กต์ที่เขียนเมธอดไว้ เป็นตัวกำหนดว่าจะควบคุมอย่างไร

ชื่อเมธอดอะไรเอาไว้ควบคุมอะไรได้ถูกกำหนดไว้แล้ว ดังนี้
ชื่อเมธอด ค่าที่ต้องคืนกลับ จังหวะที่จะไปควบคุมเป้าหมาย
get(trgt, prop) ค่าใดๆก็ได้ เมื่อดูค่าพรอเพอร์ตี
set(trgt, prop, val) true / false เมื่อตั้งค่าพรอเพอร์ตี
has(trgt, prop) true / false เมื่อใช้ตัวดำเนินการ in
deleteProperty(trgt, prop) true / false เมื่อใช้ delete
apply(trgt, this, args) ค่าใดๆก็ได้ เมื่อเรียกใช้แบบฟังก์ชัน
construct(trgt, args) อ็อบเจ็กต์ เมื่อใช้ new สร้างอินสแตนซ์

ในที่นี้
  • trgt: ออบเจ็กต์เป้าหมาย
  • prop: ชื่อพรอเพอร์ตี
  • val: ค่าที่จะตั้งให้

นอกจากนี้ก็มีเมธอดเหล่านี้ ซึ่งชื่อเหมือนกับเมธอดที่มีอยู่ในตัวออบเจ็กต์ ซึ่งเคยเขียนถึงไปแล้ว ถ้าใส่เมธอดชื่อเหล่านี้ลงไปในพร็อกซีก็จะเป็นการไปควบคุมเมธอดเหล่านี้ในออบเจ็กต์

isExtensible บทที่ ๒๕
preventExtensions
getOwnPropertyDescriptor บทที่ ๒๖
defineProperty
getPrototypeOf
setPrototypeOf

การสร้างตัวรับมือก็เขียนในลักษณะประมาณนี้
handler = {
  get(target, prop){
    //...
  },
  set(target, prop, val){
    //...
  },
  has(target, prop){
    //...
  },
  ฯลฯ(){
    //...
  },
  //...
}

ต่อไปจะยกตัวอย่างการใช้กับแต่ละเมธอด




get

ปกติแล้วเวลาดูค่าของพรอเพอร์ตีไหนแล้วถ้าค่านั้นไม่มีอยู่ก็จะได้ undefined แต่เราสามารถสร้างออบเจ็กต์ที่จะทำให้พฤติกรรมตรงนี้เปลี่ยนไปได้ เช่นให้คืนค่าอะไรบางอย่างมาแทน
let prox = new Proxy(
  {},
  {
    get: function (trgt, prop) {
      if(prop in trgt) return trgt[prop];
      else return "ที่นี่ไม่มีสิ่งที่ท่านต้องการ ณ เวลานี้";
    }
  }
);
prox.a = "เธอมีฉันอยู่ข้างใน";
prox.b = "ฉันอยู่ตรงนี้เมื่อคุณต้องการ";
alert(prox.a); // ได้ เธอมีฉันอยู่ข้างใน
alert(prox.b); // ได้ ฉันอยู่ตรงนี้เมื่อคุณต้องการ
alert(prox.h); // ได้ ที่นี่ไม่มีสิ่งที่ท่านต้องการ ณ เวลานี้

เมธอด get ในที่นี้จะรับค่ามา ๒ อย่างคือ ออบเจ็กต์ตัวนั้น (trgt) และ ชื่อพรอเพอร์ตีที่ถูกดูค่า (prop)

prop in trgt ก็คือดูว่ามีพรอเพอร์ตีนี้อยู่ในออบเจ็กต์นี้หรือเปล่า ถ้ามีก็คืนค่าพรอเพอร์ตีนั้น trgt[prop] ไปตามปกติ แต่ถ้าไม่มีก็จะคืนค่าที่กำหนดไว้ใน else ด้านล่าง

อีกตัวอย่างหนึ่ง เช่นถ้าต้องการให้ค่าที่ออกมามีกรอบสวยๆคร่อมอาจเขียนดังนี้
let prox = new Proxy(
  {x: "XXX"},
  {
    get: function (trgt, prop) {
      return "*@~" + trgt[prop] + "=$^"
    }
  }
);
alert(prox.x); // ได้ *@~XXX=$^
alert(prox.y); // ได้ *@~undefined=$^




set

เมธอด set จะทำงานเมื่อเราใช้ = เพื่อป้อนค่าให้ออบเจ็กต์ โดยปกติแล้วพอตั้งค่าให้ ค่าก็จะถูกป้อนให้กับพรอเพอร์ตีนั้นตามค่าที่ใส่ไป แต่แราสามารถเปลี่ยนแปลงพฤติกรรมตรงนี้ได้ เช่น กำหนดข้อจำกัดให้ต้องไม่เกิน 100 ไม่งั้นจะเกิดข้อผิดพลาด
let pokemon = new Proxy(
  {},
  {
    set: function (trgt, prop, val) {
      if(val>100) return false;
      else {
        trgt[prop] = val;
        return true;
      }
      
    }
  }
);

pokemon.lv = 99;
alert(pokemon.lv); // ได้ 99

pokemon.lv = 101; // ได้ TypeError: proxy set handler returned false for property '"lv"'

ในที่นี้ val คือค่าที่กำลังจะถูกตั้งให้ (ค่าที่อยู่ข้างหลังเครื่องหมาย =) และเมธอด set จะต้องคืนค่า true ไม่เช่นนั้นก็จะเกิดข้อผิดพลาดขึ้นเป็น TypeError ดังที่เห็น

ในที่นี้กำหนดให้ถ้า val เกิน 100 ก็คืนค่า false ออกไป ก็จะทำให้เกิดข้อผิดพลาดขึ้น แต่ถ้าไม่เกินก็ตั้งค่าให้ตามปกติ แล้วคืนค่า true

หรือเช่นถ้าต้องการให้เวลาตั้งพรอเพอร์ตีการแปลงข้อมูลให้เป็นไปตามที่ต้องการก่อนเก็บ เช่น ถ้าจะแปลงค่าที่ใส่เข้ามาเป็นจำนวนเต็มทั้งหมดก็อาจเขียนแบบนี้
let pikachu = new Proxy(
  {},
  {
    set: function (trgt, prop, val) {
      trgt[prop] = parseInt(val);
      return true;
    }
  }
);
pikachu.lv = 77.777;
alert(pikachu.lv); // ได้ 77

pikachu.lv = "55 มั้ง";
alert(pikachu.lv); // ได้ 55




has

ตัวดำเนินการ in ปกติใช้สำหรับดูค่าว่ามีพรอเพอร์ตีที่ต้องการอยู่ในออบเจ็กต์นั้นหรือไม่ ถ้ามีก็จะได้ true ถ้าไม่ก้ได้ false

เมธอด has จะควบคุมออบเจ็กต์เวลาที่ใช้ in เราสามารถแก้ให้เป็นออบเจ็กต์ที่แปลกๆที่ให้ผลตรงกันข้าม เช่นให้ false เมื่อมี ให้ true เมื่อไม่มี แบบนี้ก็ได้
let protozoa = new Proxy(
  {cilia: 1000000},
  {
    has: function (trgt, prop) {
      if(prop in trgt) return false
      else return true;
    }
  }
);
alert("cilia" in protozoa); // ได้ false
alert("flagella" in protozoa); // ได้ true




deleteProperty

ปกติถ้าใช้คำสั่ง delete พรอเพอร์ตีถูกลบก็จะหายไป เหมือนกับที่คนเราจะตายเมื่อถูกฆ่าและคนเราจะตายเมื่อถูกฆ่า

เราสามารถตั้งเมธอด deleteProperty เพื่อกำหนดพฤติกรรมเวลาทำการลบพรอเพอร์ตีให้แปลกไปได้ เช่นให้มีการเตือนโดยกล่องข้อความเมื่อทำการลบ
let proxa = new Proxy(
  {propa: 555},
  {
    deleteProperty(trgt, prop) {
      alert("จะลบละนะ")
      return true;
    }
  }
);
delete proxa.propa;

delete ก็ต้องคืนค่า true หรือ false เช่นเดียวกับ set ถ้าไม่คืนค่า true ก็จะเกิดข้อผิดพลาด

เช่นอาจลองทำให้ออบเจ็กต์เกิดข้อผิดพลาดเสมอเมื่อทำการลบโดยเขียนแบบนี้
let proxa = new Proxy(
  { propa: 666 },
  {
    deleteProperty(trgt, prop) {
      return false;
    }
  }
);
delete proxa.propa; // ได้ TypeError: can't delete property '"propa"': proxy deleteProperty handler returned false




apply

เมธอด apply จะควบคุมออบเจ็กต์เวลาที่ถูกเรียกในฐานะฟังก์ชัน

ตัวอย่างเช่น สร้างฟังก์ชันที่เดิมทีแค่ให้ผลออกมา จากนั้นใช้พร็อกซีเติมให้เด้งหน้าต่างแสดงผลออกมาในขณะเดียวกันด้วย
let funcprox = new Proxy(
  function(a,b) { return a*b},
  {
    apply(trgt, thisarg, args) {
      let phonkhun = trgt(...args);
      alert("ผลคูณคือ " + phonkhun);
      return phonkhun;
    }
  }
);
funcprox(5,7); // ได้ ผลคูณคือ 35

args คือแถวลำดับของอาร์กิวเมนต์ที่รับเข้ามา

ส่วน thisarg คือสิ่งที่แทนตัวออบเจ็กต์ ถ้าหากฟังก์ชันนั้นถูกเรียกในฐานะเมธอดของออบเจ็กต์

ตัวอย่างเช่น สร้างเมธอดที่เอาเลขมาคูณกัน แล้วก็ใช้พร็อกซีเติมให้ค่าที่ได้นั้นไปคูณกับพรอเพอร์ตี x ในตัวออบเจ็กต์นั้นอีกด้วยก่อนจะคืนเป็นผลลัพธ์ออกมา
let funcprox = new Proxy(
  function (a, b) { return a * b },
  {
    apply(trgt, thisarg, args) {
      let phonkhun = trgt(...args);
      return phonkhun * thisarg.x;
    }
  }
);
let obj = { x: 10, funcprox };
alert(obj.funcprox(7, 9)); // ได้ ผลคูณคือ 630




construct

เมธอด construct จะควบคุมออบเจ็กต์ที่เป็นคอนสตรักเตอร์เวลาที่ใช้เมธอด new

ตัวอย่างเช่น เติมให้มีนกรอบข้อความเด้งขึ้นมาตอนที่สร้าง
let Phukla = new Proxy(
  class {
    constructor(chue, lv) {
      this.chue = chue;
      this.lv = lv;
    }
  },
  {
    construct(trgt, args) {
      let obj = new trgt(...args);
      alert("ผู้กล้า '" + obj.chue + "' lv " + obj.lv + " ได้ถือกำเนิดขึ้นแล้ว");
      return obj;
    }
  }
);
let phukla = new Phukla("ได", 100); // ได้ ผู้กล้า 'ได' lv 100 ได้ถือกำเนิดขึ้นแล้ว

นอกจากนี้ ยังอาจทำเหมือนเป็นการสร้าง constructor ใหม่ให้คลาสเปล่าๆได้ เช่น
let ProxoPokemon = new Proxy(
  class { },
  {
    construct(trgt, args) {
      let [chue, lv] = args;
      return { chue, lv };
    }
  }
);
let ponyta = new ProxoPokemon("โพนีตา", 21);
alert(ponyta.chue); // ได้ โพนีตา
alert(ponyta.lv); // ได้ 21



ทั้งหมดนี้ก็คือตัวอย่างส่วนหนึ่งของการใช้พร็อกซี หากเข้าใจแล้วก็สามารถทำให้ออบเจ็กต์มีพฤติกรรมเปลี่ยนไป หรือเพิ่มความสามารถให้ออบเจ็กต์ได้ตามที่ต้องการ






-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> javascript

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- opencv
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
maya
javascript
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
เรียนภาษาจีน
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
หลักการเขียนทับศัพท์ภาษาจีนกวางตุ้ง
การใช้ unix shell เบื้องต้น ใน linux และ mac
หลักการเขียนทับศัพท์ภาษาจีนกลาง
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
บันทึกการเที่ยวสวีเดน 1-12 พ.ค. 2014
แนะนำองค์การวิจัยและพัฒนาการสำรวจอวกาศญี่ปุ่น (JAXA)
เล่าประสบการณ์ค่ายอบรมวิชาการทางดาราศาสตร์โดยโซวเคนได 10 - 16 พ.ย. 2013
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
บันทึกการเที่ยวญี่ปุ่นครั้งแรกในชีวิต - ทุกอย่างเริ่มต้นที่สนามบินนานาชาติคันไซ
หลักการเขียนทับศัพท์ภาษาญี่ปุ่น
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ
ทำไมถึงอยากมาเรียนต่อนอก
เหตุผลอะไรที่ต้องใช้ภาษาวิบัติ?

ไทย

日本語

中文