Способы расширения объекта Array в javascript

я пытаюсь расширить объект Array в javascript с помощью некоторых удобных для пользователя методов, таких как Array.Add() вместо Array.push() и т. д.

Я реализую 3 способа сделать это. к сожалению, 3-й способ не работает, и я хочу спросить, почему? и как это сделать.

//------------- 1st way
Array.prototype.Add=function(element){
     this.push(element);
};

var list1 = new Array();
list1.Add("Hello world");
alert(list1[0]);

//------------- 2nd way
function Array2 () {
    //some other properties and methods
};

Array2.prototype = new Array;
Array2.prototype.Add = function(element){
  this.push(element);  
};

var list2 = new Array2;
list2.Add(123);
alert(list2[0]);

//------------- 3rd way
function Array3 () {
    this.prototype = new Array;
    this.Add = function(element){
      this.push(element);  
    };
};

var list3 = new Array3;
list3.Add(456);  //push is not a function
alert(list3[0]); // undefined

третьим способом я хочу расширить объект Array внутри класса Array3. Как это сделать, чтобы не получить "push не является функцией" и "undefined"?

Здесь я добавляю 4-й способ.

//------------- 4th way
function Array4 () {
    //some other properties and methods
    this.Add = function(element){
        this.push(element);
    };
 };
Array4.prototype = new Array();

var list4 = new Array4();
list4.Add(789);
alert(list4[0]);

Здесь снова я должен использовать прототип. Я надеялся избежать использования лишних строк вне конструктора класса как Array4.prototype. Я хотел иметь компактный определенный класс со всеми частями в одном месте. Но я думаю, что я не могу поступить иначе.


person demosthenes    schedule 05.07.2012    source источник
comment
Если вы добавите метод в массив, вы сломаете foreach() для массивов.   -  person SpacedMonkey    schedule 05.07.2012
comment
Вы смотрели сценарий кофе? Я обновлю свой ответ примером   -  person SMathew    schedule 06.07.2012
comment
я не буду добавлять метод в Array.prototype, как в 1-м примере. Это был тест. Я создам класс, который будет расширять объект Array. Например jsArray. Объекты jsArray будут массивами, но с большим количеством функций.   -  person demosthenes    schedule 06.07.2012
comment
Я видел сегодня сценарий кофе. мне не понравился его синтаксис.   -  person demosthenes    schedule 06.07.2012
comment
@SpacedMonkey, если кто-то использует мою пользовательскую библиотеку js, может настроить свой foreach() так, чтобы он не включал последние 2 элемента перечисления, потому что это тип объекта и его длина.   -  person demosthenes    schedule 06.07.2012
comment
Во всех ваших примерах вы расширяете Array до нового типа (например, Array3). Есть ли причины не просто расширять массив, как в первом примере OP? Array.prototype.add = Array.prototype.push;   -  person trusktr    schedule 27.03.2014


Ответы (8)


Имена методов должны быть строчными. Прототип не должен изменяться в конструкторе.

function Array3() { };
Array3.prototype = new Array;
Array3.prototype.add = Array3.prototype.push

в кофескрипте

class Array3 extends Array
   add: (item)->
     @push(item) 

Если вам не нравится этот синтаксис, и вы ДОЛЖНЫ расширить его из конструктора, ваш единственный вариант:

// define this once somewhere
// you can also change this to accept multiple arguments 
function extend(x, y){
    for(var key in y) {
        if (y.hasOwnProperty(key)) {
            x[key] = y[key];
        }
    }
    return x;
}


function Array3() { 
   extend(this, Array.prototype);
   extend(this, {
      Add: function(item) {
        return this.push(item)
      }

   });
};

Вы также можете сделать это

ArrayExtenstions = {
   Add: function() {

   }
}
extend(ArrayExtenstions, Array.prototype);



function Array3() { }
Array3.prototype = ArrayExtenstions;

Раньше в prototype.js использовался метод Class.create. Вы можете обернуть все это таким методом

var Array3 = Class.create(Array, {
    construct: function() {

    },    
    Add: function() {

    }
});

Для получения дополнительной информации об этом и о том, как реализовать, посмотрите исходный код прототипа.js

