关于javascript:Kyle Simpson的OLOO模式与原型设计模式

Kyle Simpson's OLOO Pattern vs Prototype Design Pattern

凯尔·辛普森的"对象链接到其他对象"模式与原型设计模式有什么不同吗?除了通过特别指出"链接"(原型的行为)并澄清这里没有"复制"(类的行为)的东西创造它之外,他的模式到底引入了什么?

下面是凯尔在他的书《你不知道JS:这个&对象原型》中的模式示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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."


what exactly does his pattern introduce?

oloo embraces the prototype is as -链层,没有其他的在线needing(海事组织confusing to get the)语义的连杆。P></

我知道,这些代码片断have the same双精确的结果,但differently get there。P></

形态:构造函数P></

1
2
3
4
5
6
7
8
9
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形态:P></

1
2
3
4
5
6
7
var FooObj = { y: 11 };

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

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

在两个代码片断,安xis to an [[Prototype]]联对象(或对象Bar.prototypeBarObj),which is to三联在转弯(或对象Foo.prototypeFooObj)。P></

茶是identical between the代表关系和代码片断。the memory usage is between the identical代码片断。"to the many children"创造能力(又名多类对象,通过x1000x1is between the identical等)的代码片断。茶艺表演of the代表(x.yx.zis between the identical)代码片断。创作与表现对象slower is the oloo that,but that the slower sanity检查reveals is not an的性能问题。P></

我argue oloo offers is that这是多简单的内部对象和directly to the‘Express链接链接indirectly them to them,比一newthe构造函数/机制。后者pretends to be about the真是可怕,但只是一类表为代表(侧注:我知道口语classis es6表!)。P></

oloo is just切割出中间人。P></

这是一oloo?classVS。P></


凯尔的读书,发现它真的和不对称的,particularly the detail how is about this拓展训练。P></基础:

对我来说,有一定oloo大学院:学院夫妇P></1。简单

在线oloo relies Object.create()which is to create a new object to another [[Prototype]]联的对象。你不明白that have to have a函数prototypeproperty or any of the担心其潜在相关pitfalls that是从改性。P></2。清洁表

arguable this is,but is the syntax感觉oloo(many cases)neater简明和更多的方法比标准的JavaScript,它是particularly when to子的多态性(在super风格)。P></缺点:

我认为有问题的位设计学院there is one(真的那一个点contributes to 2以上),is to do with,shadowing:P></

In behaviour delegation, we avoid if at all possible naming things the same at different levels of the [[Prototype]] chain.

This is that the背后的想法,他们自己有特异功能的对象,那么更多的internally to which the chain下唐氏函数的代表。for example,You might have a resource对象与函数的在线save()末在JSON版本EN which of the object to the服务器,但你也有可能在clientResourcewhich has a stripAndSave()函数对象的第一removes properties,which that不应该be sent to the服务器。P></

潜在的问题是:"如果在与人*来决定让对象在specialResource,not of the Whole prototype链的全面感知,他们可以决定在reasonably * timestamp for the last to save保存在savewhich the property called,阴影对象的基础save()双功能性resourceDown on the linksthe prototype链:P></

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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!

This is a particularly contrived example is that,but not the other shadowing点专门开发CAN to some properties和重金属铅的尴尬情况使用的词库。P></

也许美好的摇篮》插图of this as an init——particularly尖锐oolo sidesteps型函数的构造函数。因为每一个相关的对象将likely需要这样的功能,它可能是在运动tedious them to name and the appropriately唯一性,可以使它很难记得to which to使用。P></

< > *子其实是不合理的(好的多particularly lastSaved会更好,但这只是an example。></次)P></


在"You don't know JS:This&object prototype"中的讨论和Oloo的演示都是发人深省的,我在这本书中学到了很多东西。其他答案中很好地描述了Oloo模式的优点;但是,我对它有以下宠物投诉(或者缺少一些阻止我有效应用它的东西):

当"class"继承经典模式中的"another"class时,可以将这两个函数声明为类似的语法("function declaration"或"function statement"):

1
2
3
4
5
6
7
8
9
10
11
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模式中,用于定义基对象和派生对象的不同语法形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
};

正如您在上面的示例中看到的,可以使用对象文字表示法定义基对象,而不能对派生对象使用相同的表示法。这种不对称让我心烦。

