รูปแบบ OLOO ของ Kyle Simpson เทียบกับรูปแบบการออกแบบต้นแบบ


109

รูปแบบ "OLOO (Objects Link to Other Objects) ของ Kyle Simpson แตกต่างจากรูปแบบการออกแบบ Prototype หรือไม่ นอกเหนือจากการสร้างโดยสิ่งที่ระบุเฉพาะ "การเชื่อมโยง" (พฤติกรรมของต้นแบบ) และชี้แจงว่าไม่มีการ "คัดลอก" เกิดขึ้นที่นี่ (พฤติกรรมของคลาส) แล้วรูปแบบของเขาแนะนำอะไรกันแน่?

นี่คือตัวอย่างรูปแบบของ Kyleจากหนังสือ "You Don't Know JS: this & Object Prototypes":

var Foo = {
    init: function(who) {
        this.me = who;
    },
    identify: function() {
        return "I am " + this.me;
    }
};

var Bar = Object.create(Foo);

Bar.speak = function() {
    alert("Hello, " + this.identify() + ".");
};

var b1 = Object.create(Bar);
b1.init("b1");
var b2 = Object.create(Bar);
b2.init("b2");

b1.speak(); // alerts: "Hello, I am b1."
b2.speak(); // alerts: "Hello, I am b2."

2
อย่างน้อยคุณสามารถเชื่อมโยงไปยังคำอธิบายของรูปแบบที่คุณกำลังถามถึง ที่ดีไปกว่านั้นคือการแสดงตัวอย่างโค้ดในคำถามของคุณ
jfriend00

4
Getify อยู่ใน Stackoverflow บางครั้ง ฉันทวีตคำถามนี้ให้เขาแล้ว :)
Pointy

คำตอบ:


155

รูปแบบของเขาแนะนำอะไร?

OLOO รวบรวมห่วงโซ่ต้นแบบตามที่เป็นอยู่โดยไม่จำเป็นต้องวางเลเยอร์บนความหมายอื่น ๆ (IMO ที่ทำให้สับสน) เพื่อรับการเชื่อมโยง

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

แบบฟอร์มตัวสร้าง:

function Foo() {}
Foo.prototype.y = 11;

function Bar() {}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.z = 31;

var x = new Bar();
x.y + x.z;  // 42

แบบฟอร์ม OLOO:

var FooObj = { y: 11 };

var BarObj = Object.create(FooObj);
BarObj.z = 31;

var x = Object.create(BarObj);
x.y + x.z;  // 42

ในตัวอย่างทั้งสองxวัตถุจะ[[Prototype]]เชื่อมโยงกับวัตถุ ( Bar.prototypeหรือBarObj) ซึ่งจะเชื่อมโยงกับวัตถุที่สาม ( Foo.prototypeหรือFooObj)

ความสัมพันธ์และการมอบหมายจะเหมือนกันระหว่างข้อมูลโค้ด การใช้หน่วยความจำจะเหมือนกันระหว่างข้อมูลโค้ด ความสามารถในการสร้าง "เด็ก" จำนวนมาก (หรือที่เรียกว่าวัตถุจำนวนมากเช่นx1ผ่านx1000ฯลฯ ) จะเหมือนกันระหว่างตัวอย่างข้อมูล ประสิทธิภาพของการมอบหมาย ( x.yและx.z) จะเหมือนกันระหว่างส่วนย่อย ประสิทธิภาพการสร้างออบเจ็กต์จะช้าลงเมื่อใช้ OLOO แต่การตรวจสอบความถูกต้องซึ่งแสดงให้เห็นว่าประสิทธิภาพที่ช้าลงนั้นไม่ใช่ปัญหา

สิ่งที่ฉันโต้แย้งข้อเสนอของ OLOO คือการแสดงวัตถุและเชื่อมโยงโดยตรงนั้นง่ายกว่าการเชื่อมโยงทางอ้อมผ่านตัวสร้าง / newกลไก หลังแสร้งทำเป็นว่าเกี่ยวกับคลาส แต่จริงๆแล้วเป็นเพียงไวยากรณ์ที่แย่มากสำหรับการแสดงการมอบหมาย ( หมายเหตุด้านข้าง:classไวยากรณ์ES6 ก็เช่นกัน !)

OLOO เพิ่งตัดชายกลางคนออกไป

นี่คือการเปรียบเทียบอื่นของclassVS OLOO


