Является ли это разумным способом "подкласса" массива javascript?
Я понимаю, что, строго говоря, это не подклассификация типа массива, но будет ли это работать так, как можно было бы ожидать, или я все еще буду сталкиваться с некоторыми проблемами с .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());
Ответы
Ответ 1
Я бы обернул массив в нужный векторный тип:
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, например, или с чем-то похожим, которые четко маркируют их вектор-вектор, а не обычные массивы, и вы делаете то же самое на векторных специальных функциях прототипа, тогда это должно быть довольно легко отслеживать.
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());
Ответ 2
EDIT - изначально я писал, что вы можете подклассифицировать Array так же, как любой другой объект, что было неправильно. Узнавайте что-то новое каждый день. Вот хорошая дискуссия
http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
В этом случае будет ли композиция работать лучше? т.е. просто создать векторный объект и подставить его под массив. Это, кажется, путь, на котором вы находитесь, вам просто нужно добавить прототип push и других методов.
Ответ 3
Еще один пример обертки. Имея некоторое удовольствие с .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( [] ) ); }());
Ответ 4
@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 -.
Метод обертки избегает настройки наследования или эмулирования отношения длины/индексов. Вместо этого функция factory -like может создать простой объект 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
Ответ 5
Существует способ, который выглядит и чувствует себя как прототипическое наследование, но он отличается только одним способом.
Сначала давайте рассмотрим один из стандартных способов реализации прототипического наследования в 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 в качестве шаблона для построения, а скорее экземпляр массива, поэтому прототип подкласса попадает на конец полностью загруженного объекта, передает тип утки массива, который затем копирует. Если вы все еще видите что-то странное здесь, и это вас беспокоит, я не уверен, что я могу объяснить это лучше - так, может быть, как это работает, хорошая тема для другого вопроса.:)
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, но это может быть еще один год. В то же время я надеюсь, что это поможет кому-то.
Ответ 6
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"]);
Ответ 7
Здесь есть много хороших способов. Лично я считаю, что лучший способ подкласса "Массив" не является подклассом фактического массива, а скорее подклассом "Array-Like-Object". Их много, лично я использую Collection.
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();
Ответ 8
В настоящее время вы можете использовать подклассы с классами 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())