คำนำ
ใน ES6 ตัวสร้างพร็อกซีเป็นวัตถุระดับโลกที่เข้าถึงได้โดยใช้มันคุณสามารถรวบรวมข้อมูลต่าง ๆ เกี่ยวกับการดำเนินการที่ร้องขอระหว่างวัตถุและพฤติกรรมของวัตถุการดำเนินการต่างๆและส่งคืนสิ่งที่คุณต้องการทำ ฟังก์ชั่นลูกศร, การรื้อถอนอาร์เรย์, พารามิเตอร์ที่เหลือและคุณสมบัติอื่น ๆ ใน ES6 ได้รับการเผยแพร่อย่างกว้างขวางเมื่อพวกเขาได้รับการดำเนินการ แต่คุณสมบัติเช่นพร็อกซีไม่ค่อยเห็นโดยนักพัฒนา ในอีกด้านหนึ่งมันเป็นเพราะความเข้ากันได้ของเบราว์เซอร์และในทางกลับกันอาจเป็นเพราะข้อดีของคุณสมบัติเหล่านี้ที่นักพัฒนาจำเป็นต้องเข้าใจสถานการณ์การใช้งานของพวกเขาอย่างลึกซึ้ง โดยส่วนตัวแล้วฉันชอบพร็อกซีของ ES6 มากเพราะมันช่วยให้เราสามารถควบคุมการเข้าถึงวัตถุภายนอกในวิธีที่กระชับและเข้าใจง่าย ในต่อไปนี้ฉันจะแนะนำวิธีการใช้งานพร็อกซีแล้วแสดงตัวอย่างตัวอย่างเฉพาะเพื่ออธิบายสถานการณ์การใช้งานของพร็อกซี
พร็อกซี ดูชื่อและความหมายมีฟังก์ชั่นที่คล้ายกันมากกับโหมดพร็อกซีในโหมดการออกแบบซึ่งมักใช้ในสามด้าน:
1. ตรวจสอบการเข้าถึงวัตถุภายนอก
2. ฟังก์ชั่นหรือความซับซ้อนของคลาส
3. ตรวจสอบการดำเนินการหรือจัดการทรัพยากรที่จำเป็นก่อนการดำเนินการ
ในสภาพแวดล้อมของเบราว์เซอร์ที่รองรับพร็อกซีพร็อกซีเป็นวัตถุระดับโลกที่สามารถใช้งานได้โดยตรง Proxy(target, handler) เป็นตัวสร้าง target คือวัตถุที่ถูกดำเนินคดีและ handlder เป็นวัตถุที่ประกาศการดำเนินงานพร็อกซีต่าง ๆ และในที่สุดก็ส่งคืนวัตถุพร็อกซี ทุกครั้งที่โลกภายนอกเข้าถึงคุณสมบัติของวัตถุ target ผ่านวัตถุพร็อกซีมันจะผ่านวัตถุ handler จากกระบวนการนี้วัตถุพร็อกซีคล้ายกับมิดเดิลแวร์ (มิดเดิลแวร์) มาก การดำเนินการใดที่สามารถสกัดกั้นพร็อกซีได้? การดำเนินการที่พบบ่อยที่สุดคือ Get (อ่าน), Set (Modify) คุณสมบัติของวัตถุ ฯลฯ โปรดคลิกที่นี่เพื่อดูรายการการดำเนินการที่สกัดกั้นได้ทั้งหมด นอกจากนี้วัตถุพร็อกซียังมีวิธี revoke ที่สามารถออกจากระบบการดำเนินงานพร็อกซีทั้งหมดได้ตลอดเวลา ก่อนที่เราจะแนะนำพร็อกซีอย่างเป็นทางการเราขอแนะนำให้คุณมีความเข้าใจในการไตร่ตรองซึ่งเป็นวัตถุระดับโลกใหม่ที่เพิ่มเข้ามาใน ES6
ขั้นพื้นฐาน
const target = {ชื่อ: 'Billy Bob', อายุ: 15}; const handler = {get (เป้าหมาย, คีย์, พร็อกซี) {const วันนี้ = วันที่ใหม่ (); console.log (`รับคำขอที่ทำสำหรับ $ {key} ที่ $ {วันนี้}`); return rechorm.get (เป้าหมาย, คีย์, พร็อกซี); }}; const proxy = new proxy (เป้าหมาย, handler); proxy.name; // => "รับคำขอที่ทำชื่อที่ Thu Jul 21 2016 15:26:20 GMT+0800 (CST)" // => "Billy Bob" ในรหัสข้างต้นก่อนอื่นเราจะกำหนดวัตถุ target ที่จะเป็นพร็อกซีจากนั้นประกาศวัตถุ handler ที่มีการดำเนินการพร็อกซีทั้งหมด ต่อไปเราจะใช้ Proxy(target, handler) เพื่อสร้าง proxy วัตถุพร็อกซี หลังจากนั้นการเข้าถึง target เป้าหมายทั้งหมดโดยใช้ proxy จะถูกประมวลผลโดย handler
1. ออกจากโมดูลการตรวจสอบ
เริ่มต้นด้วยการตรวจสอบประเภทง่าย ๆ ซึ่งแสดงให้เห็นถึงวิธีการใช้พร็อกซีเพื่อให้แน่ใจว่ามีความแม่นยำของชนิดข้อมูล:
ให้ numericDatastore = {count: 0, จำนวน: 1234, ทั้งหมด: 14}; numericDatastore = พร็อกซีใหม่ (numericDatastore, {set (เป้าหมาย, คีย์, ค่า, พร็อกซี) {ถ้า (ค่าของค่า! == 'number') }}); // ข้อผิดพลาดถูกโยนเพราะ "foo" ไม่ใช่ numericDatastore.count = "foo"; // ที่ได้รับมอบหมายให้ประสบความสำเร็จ numericDatastore.count = 333;หากคุณต้องการพัฒนาตัวตรวจสอบโดยตรงสำหรับคุณสมบัติทั้งหมดของวัตถุมันอาจทำให้โครงสร้างรหัสป่องได้อย่างรวดเร็วโดยใช้พร็อกซีคุณสามารถแยกตัวตรวจสอบออกจากตรรกะหลักและเข้ามาเป็นหนึ่งเดียว:
ฟังก์ชั่น createValidator (เป้าหมาย, ตัวตรวจสอบ) {ส่งคืนพร็อกซีใหม่ (เป้าหมาย, {_validator: ตัวตรวจสอบ, ตั้งค่า (เป้าหมาย, คีย์, ค่า, พร็อกซี) {ถ้า (target.hasownproperty (key)) {let.leidator = this._validator [key] ข้อผิดพลาด (`ไม่สามารถตั้งค่า $ {key} เป็น $ {value}. ไม่ถูกต้อง`);}} elle {throw error (`` $ {key} ไม่ใช่คุณสมบัติที่ถูกต้อง ')}}}});} const personvalidators = {ชื่อ }, อายุ (val) {return typeof ge === 'number' && อายุ> 18; }} คลาสบุคคล {constructor (ชื่ออายุ) {this.name = name; this.age = อายุ; ส่งคืน createValidator (นี่คือ personvalidators); }} const bill = บุคคลใหม่ ('บิล', 25); // การดำเนินการต่อไปนี้จะรายงานข้อผิดพลาด bill.name = 0; Bill.age = 'Bill'; Bill.age = 15; ผ่านการแยกตัวตรวจสอบและตรรกะหลักคุณสามารถขยายเนื้อหาของตัวตรวจสอบ personValidators ได้อย่างไม่สิ้นสุดโดยไม่ทำให้เกิดความเสียหายโดยตรงกับคลาสหรือฟังก์ชั่นที่เกี่ยวข้อง เพื่อให้ซับซ้อนมากขึ้นเรายังสามารถใช้พร็อกซีเพื่อจำลองการตรวจสอบประเภทเพื่อตรวจสอบว่าฟังก์ชั่นได้รับพารามิเตอร์ที่มีประเภทและปริมาณที่ถูกต้องหรือไม่:
ให้ obj = {pickymethodone: ฟังก์ชั่น (obj, str, num) { / *... * /}, pickymethodtwo: ฟังก์ชัน (num, obj) { / *... * /}}; const argtypes = {pickymethodone: ["วัตถุ" {get: function (เป้าหมาย, key, proxy) {var value = target [key]; ฟังก์ชั่น argchecker (ชื่อ, args, checkers) {สำหรับ (var idx = 0; idx <args.length; idx ++) {var arg = args [idx]; var type = checkers [idx]; if (! arg || typeof arg! == type) {console.warn (`คุณใช้ลายเซ็นของ $ {name} ไม่ถูกต้องตรวจสอบ param $ {idx + 1}`); }}} obj.pickymethodone (); //> คุณใช้ลายเซ็นของ pickymethodone ไม่ถูกต้อง ตรวจสอบพารามิเตอร์ 1 //> คุณกำลังใช้ลายเซ็นของ pickymethodone ไม่ถูกต้อง ตรวจสอบพารามิเตอร์ 2 //> คุณกำลังใช้ลายเซ็นของ pickymethodone ไม่ถูกต้อง ตรวจสอบ param 3obj.pickymethodtwo ("wopdapodoo", {}); //> คุณใช้ลายเซ็นของ pickymethodone ไม่ถูกต้อง ตรวจสอบ param 3obj.pickymethodtwo ("wopdapodoo", {}); //> คุณใช้ลายเซ็นของ pickymethodone ไม่ถูกต้อง ตรวจสอบ param 3obj.pickymethodtwo ("wopdapodoo", {}); //> คุณกำลังใช้ลายเซ็นของ Pickymethodtwo ไม่ถูกต้อง ตรวจสอบพารามิเตอร์ 1 // ไม่มีคำเตือน logedobj.pickymethodone ({}, "สตริงเล็ก ๆ ", 123); obj.pickymethodone (123, {});2. คุณลักษณะส่วนตัว
ใน JavaScript หรือภาษาอื่น ๆ เป็นเรื่องปกติที่จะเพิ่ม _ ก่อนที่ชื่อตัวแปรเพื่อระบุว่านี่เป็นคุณสมบัติส่วนตัว (ไม่ใช่ส่วนตัวจริง ๆ ) แต่เราไม่สามารถรับประกันได้ว่าจะไม่มีใครเข้าถึงหรือแก้ไขได้ ในรหัสต่อไปนี้เราขอประกาศ apiKey ส่วนตัวเพื่ออำนวยความสะดวกในการเรียกใช้วิธีการภายในวัตถุ api แต่เราไม่ต้องการเข้าถึง api._apiKey จากภายนอก:
var api = {_apikey: '123abc456def',/ * วิธีการเยาะเย้ยที่ใช้สิ่งนี้ _apikey */ getusers: function () {}, getUser: ฟังก์ชั่น (userId) {}, setUser: function (userId, config) {}}; api._apikey); // รับและกลายพันธุ์ _apikeys เป็น apikey ที่ต้องการ = api._apikey; api._apikey = '987654321';เห็นได้ชัดว่าอนุสัญญาไม่ได้ถูกควบคุม การใช้ ES6 พร็อกซีเราสามารถใช้ตัวแปรส่วนตัวจริงได้ ต่อไปนี้แสดงให้เห็นถึงวิธีการส่วนตัวที่แตกต่างกันสองวิธีสำหรับวิธีการอ่านที่แตกต่างกัน
วิธีแรกคือการใช้ชุด/รับเพื่อสกัดกั้นการอ่านและเขียนคำขอและส่งคืน undefined:
ให้ api = {_apikey: '123abc456def', getUsers: function () {}, getUser: ฟังก์ชั่น (userId) {}, setUser: ฟังก์ชั่น (userId, config) {}}; > -1) {Throw error (`$ {key} ถูก จำกัด โปรดดูเอกสาร API สำหรับข้อมูลเพิ่มเติม`)} return retch.get (เป้าหมาย, key, proxy); Reflect.get (เป้าหมาย, คีย์, ค่า, พร็อกซี);}}); // การดำเนินการต่อไปนี้จะส่งข้อผิดพลาด console.log (api._apikey); api._apikey = '987654321';วิธีที่สองคือการใช้งานมีการสกัดกั้นในการใช้งาน:
var api = {_apikey: '123abc456def', getUsers: function () {}, getUser: ฟังก์ชั่น (userId) {}, setUser: ฟังก์ชั่น (userId, config) {}}; ? พร็อกซีปิดบัง _apikey ... ")}}3. บันทึกการเข้าถึง
สำหรับแอตทริบิวต์หรืออินเทอร์เฟซที่มักเรียกใช้ช้าหรือใช้ทรัพยากรมากขึ้นในสภาพแวดล้อมการดำเนินการนักพัฒนาจะต้องการบันทึกการใช้งานหรือประสิทธิภาพของพวกเขา ในเวลานี้พวกเขาสามารถใช้พร็อกซีเพื่อทำหน้าที่เป็นมิดเดิลแวร์และใช้ฟังก์ชั่นการบันทึกได้อย่างง่ายดาย:
ให้ api = {_apikey: '123abc456def', getusers: function () { / * ... * /}, getuser: function (userId) { / * ... * /}, setUser: ฟังก์ชั่น (userId, config) { / * /}}; console.log (`$ {timestamp} - การบันทึก $ {เมธอด} คำขอแบบอะซิงโครนัส ')}, 0)} api = พร็อกซีใหม่ (API, {get: function (เป้าหมาย, key, proxy) {var value = เป้าหมาย - api.getUsers ();4. การเตือนล่วงหน้าและการสกัดกั้น
สมมติว่าคุณไม่ต้องการให้นักพัฒนารายอื่นลบแอตทริบิวต์ noDelete และต้องการให้นักพัฒนาที่เรียก oldMethod ให้เข้าใจว่าวิธีนี้ถูกทอดทิ้งหรือบอกนักพัฒนาไม่ให้ปรับเปลี่ยนแอตทริบิวต์ doNotChange จากนั้นคุณสามารถใช้พร็อกซีเพื่อนำไปใช้:
ให้ datastore = {nodelete: 1235, oldMethod: function () {/*...*/}, donotchange: "พยายามและจริง"}; const nodelete = ['nodelete']; const nochange = ['donotchange']; const เลิกใช้ = ['oldMethod']; dataastore = new proxy (datastore, {set (เป้าหมาย, คีย์, ค่า, พร็อกซี) {ถ้า (nochange.includes (คีย์)) {โยนข้อผิดพลาด (`` `` `` `$ {key} ไม่สามารถเปลี่ยนได้ ')}} retion.set (เป้าหมาย, คีย์, ค่า, proxy); ข้อผิดพลาด (`ข้อผิดพลาด! $ {คีย์} ไม่สามารถลบได้ '); ฟังก์ชั่น (... args) {rechers.apply (เป้าหมาย [คีย์], เป้าหมาย, args); ลบ dataastore.nodelete; dataastore.oldMethod ();5. การดำเนินการกรอง
การดำเนินการบางอย่างจะใช้ทรัพยากรเป็นอย่างมากเช่นการถ่ายโอนไฟล์ขนาดใหญ่ ในเวลานี้หากไฟล์ถูกส่งเป็นชิ้น ๆ แล้วไม่จำเป็นต้องทำตามคำขอใหม่ที่สอดคล้องกัน ในเวลานี้คุณสามารถใช้พร็อกซีเพื่อตรวจจับคุณลักษณะของคำขอและกรองสิ่งที่ไม่จำเป็นต้องตอบสนองและสิ่งที่จำเป็นต้องตอบสนองตามคุณสมบัติ รหัสต่อไปนี้แสดงให้เห็นถึงวิธีการกรองคุณสมบัติไม่ใช่รหัสที่สมบูรณ์ ฉันเชื่อว่าทุกคนจะเข้าใจสิ่งมหัศจรรย์:
ให้ obj = {getGiantFile: function (fileid) {/*...*/}}; obj = พร็อกซีใหม่ (obj, {get (เป้าหมาย, key, พร็อกซี) {ฟังก์ชั่นการส่งคืน (... args) {const id = args [0]; (ISENROUTE || ISDOWNLOADING) {return false;6. ตัวแทนขัดจังหวะ
พร็อกซีรองรับ target ที่ไม่ได้ใช้งานได้ตลอดเวลาซึ่งมักจะใช้เพื่อแนบการเข้าถึงข้อมูลหรืออินเทอร์เฟซอย่างสมบูรณ์ ในตัวอย่างต่อไปนี้เราใช้ Proxy.revocable วิธีการที่สามารถกำหนดได้เพื่อสร้างวัตถุพร็อกซีที่พร็อกซีที่เพิกถอนได้:
ให้ sensitivedata = {ชื่อผู้ใช้: 'devbryce'}; const {sensitivedata, revokeaccess} = proxy.revocable (sensitivedata, handler); ฟังก์ชั่น handlesusciedhack () {revokeaccess ();} // logs HADLESUSPECTEDHACK (); // typeError: revokedConsole.log (sensitivedata.username);มัณฑนากร
มัณฑนากรที่ใช้ใน ES7 นั้นเทียบเท่ากับโหมดมัณฑนากรในโหมดการออกแบบ หากคุณเพียงแค่แยกแยะสถานการณ์การใช้งานของ พร็อกซี และ มัณฑนากร มันสามารถสรุปได้ว่า: ฟังก์ชั่นหลักของพร็อกซีคือการควบคุมการเข้าถึงตัวแทนของโลกภายนอกและฟังก์ชั่นหลักของมัณฑนากรคือการเพิ่มฟังก์ชั่นของมัณฑนากร ตราบใดที่พวกเขาสร้างความแตกต่างที่ดีในสถานการณ์การใช้งานหลักของพวกเขาฟังก์ชั่นเช่นบันทึกการเข้าถึงแม้ว่าบทความนี้ใช้การใช้งานพร็อกซีพวกเขายังสามารถนำไปใช้โดยใช้มัณฑนากร นักพัฒนาสามารถเลือกได้อย่างอิสระตามความต้องการของโครงการข้อกำหนดของทีมและความชอบของตนเอง
สรุป
พร็อกซีของ ES6 ยังคงใช้งานได้จริงมาก คุณสมบัติที่เรียบง่ายดูเหมือนจะใช้งานได้ดี ฉันหวังว่ามันจะเป็นประโยชน์สำหรับทุกคนในการเรียนรู้ ES6