2

我正在尝试按照本文Array中给出的想法和建议进行子类化。

的这个子类的一个重要目标Array,在这里我称之为ArrayBase,是它本身的子类化比它自己更直接Array。我发现实现这个目标非常困难。(但在我看来可能是这样,因为我是 JavaScript n00b!)

下面是一个实现,ArrayBase它基于前面引用的文章结尾处提出的想法,并带有我自己的一些增强功能。我还包括了 的实现ArrayBase.slice,因为它说明了方案1的一个问题。

function ArrayBase () {
  var arr = Array.prototype.constructor.apply(null, arguments);
  var ctor = arr.constructor = arguments.callee;
  arr.__proto__ = ctor.prototype;
  return arr;
}

ArrayBase.prototype = new Array;
ArrayBase.prototype.slice = function () {
  var ctor = this.constructor;
  return ctor.apply(null,
                    Array.prototype.slice.apply(this, arguments));
}

var a0 = new ArrayBase(0, 1, 2, 3);
var a1 = a0.slice(2);                 // [2, 3]
console.log(a1 instanceof ArrayBase); // true
console.log(a1 instanceof Array);     // true

到目前为止,一切都很好。当我现在尝试子类化时,就会出现问题ArrayBase。我发现做到这一点的唯一方法需要基本上复制整个ArrayBase构造函数(唯一的区别,非常轻微,发生在第一行)。继承下去,这太可怜了……

function MyArray () {
  var arr = ArrayBase.apply(this, arguments);
  var ctor = arr.constructor = arguments.callee;
  arr.__proto__ = ctor.prototype;
  return arr;
}
MyArray.prototype = new ArrayBase;

// behavior of MyArray

var a2 = new MyArray(1, 2, 3, 0);
var a3 = a2.slice(1);                 // [2, 3, 0]
console.log(a3 instanceof MyArray);   // true
console.log(a3 instanceof ArrayBase); // true
console.log(a3 instanceof Array);     // true
console.log(a3.join(':'));            // "2:3:0"
a3[5] = 1;
console.log(a3.length);               // 6
a3.length = 2;
console.log(a3.toString())            // "2,3"

我的问题:

怎样才能消除存在于ArrayBaseMyArray构造函数之间的重复,同时仍然保留行后行所说明的行为// behavior of MyArr?该方案在子类化时是否也有效MyArray

(我知道反对建立高大的继承塔的论点,但无论它们是否是好的设计,我都希望它们至少能够得到很好的实施。)


1如果继承自是Array我认为应该的那样,则没有必要实现ArrayBase.slice,但不幸的是,继承自的slice方法没有显示返回与 的对象相同类的对象的基本 OOP 礼貌。ArrayBaseArraythis

4

1 回答 1

2

在回答您的问题之前,对代码进行一些评论:-)

var arr = Array.prototype.constructor.apply(null, arguments);

因为Array.prototype.constructor === Array,不要重复这个。

var ctor = arr.constructor = …

没有理由在这里创建属性。如果constructor任何东西都需要该属性,则应该从该构造函数的.prototype对象继承。

arguments.callee;

不要使用已弃用arguments.callee!你知道它指向ArrayBase.

arr.__proto__ = ctor.prototype;

您可能知道这__proto__是非标准的(尤其是在 IE 中不起作用),但我们在这里需要它用于原型注入技术。不过,不要忘记这个事实!

ArrayBase.prototype = new Array;

不要用于new设置继承!您不想在这里调用初始化程序(即“构造函数”)。改为使用Object.create

现在,回到你的问题:

如何消除我ArrayBaseMyArray构造函数之间存在的重复?

其实你已经用过这个概念了。您的ArrayBase.prototype.slice实现适用于每个子类 - 再次构造实例this.constructor。您可以对构造函数使用相同的方法ArrayBase

function ArrayBase() {
  var arr = Array.apply(null, arguments);
  var ctor = this.constructor;
  arr.__proto__ = ctor.prototype;
  return arr;
}
/* irrelevant for the answer, but helpful:
ArrayBase.prototype = Object.create(Array.prototype, {
  constructor: {value: ArrayBase}
});
Object.keys(Array.prototype).forEach(function(k) {
  if (typeof Array.prototype[k] != "function") return;
  ArrayBase.prototype[k] = function() {
    var arr = Array.prototype[k].apply(this, arguments);
    if (Array.isArray(arr))
      arr.__proto__ = Object.getPrototypeOf(this);
    return arr;
  };
});
*/
function MyArray() {
  return ArrayBase.apply(this, arguments);
}
MyArray.prototype = Object.create(ArrayBase.prototype, {
  constructor: {value: MyArray}
});
于 2013-09-24T15:17:02.530 回答