person SMathew    schedule 05.07.2012
comment
да это 2 способ. Я использую в целях Добавить вместо добавления, потому что я намерен написать некоторые методы расширения, такие как визуальный базовый внешний вид. - person demosthenes; 05.07.2012
comment
хорошо, я не могу использовать прототип внутри конструктора. Есть ли способ расширить объект Array внутри конструктора? - person demosthenes; 05.07.2012
comment
Да, но это дорого, если вам нужно создать множество объектов. См. api.jquery.com/jQuery.extend для примера реализации. Используя эту технику, вы можете. function Array3 () {расширить (это, { добавить: функция (элемент) { вернуть это. нажать (элемент) } }) }; - person SMathew; 05.07.2012
comment
О, вы также можете использовать «Object.defineProperty» для настройки геттеров и сеттеров. developer.mozilla.org/en/JavaScript/Reference/Global_Objects/ - person SMathew; 05.07.2012
comment
его синтаксис является чем-то новым для меня. я чувствую javascript более знакомым - person demosthenes; 06.07.2012
comment
Почему в первом примере Array3.prototype = new Array;, а не new Array();? (со скобками и без). Спасибо. - person pilau; 24.05.2014

ES6

class SubArray extends Array {
    last() {
        return this[this.length - 1];
    }
}
var sub = new SubArray(1, 2, 3);
sub // [1, 2, 3]
sub instanceof SubArray; // true
sub instanceof Array; // true

Использование __proto__

(старый ответ, не рекомендуется, может вызвать проблемы с производительностью)

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

Теперь вы можете добавлять свои методы в SubArray

SubArray.prototype.last = function() {
  return this[this.length - 1];
};

Инициализировать как обычные массивы

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

ведет себя как обычные массивы

sub instanceof SubArray; // true
sub instanceof Array; // true
person laggingreflex    schedule 22.05.2015
comment
Хотя использование __proto__ поддерживается в большинстве реализаций JavaScript, оно не было стандартной функцией, пока не было помечено как устаревшая функция в es6. Также это может вызвать проблемы с производительностью при интенсивном использовании. Использование его, как правило, плохая идея. - person Insomniac; 13.02.2016
comment
@TBotV63 согласился. в es6 вы можете просто расширить класс Array. обновил мой ответ - person laggingreflex; 14.02.2016
comment
Вы должны передать значения из constructor в super, например: constructor(...items) {super(...items) } - person Kevin Beal; 15.10.2016
comment
добавили дополнительное свойство в конструктор this.weight, теперь он итерируется с помощью for-in, как сделать его неитерируемым? - person shivshankar; 07.08.2019
comment
@shivshankar Вы уверены, что хотите использовать for-in, а не for-of? Первый будет рассматривать его как объект, последний как массив (/ iterable). - person laggingreflex; 07.08.2019
comment
Это не проблема, я просто не хочу повторять новую опору. - person shivshankar; 07.08.2019

Недавно я прочитал книгу Javascript Ninja, написанную Джоном Резигом, создателем jQuery. Он предложил способ имитировать методы, подобные массивам, с помощью простого объекта JS. По сути, требуется только length.

var obj = {
    length: 0, //only length is required to mimic an Array
    add: function(elem){
        Array.prototype.push.call(this, elem);
    },
    filter: function(callback) {
        return Array.prototype.filter.call(this, callback); //or provide your own implemetation
    }
};

obj.add('a');
obj.add('b');
console.log(obj.length); //2
console.log(obj[0], obj[1]); //'a', 'b'

Я не имею в виду, хорошо это или плохо. Это оригинальный способ выполнения Array операций. Преимущество в том, что вы не расширяете Array prototype. Имейте в виду, что obj — это просто object, а не Array. Поэтому obj instanceof Array вернет false. Воспринимайте obj как фасад.

Если вам интересен этот код, прочитайте отрывок Листинг 4.10. Моделирование методов, подобных массиву.

person roland    schedule 12.04.2016

В третьем примере вы просто создаете новое свойство с именем prototype для объекта Array3. Когда вы делаете new Array3, который должен быть new Array3(), вы создаете экземпляр этого объекта в переменной list3. Следовательно, метод Add не будет работать, поскольку this, который является рассматриваемым объектом, не имеет допустимого метода push. Надеюсь, вы понимаете.

Изменить: ознакомьтесь с Понимание контекста JavaScript чтобы узнать больше о this.

person elclanrs    schedule 05.07.2012
comment
я вижу, this.prototype рассматривается как свойство Array3, спасибо - person demosthenes; 05.07.2012
comment
Также не забудьте добавить () при создании объектов. Array также является объектом, поэтому new Array() (или обычно просто []). Это работает, потому что браузеры умны. - person elclanrs; 05.07.2012

