Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/388.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在JavaScript中作为对象本身的Mixin对象属性_Javascript_Constructor_Prototype_Mixins_Composition - Fatal编程技术网

在JavaScript中作为对象本身的Mixin对象属性

在JavaScript中作为对象本身的Mixin对象属性,javascript,constructor,prototype,mixins,composition,Javascript,Constructor,Prototype,Mixins,Composition,我已经阅读了很多JavaScript混合设计模式,但没有找到一个适合我的需要。我所看到的大部分内容都使用某种程度的函数,将一个对象的方法复制到另一个对象的原型。对于值属性,它的工作方式与方法类似,但对于本身就是对象的属性,它会失败,因为复制它们意味着类的每个实例都将指向该属性的同一对象 我用一个简单的point类制作了一个代码示例: var Point = function ( x, y ) { this.x = x; this.y = y; }; Point.prototyp

我已经阅读了很多JavaScript混合设计模式,但没有找到一个适合我的需要。我所看到的大部分内容都使用某种程度的函数,将一个对象的方法复制到另一个对象的原型。对于值属性,它的工作方式与方法类似,但对于本身就是对象的属性,它会失败,因为复制它们意味着类的每个实例都将指向该属性的同一对象

我用一个简单的point类制作了一个代码示例:

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

Point.prototype = {
    constructor: Point,
    print: function () {
        console.log( this.x + this.offset[0], this.y + this.offset[1] );
    }
};
var Transformable = {
    offset: [ 0, 0 ],
    setOffset: function ( x, y ) {
        this.offset[ 0 ] = x;
        this.offset[ 1 ] = y;
    }
};
Point.prototype = {
    constructor: Point,
    print: function () {...},
    ...
};
print函数使用了一个我还没有声明的offset属性,因为它源自一个我想混入Point类的可转换类:

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

Point.prototype = {
    constructor: Point,
    print: function () {
        console.log( this.x + this.offset[0], this.y + this.offset[1] );
    }
};
var Transformable = {
    offset: [ 0, 0 ],
    setOffset: function ( x, y ) {
        this.offset[ 0 ] = x;
        this.offset[ 1 ] = y;
    }
};
Point.prototype = {
    constructor: Point,
    print: function () {...},
    ...
};
要制作mixin,我使用一些类似以下的扩展函数:

var extent = function ( target, mixin ) {
    for ( var property in mixin ) {
        if ( !target.prototype[ property ] ) {
            target.prototype[ property ] = mixin[ property ];
        }
    }
};
extent( Point, Transformable );
现在,代码已编译并可以使用:

var myPoint1 = new Point( 1, 2 );
myPoint1.setOffset( 5, 5 );
var myPoint2 = new Point( 1, 2 );
myPoint1.print();
myPoint2.print();
但它同时打印“6 7”,因为myPoint2中使用的偏移量数组与myPoint1中使用的偏移量数组相同

那么,如何才能在仍然从mixin开始的情况下实现每个点都有自己的偏移数组呢?除了数组之外,它还应该适用于每个对象,因为如果不是为了简单起见,我会在这里使用向量类

实际上实现了我想要的功能,但作为进一步的要求,我希望仍然能够使用object literal语法来添加Point类的方法:

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

Point.prototype = {
    constructor: Point,
    print: function () {
        console.log( this.x + this.offset[0], this.y + this.offset[1] );
    }
};
var Transformable = {
    offset: [ 0, 0 ],
    setOffset: function ( x, y ) {
        this.offset[ 0 ] = x;
        this.offset[ 1 ] = y;
    }
};
Point.prototype = {
    constructor: Point,
    print: function () {...},
    ...
};

不过,对于mixin对象的语法可能是什么样子,我还是很灵活的。

在ECMAScript 6中,我会使用符号来存储内部数据,并将公共属性定义为使用该数据的访问器:

var Transformable = (function() {
  var defaultOffset = [0, 0],
      offset = Symbol('offset');
  return {
    get offset() {
      if(!this[offset]) this[offset] = Object.create(defaultOffset);
      return this[offset];
    },
    setOffset: function ( x, y ) {
      this.offset[0] = x;
      this.offset[1] = y;
    }
  };
})();
为了复制描述符,您需要更复杂的
扩展

var extent = function(target, mixin) {
  Object.getOwnPropertyNames(mixin).forEach(function(prop) {
    var desc = Object.getOwnPropertyDescriptor(mixin, prop);
    Object.defineProperty(target, prop, desc);
  });
};
extent(Point.prototype, Transformable);

这一点,你也可以考虑去掉<代码> SETOBSE/<代码>,并定义一个用于<代码>偏移的设置器,这样你就可以使用<代码> MyPooTiT1.Office=[5, 5 ] < /Cord>。

< P>我有一个工作解决方案,但我自己会认为这是有争议的。基本上每次混合时,我都会使用mixin类的一个新实例进行复制。所以我不能扩展我的Point类,而是在构造函数中进行mixin。(使用与问题中相同的范围函数。)