2
ฉันพบว่าคำตอบของคุณน่าสนใจมากและแนวคิดเกี่ยวกับ OLOO ที่อธิบายไว้ในหนังสือของคุณฉันต้องการความคิดเห็นของคุณเกี่ยวกับคำถามนี้: stackoverflow.com/questions/40395762/…โดย เฉพาะอย่างยิ่งหากคุณพบว่าการใช้งานนี้ถูกต้องและวิธีแก้ปัญหาที่เกี่ยวข้องกับการเข้าถึง สมาชิกส่วนตัว ขอขอบคุณสำหรับเวลาล่วงหน้าและขอแสดงความยินดีสำหรับหนังสือเล่มล่าสุดของคุณ
GibboK

แต่มีหลายครั้งที่ช้ากว่าObject.create(...) jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/…new
ท่าเรือ

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

2
และ jQuery ช้ากว่า DOM API ใช่ไหม? แต่มันเป็นปีปัจจุบันผู้ชาย - ฉันอยากจะเขียนอย่างหรูหราและเรียบง่ายมากกว่ากังวลเกี่ยวกับการปรับให้เหมาะสม หากฉันต้องการเพิ่มประสิทธิภาพไมโครในภายหลังฉันจะกังวลเกี่ยวกับเรื่องนี้เมื่อถึงเวลา
Eirik Birkeland

6
ฉันต้องการเพิ่มว่าตอนนี้ในอีกหนึ่งปีต่อมา Object.create () ได้รับการปรับให้เหมาะสมอย่างมากใน chrome และ jsperf ก็แสดงให้เห็นซึ่งเป็นหนึ่งในตัวเลือกที่เร็วที่สุดในตอนนี้ สิ่งนี้แสดงให้เห็นว่าทำไมคุณไม่ควรกังวลกับตัวเองกับการเพิ่มประสิทธิภาพขนาดเล็กเช่นนี้และแทนที่จะเขียนโค้ดเสียงตามอัลกอริทึม
Kyle Baker

25

ฉันอ่านหนังสือของ Kyle และพบว่ามันให้ข้อมูลจริงๆโดยเฉพาะอย่างยิ่งรายละเอียดเกี่ยวกับความthisผูกพัน

ข้อดี:

สำหรับฉันมีข้อดีหลายประการของ OLOO:

1. ความเรียบง่าย

OLOO อาศัยObject.create()การสร้างวัตถุใหม่ซึ่ง[[prototype]]เชื่อมโยงกับวัตถุอื่น คุณไม่จำเป็นต้องเข้าใจว่าฟังก์ชันมีprototypeคุณสมบัติหรือกังวลเกี่ยวกับข้อผิดพลาดที่อาจเกิดขึ้นจากการดัดแปลง

2. ไวยากรณ์ที่สะอาดกว่า

นี่เป็นที่ถกเถียงกันอยู่ แต่ฉันรู้สึกว่าไวยากรณ์ของ OLOO นั้น (ในหลาย ๆ กรณี) ดูดีกว่าและกระชับกว่าวิธีจาวาสคริปต์ 'มาตรฐาน' โดยเฉพาะอย่างยิ่งเมื่อพูดถึงความหลากหลาย ( super- การเรียกสไตล์)

จุดด้อย:

ฉันคิดว่ามีการออกแบบที่น่าสงสัยอย่างหนึ่ง (อันที่จริงมีส่วนช่วยในประเด็นที่ 2 ด้านบน) และนั่นคือการทำเงา:

ในการมอบหมายพฤติกรรมเราหลีกเลี่ยงหากเป็นไปได้ที่จะตั้งชื่อสิ่งที่เหมือนกันในระดับต่างๆของ[[Prototype]]ห่วงโซ่

แนวคิดที่อยู่เบื้องหลังสิ่งนี้คือวัตถุมีฟังก์ชันที่เฉพาะเจาะจงมากขึ้นซึ่งจะมอบหมายภายในให้กับฟังก์ชันลดระดับโซ่ ตัวอย่างเช่นคุณอาจมีresourceวัตถุที่มีsave()ฟังก์ชันซึ่งส่งเวอร์ชัน JSON ของวัตถุไปยังเซิร์ฟเวอร์ แต่คุณอาจมีclientResourceวัตถุที่มีstripAndSave()ฟังก์ชันซึ่งก่อนอื่นจะลบคุณสมบัติที่ไม่ควรส่งไปยังเซิร์ฟเวอร์ .

ปัญหาที่อาจเกิดขึ้นคือ: หากมีคนอื่นเข้ามาและตัดสินใจที่จะสร้างspecialResourceวัตถุโดยไม่ทราบถึงห่วงโซ่ต้นแบบทั้งหมดอย่างเต็มที่พวกเขาอาจตัดสินใจที่จะบันทึกการประทับเวลาสำหรับการบันทึกครั้งสุดท้ายภายใต้คุณสมบัติที่เรียกว่าsaveซึ่งจะทำให้save()ฟังก์ชันพื้นฐานบนresourceวัตถุสองลิงค์ลงห่วงโซ่ต้นแบบ:

var resource = {
  save: function () { 
    console.log('Saving');
  }
};

var clientResource = Object.create(resource);

clientResource.stripAndSave = function () {
  // Do something else, then delegate
  console.log('Stripping unwanted properties');
  this.save();
};

var specialResource = Object.create( clientResource );

specialResource.timeStampedSave = function () {
  // Set the timestamp of the last save
  this.save = Date.now();
  this.stripAndSave();
};

a = Object.create(clientResource);
b = Object.create(specialResource);

a.stripAndSave();    // "Stripping unwanted properties" & "Saving".
b.timeStampedSave(); // Error!

นี่เป็นตัวอย่างที่ออกแบบมาโดยเฉพาะ แต่ประเด็นก็คือการไม่เงาคุณสมบัติอื่น ๆโดยเฉพาะอาจนำไปสู่สถานการณ์ที่น่าอึดอัดใจและการใช้อรรถาภิธานอย่างหนัก!

บางทีภาพประกอบที่ดีกว่านี้อาจเป็นinitวิธีการ - โดยเฉพาะอย่างยิ่งที่รุนแรงเมื่อ OOLO ช่วยป้องกันฟังก์ชันประเภทตัวสร้าง เนื่องจากวัตถุที่เกี่ยวข้องทุกชิ้นมีแนวโน้มที่จะต้องการฟังก์ชั่นดังกล่าวจึงอาจเป็นแบบฝึกหัดที่น่าเบื่อในการตั้งชื่อให้เหมาะสมและความเป็นเอกลักษณ์อาจทำให้ยากต่อการจดจำว่าจะใช้อะไร

* อันที่จริงมันไม่สมเหตุสมผลอย่างยิ่ง ( lastSavedจะดีกว่านี้มาก แต่เป็นเพียงตัวอย่างเท่านั้น)


23
ฉันยอมรับว่าอาจเกิดการชนกันของชื่อเป็นข้อเสียเปรียบ ... แต่จริงๆแล้วมันเป็นข้อเสียของ[[Prototype]]ระบบเองไม่ใช่ OLOO โดยเฉพาะ
Kyle Simpson

บางทีอาจจะมีการพูดถึงในหนังสือด้วย?
เช่า

ฉันไม่แน่ใจจริงๆว่านี่เป็นวิธีแก้ปัญหาจริงๆที่ @Ed Hinchliffe อธิบายเนื่องจากเพิ่งย้ายบันทึก () ไปยังเนมสเปซของตัวเอง แต่ใช้งานได้codepen.io/tforward/pen/govEPr?editors=1010
Tristan Forward

ฉันคิดว่า @ ed-hinchliffe หมายถึงb.timeStampedSave();แทนที่จะa.timeStampedSave();อยู่ในบรรทัดสุดท้ายของข้อมูลโค้ด
amangpt777

1
@ tristan-forward ขอบคุณที่นำ Rick and Morty มาสู่เรื่องนี้!
Eric Bishard

13

การอภิปรายใน "You Don't Know JS: this & Object Prototypes" และการนำเสนอ OLOO นั้นกระตุ้นความคิดและฉันได้เรียนรู้มากมายจากหนังสือเล่มนี้ ข้อดีของรูปแบบ OLOO มีการอธิบายไว้อย่างดีในคำตอบอื่น ๆ อย่างไรก็ตามฉันมีข้อร้องเรียนเกี่ยวกับสัตว์เลี้ยงต่อไปนี้ (หรือมีบางอย่างที่ทำให้ฉันไม่สามารถใช้งานได้อย่างมีประสิทธิภาพ):

1

เมื่อ "คลาส" "สืบทอด" คลาส "อื่น" ในรูปแบบคลาสสิกทั้งสองฟังก์ชันสามารถประกาศไวยากรณ์ที่คล้ายกันได้ ( "การประกาศฟังก์ชัน" หรือ "คำสั่งฟังก์ชัน" ):

function Point(x,y) {
    this.x = x;
    this.y = y;
};

function Point3D(x,y,z) {
    Point.call(this, x,y);
    this.z = z;
};

Point3D.prototype = Object.create(Point.prototype);

ในทางตรงกันข้ามในรูปแบบ OLOO รูปแบบการสังเคราะห์ที่แตกต่างกันที่ใช้เพื่อกำหนดฐานและวัตถุที่ได้รับ:

var Point = {
    init  : function(x,y) {
        this.x = x;
        this.y = y;
    }
};


var Point3D = Object.create(Point);
Point3D.init = function(x,y,z) {
    Point.init.call(this, x, y);
    this.z = z;
};

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

2

ในรูปแบบ OLOO การสร้างวัตถุมีสองขั้นตอน:

  1. โทร Object.create
  2. เรียกใช้เมธอดที่กำหนดเองและไม่ใช่มาตรฐานเพื่อเริ่มต้นอ็อบเจ็กต์ (ซึ่งคุณต้องจำไว้เนื่องจากอาจแตกต่างกันไปในแต่ละอ็อบเจกต์ถัดไป):

     var p2a = Object.create(Point);
    
     p2a.init(1,1);

ในทางตรงกันข้ามในรูปแบบ Prototype คุณใช้ตัวดำเนินการมาตรฐานnew:

var p2a = new Point(1,1);

3

ในรูปแบบคลาสสิกฉันสามารถสร้างฟังก์ชันยูทิลิตี้ "คงที่" ที่ไม่ได้ใช้กับ "ทันที" โดยตรงโดยกำหนดให้กับฟังก์ชัน "คลาส" โดยตรง (ตรงข้ามกับฟังก์ชัน.prototype) เช่นฟังก์ชันsquareในโค้ดด้านล่าง:

Point.square = function(x) {return x*x;};

Point.prototype.length = function() {
    return Math.sqrt(Point.square(this.x)+Point.square(this.y));
};

ในทางตรงกันข้ามในรูปแบบ OLOO ฟังก์ชัน "คงที่" จะพร้อมใช้งาน (ผ่านห่วงโซ่ [[ต้นแบบ]]) บนอินสแตนซ์ออบเจ็กต์ด้วย:

var Point = {
    init  : function(x,y) {
        this.x = x;
        this.y = y;
    },
    square: function(x) {return x*x;},
    length: function() {return Math.sqrt(Point.square(this.x)+Point.square(this.y));}
};

2
ไม่มีตัวอักษรในตัวอย่างโค้ดแรกของคุณ คุณอาจใช้คำว่า "ตามตัวอักษร" ในทางที่ผิดซึ่งให้ความหมายอื่น แค่บอกว่า ...
Ivan Kleshnin

2
เกี่ยวกับประเด็นที่ 2 ผู้เขียนระบุว่าเป็นการแยกความกังวลที่ "ดีกว่า" ในการแยกการสร้างและการเริ่มต้นและอ้างว่าอาจมีบางกรณีการใช้งานที่หายากซึ่งสิ่งนี้อาจส่องแสง (เช่นกลุ่มวัตถุ) ฉันพบว่าการโต้แย้งนั้นอ่อนแอมาก
เช่า

2
อีกครั้งเกี่ยวกับจุดที่ 2 ด้วย OLOO คุณสามารถสร้างวัตถุของคุณในครั้งเดียวและรอเพื่อเริ่มต้นในขณะที่ตัวสร้างคุณต้องเริ่มต้นในการสร้างดังนั้น Kyle จึงคิดว่านี่เป็นประโยชน์
taco

5

"ฉันคิดว่าจะทำมันทำให้แต่ละ obj ขึ้นอยู่กับอีกอันหนึ่ง"

ดังที่ไคล์อธิบายเมื่อสองวัตถุ[[Prototype]]เชื่อมโยงกันพวกมันไม่ได้ขึ้นอยู่กับกันและกันจริงๆ แทนที่จะเป็นวัตถุแต่ละชิ้น คุณกำลังเชื่อมโยงวัตถุหนึ่งกับอีกชิ้นหนึ่งด้วยการ[[Prototype]]เชื่อมโยงซึ่งคุณสามารถเปลี่ยนแปลงได้ตลอดเวลาที่คุณต้องการ หากคุณใช้[[Prototype]]วัตถุที่เชื่อมโยงสองชิ้นที่สร้างขึ้นผ่านสไตล์ OLOO โดยพึ่งพาซึ่งกันและกันคุณควรคิดเหมือนกันเกี่ยวกับวัตถุที่สร้างขึ้นผ่านการconstructorโทร

var foo= {},
    bar= Object.create(foo),
    baz= Object.create(bar);


console.log(Object.getPrototypeOf(foo)) //Object.prototype

console.log(Object.getPrototypeOf(bar)) //foo

console.log(Object.getPrototypeOf(baz)) //bar

ลองคิดดูว่าคุณนึกถึงfoo barและbazพึ่งพาซึ่งกันและกันหรือไม่?

ทีนี้มาทำconstructorโค้ดลักษณะเดียวกันนี้กัน-