在oloo模式中,创建对象有两个步骤:

  • 呼叫Object.create
  • 调用一些自定义的、非标准的方法来初始化对象(必须记住,因为对象可能因对象而异):

    1
    2
    3
     var p2a = Object.create(Point);

     p2a.init(1,1);
  • 相反,在原型模式中,您使用标准运算符new

    1
    var p2a = new Point(1,1);

    在经典模式中,我可以创建不直接应用于"即时"的"静态"实用程序函数,方法是将它们直接分配给"类"函数(而不是它的.prototype)。如下面代码中的函数square

    1
    2
    3
    4
    5
    Point.square = function(x) {return x*x;};

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

    相反,在oloo模式中,任何"静态"功能(通过[[原型]]链)在对象实例上也可用:

    1
    2
    3
    4
    5
    6
    7
    8
    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));}
    };


    "I figured to do it makes each obj dependent on the other"

    当两个物体都是EDOCX1&0时取决于其他人;Instead they are individual object.你是链接一号对方用[[Prototype]]连接,你可以改变你的任何愿望。如果你带走两个物体,通过奥卢风格,作为相互依存的一部分,你也应该考虑同样的物体,通过constructor

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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

    现在想想第二个你认为依靠别人吗?

    现在让我们做同样的constructor样式代码。

    ZZU1

    唯一的差别是在一个侧面通过任意对象将BBJOCS连接到每一个对象但在前一个(EDOCX1&15)中,它们是直接连接的。这两种方式,你只是把foobar与其他每一种方式联系在一起,直接与前一种方式联系在一起,间接地与前一种方式联系在一起。但是,在这两种情况下,物体都是独立的,因为它不象一个实例,任何一类的物体,一旦被安装,就不可能从另一类物体中生成。你可以随时改变一个对象也应该委托他人做的事情。

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

    所以他们都是彼此独立的

    BLCK1/

    是的,这是不可能的。

    作为一个有用的对象

    1
    2
    3
    4
    5
    6
     var Tech= {
         tag:"technology",
         setName= function(name) {
                  this.name= name;
    }
    }

    创建许多对象,如你希望链接到Tech

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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

    你认为这些物体相互连接吗?不,他们没有。现在让我们看看我们是如何做到这一点的。

    1
    2
    3
    4
    5
    6
    7
    function Tech() { }

    Tech.prototype.tag="technology";

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

    创建许多对象,如你希望链接到Tech.proptotype

    1
    2
    3
    var html= new Tech(),
         css= new Tech(),
          js= new Tech();

    一些检查(避开控制台.log)--

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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

    你觉得这些东西怎么样?不同的对象来自于事实上,他们为同一目的服务。样式一个对象代表Tech(代表团已确定)While in constructor-样式一个对象代表EDOCX1〕〔34〕(代表团已隐含地确定)。最后,你将三个对象连接起来,与每一个对象没有任何联系,直接使用EDOCX1[…]15]样式,间接使用EDOCX1[…]3]样式。

    "As is, ObjB has to be created from ObjA.. Object.create(ObjB) etc"

    这里不像是一个经典语言的论坛。法国电力公司Object is made delegate to ObjAObject at it's creation时间如果你使用的是制造商,你也可以用.prototype间接地做同样的连接。


    @Marcus@Bholben

    也许我们可以做类似的事情。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
        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;
            }
        };

    Of course,creating a point3d object that links to the protototype of a point2d object i s kind of silly,but that's beside the point(I wanted to be consistent with your example).无论如何,只要抱怨继续:

  • 该不对称可以与ES6的对象固定。我用的是我们也可以使用超级标准对象,如同在Point3D.init()中看到的那样。另一种方式是做类似的事情

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

    虽然我不喜欢语法

  • 我们总可以把p = Object.create(Point)写成一个建筑师。E.G.Point.create(x,y)使用上面的代码,我们可以在曼纳的后面创建一个"实例"。

    1
    2
    3
    4
    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
  • 我只是用这个电池来模拟奥卢的静态方法。我不确定我喜欢还是不喜欢它要求在任何"静态"方法的顶端有一个特殊性能。例如,我制作了Point.create()静态方法。

    1
    2
        var p = Point.create(1,2);
        var q = p.create(4,1);          // Error!
  • 另一方面,ES6符号可以安全扩展Javascript Base Classes。所以你可以保存自己的一些代码,并确定对象的特殊性能。For example,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
        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);
                ...

    @詹姆斯·伊曼农——所以,你指的是多重继承(在第75页的书"你不知道JS:这个&对象原型")。我们可以在下划线的"extend"函数中找到这个机制。您在示例中所述对象的名称有点混合了苹果、桔子和糖果,但我理解其背后的含义。根据我的经验,这将是oolo版本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    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

    这是一个简单的例子,但所显示的点是,我们只是以相当平坦的结构/形式将对象链接在一起,并且仍然有可能使用来自多个对象的方法和属性。我们实现了与使用class/"copying the properties"方法相同的事情。由Kyle总结(第114页,"此对象原型"):

    In other words, the actual mechanism, the essence of what’s important
    to the functionality we can leverage in JavaScript, is all about objects
    being linked to other objects.

    我理解您更自然的方法是在一个位置/函数调用中声明所有"parent"(小心:)对象,而不是建模整个链。

    它需要的是在我们的应用程序中根据这一点进行思考和建模问题的转换。我也逐渐习惯了。希望这有帮助,凯尔人自己的最终裁决会很好。:)


    有没有一种方法可以将两个以上的对象我所用的所有示例都是基于示例的(请参阅op的示例)。假设我们有以下对象,我们如何创建一个"第四"对象,它具有"其他"三个属性?阿拉巴马州

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var Button = {
         init: function(name, cost) {
           this.buttonName = name;
           this.buttonCost = cost;
         }
    }

    var Shoe = {
         speed: 100
    }

    var Bike = {
         range: '4 miles'
    }

    这些对象是任意的,可以包含各种行为。但要点是,我们有n个对象,我们的新对象需要这三个对象中的某个。

    而不是给出的例子ala:

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

    但是,我们的新对象=(按钮,自行车,鞋子)……

    在奥洛奥,这是什么样的模式?


    @马库斯,和你一样,我对奥洛很感兴趣,也不喜欢你第一点所描述的不对称性。我一直在玩抽象来恢复对称性。您可以创建一个用于替代Object.create()link()函数。当使用时,您的代码可能看起来像这样…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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()有第二个参数可以传入。下面是利用第二个参数的链接函数。它还允许一些自定义配置…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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不会支持在point3d对象中隐藏init()函数。;-)