Вы пытаетесь сделать что-то более сложное, чем просто добавить псевдоним для «push» под названием «Add»?

Если нет, то, вероятно, лучше этого не делать. Причина, по которой я полагаю, что это плохая идея, заключается в том, что, поскольку Array является встроенным типом javascript, его изменение приведет к тому, что все сценарии типа Array будут иметь ваш новый метод «Добавить». Потенциал конфликтов имен с другим третьим лицом высок и может привести к тому, что сторонний скрипт потеряет свой метод в пользу вашего.

Мое общее правило состоит в том, чтобы сделать вспомогательную функцию для работы с массивом, если она еще где-то не существует, и только расширять массив, если это крайне необходимо.

person duyker    schedule 05.07.2012
comment
пока я просто узнаю кое-что новое о js. Я хочу написать несколько методов расширения для js, чтобы они выглядели как основные функции Visual Basic. Это мне больше подходит и проще в написании веб-проектов. - person demosthenes; 05.07.2012
comment
Звучит как отличное упражнение! Как раз то, что вам нужно узнать больше JS. - person elclanrs; 05.07.2012
comment
классно, удачи с ним. Я просто не хотел, чтобы вы столкнулись с распространенной ошибкой, которую люди совершают, расширяя встроенный тип и уничтожая стороннее расширение, и тратя часы, пытаясь понять, почему библиотека не работает (что я делал раньше: S) - person duyker; 05.07.2012
comment
нет, я не буду использовать какой-либо другой сторонний API. Оно будет независимым. - person demosthenes; 05.07.2012

Вы НЕ МОЖЕТЕ расширить объект Array в JavaScript.

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

Пример:

function MyArray() {
  var tmp_array = Object.create(Array.prototype);
  tmp_array = (Array.apply(tmp_array, arguments) || tmp_array);
  //Now extend tmp_array
  for( var meth in MyArray.prototype )
    if(MyArray.prototype.hasOwnProperty(meth))
      tmp_array[meth] = MyArray.prototype[meth];
  return (tmp_array);
}
//Now define the prototype chain.
MyArray.prototype = {
  customFunction: function() { return "blah blah"; },
  customMetaData: "Blah Blah",
}

Просто пример кода, вы можете изменить его и использовать по своему усмотрению. Но основная концепция, которой я рекомендую вам следовать, остается прежней.

person Boopathi Rajaa    schedule 07.05.2013
comment
Я считаю плохой практикой назначать prototype. Всегда лучше назначать свойства существующего объекта prototype. - person jchook; 09.06.2014
comment
Вы говорите, что не можете расширить объект Array, но признаете, что добавление функций в Array.prototype возможно... ваш ответ кажется мне противоречивым - person Purefan; 19.02.2015
comment
@Purefan - это может быть противоречивым, но это помогает понять, что делает (и не делает) Javascript, что нелогично для людей, не связанных с миром JS. - person Katinka Hesselink; 19.09.2017

Вы также можете использовать этот способ в ES6:

Object.assign(Array.prototype, {
    unique() {
      return this.filter((value, index, array) => {
        return array.indexOf(value) === index;
      });
    }
});

Результат:

let x = [0,1,2,3,2,3];
let y = x.unique();
console.log(y); // => [0,1,2,3]
person selahattinunlu    schedule 08.08.2016
comment
Это расширяет прототип массива и этого делать не следует (поскольку это влияет на весь сайт). - person fregante; 19.09.2017

var SubArray = function() {                                           
    var arrInst = new Array(...arguments); // spread arguments object
    /* Object.getPrototypeOf(arrInst) === Array.prototype */
    Object.setPrototypeOf(arrInst, SubArray.prototype);     //redirectionA
    return arrInst; // now instanceof SubArray
};

SubArray.prototype = {
    // SubArray.prototype.constructor = SubArray;
    constructor: SubArray,

    // methods avilable for all instances of SubArray
    add: function(element){return this.push(element);},
    ...
};

Object.setPrototypeOf(SubArray.prototype, Array.prototype); //redirectionB

var subArr = new SubArray(1, 2);
subArr.add(3); subArr[2]; // 3

Ответ — это компактный обходной путь, который работает, как задумано, во всех поддерживаемых браузерах.

person Neni    schedule 28.05.2017