function Foo() {}

function Bar() {}

function Baz() {}

Bar.prototype= Object.create(Foo);
Baz.prototype= Object.create(Bar);

var foo= new Foo(),
    bar= new Bar().
    baz= new Baz();

console.log(Object.getPrototypeOf(foo)) //Foo.prototype
console.log(Object.getPrototypeOf(Foo.prototype)) //Object.prototype

console.log(Object.getPrototypeOf(bar)) //Bar.prototype
console.log(Object.getPrototypeOf(Bar.prototype)) //Foo.prototype

console.log(Object.getPrototypeOf(baz)) //Baz.prototype
console.log(Object.getPrototypeOf(Baz.prototype)) //Bar.prototype

เพียงแตกต่าง b / w หลังและรหัสอดีตก็คือว่าในหนึ่งหลัง foo, bar, bazbbjects จะเชื่อมโยงกับแต่ละอื่น ๆ ผ่านทางวัตถุโดยพลการของพวกเขาconstructorฟังก์ชั่น ( Foo.prototype, Bar.prototype, Baz.prototype) แต่ในอดีตหนึ่ง ( OLOOรูปแบบ) พวกเขาจะเชื่อมโยงโดยตรง ทั้งสองวิธีคุณเพียงแค่การเชื่อมโยงfoo, bar, bazกับแต่ละอื่น ๆ โดยตรงในอดีตหนึ่งและทางอ้อมในสมัยหนึ่ง แต่ในทั้งสองกรณีอ็อบเจกต์นั้นไม่ขึ้นกับกันและกันเพราะมันไม่เหมือนกับอินสแตนซ์ของคลาสใด ๆ ที่เมื่อสร้างอินสแตนซ์แล้วจะไม่สามารถสืบทอดจากคลาสอื่นได้ คุณสามารถเปลี่ยนวัตถุที่ควรมอบสิทธิ์ได้เสมอ

var anotherObj= {};
Object.setPrototypeOf(foo, anotherObj);

ดังนั้นทั้งหมดจึงเป็นอิสระจากกัน

"ฉันหวังว่าOLOOจะแก้ปัญหาที่วัตถุแต่ละชิ้นไม่รู้อะไรเกี่ยวกับอีกฝ่าย"

ใช่เป็นไปได้แน่นอน -

มาใช้Techเป็นวัตถุยูทิลิตี้ -

 var Tech= {
     tag: "technology",
     setName= function(name) {
              this.name= name;
}
}

สร้างวัตถุได้มากเท่าที่คุณต้องการเชื่อมโยงTech-

var html= Object.create(Tech),
     css= Object.create(Tech),
     js= Object.create(Tech);

Some checking (avoiding console.log)- 

    html.isPrototypeOf(css); //false
    html.isPrototypeOf(js); //false

    css.isPrototypeOf(html); //false
    css.isPrototypeOf(js); //false

    js.isPrototypeOf(html); //false
    js.isPrototypwOf(css); //false

    Tech.isPrototypeOf(html); //true
    Tech.isPrototypeOf(css); //true
    Tech.isPrototypeOf(js); //true

คุณคิดว่าhtml, css, jsวัตถุที่มีการเชื่อมต่อกับแต่ละอื่น ๆ ? ไม่พวกเขาไม่ได้ ตอนนี้เรามาดูกันว่าเราจะทำได้อย่างไรด้วยconstructorฟังก์ชัน -

function Tech() { }

Tech.prototype.tag= "technology";

Tech.prototype.setName=  function(name) {
              this.name= name;
}

สร้างวัตถุได้มากเท่าที่คุณต้องการเชื่อมโยงTech.proptotype-

var html= new Tech(),
     css= new Tech(),
      js= new Tech();

การตรวจสอบบางอย่าง (หลีกเลี่ยง console.log) -

html.isPrototypeOf(css); //false
html.isPrototypeOf(js); //false

css.isPrototypeOf(html); //false
css.isPrototypeOf(js); //false

js.isPrototypeOf(html); //false
js.isPrototypeOf(css); //false

Tech.prototype.isPrototypeOf(html); //true
Tech.prototype.isPrototypeOf(css); //true
Tech.prototype.isPrototypeOf(js); //true