与我在问题中链接到的多重继承答案相比,我认为它更具可读性,因为我可以使用Point类的对象文字语法,并且只需声明一次从何处混合

然而,性能可能是一个问题,因为我并没有混入到类中,而是混入到每个实例中,尽管我不知道这有多严重


我欢迎任何关于为什么这可能是一个糟糕的解决方案的精确论据。

回答你的第二篇文章;不,这个解决方案没有什么不好的地方,除了Bergi已经提到的——基于函数的Mixin永远不应该被实例化,而是总是通过
call
apply
应用于对象。 性能不会成为问题。JS在处理对象、函数委托和处理闭包方面速度极快。 您的胜利在于关注点分离、代码重用、仍然坚持ES3语言核心(不需要第三方库),能够引入附加状态,同时控制如何公开或隐藏此类附加状态

原始帖子提供的重构示例:

var Transformable = function () {       // a function based mixin approach
    var offset = [0, 0];                // is library agnostic and already
                                        // at ES3 language core level enables
    this.setOffset = function (x, y) {  // composition as well as creation of
        offset[0] = x;                  // and/or passsing around additional
        offset[1] = y;                  // state.
    };
    this.getOffsetX = function () {
        return offset[0];
    };
    this.getOffsetY = function () {
        return offset[1];
    };
};

var Point = function (x, y) {

    this.x = x;
    this.y = y;
                                // applying the `Transformable` mixin
    Transformable.call(this);   // onto the new `Point` instance.
};
Point.prototype = {
    constructor: Point,
    print: function () {
        console.log(this.x + this.getOffsetX(), this.y + this.getOffsetY());
    }
};


var myPoint1 = new Point( 1, 2 );
myPoint1.setOffset( 5, 5 );

var myPoint2 = new Point( 1, 2 );

myPoint1.print(); // 6 7
myPoint2.print(); // 1 2
仍然以OP的示例为起点,下一种方法可能更简洁,因为
s原型
打印
方法不会对既不属于
构造函数
也不属于
原型
的方法进行任何假设

var Transformable = function () {
    var offset = [0, 0];

    this.setOffset = function (x, y) {
        offset[0] = x;
        offset[1] = y;
    };
    this.getOffsetX = function () {
        return offset[0];
    };
    this.getOffsetY = function () {
        return offset[1];
    };

    return this;
};


var Point = function (x, y) {

    this.x = x;
    this.y = y;

    return this;
};
Point.prototype.print = function () {

    console.log(this.x, this.y);
};


var TransformablePoint = function () {

    return Transformable.call(Point.apply(this, arguments));
};
TransformablePoint.prototype.print = function () {

    console.log(this.x + this.getOffsetX(), this.y + this.getOffsetY());
};


var myPoint1 = new TransformablePoint( 1, 2 );
myPoint1.setOffset( 5, 5 );

var myPoint2 = new TransformablePoint( 1, 2 );

myPoint1.print(); // 6 7
myPoint2.print(); // 1 2

需要实例初始化的mixin也需要在构造函数中混合。你可能想看看@Bergi谢谢,也许我应该从一开始就寻找decorator而不是mixin。同时,在构造函数中混合也是我一直认为的。顺便说一句,它是
extend
而不是
extent
:-)如果你的混合像类一样(构造函数),我宁愿使用
Transformable.call(this)
而不是
extend(this,new Transformable)
。但是,当将其设置为用作mixin而不是其自身的构造函数时,请使该函数可以被调用为可转换的(这一点)。在适用的情况下,您仍然应该在原型中添加和混合方法。就像一个普通的班级一样,你们的混音应该同时做到这两个方面。这就像我需要它一样有效。虽然我需要更多的时间来理解它,因为我还不习惯ECMAScript 6。我不太愿意采用这种方式,因为我还在一些配置上使用Chromium 37,因为它是Ubuntu Precise Pangolin中仍然受支持的版本,缺乏对Symbol的支持。你的第二种方式非常类似于标准Javascript继承设计模式。我很少使用它,因为对于每个函数,都必须从
Namespace.Class.prototype.functionName=
开始。我猜大多数代码都会发生在TransformablePoint中。关于第一种方法,我认为在
Point
中使用混合方法和属性并不坏,因为构造函数中声明了一些功能插入到这个类中。这比web上的大多数mixin设计模式要好得多,在web上,只有在两个对象都布置好之后,才能进行混合,事先访问所有mixin功能,没有任何后悔。这只是因为最初提供的示例代码看起来如此<代码>可转换点
不会从
继承任何内容。忽略整个原型链。相反,它直接使用基于函数的mixin组合,将
构造器升级为一个mixin,正如打算使用的
可转换的
一样。唯一发生的继承是为
print
(两次),而
Point
甚至不需要它。如果我知道你的架构应该是什么样子,我可以