Это разумный способ «подкласса» массива javascript? - PullRequest
10 голосов
/ 21 января 2011

Я понимаю, что, строго говоря, это не подкласс типа массива, но будет ли это работать так, как можно было ожидать, или я все еще буду сталкиваться с некоторыми проблемами с .length и тому подобным? Есть ли какие-либо недостатки, которых бы у меня не было, если бы было возможно нормальное создание подклассов?

        function Vector()
        {
            var vector = [];
            vector.sum = function()
            {
                sum = 0.0;
                for(i = 0; i < this.length; i++)
                {
                    sum += this[i];
                }
                return sum;
            }            
            return vector;
        }

        v = Vector();
        v.push(1); v.push(2);
        console.log(v.sum());

Ответы [ 8 ]

7 голосов
/ 21 января 2011

Я бы обернул массив внутри правильного типа вектора следующим образом:

window.Vector = function Vector() {
  this.data = [];
}

Vector.prototype.push = function push() {
  Array.prototype.push.apply(this.data, arguments);
}

Vector.prototype.sum = function sum() {
  for(var i = 0, s=0.0, len=this.data.length; i < len; s += this.data[i++]);
  return s;
}

var vector1 = new Vector();
vector1.push(1); vector1.push(2);
console.log(vector1.sum());

В качестве альтернативы вы можете создавать новые функции-прототипы для массивов, а затем просто использовать обычные массивы.

Если вы согласны с именованием массивов, так что все они начинаются со строчной буквы v, например, или с чего-то подобного, что четко обозначает их вектор w, а не нормальные массивы, и вы делаете то же самое для функций прототипа, специфичных для вектора, тогда быть довольно легко отслеживать.

Array.prototype.vSum = function vSum() {
  for(var i = 0, s=0.0, len=this.length; i < len; s += this[i++]);
  return s;
}

var vector1 = [];
vector1.push(1); vector1.push(2);
console.log(vector1.vSum());
5 голосов
/ 21 января 2011

EDIT - я изначально писал, что вы можете создать подкласс Array, как и любой другой объект, что было неправильно. Узнавайте что-то новое каждый день. Вот хорошая дискуссия

http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/

В таком случае, будет ли композиция работать лучше? Т.е. просто создайте объект Vector и поддержите его массивом. Похоже, это путь, по которому вы идете, вам просто нужно добавить push и любые другие методы к прототипу.

2 голосов
/ 17 декабря 2013

Просто еще один пример обертки. Повеселиться с .bind.

var _Array = function _Array() {
    if ( !( this instanceof _Array ) ) {
        return new _Array();
    };
};

_Array.prototype.push = function() {
    var apContextBound = Array.prototype.push,
        pushItAgainst = Function.prototype.apply.bind( apContextBound );

    pushItAgainst( this, arguments );
};

_Array.prototype.pushPushItRealGood = function() {
    var apContextBound = Array.prototype.push,
        pushItAgainst = Function.prototype.apply.bind( apContextBound );

    pushItAgainst( this, arguments );
};

_Array.prototype.typeof = (function() { return ( Object.prototype.toString.call( [] ) ); }());
0 голосов
/ 07 апреля 2017

В настоящее время вы можете использовать подклассы с классами ES6:

class Vector extends Array {
  sum(){
    return this.reduce((total, value) => total + value)
  }
}

let v2 = new Vector();
v2.push(1);
v2.push(2);
console.log(v2.sum())
0 голосов
/ 11 июля 2016

Здесь есть много хороших способов.Лично я считаю, что лучший способ создать подкласс «Array» - это не подкласс реального массива, а подкласс «Array-Like-Object».Их там много, лично я пользуюсь Коллекция .

http://codepen.io/dustinpoissant/pen/AXbjxm?editors=0011

var MySubArray = function(){
  Collection.apply(this, arguments);
  this.myCustomMethod = function(){
    console.log("The second item is "+this[1]);
  };
};
MySubArray.prototype = Object.create(Collection.prototype);

var msa = new MySubArray("Hello", "World");
msa[2] = "Third Item";
console.log(msa);
msa.myCustomMethod();
0 голосов
/ 18 декабря 2015
function SubArray(arrayToInitWith){
  Array.call(this);
  var subArrayInstance = this;
  subArrayInstance.length = arrayToInitWith.length;
  arrayToInitWith.forEach(function(e, i){
    subArrayInstance[i] = e;
  });
}

SubArray.prototype = Object.create(Array.prototype);
SubArray.prototype.specialMethod = function(){alert("baz");};

var subclassedArray = new SubArray(["Some", "old", "values"]);
0 голосов
/ 08 ноября 2015

Существует способ, который выглядит и ощущается как прототипное наследование, но он отличается только одним способом.

Сначала давайте взглянем на один из стандартных способов реализации прототипного наследования.в javascript:

var MyClass = function(bar){
    this.foo = bar;
};

MyClass.prototype.awesomeMethod = function(){
    alert("I'm awesome")
};

// extends MyClass
var MySubClass = function(bar){
    MyClass.call(this, bar); // <- call super constructor
}

