Mẫu OLOO của Kyle Simpson so với Mẫu thiết kế nguyên mẫu


109

Mẫu "OLOO (Đối tượng liên kết với các đối tượng khác)" của Kyle Simpson có khác với mẫu thiết kế Nguyên mẫu không? Ngoài việc coi nó bằng thứ gì đó chỉ ra cụ thể "liên kết" (hành vi của các nguyên mẫu) và làm rõ rằng không có "sao chép" đang xảy ra ở đây (hành vi của các lớp), chính xác thì khuôn mẫu của anh ta giới thiệu điều gì?

Đây là một ví dụ về mô hình của Kyle từ cuốn sách của anh ấy, "Bạn không biết 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
Ít nhất bạn có thể liên kết đến mô tả về mẫu bạn đang hỏi. Thậm chí tốt hơn là hiển thị một ví dụ mã về nó trong câu hỏi của bạn.
jfriend00

4
Getify đôi khi có trên Stackoverflow. Tôi đã tweet cho anh ấy câu hỏi này :)
Pointy

Câu trả lời:


155

chính xác thì mô hình của anh ấy giới thiệu là gì?

OLOO nắm lấy chuỗi nguyên mẫu như hiện tại, mà không cần phải xếp lớp trên các ngữ nghĩa (IMO khó hiểu) khác để có được liên kết.

Vì vậy, hai đoạn mã này có kết quả CHÍNH XÁC giống nhau, nhưng đến đó khác nhau.

Hình thức xây dựng:

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

Biểu mẫu OLOO:

var FooObj = { y: 11 };

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

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

Trong cả hai đoạn mã, một xđối tượng được [[Prototype]]liên kết với một đối tượng ( Bar.prototypehoặc BarObj), đến lượt nó được liên kết với đối tượng thứ ba ( Foo.prototypehoặc FooObj).

Các mối quan hệ và sự ủy quyền giống hệt nhau giữa các đoạn mã. Việc sử dụng bộ nhớ giống hệt nhau giữa các đoạn mã. Khả năng tạo nhiều "con" (hay còn gọi là nhiều đối tượng như x1xuyên qua x1000, v.v.) giống hệt nhau giữa các đoạn mã. Hiệu suất của đoàn ( x.yx.z) giống hệt nhau giữa các đoạn trích. Hiệu suất tạo đối tượng chậm hơn với OLOO, nhưng sự tỉnh táo kiểm tra xem tiết lộ rằng việc thực hiện chậm hơn thực sự không phải là một vấn đề.

Những gì tôi lập luận OLOO đưa ra là việc chỉ thể hiện các đối tượng và liên kết trực tiếp chúng đơn giản hơn nhiều so với liên kết gián tiếp chúng thông qua phương thức khởi tạo / newcơ chế. Cái sau giả vờ là về các lớp nhưng thực sự chỉ là một cú pháp khủng khiếp để thể hiện sự ủy nhiệm ( lưu ý bên:class cú pháp ES6 cũng vậy !).

OLOO chỉ đang cắt bỏ người trung gian.

Đây là một so sánh khác của classso với OLOO.


2
Tôi thấy câu trả lời của bạn thực sự thú vị và ý tưởng về OLOO được mô tả trong sách của bạn, tôi muốn nhận được phản hồi của bạn về câu hỏi này: stackoverflow.com/questions/40395762/… Đặc biệt nếu bạn thấy cách triển khai này đúng và cách giải quyết vấn đề liên quan đến quyền truy cập thành viên tư nhân. Cảm ơn bạn đã dành thời gian trước và chúc mừng cuốn sách mới nhất của bạn.
GibboK

Nhưng Object.create(...)là chậm hơn nhiều lần so với new. jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/…
Pier

3
@Pier hiệu suất thực sự không phải là vấn đề quá lớn. đã sửa liên kết bài đăng trên blog bị hỏng về việc kiểm tra hiệu quả hoạt động của việc tạo đối tượng, giải thích cách suy nghĩ đúng đắn về điều này.
Kyle Simpson

2
Và jQuery chậm hơn API DOM, phải không? Nhưng, đây là năm hiện tại, anh bạn - tôi thà viết một cách trang nhã và đơn giản hơn là lo lắng về việc tối ưu hóa. Nếu tôi cần tối ưu hóa vi mô sau này, tôi sẽ lo lắng về điều đó khi đến thời điểm.
Eirik Birkeland

