search

Home  >  Q&A  >  body text

javascript - js中如何继承实现Array的子类

试想需要实现一个数组的子类ZeroArray,其构造函数接收一个长度参数n,自动初始化数组元素都为0。

我试图继承原生的Array类型,成员变量通过apply()方法窃取,成员方法则通过原型链引用。代码如下:

function ZeroArray(n) {
    // 构造函数窃取
    Array.apply(this);

    // 自动塞入0元素
    for (var i = 0; i < n; i++) {
        this.push(0);
    }
}

// 利用空函数作为过渡,ZeroArray原型的原型指向Array.prototype
// 既建立原型链,又不影响Array.prototype本身,而且防止Array构造函数重复调用两次
var F = function() {};
F.prototype = Array.prototype;
ZeroArray.prototype = new F();
ZeroArray.prototype.constructor = ZeroArray;

那么问题来了:

Object.getOwnPropertyDescriptor(new Array(), "length")
// 输出:Object {value: 0, writable: true, enumerable: false, configurable: false}
Object.getOwnPropertyDescriptor(new ZeroArray(3), "length")
// 输出:Object {value: 3, writable: true, enumerable: true, configurable: true}

为什么两者的length属性enumerable/configurable会不同?是不是因为Array本质上并不是Object?

迷茫迷茫2902 days ago608

reply all(2)I'll reply

  • PHP中文网

    PHP中文网2017-04-10 14:58:21

    单纯的解决方案只要脚痛医脚就好 Object.defineProperty(this, 'length', {enumerable: false, configurable: false, writable: true});

    具体原因我观察了一下Array的行为

    > Array.call({})
    []
    > Array.call(undefined)
    []
    

    瞧,this是啥完全不影响Array工作,Array这个constructor应该是类似

    function Array() {
      var array = [Native Code];
      return array;
    }
    

    的形式(并非修饰this的风格),所以代码里的Array.apply完全没有起到效果。

    祭出翻ECMA大法,很快就能找到标准里对应的行为

    15.4.1 The Array Constructor Called as a Function

    length的行为也有描述

    关于Array究竟是不是Object,答案毫无疑问是肯定的,JS中除了原始值(null/undefined/数字/布尔/字符串)之外一切皆对象


    在我看来,题主掉到“以其他OO语言经验看JS”的大坑里了,有大量的材料会把人往这个坑里带,他们标题往往叫“JS OOP指南/入门/精通”,“JS实现继承的X种方法”等等

    实际上,如果需求只是“接收一个长度参数n,自动初始化数组元素都为0”的话,更好的实现可能是

    function makeZeroArray(n) {
      var arr = [];
      while(n-- > 0) {
        arr[arr.length] = 0;
      }
    
      return arr;
    }
    

    继承在JS中并非一等居民,而是普通的一个模式而已,合适用就用,不合适没必要硬套

    补充一下,JS中没有类的概念,JS中没有类的概念,JS中没有类的概念。
    JS里,对象才是一等居民,我们写的是方便地创建某种对象的构造器,而不是类

    reply
    0
  • 阿神

    阿神2017-04-10 14:58:21

    你提了个有意思的问题,只指出一点,但解决不了你的问题。

    继承的话,下面这种写法更好。

    function ZeroArray(n) {
        // 自动塞入0元素
        for (var i = 0; i < n; i++) {
            this.valueOf().push(0);
        }
    }
    
    ZeroArray.prototype = [];
    
    //但enumerable和configurable依旧变了
    console.log(Object.getOwnPropertyDescriptor([1,2,3], "length"))
    console.log(Object.getOwnPropertyDescriptor(new ZeroArray(3), "length"))
    

    reply
    0
  • Cancelreply