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

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

以下是ArrayBase的实现,该实现基于先前引用的文章towards the end提出的思想,并对我自己进行了一些增强。我还包括了ArrayBase.slice的实现,因为它说明了scheme1的一个问题。

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继承自ArrayBaseArray方法并不显示返回的基本OOP礼貌。与this属于同一类的对象。

最佳答案

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


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;



not use new for setting up inheritance!您不想在此处调用初始化程序(即“构造函数”)。请改用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}
});

关于javascript - 试图实现一个易于“继承”的Array子类,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18970703/

10-11 22:14