คุณคิดอย่างไรเหล่านี้constructorวัตถุสไตล์ ( html, css, js) วัตถุที่แตกต่างจากOLOOรหัสสไตล์? ในความเป็นจริงพวกเขามีจุดประสงค์เดียวกัน ในOLOOรูปแบบหนึ่งอ็อบเจ็กต์ที่มอบหมายให้Tech(การมอบหมายถูกตั้งค่าอย่างชัดเจน) ในขณะที่constructorอ็อบเจ็กต์หนึ่งในสไตล์ที่มอบสิทธิ์ให้Tech.prototype(การมอบหมายถูกตั้งค่าโดยปริยาย) ท้ายที่สุดคุณจะเชื่อมโยงวัตถุทั้งสามโดยไม่มีการเชื่อมโยงซึ่งกันและกันไปยังวัตถุเดียวโดยใช้OLOO-style โดยอ้อมโดยใช้constructor-style

"ตามที่เป็นอยู่ ObjB จะต้องสร้างจาก ObjA .. Object.create (ObjB) เป็นต้น"

ไม่มีObjBที่นี่ไม่เหมือนอินสแตนซ์ (ในภาษาคลาสสิก-based) ObjAของชั้นใด มันน่าจะพูดได้ว่าเหมือนobjBobject ถูกมอบหมายให้เป็นObjAobject ในตอนที่สร้าง "ถ้าคุณใช้ constructor คุณจะต้องทำ 'coupling' เหมือนกันแม้ว่าจะใช้ประโยชน์ทางอ้อมโดยทางอ้อม.prototypeก็ตาม


3

@Marcus @bholben

บางทีเราอาจทำอะไรแบบนี้ได้

    const Point = {

        statics(m) { if (this !== Point) { throw Error(m); }},

        create (x, y) {
            this.statics();
            var P = Object.create(Point);
            P.init(x, y);
            return P;
        },

        init(x=0, y=0) {
            this.x = x;
            this.y = y;
        }
    };


    const Point3D = {

        __proto__: Point,

        statics(m) { if (this !== Point3D) { throw Error(m); }},

        create (x, y, z) {
            this.statics();
            var P = Object.create(Point3D);
            P.init(x, y, z);
            return P;
        },

        init (x=0, y=0, z=0) {
            super.init(x, y);
            this.z = z;
        }
    }; 