6
Tôi muốn nói thêm rằng bây giờ, chỉ hơn một năm sau, Object.create () được tối ưu hóa rất nhiều trong chrome và jsperf cho thấy điều đó - đó là một trong những tùy chọn nhanh nhất hiện nay. Điều này cho thấy chính xác lý do tại sao bạn không nên quan tâm đến những tối ưu hóa vi mô như vậy, và thay vào đó chỉ cần viết mã âm thanh theo thuật toán.
Kyle Baker

25

Tôi đã đọc cuốn sách của Kyle, và tôi thấy nó thực sự giàu thông tin, đặc biệt là chi tiết về cách thisràng buộc.

Ưu điểm:

Đối với tôi, có một số ưu điểm lớn của OLOO:

1. Tính đơn giản

OLOO dựa vào Object.create()để tạo một đối tượng mới được [[prototype]]liên kết với một đối tượng khác. Bạn không cần phải hiểu rằng các hàm cóprototype tính hoặc lo lắng về bất kỳ cạm bẫy tiềm ẩn liên quan nào đến từ việc sửa đổi nó.

2. Cú pháp rõ ràng hơn

Điều này có thể tranh cãi, nhưng tôi cảm thấy cú pháp OLOO (trong nhiều trường hợp) gọn gàng và ngắn gọn hơn so với cách tiếp cận javascript 'tiêu chuẩn', đặc biệt khi nói đến tính đa hình ( superlệnh gọi kiểu).

Nhược điểm:

Tôi nghĩ rằng có một chút nghi vấn về thiết kế (một phần thực sự đóng góp vào điểm 2 ở trên) và điều đó liên quan đến việc tạo bóng:

Trong ủy quyền hành vi, chúng tôi tránh nếu có thể đặt tên mọi thứ giống nhau ở các cấp độ khác nhau của [[Prototype]]chuỗi.

Ý tưởng đằng sau điều này là các đối tượng có các chức năng cụ thể hơn của riêng chúng, sau đó ủy quyền nội bộ cho các chức năng thấp hơn trong chuỗi. Ví dụ: bạn có thể có một resourceđối tượng với một save()hàm trên nó sẽ gửi một phiên bản JSON của đối tượng đến máy chủ, nhưng bạn cũng có thể có một clientResourceđối tượng cóstripAndSave() hàm, trước tiên sẽ xóa các thuộc tính không được gửi đến máy chủ .

Vấn đề tiềm năng là: nếu người khác đến cùng và quyết định để thực hiện một specialResourceđối tượng, không nhận thức đầy đủ của chuỗi nguyên mẫu toàn bộ, họ có thể hợp lý * quyết định để lưu dấu thời gian cho người cuối cùng tiết kiệm dưới một tài sản được gọi save, mà bóng tối căn cứ save()chức năng trên các resourceđối tượng hai liên kết xuống chuỗi nguyên mẫu:

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!

Đây là một ví dụ đặc biệt giả tạo, nhưng vấn đề là đặc biệt không bóng các thuộc tính khác có thể dẫn đến một số tình huống khó xử và sử dụng nhiều từ điển đồng nghĩa!

Có lẽ một minh họa tốt hơn cho điều này sẽ là một initphương pháp - đặc biệt sâu sắc như các hàm kiểu phương thức khởi tạo bước qua OOLO. Vì mọi đối tượng liên quan có thể sẽ cần một chức năng như vậy, có thể là một bài tập tẻ nhạt để đặt tên cho chúng một cách thích hợp và tính duy nhất có thể khiến bạn khó nhớ nên sử dụng cái nào.

* Thực ra nó không đặc biệt hợp lý ( lastSavedsẽ tốt hơn nhiều, nhưng nó chỉ là một ví dụ.)


23
Tôi đồng ý rằng khả năng xảy ra va chạm tên là một nhược điểm ... nhưng thực ra đó là một nhược điểm của [[Prototype]]chính hệ thống, không phải OLOO cụ thể.
Kyle Simpson

Có lẽ điều đó cũng nên được đề cập trong cuốn sách?
giá thuê

Tôi không thực sự chắc chắn đây thực sự là một giải pháp cho vấn đề mà @Ed Hinchliffe mô tả vì nó chỉ di chuyển save () đến không gian tên riêng của nó nhưng nó hoạt động codepen.io/tosystem/pen/govEPr?editors=1010
Tristan Forward

Tôi nghĩ @ ed-hinchliffe có nghĩa là b.timeStampedSave();thay vì a.timeStampedSave();ở dòng cuối cùng của đoạn mã.
amangpt777

1
@ tristan-forward cảm ơn bạn đã đưa Rick và Morty vào cuộc!
Eric Bishard

13