// which happens here
MySubClass.prototype = Object.create(MyClass.prototype);  // prototype object with MyClass as its prototype
// allows us to still walk up the prototype chain as expected
Object.defineProperty(MySubClass.prototype, "constructor", {
    enumerable: false,  // this is merely a preference, but worth considering, it won't affect the inheritance aspect
    value: MySubClass
});

// place extended/overridden methods here
MySubClass.prototype.superAwesomeMethod = function(){
    alert("I'm super awesome!");
};

var testInstance = new MySubClass("hello");
alert(testInstance instanceof MyClass); // true
alert(testInstance instanceof MySubClass); // true

Следующий пример просто оборачивает вышеупомянутую структуру, чтобы сохранить все в чистоте.И есть небольшой твик, который на первый взгляд кажется чудом.Однако все, что на самом деле происходит, - это то, что каждый экземпляр подкласса использует не прототип Array в качестве шаблона для построения, а скорее экземпляр Array - так что прототип подкласса подключается к концу полностью загруженного объекта, которыйпередает тип утки массива - который затем копирует.Если вы все еще видите здесь что-то странное и это беспокоит вас, я не уверен, что смогу объяснить это лучше - так что, возможно, как это работает, это хорошая тема для другого вопроса.:)

var extend = function(child, parent, optionalArgs){ //...
    if(parent.toString() === "function "+parent.name+"() { [native code] }"){
        optionalArgs = [parent].concat(Array.prototype.slice.call(arguments, 2));
        child.prototype = Object.create(new parent.bind.apply(null, optionalArgs));
    }else{
        child.prototype = Object.create(parent.prototype);
    }
    Object.defineProperties(child.prototype, {
        constructor: {enumerable: false, value: child},
        _super_: {enumerable: false, value: parent}  // merely for convenience (for future use), its not used here because our prototype is already constructed!
    });
};
var Vector = (function(){
    // we can extend Vector prototype here because functions are hoisted
    // so it keeps the extend declaration close to the class declaration
    // where we would expect to see it
    extend(Vector, Array);

    function Vector(){
        // from here on out we are an instance of Array as well as an instance of Vector

        // not needed here
        // this._super_.call(this, arguments);  // applies parent constructor (in this case Array, but we already did it during prototyping, so use this when extending your own classes)

        // construct a Vector as needed from arguments
        this.push.apply(this, arguments);
    }

    // just in case the prototype description warrants a closure
    (function(){
        var _Vector = this;

        _Vector.sum = function sum(){
            var i=0, s=0.0, l=this.length;
            while(i<l){
                s = s + this[i++];
            }
            return s;
        };
    }).call(Vector.prototype);

    return Vector;
})();

var a = new Vector(1,2,3);                         // 1,2,3
var b = new Vector(4,5,6,7);                       // 4,5,6,7
alert(a instanceof Array && a instanceof Vector);  // true
alert(a === b);                                    // false
alert(a.length);                                   // 3
alert(b.length);                                   // 4
alert(a.sum());                                    // 6
alert(b.sum());                                    // 22

Скоро у нас будет класс и возможность расширять нативные классы в ES6, но это может быть еще один год.В то же время я надеюсь, что это кому-то поможет.

0 голосов
/ 25 июля 2013

@ hvgotcodes ответ содержит потрясающую ссылку .Я просто хотел обобщить заключение здесь.

Упаковщики.Внедрение цепочки прототипов

Похоже, что это лучший метод для расширения массива из статьи.

оболочки могут быть использованы ... в которых цепочка прототипов объекта увеличивается,а не сам объект.

function SubArray() {
  var arr = [ ];
  arr.push.apply(arr, arguments);
  arr.__proto__ = SubArray.prototype;
  return arr;
}
SubArray.prototype = new Array;

// Add custom functions here to SubArray.prototype.
SubArray.prototype.last = function() {
  return this[this.length - 1];
};

var sub = new SubArray(1, 2, 3);

sub instanceof SubArray; // true
sub instanceof Array; // true

К сожалению, для меня этот метод использует arr.__proto__, неподдерживаемый в IE 8-, браузер, который я должен поддерживать.

Упаковщики.Прямое внедрение свойства.

Этот метод немного медленнее, чем выше, но работает в IE 8 -.

Подход обертки позволяет избежать настройки наследования или эмуляции длины / индексовсвязь.Вместо этого фабрично-подобная функция может создать простой объект Array, а затем напрямую дополнить его любыми пользовательскими методами.Поскольку возвращаемый объект является объектом Array, он поддерживает правильное отношение длины / индексов, а также [[Class]] для «Array».Естественно, он также наследуется от Array.prototype.

function makeSubArray() {
  var arr = [ ];
  arr.push.apply(arr, arguments);

  // Add custom functions here to arr.
  arr.last = function() {
    return this[this.length - 1];
  };
  return arr;
}

var sub = makeSubArray(1, 2, 3);
sub instanceof Array; // true

sub.length; // 3
sub.last(); // 3
...