แน่นอนว่าการสร้างออบเจ็กต์ Point3D ที่เชื่อมโยงกับต้นแบบของวัตถุ Point2D นั้นเป็นเรื่องงี่เง่า แต่นั่นก็อยู่ข้างประเด็น (ฉันต้องการให้สอดคล้องกับตัวอย่างของคุณ) อย่างไรก็ตามเท่าที่มีการร้องเรียน:

  1. สมส่วนสามารถแก้ไขด้วย ES6 ของObject.setPrototypeOfหรือขมวดคิ้วมากขึ้นอยู่กับ__proto__ = ...การใช้งานผมว่า เรายังสามารถใช้superกับวัตถุทั่วไปได้เช่นกันดังที่เห็นในPoint3D.init(). อีกวิธีหนึ่งคือการทำสิ่งที่ชอบ

    const Point3D = Object.assign(Object.create(Point), {  
        ...  
    }   

    แม้ว่าฉันจะไม่ชอบไวยากรณ์เป็นพิเศษ


  1. เราสามารถห่อp = Object.create(Point)แล้วp.init()เป็นตัวสร้างได้เสมอ เช่นPoint.create(x,y). การใช้โค้ดด้านบนเราสามารถสร้างPoint3D"อินสแตนซ์" ได้ในลักษณะต่อไปนี้

    var b = Point3D.create(1,2,3);
    console.log(b);                         // { x:1, y:2, z:3 }
    console.log(Point.isPrototypeOf(b));    // true
    console.log(Point3D.isPrototypeOf(b))   // true

  1. ฉันเพิ่งมากับแฮ็คนี้เพื่อเลียนแบบวิธีการคงที่ใน OLOO ฉันไม่แน่ใจว่าชอบหรือเปล่า ต้องเรียกคุณสมบัติพิเศษที่ด้านบนของเมธอด "คงที่" ตัวอย่างเช่นฉันทำให้Point.create()เมธอดคงที่

        var p = Point.create(1,2);
        var q = p.create(4,1);          // Error!  

อีกทางเลือกหนึ่งด้วย ES6 Symbolsคุณสามารถขยายคลาสพื้นฐานของ Javascript ได้อย่างปลอดภัย ดังนั้นคุณสามารถบันทึกรหัสตัวเองและกำหนดคุณสมบัติพิเศษบน Object.prototype ตัวอย่างเช่น,

    const extendedJS = {};  

    ( function(extension) {

        const statics = Symbol('static');

        Object.defineProperty(Object.prototype, statics, {
            writable: true,
            enumerable: false,
            configurable: true,
            value(obj, message) {
                if (this !== obj)
                    throw Error(message);
            }
        });

        Object.assign(extension, {statics});

    })(extendedJS);


    const Point = {
        create (x, y) {
            this[extendedJS.statics](Point);
            ...


2

@james emanon - ดังนั้นคุณกำลังอ้างถึงการสืบทอดหลายรายการ (กล่าวถึงในหน้า 75 ในหนังสือ "You Don't Know JS: this & Object Prototypes") และกลไกนั้นเราสามารถพบได้ในฟังก์ชัน "ขยาย" ของขีดล่าง ชื่อวัตถุที่คุณระบุไว้ในตัวอย่างของคุณคือแอปเปิ้ลส้มและลูกอมผสมกันเล็กน้อย แต่ฉันเข้าใจประเด็นที่อยู่เบื้องหลัง จากประสบการณ์ของฉันนี่จะเป็นรุ่น OOLO:

var ObjA = {
  setA: function(a) {
    this.a = a;
  },
  outputA: function() {
    console.log("Invoking outputA - A: ", this.a);
  }
};

// 'ObjB' links/delegates to 'ObjA'
var ObjB = Object.create( ObjA );

ObjB.setB = function(b) {
   this.b = b;
}

ObjB.setA_B = function(a, b) {
    this.setA( a ); // This is obvious. 'setA' is not found in 'ObjB' so by prototype chain it's found in 'ObjA'
    this.setB( b );
    console.log("Invoking setA_B - A: ", this.a, " B: ", this.b);
};

// 'ObjC' links/delegates to 'ObjB'
var ObjC = Object.create( ObjB );

ObjC.setC = function(c) {
    this.c = c;  
};

ObjC.setA_C = function(a, c) {
    this.setA( a ); // Invoking 'setA' that is clearly not in ObjC shows that prototype chaining goes through ObjB all the way to the ObjA
    this.setC( c );
    console.log("Invoking setA_C - A: ", this.a, " C: ", this.c);
};

ObjC.setA_B_C = function(a, b, c){
    this.setA( a ); // Invoking 'setA' that is clearly not in ObjC nor ObjB shows that prototype chaining got all the way to the ObjA
    this.setB( b );
    this.setC( c );
    console.log("Invoking setA_B_C - A: ", this.a, " B: ", this.b, " C: ", this.c);
};

ObjA.setA("A1");
ObjA.outputA(); // Invoking outputA - A:  A1

ObjB.setA_B("A2", "B1"); // Invoking setA_B - A:  A2  B:  B1

ObjC.setA_C("A3", "C1"); // Invoking setA_C - A:  A3  C:  C1
ObjC.setA_B_C("A4", "B2", "C1"); // Invoking setA_B_C - A:  A4  B:  B2  C:  C1

เป็นตัวอย่างง่ายๆ แต่ประเด็นที่แสดงคือเราเป็นเพียงการผูกมัดวัตถุเข้าด้วยกันในโครงสร้าง / การก่อตัวที่ค่อนข้างแบนและยังมีความเป็นไปได้ที่จะใช้วิธีการและคุณสมบัติจากหลาย ๆ วัตถุ เราบรรลุสิ่งเดียวกันกับคลาส / วิธี "คัดลอกคุณสมบัติ" สรุปโดย Kyle (หน้า 114 "this & Object Prototypes"):

ในคำอื่น ๆ กลไกที่เกิดขึ้นจริงสาระสำคัญของสิ่งที่สำคัญต่อการทำงานที่เราสามารถใช้ประโยชน์ใน JavaScript คือทั้งหมดที่เกี่ยวกับวัตถุที่ถูกเชื่อมโยงกับวัตถุอื่น

ฉันเข้าใจว่าวิธีที่เป็นธรรมชาติมากขึ้นสำหรับคุณคือการระบุอ็อบเจ็กต์ "parent" (ระวัง :)) ทั้งหมดในที่เดียว / การเรียกใช้ฟังก์ชันแทนที่จะสร้างแบบจำลองทั้งห่วงโซ่

สิ่งที่ต้องมีคือเปลี่ยนความคิดและการสร้างแบบจำลองปัญหาในแอปพลิเคชันของเราตามนั้น ฉันก็ชินเหมือนกัน หวังว่ามันจะช่วยได้และคำตัดสินสุดท้ายจาก Kyle เองจะดีมาก :)


ใช่ - ขอบคุณ - แต่ฉันหวังว่าจะย้ายออกไปจากวิธีการนี้เพราะวิธีที่คุณมีและวิธีที่ฉันคิดว่าจะทำมันทำให้ obj แต่ละอันขึ้นอยู่กับอีกอันหนึ่งฉันหวังว่า OLOO จะแก้ปัญหาที่ วัตถุแต่ละชิ้นไม่รู้อะไรเกี่ยวกับกันและกัน ตามที่เป็นอยู่ objB จะต้องถูกสร้างขึ้นจาก ObjA .. Object.create (ObjB) ฯลฯ .. ที่อยู่คู่กันเกินไป ความคิดใด ๆ
james emanon

-1

@Marcus เช่นเดียวกับคุณฉันสนใจ OLOO และไม่ชอบความไม่สมมาตรตามที่อธิบายไว้ในจุดแรกของคุณ ฉันเล่นกับนามธรรมเพื่อดึงความสมมาตรกลับมา คุณสามารถสร้างlink()ฟังก์ชันที่ใช้แทนObject.create()ไฟล์. เมื่อใช้รหัสของคุณอาจมีลักษณะดังนี้ ...

var Point = {
    init  : function(x,y) {
        this.x = x;
        this.y = y;
    }
};


var Point3D = link(Point, {
    init: function(x,y,z) {
        Point.init.call(this, x, y);
        this.z = z;
    }
});

โปรดจำไว้ว่าObject.create()มีพารามิเตอร์ที่สองที่สามารถส่งผ่านได้นี่คือฟังก์ชันลิงก์ที่ใช้ประโยชน์จากพารามิเตอร์ที่สอง นอกจากนี้ยังช่วยให้สามารถกำหนดค่าเองได้เล็กน้อย ...

function link(delegate, props, propsConfig) {
  props = props || {};
  propsConfig = propsConfig || {};

  var obj = {};
  Object.keys(props).forEach(function (key) {
    obj[key] = {
      value: props[key],
      enumerable: propsConfig.isEnumerable || true,
      writable: propsConfig.isWritable || true,
      configurable: propsConfig.isConfigurable || true
    };
  });

  return Object.create(delegate, obj);
}

แน่นอนฉันคิดว่า @Kyle จะไม่รับรองการสร้างเงาinit()ฟังก์ชันในวัตถุ Point3D ;-)