Cuộc thảo luận trong "Bạn chưa biết JS: this & Object Prototypes" và phần trình bày của OLOO rất kích thích tư duy và tôi đã học được rất nhiều điều qua cuốn sách. Giá trị của mẫu OLOO được mô tả kỹ trong các câu trả lời khác; tuy nhiên, tôi có những phàn nàn về thú cưng sau với nó (hoặc thiếu thứ gì đó khiến tôi không thể áp dụng nó một cách hiệu quả):

1

Khi một "lớp" "kế thừa" một "lớp" khác trong mẫu cổ điển, hai hàm có thể được khai báo cú pháp tương tự ( "khai báo hàm" hoặc "câu lệnh hàm" ):

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);

Ngược lại, trong mẫu OLOO, các dạng cú pháp khác nhau được sử dụng để xác định cơ sở và các đối tượng dẫn xuất:

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;
};

Như bạn có thể thấy trong ví dụ trên, đối tượng cơ sở có thể được định nghĩa bằng cách sử dụng ký hiệu theo nghĩa đen của đối tượng, trong khi không thể sử dụng ký hiệu tương tự cho đối tượng dẫn xuất. Sự bất đối xứng này làm tôi khó chịu.

2

Trong mẫu OLOO, việc tạo một đối tượng gồm hai bước:

  1. gọi Object.create
  2. gọi một số phương thức tùy chỉnh, không chuẩn để khởi tạo đối tượng (bạn phải nhớ vì nó có thể thay đổi từ đối tượng này sang đối tượng tiếp theo):

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

Ngược lại, trong mẫu Nguyên mẫu, bạn sử dụng toán tử chuẩn new:

var p2a = new Point(1,1);

3

Trong mẫu cổ điển, tôi có thể tạo các hàm tiện ích "tĩnh" không áp dụng trực tiếp cho "tức thì" bằng cách gán chúng trực tiếp cho hàm "lớp" (trái ngược với hàm .prototype). Ví dụ như hàm like squaretrong đoạn mã dưới đây:

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

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

Ngược lại, trong mẫu OLOO, bất kỳ hàm "tĩnh" nào cũng có sẵn (thông qua chuỗi [[nguyên mẫu]]) trên các cá thể đối tượng:

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
Không có chữ nào trong ví dụ mã đầu tiên của bạn. Có thể bạn đang sử dụng sai thuật ngữ "nghĩa đen" cho nó một nghĩa khác. Chỉ nói ...
Ivan Kleshnin

2
Về điểm thứ 2, tác giả cho rằng việc tách biệt việc tạo và khởi tạo là tách biệt "tốt hơn" và trích dẫn rằng có thể có một số trường hợp sử dụng hiếm hoi mà điều này có thể tỏa sáng (ví dụ một nhóm đối tượng). Tôi thấy lập luận yếu một cách khủng khiếp.
giá thuê

2
Một lần nữa liên quan đến điểm thứ 2, với OLOO, bạn có thể tạo các đối tượng của mình cùng một lúc và đợi khởi tạo, trong khi với hàm tạo, bạn phải khởi tạo khi tạo, vì vậy Kyle coi đây là một lợi ích.
taco

5

"Tôi đã tìm cách làm điều đó khiến mỗi đối tượng phụ thuộc vào đối tượng kia"

Như Kyle giải thích khi hai đối tượng được [[Prototype]]liên kết, chúng không thực sự phụ thuộc vào nhau; thay vào đó chúng là đối tượng riêng lẻ. Bạn đang liên kết một đối tượng này với một đối tượng khác bằng một [[Prototype]]liên kết mà bạn có thể thay đổi bất cứ lúc nào bạn muốn. Nếu bạn coi hai [[Prototype]]đối tượng được liên kết được tạo thông qua kiểu OLOO là phụ thuộc vào nhau, bạn cũng nên nghĩ tương tự về những đối tượng được tạo thông qua constructorcuộc gọi.

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

Bây giờ, hãy nghĩ trong giây lát, bạn có nghĩ đến foo barbaz phụ thuộc vào nhau không?

Bây giờ chúng ta hãy làm tương tự constructormã kiểu này-

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

Chỉ khác biệt b / w sau này và mã cũ là trong một thứ hai foo, bar, bazbbjects được liên kết với nhau, thông qua đối tượng tùy tiện của họ constructorfunction ( Foo.prototype, Bar.prototype, Baz.prototype) nhưng trong một cựu ( OLOOphong cách) họ được liên kết trực tiếp. Cả hai cách bạn chỉ cần kết nối foo, bar, bazvới nhau, trực tiếp trong một cựu tiếp và gián tiếp trong một thứ hai. Tuy nhiên, trong cả hai trường hợp, các đối tượng độc lập với nhau bởi vì nó không thực sự giống như một thể hiện của bất kỳ lớp nào từng được khởi tạo, không thể được thực hiện để kế thừa từ một số lớp khác. Bạn luôn có thể thay đổi đối tượng nào mà một đối tượng cũng sẽ ủy quyền.

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

