แนะนำ
ในบทความนี้เราพิจารณาแง่มุมต่าง ๆ ของการเขียนโปรแกรมเชิงวัตถุใน eCmascript (แม้ว่าหัวข้อนี้ได้ถูกกล่าวถึงในหลายบทความมาก่อน) เราจะดูปัญหาเหล่านี้เพิ่มเติมจากมุมมองทางทฤษฎี โดยเฉพาะอย่างยิ่งเราจะพิจารณาอัลกอริทึมการสร้างของวัตถุว่าความสัมพันธ์ระหว่างวัตถุ (รวมถึงความสัมพันธ์ขั้นพื้นฐาน - การสืบทอด) มีอยู่ในการอภิปราย (ฉันหวังว่าความกำกวมแนวคิดบางอย่างของ OOP ใน JavaScript จะถูกลบออก)
ข้อความภาษาอังกฤษต้นฉบับ: http://dmitrysoshnikov.com/ecmascript/chapter-7-1-oop-eneral-theory/
บทนำกระบวนทัศน์และความคิด
ก่อนที่จะทำการวิเคราะห์ทางเทคนิคของ OOP ใน ECMASCript มีความจำเป็นที่จะต้องเชี่ยวชาญลักษณะพื้นฐานบางอย่างของ OOP และชี้แจงแนวคิดหลักในการแนะนำ
Ecmascript สนับสนุนวิธีการเขียนโปรแกรมที่หลากหลายรวมถึงโครงสร้างเชิงวัตถุการใช้งานการใช้งานความจำเป็น ฯลฯ และในบางกรณีมันยังรองรับการเขียนโปรแกรมที่มุ่งเน้นด้าน แต่บทความนี้กล่าวถึงการเขียนโปรแกรมเชิงวัตถุดังนั้นเราจะให้คำจำกัดความของการเขียนโปรแกรมเชิงวัตถุใน eCmascript:
Ecmascript เป็นภาษาการเขียนโปรแกรมเชิงวัตถุโดยใช้การใช้งานต้นแบบ
มีความแตกต่างมากมายระหว่าง OOP ที่ใช้ต้นแบบและวิธีการเรียนแบบคงที่ ลองมาดูความแตกต่างโดยละเอียดโดยตรง
ชั้นเรียนและต้นแบบ
โปรดทราบว่าจุดสำคัญในประโยคก่อนหน้าได้ชี้ให้เห็นแล้ว - มันขึ้นอยู่กับคลาสคงที่ทั้งหมด ด้วยคำว่า "คงที่" เราเข้าใจวัตถุคงที่และคลาสคงที่พิมพ์อย่างมาก (แม้ว่าจะไม่จำเป็น)
เกี่ยวกับสถานการณ์นี้เอกสารฟอรัมจำนวนมากเน้นว่านี่คือเหตุผลหลักว่าทำไมพวกเขาถึงคัดค้านการเปรียบเทียบ "คลาสกับต้นแบบ" ใน JavaScript แม้ว่าความแตกต่างของการใช้งานของพวกเขา (เช่น Python และ Ruby ที่อยู่บนพื้นฐานของคลาสแบบไดนามิก) ไม่ได้ตรงกันข้ามกับประเด็น (เงื่อนไขบางอย่างถูกเขียนแม้ว่าจะมีความแตกต่างในความคิด แต่ JavaScript ไม่ใช่ทางเลือกอื่น) เพื่อความแม่นยำคลาสคงที่ (เช่น C ++, Java) และกลไกการนิยามของผู้ใต้บังคับบัญชาและวิธีการกำหนดวิธีสามารถให้เราเห็นความแตกต่างที่แน่นอนระหว่างการใช้งานและการใช้งานต้นแบบ
แต่มาแสดงรายการกันทีละรายการ ให้เราพิจารณาหลักการทั่วไปและแนวคิดหลักของกระบวนทัศน์เหล่านี้
ขึ้นอยู่กับชั้นเรียนคงที่
ในรูปแบบที่ใช้คลาสมีแนวคิดเกี่ยวกับคลาสและอินสแตนซ์ อินสแตนซ์ของชั้นเรียนมักจะตั้งชื่อวัตถุหรือกระบวนทัศน์
ชั้นเรียนและวัตถุ
คลาสแสดงถึงสิ่งที่เป็นนามธรรมของอินสแตนซ์ (เช่นวัตถุ) นี่เป็นเหมือนคณิตศาสตร์เล็กน้อย แต่เราเรียกมันว่าประเภทหรือการจำแนกประเภท
ตัวอย่างเช่น (ตัวอย่างที่นี่และด้านล่างเป็นรหัสหลอก):
การคัดลอกรหัสมีดังนี้:
c = คลาส {a, b, c} // คลาส C รวมถึงคุณสมบัติ A, B, C
ลักษณะของอินสแตนซ์คือ: แอตทริบิวต์ (คำอธิบายวัตถุ) และวิธีการ (กิจกรรมวัตถุ) คุณลักษณะของตัวเองยังถือได้ว่าเป็นวัตถุ: นั่นคือไม่ว่าจะเป็นแอตทริบิวต์ที่สามารถเขียนได้สามารถกำหนดค่าได้ (getter/setter) ฯลฯ ดังนั้นวัตถุเก็บสถานะ (เช่นค่าเฉพาะของแอตทริบิวต์ทั้งหมดที่อธิบายไว้ในชั้นเรียน)
การคัดลอกรหัสมีดังนี้:
c = คลาส {a, b, c, method1, method2}
c1 = {a: 10, b: 20, c: 30} // คลาส C เป็นอินสแตนซ์: วัตถุс1
c2 = {a: 50, b: 60, c: 70} // คลาส C เป็นอินสแตนซ์: วัตถุс2ซึ่งมีสถานะของตัวเอง (นั่นคือค่าแอตทริบิวต์)
มรดกแบบลำดับชั้น
ในการปรับปรุงการใช้รหัสซ้ำคลาสสามารถขยายจากที่หนึ่งไปอีกที่หนึ่งโดยเพิ่มข้อมูลเพิ่มเติม กลไกนี้เรียกว่าการสืบทอด (ลำดับชั้น)
การคัดลอกรหัสมีดังนี้:
d = คลาสขยาย c = {d, e} // {a, b, c, d, e}
d1 = {a: 10, b: 20, c: 30, d: 40, e: 50}
เมื่อโทรหาปาร์ตี้ในอินสแตนซ์ของคลาสคลาสดั้งเดิมมักจะมองหาวิธีการในขณะนี้ หากไม่พบให้ไปที่คลาสหลักเพื่อค้นหาโดยตรง หากไม่พบให้ไปที่คลาสหลักของคลาสแม่เพื่อค้นหา (ตัวอย่างเช่นในห่วงโซ่การสืบทอดที่เข้มงวด) หากไม่พบการสืบทอดด้านบนของมรดกไม่พบผลลัพธ์คือ: วัตถุไม่มีพฤติกรรมที่คล้ายกันและไม่สามารถรับผลลัพธ์ได้
การคัดลอกรหัสมีดังนี้:
D1.Method1 () // D.Method1 (ไม่) -> C.Method1 (ใช่)
d1.method5 () // D.Method5 (ไม่) -> C.Method5 (ไม่) -> ไม่มีผลลัพธ์
เมื่อเปรียบเทียบกับวิธีการสืบทอดที่ไม่ได้คัดลอกไปยังคลาสย่อยคุณสมบัติจะซับซ้อนในคลาสย่อยเสมอ เราจะเห็นได้ว่า subclass D สืบทอดมาจากคลาสหลัก C: แอตทริบิวต์ A, B, C ถูกคัดลอกและโครงสร้างของ D คือ {A, B, C, D, E}} อย่างไรก็ตามวิธี {method1, method2} ไม่ได้คัดลอกอดีต แต่สืบทอดอดีต ดังนั้นนั่นคือถ้าคลาสที่ลึกมากมีคุณสมบัติบางอย่างที่วัตถุไม่ต้องการเลยดังนั้นคลาสย่อยก็มีคุณสมบัติเหล่านี้ด้วย
แนวคิดหลักขึ้นอยู่กับชั้นเรียน
ดังนั้นเราจึงมีแนวคิดหลักต่อไปนี้:
1. ก่อนที่จะสร้างวัตถุจะต้องประกาศคลาส ก่อนอื่นจำเป็นต้องกำหนดชั้นเรียน
2. ดังนั้นวัตถุจะถูกสร้างขึ้นจากชั้นเรียนที่แยกออกเป็น "ภาพและความคล้ายคลึงกัน" ของตัวเอง (โครงสร้างและพฤติกรรม) ของตัวเอง
3. วิธีการจัดการผ่านห่วงโซ่การสืบทอดที่เข้มงวดตรงและไม่เปลี่ยนแปลง
4. คลาสย่อยมีแอตทริบิวต์ทั้งหมดในห่วงโซ่การสืบทอด (แม้ว่าแอตทริบิวต์บางส่วนจะไม่ต้องการโดยคลาสย่อย);
5. สร้างอินสแตนซ์คลาส คลาสไม่สามารถ (เนื่องจากแบบจำลองคงที่) เพื่อเปลี่ยนลักษณะ (แอตทริบิวต์หรือวิธีการ) ของอินสแตนซ์ของมัน
6. อินสแตนซ์ (เนื่องจากแบบจำลองคงที่อย่างเข้มงวด) ไม่สามารถมีพฤติกรรมหรือคุณลักษณะเพิ่มเติมยกเว้นพฤติกรรมและคุณลักษณะที่ประกาศไว้ในคลาสที่สอดคล้องกันกับอินสแตนซ์
มาดูวิธีการแทนที่โมเดล OOP ใน JavaScript ซึ่งเป็น OOP ที่ใช้ต้นแบบที่เราแนะนำ
ขึ้นอยู่กับต้นแบบ
แนวคิดพื้นฐานที่นี่คือวัตถุที่เปลี่ยนแปลงได้แบบไดนามิก การแปลง (การแปลงเต็มรูปแบบรวมถึงไม่เพียง แต่ค่า แต่ยังมีคุณสมบัติ) เกี่ยวข้องโดยตรงกับภาษาไดนามิก วัตถุเช่นต่อไปนี้สามารถเก็บคุณสมบัติทั้งหมด (คุณสมบัติวิธีการ) อย่างอิสระโดยไม่ต้องเรียน
การคัดลอกรหัสมีดังนี้:
Object = {a: 10, b: 20, c: 30, วิธี: fn};
Object.a; // 10
Object.c; // 30
Object.method ();
นอกจากนี้เนื่องจากไดนามิกพวกเขาสามารถเปลี่ยนแปลงได้อย่างง่ายดาย (เพิ่มลบแก้ไข) คุณสมบัติของตัวเอง:
การคัดลอกรหัสมีดังนี้:
Object.method5 = function () {... }; // เพิ่มวิธีการใหม่
Object.d = 40; // เพิ่มแอตทริบิวต์ใหม่ "D"
ลบ Object.c; // ลบแอตทริบิวต์ "с"
Object.a = 100; // แก้ไขแอตทริบิวต์ "а"
// ผลลัพธ์คือ: วัตถุ: {a: 100, b: 20, d: 40, วิธี: fn, method5: fn};
นั่นคือเมื่อมีการกำหนดหากไม่มีคุณสมบัติบางอย่างจะถูกสร้างขึ้นและการมอบหมายจะเริ่มต้นด้วยและถ้ามีอยู่ก็จะได้รับการอัปเดต
ในกรณีนี้การใช้รหัสซ้ำจะไม่ถูกนำมาใช้โดยการขยายชั้นเรียน (โปรดทราบว่าเราไม่ได้บอกว่าชั้นเรียนไม่สามารถเปลี่ยนแปลงได้เนื่องจากไม่มีแนวคิดของชั้นเรียนที่นี่) แต่ถูกนำไปใช้โดยต้นแบบ
ต้นแบบเป็นวัตถุที่ใช้เป็นสำเนาดั้งเดิมของวัตถุอื่น ๆ หรือหากวัตถุบางอย่างไม่มีลักษณะที่จำเป็นของตัวเองต้นแบบสามารถใช้เป็นตัวแทนของวัตถุเหล่านี้และทำหน้าที่เป็นวัตถุเสริม
ขึ้นอยู่กับการมอบหมาย
วัตถุใด ๆ สามารถใช้เป็นวัตถุต้นแบบสำหรับวัตถุอื่นได้เนื่องจากวัตถุสามารถเปลี่ยนการเปลี่ยนแปลงต้นแบบได้อย่างง่ายดายเมื่อรันไทม์
โปรดทราบว่าขณะนี้เรากำลังพิจารณาการแนะนำมากกว่าการใช้งานที่เป็นรูปธรรมและเมื่อเราหารือเกี่ยวกับการใช้งานที่เป็นรูปธรรมใน ECMASCript เราจะเห็นลักษณะบางอย่างของตัวเอง
ตัวอย่าง (pseudocode):
การคัดลอกรหัสมีดังนี้:
x = {a: 10, b: 20};
y = {a: 40, c: 50};
y. [[ต้นแบบ]] = x; // x เป็นต้นแบบของ y
ใช่; // 40 ลักษณะของตัวเอง
YC; // 50 มันเป็นลักษณะของตัวเอง
yb; // 20 รับจากต้นแบบ: yb (ไม่) -> y. [[ต้นแบบ]]. b (ใช่): 20
ลบคุณ; // ลบ "а" ของคุณเอง
ใช่; // 10 รับจากต้นแบบ
z = {a: 100, e: 50}
y. [[ต้นแบบ]] = z; // แก้ไขต้นแบบของ y เป็น z
ใช่; // 100 รับจากต้นแบบ z
Ye // 50 ยังได้รับจาก Prototype Z
zq = 200 // เพิ่มแอตทริบิวต์ใหม่ลงในต้นแบบ
yq // การดัดแปลงยังใช้กับ y
ตัวอย่างนี้แสดงฟังก์ชั่นและกลไกที่สำคัญของต้นแบบเป็นแอตทริบิวต์วัตถุเสริมเช่นเดียวกับที่คุณต้องการคุณลักษณะของคุณเอง เมื่อเทียบกับคุณลักษณะของคุณเองคุณลักษณะเหล่านี้เป็นคุณลักษณะที่ได้รับมอบหมาย กลไกนี้เรียกว่าผู้แทนและแบบจำลองต้นแบบที่ใช้เป็นผู้ได้รับมอบหมายต้นแบบ (หรือต้นแบบที่ได้รับมอบหมาย) กลไกการอ้างอิงเรียกว่าการส่งข้อมูลไปยังวัตถุ หากวัตถุไม่ได้รับการตอบกลับมันจะถูกมอบหมายให้ต้นแบบเพื่อค้นหา (ต้องการให้พยายามตอบกลับข้อความ)
การใช้รหัสซ้ำในกรณีนี้เรียกว่าการสืบทอดการสืบทอดหรือการสืบทอดตามต้นแบบ เนื่องจากวัตถุใด ๆ สามารถถือได้ว่าเป็นต้นแบบนั่นคือต้นแบบสามารถมีต้นแบบของตัวเองได้ ต้นแบบเหล่านี้เชื่อมต่อเข้าด้วยกันเพื่อสร้างห่วงโซ่ต้นแบบที่เรียกว่า โซ่ยังมีลำดับชั้นในชั้นเรียนคงที่ แต่สามารถจัดเรียงใหม่ได้ง่ายเปลี่ยนลำดับชั้นและโครงสร้าง
การคัดลอกรหัสมีดังนี้:
x = {a: 10}
y = {b: 20}
y. [[ต้นแบบ]] = x
z = {c: 30}
z. [[ต้นแบบ]] = y
Za // 10
// za พบในห่วงโซ่ต้นแบบ:
// za (ไม่) ->
// z. [[ต้นแบบ]]. a (ไม่) ->
// z. [[ต้นแบบ]]. [[ต้นแบบ]]. a (ใช่): 10
หากวัตถุและห่วงโซ่ต้นแบบไม่สามารถตอบสนองต่อการส่งข้อความวัตถุสามารถเปิดใช้งานสัญญาณระบบที่เกี่ยวข้องซึ่งอาจถูกประมวลผลโดยผู้ได้รับมอบหมายคนอื่น ๆ ในห่วงโซ่ต้นแบบ
สัญญาณระบบนี้มีอยู่ในการใช้งานหลายอย่างรวมถึงระบบที่ใช้คลาสไดนามิก: #DoesNotUnderStand ใน SmallTalk, Method_missing ใน Ruby; __getAttr__ ใน Python, __CALL ใน PHP; และ __nosuchmethod__ การใช้งานใน ecmascript ฯลฯ
ตัวอย่าง (การใช้งาน ECMASCRIPT ของ SpiderMonkey):
การคัดลอกรหัสมีดังนี้:
var object = {
// จับสัญญาณระบบที่ไม่สามารถตอบสนองต่อข้อความได้
__nosuchmethod__: ฟังก์ชั่น (ชื่อ, args) {
การแจ้งเตือน ([ชื่อ, args]);
if (name == 'test') {
return '.test () วิธีการจัดการ';
-
ส่งคืนผู้แทน [ชื่อ]. Apply (นี่, args);
-
-
VAR Delegate = {
สแควร์: ฟังก์ชั่น (a) {
คืน A * A;
-
-
การแจ้งเตือน (Object.square (10)); // 100
การแจ้งเตือน (object.test ()); // .test () วิธีการจัดการ
กล่าวคือขึ้นอยู่กับการใช้งานคลาสคงที่เมื่อข้อความไม่สามารถตอบกลับได้ข้อสรุปคือวัตถุปัจจุบันไม่มีลักษณะที่จำเป็น แต่ถ้าคุณพยายามที่จะได้รับจากห่วงโซ่ต้นแบบคุณอาจยังได้รับผลลัพธ์หรือวัตถุมีลักษณะหลังจากการเปลี่ยนแปลง
เกี่ยวกับ ECMASCRIPT การใช้งานเฉพาะคือการใช้ต้นแบบที่ได้รับมอบหมาย อย่างไรก็ตามอย่างที่เราจะเห็นจากข้อกำหนดและการใช้งานพวกเขายังมีลักษณะของตัวเอง
รูปแบบการต่อกัน
สุจริตมีความจำเป็นที่จะต้องพูดอะไรบางอย่างเกี่ยวกับสถานการณ์อื่น (ไม่ได้ใช้ใน ecmascript โดยเร็วที่สุด): สถานการณ์นี้เมื่อต้นแบบซับซ้อนจากวัตถุอื่น ๆ ไปยังการเปลี่ยนวัตถุดั้งเดิม ในกรณีนี้การใช้รหัสซ้ำเป็นสำเนาจริง (โคลน) ของวัตถุแทนที่จะเป็นตัวแทนในระหว่างขั้นตอนการสร้างวัตถุ ต้นแบบนี้เรียกว่าต้นแบบ concatenative การคัดลอกคุณสมบัติของต้นแบบทั้งหมดของวัตถุสามารถเปลี่ยนคุณสมบัติและวิธีการของมันได้อย่างสมบูรณ์และเป็นต้นแบบคุณสามารถเปลี่ยนตัวเองได้ (ในแบบจำลองที่ได้รับมอบหมายการเปลี่ยนแปลงนี้จะไม่เปลี่ยนพฤติกรรมของวัตถุที่มีอยู่ แต่จะเปลี่ยนลักษณะต้นแบบ) ข้อดีของวิธีนี้คือสามารถลดเวลาของการกำหนดเวลาและการมอบหมายในขณะที่ข้อเสียคือใช้หน่วยความจำ
ประเภทเป็ด
กลับไปที่วัตถุเปลี่ยนประเภทที่อ่อนแอแบบไดนามิกเมื่อเทียบกับโมเดลที่ใช้คลาสแบบคงที่มีความจำเป็นที่จะต้องตรวจสอบว่ามันสามารถทำสิ่งเหล่านี้ได้หรือไม่และประเภทใด (คลาส) วัตถุนั้นมี แต่ไม่ว่าจะเกี่ยวข้องกับข้อความที่เกี่ยวข้อง (เช่นไม่ว่าจะมีความสามารถในการตรวจสอบหลังจากการตรวจสอบเป็นสิ่งจำเป็นหรือไม่)
ตัวอย่างเช่น:
การคัดลอกรหัสมีดังนี้:
// ในโมเดลแบบคงที่
if (วัตถุอินสแตนซ์ของ someclass) {
// พฤติกรรมบางอย่างกำลังทำงานอยู่
-
// ในการใช้งานแบบไดนามิก
// มันไม่สำคัญว่าในเวลานี้วัตถุประเภทใด
// เนื่องจากการกลายพันธุ์ประเภทและลักษณะสามารถเปลี่ยนแปลงได้อย่างอิสระ
// ไม่ว่าวัตถุสำคัญจะตอบสนองต่อข้อความทดสอบหรือไม่
if (isfunction (object.test)) // ecmascript
ถ้า object.respond_to? (: ทดสอบ) // ทับทิม
ถ้า hasattr (วัตถุ, 'ทดสอบ'): // Python
สิ่งนี้เรียกว่าประเภทท่าเรือ นั่นคือวัตถุสามารถระบุได้ด้วยลักษณะของตัวเองเมื่อตรวจสอบแทนที่จะเป็นตำแหน่งของวัตถุในลำดับชั้นหรือเป็นของประเภทใด ๆ ที่เฉพาะเจาะจง
แนวคิดหลักขึ้นอยู่กับต้นแบบ
ลองมาดูคุณสมบัติหลักของวิธีนี้:
1. แนวคิดพื้นฐานคือวัตถุ
2. วัตถุมีไดนามิกและไม่แน่นอนอย่างสมบูรณ์ (ในทางทฤษฎีมันสามารถเปลี่ยนจากประเภทหนึ่งไปอีกประเภทหนึ่งได้)
3. วัตถุไม่มีคลาสที่เข้มงวดที่อธิบายโครงสร้างและพฤติกรรมของตนเองและวัตถุไม่ต้องการคลาส
4. วัตถุไม่มีคลาสคลาส แต่สามารถมีต้นแบบได้ หากพวกเขาไม่สามารถตอบกลับข้อความได้พวกเขาสามารถมอบหมายให้ต้นแบบได้
5. ต้นแบบของวัตถุสามารถเปลี่ยนแปลงได้ตลอดเวลาในระหว่างการรันไทม์
6. ในโมเดลที่ได้รับมอบหมายการเปลี่ยนลักษณะของต้นแบบจะส่งผลกระทบต่อวัตถุทั้งหมดที่เกี่ยวข้องกับต้นแบบ
7. ในโมเดลต้นแบบ concatenative ต้นแบบเป็นสำเนาดั้งเดิมที่โคลนจากวัตถุอื่น ๆ และกลายเป็นสำเนาที่เป็นอิสระอย่างสมบูรณ์ การเปลี่ยนแปลงของลักษณะของต้นแบบจะไม่ส่งผลกระทบต่อวัตถุที่ถูกโคลนจากมัน
8. หากข้อความไม่สามารถตอบกลับได้ผู้โทรสามารถใช้มาตรการเพิ่มเติมได้ (เช่นเปลี่ยนการกำหนดเวลา)
9. ความล้มเหลวของวัตถุไม่สามารถกำหนดได้ตามระดับและชั้นเรียนที่พวกเขาอยู่ แต่ตามลักษณะปัจจุบัน
อย่างไรก็ตามมีรูปแบบอื่นที่เราควรพิจารณาเช่นกัน
ขึ้นอยู่กับคลาสไดนามิก
เราเชื่อว่าความแตกต่าง "ต้นแบบคลาสกับคลาส" ที่แสดงในตัวอย่างข้างต้นนั้นไม่สำคัญในโมเดลที่ใช้คลาสแบบไดนามิกนี้ (โดยเฉพาะอย่างยิ่งหากห่วงโซ่ต้นแบบไม่เปลี่ยนแปลงมันยังจำเป็นที่จะต้องพิจารณาคลาสคงที่เพื่อความแตกต่างที่แม่นยำยิ่งขึ้น) ตัวอย่างเช่นยังสามารถใช้ใน Python หรือ Ruby (หรือภาษาอื่น ๆ ที่คล้ายกัน) ภาษาเหล่านี้ใช้กระบวนทัศน์ที่ใช้คลาสแบบไดนามิก อย่างไรก็ตามในบางแง่มุมเราสามารถเห็นฟังก์ชั่นบางอย่างที่นำมาใช้ตามต้นแบบ
ในตัวอย่างต่อไปนี้เราจะเห็นได้ว่าขึ้นอยู่กับต้นแบบผู้ได้รับมอบหมายเราสามารถขยายคลาส (ต้นแบบ) ซึ่งส่งผลกระทบต่อวัตถุทั้งหมดที่เกี่ยวข้องกับคลาสนี้เราสามารถเปลี่ยนคลาสของวัตถุนี้ได้ที่รันไทม์ (จัดหาวัตถุใหม่สำหรับผู้ได้รับมอบหมาย)
การคัดลอกรหัสมีดังนี้:
# Python
คลาส A (วัตถุ):
def __init __ (ตัวเอง, a):
self.a = a
def square (ตัวเอง):
คืน self.a * self.a
A = A (10) # สร้างอินสแตนซ์
พิมพ์ (AA) # 10
ab = 20 # ให้คุณสมบัติใหม่สำหรับชั้นเรียน
พิมพ์ (ab) # 20 คุณสามารถเข้าถึงได้ในอินสแตนซ์ "A"
A = 30 # สร้างคุณสมบัติของตัวเอง
พิมพ์ (ab) # 30
del ab # ลบคุณลักษณะของตัวเอง
พิมพ์ (ab) # 20 - รับจากชั้นเรียนอีกครั้ง (ต้นแบบ)
# เหมือนรุ่นที่ใช้ต้นแบบ
# สามารถเปลี่ยนต้นแบบของวัตถุได้ที่รันไทม์
คลาส B (Object): # empty Class B
ผ่าน
b = b () # b อินสแตนซ์
b .__ class__ = A # เปลี่ยนคลาสแบบไดนามิก (ต้นแบบ)
ba = 10 # สร้างแอตทริบิวต์ใหม่
พิมพ์ (b.square ()) # 100 - วิธีการคลาส A พร้อมใช้งานในขณะนี้
# สามารถแสดงการอ้างอิงในคลาสที่ถูกลบ
del a
del b
# แต่วัตถุยังคงมีการอ้างอิงโดยนัยและวิธีการเหล่านี้ยังคงมีอยู่
พิมพ์ (b.square ()) # 100
# แต่คุณไม่สามารถเปลี่ยนชั้นเรียนได้ในเวลานี้
# นี่คือคุณสมบัติของการใช้งาน
b .__ class__ = dict # ข้อผิดพลาด
การใช้งานในทับทิมนั้นคล้ายคลึงกัน: นอกจากนี้ยังใช้คลาสไดนามิกอย่างสมบูรณ์ (โดยวิธีการในรุ่นปัจจุบันของ Python เมื่อเทียบกับทับทิมและ ecmascript การขยายคลาส (ต้นแบบ) ไม่สามารถทำได้) เราสามารถเปลี่ยนลักษณะของวัตถุ (หรือคลาส)
อย่างไรก็ตามบทความนี้ไม่ได้เป็นพิเศษสำหรับ Python และ Ruby ดังนั้นเราจะไม่พูดมากนักเรามาพูดคุยเกี่ยวกับ ecmascript ต่อไป
แต่ก่อนหน้านี้เราต้องดู "น้ำตาลสังเคราะห์" ที่พบในอ๊ะบางอย่างเพราะบทความก่อนหน้านี้เกี่ยวกับ JavaScript มักจะครอบคลุมปัญหาเหล่านี้
ประโยคที่ไม่ถูกต้องเพียงอย่างเดียวที่ต้องสังเกตในส่วนนี้คือ: "JavaScript ไม่ใช่คลาส แต่มีต้นแบบและสามารถแทนที่คลาสได้" มีความจำเป็นอย่างยิ่งที่จะต้องรู้ว่าการใช้งานแบบคลาสทั้งหมดนั้นไม่แตกต่างกันอย่างสิ้นเชิง แม้ว่าเราอาจพูดว่า "JavaScript นั้นแตกต่างกัน" ก็จำเป็นต้องพิจารณา (นอกเหนือจากแนวคิดของ "คลาส") ว่ามีคุณสมบัติอื่น ๆ ที่เกี่ยวข้อง
คุณสมบัติอื่น ๆ ของการใช้งาน OOP ต่างๆ
ในส่วนนี้เราแนะนำคุณสมบัติอื่น ๆ และการใช้ OOP ที่หลากหลายเกี่ยวกับการใช้รหัสซ้ำรวมถึงการใช้ OOP ใน ECMASCript เหตุผลก็คือการปรากฏตัวครั้งก่อนของการใช้ OOP ใน JavaScript มีข้อ จำกัด การคิดเป็นนิสัย ข้อกำหนดหลักเพียงอย่างเดียวคือควรได้รับการพิสูจน์ทางเทคนิคและอุดมการณ์ ไม่สามารถพูดได้ว่าคุณไม่ได้ค้นพบฟังก์ชั่นน้ำตาลไวยากรณ์ในการใช้งาน OOP อื่น ๆ และคุณไม่รู้ว่า JavaScript ไม่ใช่ภาษา OOP บริสุทธิ์ซึ่งผิด
เกี่ยวกับความหลากหลาย
ใน ecmascript มีความหลากหลายหลายอย่างที่วัตถุหมายถึง
ตัวอย่างเช่นฟังก์ชันสามารถนำไปใช้กับวัตถุที่แตกต่างกันเช่นเดียวกับคุณสมบัติของวัตถุดั้งเดิม (เนื่องจากค่านี้ถูกกำหนดเมื่อป้อนบริบทการดำเนินการ):
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่นทดสอบ () {
การแจ้งเตือน ([this.a, this.b]);
-
test.call ({a: 10, b: 20}); // 10, 20
test.call ({a: 100, b: 200}); // 100, 200
var a = 1;
var b = 2;
ทดสอบ(); // 1, 2
อย่างไรก็ตามมีข้อยกเว้น: date.prototype.getTime () วิธีการตามมาตรฐานควรมีวัตถุวันที่เสมอมิฉะนั้นจะมีข้อยกเว้น
การคัดลอกรหัสมีดังนี้:
Alert (date.prototype.gettime.call (วันที่ใหม่ ())); // เวลา
Alert (date.prototype.gettime.call (สตริงใหม่ (''))); // typeerror
พารามิเตอร์ที่เรียกว่าพารามิเตอร์ polymorphism เมื่อกำหนดฟังก์ชันเทียบเท่ากับประเภทข้อมูลทั้งหมด แต่จะยอมรับพารามิเตอร์ polymorphic เท่านั้น (เช่นวิธีการเรียงลำดับ. ของอาร์เรย์และพารามิเตอร์ - ฟังก์ชั่นการเรียงลำดับของ polymorphism) โดยวิธีการตัวอย่างข้างต้นยังถือได้ว่าเป็นพารามิเตอร์ polymorphism
วิธีการในต้นแบบสามารถกำหนดเป็นว่างและวัตถุที่สร้างทั้งหมดควรได้รับการกำหนดใหม่ (นำไปใช้) วิธีการ (เช่น "หนึ่งอินเตอร์เฟส (ลายเซ็น), การใช้งานหลายครั้ง")
Polymorphism เกี่ยวข้องกับประเภทเป็ดที่เรากล่าวถึงข้างต้น: เช่นประเภทของวัตถุและตำแหน่งในลำดับชั้นไม่สำคัญ แต่สามารถยอมรับได้ง่ายหากมีคุณสมบัติที่จำเป็นทั้งหมด (เช่นอินเทอร์เฟซทั่วไปมีความสำคัญและการใช้งานสามารถเปลี่ยนแปลงได้)
บรรจุุภัณฑ์
มักจะมีมุมมองที่ผิดเกี่ยวกับการห่อหุ้ม ในส่วนนี้เราจะหารือเกี่ยวกับน้ำตาลวากยสัมพันธ์บางอย่างในการใช้งาน OOP - นั่นคือตัวดัดแปลงที่รู้จักกันดี: ในกรณีนี้เราจะหารือเกี่ยวกับการใช้งาน OOP ที่สะดวก "น้ำตาล" - ตัวดัดแปลงที่รู้จักกันดี: ส่วนตัว, ป้องกันและสาธารณะ
ที่นี่ฉันอยากจะเตือนคุณเกี่ยวกับวัตถุประสงค์หลักของการห่อหุ้ม: การห่อหุ้มเป็นสิ่งที่เป็นนามธรรมมากกว่าเลือก "แฮ็กเกอร์ที่เป็นอันตราย" ที่ซ่อนอยู่ซึ่งเขียนบางสิ่งบางอย่างลงในชั้นเรียนของคุณโดยตรง
นี่เป็นความผิดพลาดครั้งใหญ่: ใช้ซ่อนเพื่อซ่อน
ระดับการเข้าถึง (ส่วนตัวได้รับการปกป้องและสาธารณะ) เพื่อความสะดวกในการเขียนโปรแกรมได้ถูกนำไปใช้ในวัตถุที่มุ่งเน้นวัตถุ (น้ำตาลไวยากรณ์ที่สะดวกมาก) และอธิบายและสร้างระบบที่เป็นนามธรรมมากขึ้น
สิ่งเหล่านี้สามารถเห็นได้ในการใช้งานบางอย่าง (เช่น Python และ Ruby ที่กล่าวถึงแล้ว) ในอีกด้านหนึ่ง (ใน Python) คุณสมบัติ __private _protected เหล่านี้ (ผ่านข้อกำหนดชื่อขีดล่าง) ไม่สามารถเข้าถึงได้จากภายนอก ในทางกลับกัน Python สามารถเข้าถึงได้จากภายนอกผ่านกฎพิเศษ (_classname__field_name)
การคัดลอกรหัสมีดังนี้:
คลาส A (วัตถุ):
def __init __ (ตัวเอง):
self.public = 10
ตัวเอง. __ ส่วนตัว = 20
def get_private (ตัวเอง):
กลับตัวเอง. __ ส่วนตัว
# ข้างนอก:
A = A () # ตัวอย่างของก
พิมพ์ (A.Public) # ตกลง, 30
พิมพ์ (a.get_private ()) # ตกลง, 20
พิมพ์ (a .__ ส่วนตัว) # ล้มเหลวเพราะสามารถใช้งานได้ในไฟล์
# แต่ใน Python สามารถเข้าถึงกฎพิเศษได้
พิมพ์ (a._a__private) # ตกลง 20
ในทับทิม: ในอีกด้านหนึ่งมันมีความสามารถในการกำหนดลักษณะของส่วนตัวและการป้องกันและในทางกลับกันยังมีวิธีการพิเศษ (เช่น instance_variable_get, instance_variable_set, ส่ง ฯลฯ ) เพื่อรับข้อมูลที่ห่อหุ้ม
การคัดลอกรหัสมีดังนี้:
คลาส A
def เริ่มต้น
@a = 10
จบ
def public_method
Private_method (20)
จบ
ส่วนตัว
def private_method (b)
return @a + b
จบ
จบ
a = a.new # อินสแตนซ์ใหม่
a.public_method # ตกลง, 30
AA # ล้มเหลว @A - เป็นตัวแปรอินสแตนซ์ส่วนตัว
# "private_method" เป็นส่วนตัวและสามารถเข้าถึงได้ในคลาส A เท่านั้น
a.private_method # ข้อผิดพลาด
# แต่มีชื่อเมธอดพิเศษที่สามารถรับข้อมูลได้
A.Send (: Private_Method, 20) # ตกลง, 30
a.instance_variable_get (:@a) # ตกลง, 10
เหตุผลหลักคือการห่อหุ้ม (โปรดทราบว่าฉันไม่ได้ใช้ข้อมูล "ซ่อนเร้น") ที่โปรแกรมเมอร์ต้องการได้รับ หากข้อมูลนี้มีการเปลี่ยนแปลงอย่างไม่ถูกต้องในบางวิธีหรือมีข้อผิดพลาดใด ๆ ความรับผิดชอบทั้งหมดคือโปรแกรมเมอร์ แต่ไม่ใช่แค่ "Spellow" หรือ "เปลี่ยนฟิลด์บางอย่างอย่างไม่เป็นทางการ" แต่ถ้าสิ่งนี้เกิดขึ้นบ่อยครั้งมันเป็นนิสัยและสไตล์การเขียนโปรแกรมที่ไม่ดีเพราะมันมักจะเป็น API ทั่วไปที่จะ "พูดคุย" กับวัตถุ
ทำซ้ำวัตถุประสงค์พื้นฐานของการห่อหุ้มคือการสรุปจากผู้ใช้ข้อมูลเสริมแทนที่จะป้องกันไม่ให้แฮ็กเกอร์ซ่อนข้อมูล การห่อหุ้มที่ร้ายแรงกว่านั้นจะไม่ใช้ส่วนตัวเพื่อแก้ไขข้อมูลเพื่อให้บรรลุวัตถุประสงค์ของความปลอดภัยของซอฟต์แวร์
การห่อหุ้มวัตถุผู้ช่วย (ท้องถิ่น) เราให้ความเป็นไปได้สำหรับการเปลี่ยนแปลงพฤติกรรมของอินเทอร์เฟซสาธารณะด้วยต้นทุนน้อยที่สุดการแปลและการเปลี่ยนแปลงการทำนายซึ่งเป็นจุดประสงค์ของการห่อหุ้ม
นอกจากนี้วัตถุประสงค์ที่สำคัญของวิธีการตั้งค่าคือการคำนวณเชิงนามธรรมที่ซับซ้อน ตัวอย่างเช่น element.innerhtml setter - คำสั่งนามธรรม - "HTML ภายในองค์ประกอบนี้มีดังนี้" และฟังก์ชั่น setter ในแอตทริบิวต์ InnerHTML จะยากต่อการคำนวณและตรวจสอบ ในกรณีนี้ปัญหาส่วนใหญ่เกี่ยวข้องกับสิ่งที่เป็นนามธรรม แต่การห่อหุ้มสามารถเกิดขึ้นได้
แนวคิดของการห่อหุ้มไม่เพียง แต่เกี่ยวข้องกับ OOP ตัวอย่างเช่นมันอาจเป็นฟังก์ชั่นง่าย ๆ ที่ห่อหุ้มการคำนวณทุกชนิดเพื่อให้เป็นนามธรรม (ไม่จำเป็นต้องแจ้งให้ผู้ใช้ทราบตัวอย่างเช่นวิธีการใช้ฟังก์ชัน math.round (...... ) ถูกนำไปใช้ มันเป็นการห่อหุ้มโปรดทราบว่าฉันไม่ได้บอกว่ามันเป็น "ส่วนตัวปกป้องและสาธารณะ"
สเปคเวอร์ชัน ECMASCRIPT เวอร์ชันปัจจุบันไม่ได้กำหนดตัวดัดแปลงส่วนตัวป้องกันและสาธารณะ
อย่างไรก็ตามในทางปฏิบัติมันเป็นไปได้ที่จะเห็นบางสิ่งบางอย่างที่ชื่อ "เลียนแบบ JS encapsulation" โดยทั่วไปวัตถุประสงค์ของบริบทนี้จะต้องใช้ (ตามกฎแล้วตัวสร้างเอง) น่าเสียดายที่การใช้งาน "เลียนแบบ" นี้มักจะทำได้และโปรแกรมเมอร์สามารถสร้างเอนทิตีที่ไม่ได้ใช้หลอกอย่างแท้จริงเพื่อตั้งค่า "วิธี getter/setter" (ฉันพูดอีกครั้งมันผิด):
การคัดลอกรหัสมีดังนี้:
ฟังก์ชัน A () {
var _a; // "ส่วนตัว" ก
this.geta = ฟังก์ชั่น _geta () {
กลับ _a;
-
this.seta = ฟังก์ชั่น _seta (a) {
_a = a;
-
-
var a = new a ();
A.Seta (10);
การแจ้งเตือน (a._a); // ไม่ได้กำหนด "ส่วนตัว"
การแจ้งเตือน (a.geta ()); // 10
ดังนั้นทุกคนเข้าใจว่าสำหรับวัตถุที่สร้างขึ้นแต่ละรายการวิธีการ GETA/SETA ก็ถูกสร้างขึ้นด้วยซึ่งก็เป็นสาเหตุของการเพิ่มหน่วยความจำ (เมื่อเทียบกับคำจำกัดความต้นแบบ) แม้ว่าในทางทฤษฎีวัตถุสามารถปรับให้เหมาะสมในกรณีแรก
นอกจากนี้บทความ JavaScript บางบทความมักจะพูดถึงแนวคิดของ "วิธีการส่วนตัว" หมายเหตุ: ECMA-262-3 มาตรฐานไม่ได้กำหนดแนวคิดใด ๆ ของ "วิธีส่วนตัว"
อย่างไรก็ตามในบางกรณีมันสามารถสร้างขึ้นในคอนสตรัคเตอร์ได้เนื่องจาก JS เป็นภาษาอุดมการณ์ - วัตถุนั้นไม่แน่นอนและมีลักษณะเฉพาะ (ภายใต้เงื่อนไขบางประการในคอนสตรัคเตอร์วัตถุบางอย่างสามารถรับวิธีการเพิ่มเติมได้
นอกจากนี้ใน JavaScript หากการห่อหุ้มยังคงตีความผิดว่าเป็นวิธีที่จะป้องกันไม่ให้แฮ็กเกอร์ที่เป็นอันตรายจากการเขียนค่าบางอย่างโดยอัตโนมัติแทนการใช้วิธีการตั้งค่าซึ่งเรียกว่า "ซ่อน" และ "ส่วนตัว" ไม่ใช่ "ซ่อน" การใช้งานบางอย่างสามารถรับค่าในห่วงโซ่ขอบเขตที่เกี่ยวข้อง (และวัตถุตัวแปรที่เกี่ยวข้องทั้งหมด) โดยการเรียกบริบทไปยังฟังก์ชันการประเมิน (ซึ่งสามารถทดสอบได้บน SpiderMonkey 1.7)
การคัดลอกรหัสมีดังนี้:
eval ('_ a = 100', a.geta); // หรือ a.seta เพราะวิธีการ "_a" บน [[ขอบเขต]]
A.geta (); // 100
อีกทางเลือกหนึ่งในการใช้งานอนุญาตให้เข้าถึงวัตถุที่ใช้งานได้โดยตรง (เช่นแรด) โดยการเข้าถึงคุณสมบัติที่สอดคล้องกันของวัตถุค่าของตัวแปรภายในสามารถเปลี่ยนแปลงได้:
การคัดลอกรหัสมีดังนี้:
// แรด
var foo = (function () {
var x = 10; // "ส่วนตัว"
return function () {
พิมพ์ (x);
-
-
foo (); // 10
foo .__ ผู้ปกครอง __. x = 20;
foo (); // 20
บางครั้งใน JavaScript วัตถุประสงค์ของข้อมูล "ส่วนตัว" และ "ได้รับการป้องกัน" นั้นทำได้โดยตัวแปรที่เน้นย้ำ (แต่เมื่อเทียบกับ Python นี่เป็นเพียงข้อกำหนดการตั้งชื่อ):
var _myprivatedata = 'testString';
มันมักจะใช้ในการแนบบริบทการดำเนินการด้วยวงเล็บ แต่สำหรับข้อมูลเสริมจริงมันไม่มีการเชื่อมโยงโดยตรงกับวัตถุและมันก็สะดวกในการเป็นนามธรรมจาก API ภายนอก:
การคัดลอกรหัสมีดังนี้:
(การทำงาน () {
// เริ่มต้นบริบท
-
มรดกหลายอย่าง
Multi-inheritance เป็นน้ำตาลวากยสัมพันธ์ที่สะดวกมากสำหรับการปรับปรุงการใช้รหัสซ้ำ (ถ้าเราสามารถสืบทอดครั้งละหนึ่งชั้นได้ทำไมเราไม่สามารถสืบทอด 10 ครั้งในแต่ละครั้ง?) อย่างไรก็ตามเนื่องจากข้อบกพร่องบางอย่างในการสืบทอดหลายครั้งจึงไม่ได้รับความนิยมในการดำเนินการ
Ecmascript ไม่รองรับการสืบทอดหลายครั้ง (เช่นวัตถุเดียวเท่านั้นที่สามารถใช้เป็นต้นแบบโดยตรง) แม้ว่าภาษาที่เขียนโปรแกรมด้วยตนเองของบรรพบุรุษจะมีความสามารถดังกล่าว แต่ในการใช้งานบางอย่าง (เช่น Spidermonkey) โดยใช้ __NosuchMethod__ สามารถใช้ในการจัดการการกำหนดเวลาและมอบหมายให้แทนที่โซ่ต้นแบบ
มิกซ์
Mixins เป็นวิธีที่สะดวกในการใช้รหัสใหม่ Mixins ได้รับการแนะนำเพื่อทดแทนการสืบทอดหลายครั้ง องค์ประกอบอิสระเหล่านี้สามารถผสมกับวัตถุใด ๆ เพื่อขยายการทำงานของพวกเขา (ดังนั้นวัตถุสามารถผสมหลาย mixins) ข้อกำหนด ECMA-262-3 ไม่ได้กำหนดแนวคิดของ "mixins" แต่ตามคำจำกัดความของ Mixins และ Ecmascript มีวัตถุที่ไม่แน่นอนแบบไดนามิกดังนั้นจึงไม่มีอุปสรรคในการขยายคุณสมบัติโดยใช้ mixins
ตัวอย่างทั่วไป:
การคัดลอกรหัสมีดังนี้:
// ผู้ช่วยสำหรับการเสริม
Object.extend = function (ปลายทาง, แหล่งที่มา) {
สำหรับ (คุณสมบัติในแหล่งที่มา) ถ้า (source.hasownproperty (คุณสมบัติ)) {
ปลายทาง [คุณสมบัติ] = แหล่งที่มา [คุณสมบัติ];
-
ปลายทางกลับ;
-
var x = {a: 10, b: 20};
var y = {c: 30, d: 40};
Object.extend (x, y); // ผสม y ลงใน x
การแจ้งเตือน ([XA, XB, XC, XD]); 10, 20, 30, 40
โปรดทราบว่าฉันใช้คำจำกัดความเหล่านี้ ("Mixin", "Mix") ในคำพูดที่กล่าวถึงใน ECMA-262-3 ไม่มีแนวคิดดังกล่าวในข้อกำหนดและไม่ใช่การผสมผสาน แต่เป็นวิธีที่ใช้กันทั่วไปในการขยายวัตถุผ่านคุณสมบัติใหม่ (แนวคิดของการผสมในทับทิมมีการกำหนดอย่างเป็นทางการ Mixin สร้างการอ้างอิงถึงโมดูลแทนที่จะคัดลอกแอตทริบิวต์ทั้งหมดของโมดูลไปยังโมดูลอื่น - อันที่จริง: การสร้างวัตถุเพิ่มเติม (ต้นแบบ) สำหรับผู้แทน)
ลักษณะ
ลักษณะและ mixins มีแนวคิดที่คล้ายกัน แต่มีฟังก์ชั่นมากมาย (ตามคำนิยามเนื่องจากสามารถใช้ mixins ได้ดังนั้นจึงไม่สามารถมีสถานะได้เนื่องจากมีศักยภาพที่จะทำให้เกิดความขัดแย้งในการตั้งชื่อ) ตาม Ecmascript ลักษณะและ mixins เป็นไปตามหลักการเดียวกันดังนั้นข้อกำหนดไม่ได้กำหนดแนวคิดของ "ลักษณะ"
ส่วนต่อประสาน
อินเทอร์เฟซที่ใช้ใน OOPs บางตัวคล้ายกับ Mixins และลักษณะ อย่างไรก็ตามเมื่อเทียบกับ Mixins และลักษณะอินเตอร์เฟสบังคับใช้คลาสการใช้งานเพื่อใช้พฤติกรรมลายเซ็นของวิธีการ
อินเตอร์เฟสถือได้ว่าเป็นคลาสนามธรรม อย่างไรก็ตามเมื่อเทียบกับคลาสนามธรรม (วิธีการในคลาสนามธรรมสามารถใช้งานส่วนหนึ่งของพวกเขาเท่านั้นและส่วนอื่น ๆ ยังคงถูกกำหนดให้เป็นลายเซ็น) การสืบทอดสามารถเป็นคลาสฐานมรดกเดียวเท่านั้น แต่สามารถสืบทอดหลายอินเทอร์เฟซ นี่คือเหตุผลที่อินเทอร์เฟซ (มิกซ์หลายตัว) ถือได้ว่าเป็นทางเลือกแทนการสืบทอดหลายครั้ง
มาตรฐาน ECMA-262-3 ไม่ได้กำหนดแนวคิดของ "อินเทอร์เฟซ" หรือแนวคิดของ "คลาสนามธรรม" อย่างไรก็ตามในการเลียนแบบมันสามารถนำไปใช้โดยวัตถุที่วิธีการ "ว่าง" (หรือข้อยกเว้นที่ถูกโยนลงไปในวิธีที่ว่างเปล่าบอกผู้พัฒนาว่าวิธีนี้จะต้องดำเนินการ)
การรวมวัตถุ
การรวมกันของวัตถุยังเป็นหนึ่งในเทคนิคการใช้รหัสซ้ำแบบไดนามิก การรวมกันของวัตถุนั้นแตกต่างจากการสืบทอดความยืดหยุ่นสูงซึ่งใช้ผู้แทนแบบไดนามิกและตัวแปร และนี่ก็ขึ้นอยู่กับต้นแบบหลัก นอกเหนือจากต้นแบบที่ไม่แน่นอนแบบไดนามิกแล้ววัตถุยังสามารถเป็นตัวแทนที่ได้รับมอบหมาย (สร้างชุดค่าผสมเป็นผลลัพธ์ - การรวม) และส่งข้อความเพิ่มเติมไปยังวัตถุและมอบหมายให้ผู้แทน นี่อาจเป็นมากกว่าสองผู้ได้รับมอบหมายเนื่องจากธรรมชาติแบบไดนามิกกำหนดว่าสามารถเปลี่ยนแปลงได้ในเวลารันไทม์
ตัวอย่าง __nosuchmethod__ ที่กล่าวถึงเป็นเช่นนี้ แต่ยังให้เราแสดงวิธีการใช้ผู้ได้รับมอบหมายอย่างชัดเจน:
ตัวอย่างเช่น:
การคัดลอกรหัสมีดังนี้:
var _delegate = {
foo: function () {
การแจ้งเตือน ('_ Delegate.foo');
-
-
var aggregate = {
ผู้แทน: _delegate,
foo: function () {
ส่งคืนสิ่งนี้ delegate.foo.call (นี่);
-
-
รวม. foo (); // delegate.foo
Aggregate.delegate = {
foo: function () {
การแจ้งเตือน ('foo จากผู้แทนใหม่');
-
-
รวม. foo (); // foo จากผู้แทนใหม่
ความสัมพันธ์ของวัตถุนี้เรียกว่า "Have-A" ในขณะที่การรวมเป็นความสัมพันธ์ของ "IS-A"
เนื่องจากขาดการรวมการแสดงผล (ความยืดหยุ่นเมื่อเทียบกับการสืบทอด) จึงสามารถเพิ่มรหัสระดับกลางได้
คุณสมบัติ AOP
ในฐานะที่เป็นฟังก์ชั่นที่มุ่งเน้นด้านสามารถใช้งานได้ ข้อกำหนด ECMA-262-3 ไม่ได้มีแนวคิดที่กำหนดไว้อย่างชัดเจนของ "นักตกแต่งฟังก์ชั่น" (ตรงข้ามกับ Python คำนี้ถูกกำหนดอย่างเป็นทางการใน Python) อย่างไรก็ตามฟังก์ชั่นที่มีพารามิเตอร์การทำงานนั้นตกแต่งและเปิดใช้งานในบางวิธี (โดยใช้คำแนะนำที่เรียกว่า):
ตัวอย่างที่ง่ายที่สุดของมัณฑนากร:
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น checkDecorator (OriginalFunction) {
return function () {
if (foobar! = 'test') {
การแจ้งเตือน ('พารามิเตอร์ผิด');
กลับเท็จ;
-
ส่งคืน OriginalFunction ();
-
-
ฟังก์ชั่นทดสอบ () {
การแจ้งเตือน ('ฟังก์ชั่นทดสอบ');
-
var testwithCheck = checkDecorator (ทดสอบ);
var foobar = false;
ทดสอบ(); // 'ฟังก์ชั่นทดสอบ'
testwithCheck (); // 'พารามิเตอร์ผิด'
foobar = 'ทดสอบ';
ทดสอบ(); // 'ฟังก์ชั่นทดสอบ'
testwithCheck (); // 'ฟังก์ชั่นทดสอบ'
สรุปแล้ว
ในบทความนี้เราได้จัดเรียงบทนำสู่ OOP (ฉันหวังว่าข้อมูลนี้จะเป็นประโยชน์กับคุณ) และในบทต่อไปเราจะดำเนินการ ECMASCRIPT ในการเขียนโปรแกรมเชิงวัตถุต่อไป