เมื่อมองย้อนกลับไปในสิ่งนี้ตอนนี้ฉันคิดว่าเมื่อรวมObject.assign()กับObject.create()เราสามารถลดความซับซ้อนของlink()ฟังก์ชันด้านบนได้มาก function create(delegate, props) { return Object.assign(Object.create(delegate), props); }ในสถานที่ที่เราสามารถใช้นี้ หรือดีกว่า แต่เราสามารถใช้ขีดหรือ Lodash _.create(delegate, props)ที่จะทำให้มันกระชับจริงๆ:
bholben

-1

มีวิธีที่จะ OLOO มากกว่า "สอง" วัตถุหรือไม่ .. ตัวอย่างทั้งหมดที่ฉันประกอบด้วยตัวอย่างพื้นฐาน (ดูตัวอย่างของ OP) สมมติว่าเรามีวัตถุต่อไปนี้เราจะสร้างวัตถุ "ที่สี่" ที่มีคุณลักษณะของ "อื่น ๆ " ทั้งสามได้อย่างไร? อลา ...

var Button = {
     init: function(name, cost) {
       this.buttonName = name;
       this.buttonCost = cost;
     }
}

var Shoe = {
     speed: 100
}

var Bike = {
     range: '4 miles'
}

วัตถุเหล่านี้เป็นไปตามอำเภอใจและอาจครอบคลุมพฤติกรรมทุกประเภท แต่สาระสำคัญคือเรามีวัตถุจำนวน 'n' และวัตถุใหม่ของเราต้องการบางสิ่งจากทั้งสาม

แทนที่จะเป็นตัวอย่างที่ให้มา:

var newObj = Object.create(oneSingularObject);
    newObj.whatever..

แต่วัตถุใหม่ของเรา = (ปุ่มจักรยานรองเท้า) ......

รูปแบบที่จะทำให้สิ่งนี้เกิดขึ้นใน OLOO คืออะไร?


1
ฟังดูเหมือน "ชอบการเรียบเรียงมากกว่าการสืบทอด" ซึ่งเป็นกลยุทธ์ที่ยอดเยี่ยม ใน ES6 คุณสามารถใช้Object.assign()- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... ถ้าเขียนใน ES5 คุณสามารถใช้ขีดของ_.extend()หรือ _.assign()Lodash นี่เป็นวิดีโอที่ยอดเยี่ยมที่จะอธิบาย ... youtu.be/wfMtDGfHWpA หากคุณมีคุณสมบัติที่ชนกันข้อสุดท้ายจะชนะดังนั้นลำดับจึงมีความสำคัญ
bholben
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.