Vì vậy, tất cả chúng đều độc lập với nhau.

"Tôi đã hy vọng OLOOsẽ giải quyết được vấn đề mà mỗi đối tượng không biết gì về đối tượng kia."

Vâng, điều đó thực sự có thể-

Hãy sử dụng Technhư một đối tượng tiện ích-

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

tạo nhiều đối tượng liên kết với bạn muốn 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

Bạn có nghĩ rằng html, css, jsđối tượng được kết nối với nhau-khác? Không, họ không. Bây giờ chúng ta hãy xem làm thế nào chúng ta có thể làm điều đó với constructorhàm-

function Tech() { }

Tech.prototype.tag= "technology";

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

tạo nhiều đối tượng liên kết với bạn muốn Tech.proptotype-

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

Một số kiểm tra (tránh 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

Làm thế nào để bạn nghĩ rằng những constructorđối tượng kiểu ( html, css, js) Đối tượng khác với OLOOđang kiểu? Trên thực tế, chúng phục vụ cùng một mục đích. Trong OLOOkiểu một đối tượng ủy quyền cho Tech(ủy quyền được đặt rõ ràng) trong khi trong constructorkiểu một đối tượng ủy quyền Tech.prototype(ủy quyền được đặt ngầm định). Cuối cùng, bạn sẽ liên kết ba đối tượng, không có liên kết với nhau, thành một đối tượng, trực tiếp sử dụng OLOO-style, gián tiếp sử dụng constructor-style.

"Như vậy, objB phải được tạo từ objA .. Object.create (ObjB), v.v."

Không, ObjBở đây không giống như một thể hiện (trong các ngôn ngữ dựa trên cổ điển) của bất kỳ lớp nào ObjA. Có thể nói như objBđối tượng được thực hiện ủy nhiệm cho ObjAđối tượng tại thời điểm tạo đối tượng " . Nếu bạn sử dụng hàm tạo, bạn sẽ thực hiện 'ghép' tương tự, mặc dù gián tiếp bằng cách sử dụng .prototypes.


3

@Marcus @bholben

Có lẽ chúng ta có thể làm điều gì đó như thế này.

    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;
        }
    }; 

Tất nhiên, việc tạo ra một đối tượng Point3D liên kết với nguyên mẫu của một đối tượng Point2D là một điều ngớ ngẩn, nhưng đó là vấn đề đáng chú ý (tôi muốn nhất quán với ví dụ của bạn). Dù sao, theo như các khiếu nại:

  1. Sự bất đối xứng có thể được khắc phục bằng Object.setPrototypeOf của ES6 hoặc __proto__ = ...tôi sử dụng nó càng khó hơn. Bây giờ chúng ta cũng có thể sử dụng super trên các đối tượng thông thường, như đã thấy trongPoint3D.init() . Một cách khác là làm điều gì đó như

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

    mặc dù tôi không đặc biệt thích cú pháp.


  1. Chúng ta luôn có thể chỉ quấn p = Object.create(Point)và sau đó p.init()thành một hàm tạo. vd Point.create(x,y). Sử dụng đoạn mã trên, chúng ta có thể tạo một Point3D"instance" theo cách sau.

    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. Tôi vừa nghĩ ra bản hack này để mô phỏng các phương thức tĩnh trong OLOO. Tôi không chắc liệu tôi có thích nó hay không. Nó yêu cầu gọi một thuộc tính đặc biệt ở đầu bất kỳ phương thức "tĩnh" nào. Ví dụ, tôi đã làm cho Point.create()phương thức tĩnh.

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

Ngoài ra, với Ký hiệu ES6, bạn có thể mở rộng các lớp cơ sở Javascript một cách an toàn. Vì vậy, bạn có thể lưu cho mình một số mã và xác định thuộc tính đặc biệt trên Object.prototype. Ví dụ,

    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 - Vì vậy, bạn đang đề cập đến đa kế thừa (được thảo luận ở trang 75 trong cuốn sách "Bạn chưa biết JS: this & Object Prototypes"). Và cơ chế đó chúng ta có thể tìm thấy trong chức năng "mở rộng" của dấu gạch dưới chẳng hạn. Tên của đồ vật bạn đã nêu trong ví dụ của mình hơi pha trộn giữa táo, cam và kẹo nhưng tôi hiểu điểm sau. Theo kinh nghiệm của tôi, đây sẽ là phiên bản 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

Đó là một ví dụ đơn giản nhưng điểm được chỉ ra là chúng ta chỉ đang xâu chuỗi các đối tượng lại với nhau trong cấu trúc / hình thành khá phẳng và vẫn có khả năng sử dụng các phương thức và thuộc tính từ nhiều đối tượng. Chúng tôi đạt được những điều tương tự như với cách tiếp cận lớp / "sao chép các thuộc tính". Được Kyle tổng hợp (trang 114, "this & Object Prototypes"):

Nói cách khác, cơ chế thực tế, bản chất của điều quan trọng đối với chức năng mà chúng ta có thể tận dụng trong JavaScript, là tất cả về các đối tượng được liên kết với các đối tượng khác .

Tôi hiểu rằng cách tự nhiên hơn đối với bạn là nêu tất cả các đối tượng "cha" (cẩn thận :)) ở một nơi / lệnh gọi hàm thay vì mô hình hóa toàn bộ chuỗi.

Những gì nó yêu cầu là thay đổi tư duy và mô hình hóa các vấn đề trong các ứng dụng của chúng tôi theo đó. Tôi cũng đã quen với nó. Hy vọng nó sẽ giúp ích và phán quyết cuối cùng từ Kyle sẽ là tuyệt vời. :)


Vâng - cảm ơn - nhưng tôi đã hy vọng tránh xa phương pháp luận này bởi vì cách bạn có nó và cách tôi tìm cách thực hiện nó khiến mỗi đối tượng phụ thuộc vào đối tượng khác .. Tôi hy vọng OLOO sẽ giải quyết được vấn đề trong đó mỗi đối tượng không biết gì về đối tượng kia. Như vậy, objB phải được tạo từ objA .. Object.create (ObjB), v.v ... quá ghép. bất kỳ ý tưởng?
james emanon

-1

@Marcus, cũng giống như bạn, tôi rất quan tâm đến OLOO và cũng không thích sự bất đối xứng như được mô tả ở điểm đầu tiên của bạn. Tôi đã chơi với một sự trừu tượng để mang lại sự đối xứng. Bạn có thể tạo một link()hàm được sử dụng thay cho Object.create(). Khi được sử dụng, mã của bạn có thể trông giống như thế này ...

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;
    }
});

Hãy nhớ rằng Object.create()có một tham số thứ hai có thể được truyền vào. Đây là hàm liên kết tận dụng tham số thứ hai. Nó cũng cho phép một chút cấu hình tùy chỉnh ...

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);
}

Tất nhiên, tôi nghĩ @Kyle sẽ không xác nhận việc phủ bóng init()hàm trong đối tượng Point3D. ;-)


Khi nhìn lại vấn đề này, bây giờ tôi nghĩ rằng bằng cách kết hợp Object.assign()với Object.create(), chúng ta có thể đơn giản hóa link()hàm trên rất nhiều . Thay vào đó, chúng ta có thể sử dụng này: function create(delegate, props) { return Object.assign(Object.create(delegate), props); }. Hoặc tốt hơn, chúng ta có thể sử dụng dấu gạch dưới hoặc Lodash để làm cho nó thực sự ngắn gọn: _.create(delegate, props).
bholben

-1

Có cách nào để OLOO nhiều hơn "hai" đối tượng .. tất cả các ví dụ tôi bao gồm ví dụ dựa trên (xem ví dụ của OP). Giả sử chúng ta có các đối tượng sau, làm thế nào chúng ta có thể tạo đối tượng "thứ tư" có các thuộc tính của ba đối tượng 'khác'? ala ...

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

var Shoe = {
     speed: 100
}

var Bike = {
     range: '4 miles'
}

các đối tượng này là tùy ý và có thể bao gồm tất cả các loại hành vi. Nhưng ý chính là, chúng ta có số đối tượng là 'n' và đối tượng mới của chúng ta cần thứ gì đó từ cả ba.

thay vì các ví dụ đã cho ala:

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

NHƯNG, newObject của chúng tôi = (Nút, Xe đạp, Giày) ......

Mô hình để thực hiện điều này trong OLOO là gì?


1
Điều này nghe có vẻ giống như "thành phần ưu tiên hơn thừa kế" - một chiến lược tuyệt vời. Trong ES6, bạn có thể sử dụng Object.assign()- developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… . Nếu viết bằng ES5, bạn có thể sử dụng Underscore's _.extend()hoặc Lodash's _.assign(). Đây là một video tuyệt vời để giải thích ... youtu.be/wfMtDGfHWpA . Nếu bạn có bất kỳ tài sản va chạm nào, cái cuối cùng sẽ thắng - vì vậy thứ tự rất quan trọng.
bholben
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.