Массовое объявление JavaScript-ассоциативного массива

Есть ли способ массового задания ключей и значений ассоциативного массива в JavaScript, аналогично краткому объявлению PHP-массива ниже?

$array = [
  'foo' => 'val1',
  'bar' => 'val2',
  'baz' => 'val3'
];

Единственный способ объявить ассоциативные массивы в JS - это примерно так:

var newArray = new Array();
newArray['foo'] = 'val1';
newArray['bar'] = 'val2';
newArray['baz'] = 'val3';

Или как то так:

var newArray = new Array(),
  keys = ['foo', 'bar', 'baz'],
  vals = ['val1', 'val2', 'val3'];

for(var i = 0; i < keys.length; i++) {
  newArray[ keys[i] ] = vals[i];
}

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

Если нет такого способа, как этот лучший пример, то это нормально. Это было бы хорошо. Я попытался использовать объектный литерал JS уже с jQuery следующим образом:

var myObject = {
  foo: $('foo'),
  bar: $(this.foo).find('bar'),
  baz: $(this.bar).data('baz')
}

К сожалению, кажется, что литералы объектов JS не позволяют оценивать код, чтобы установить значения для их свойств. Если бы я превратил эти свойства в методы, которые возвращали значения вместо постоянного хранения значений / ссылок в свойствах, то они должны были бы запускаться при каждом вызове; Я действительно не хочу делать это, если это возможно.

Заранее спасибо.

2 ответа

Решение

PHP "ассоциативные массивы" - упорядоченный набор пар имя / значение - довольно редкая структура данных, особенно редко встречающаяся как функция языка, а не как библиотечный класс. У JavaScript их нет - пока. ES6 представит лучшую семантику итерации для языка в целом и стандартную библиотеку Map тип, использующий преимущества этой семантики, так что при повторении он просматривает записи в порядке вставки ключей; подробнее ниже.

До ES6

До ES6 вы можете использовать объекты, как вы знаете, так:

var obj = {
  foo: 'val1',
  bar: 'val2',
  baz: 'val3'
};

... но свойства не имеют порядка. Если порядок важен, вам придется отслеживать порядок отдельно, возможно, имея ключи в массиве:

var keys = ['foo', 'bar', 'baz'];
var obj = {
  foo: 'val1',
  bar: 'val2',
  baz: 'val3'
};
var i;
for (i = 0; i < keys.length; ++i) {
    console.log(obj[keys[i]]);
}

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

Начиная с ES6

ES6-х Map определяет, что при повторении записи карты посещаются в порядке, в котором были вставлены ключи. Вы также можете инициализировать Map экземпляр с помощью массива key/value массивы, вот так:

var map = new Map([
    ["foo", "val1"],
    ["bar", "val2"],
    ["baz", "val3"]
]);

Так что это будет эквивалентная форма, когда она широко поддерживается.

Больше о Map в этой статьепроект спецификации).


К сожалению, кажется, что литералы объектов JS не позволяют оценивать код, чтобы установить значения для их свойств

Да, они делают. В вашем примере foo свойство будет объектом jQuery с набором элементов, соответствующих селектору CSS foo (набор, который, вероятно, пуст, так как нет foo элемент).

Пример:

var obj = {
    foo: 6 * 7
};
console.log(obj.foo); // 42

Вы не можете (пока) использовать выражение для имени свойства в инициализаторе свойства, как это, только значение. В ES6 вы также сможете использовать выражение для имени свойства:

// As of ES6
var obj = {
    ['f' + 'o' + 'o']: 6 * 7
};
console.log(obj.foo); // 42

Между тем, в ES5 мы можем это сделать, но не в инициализаторе:

// Now
var obj = {};
obj['f' + 'o' + 'o'] = 6 * 7;
console.log(obj.foo); // 42

Я попытался использовать объектный литерал JS уже с jQuery следующим образом:

var myObject = {
  foo: $('foo'),
  bar: $(this.foo).find('bar'),
  baz: $(this.bar).data('baz')
}

Там есть пара проблем:

  1. $('foo') ищет элементы с тегом foo - здесь нет foo HTML-тег Возможно, вы имели в виду #foo или же .foo (или возможно foo был предназначен для замены div или же span или что угодно).

  2. Ваш foo значение свойства уже является объектом jQuery, нет необходимости переносить его в другое $() вызов.

  3. this не имеет особого значения в инициализаторе объекта, это так же, как this вне инициализатора объекта, а не ссылки на инициализируемый объект. Предложение Малка также не будет работать, потому что на момент инициализации свойств, myObject ему еще не было присвоено значение. Вот как оценивается ваше общее выражение выше:

    1. Перед любым пошаговым кодом в области видимости, переменная myObject создается со значением undefined,

    2. Когда выражение достигается при пошаговом выполнении кода, запустите выражение присваивания, оценивая правую часть (бит после =).

    3. Чтобы запустить выражение инициализатора объекта, новый объект создается, но нигде не сохраняется.

    4. Каждый инициализатор свойства в инициализаторе объекта обрабатывается в порядке исходного кода (да, это гарантируется спецификацией), например так:

      1. Оцените выражение, определяющее его свойство.

      2. Создайте свойство объекта, используя заданный ключ и оцененное значение.

    5. Когда инициализатор объекта завершен, результирующее значение (ссылка на объект) присваивается myObject,

Чтобы обратиться к свойствам объекта из выражений, определяющих другие свойства объекта, вы должны сделать ряд операторов, а не один оператор:

var myObject = {
  foo: $('foo') // or $('#foo') or $('.foo') or whatever it is
};
myObject.bar = myObject.foo.find('bar');
myObject.baz = myObject.bar.data('baz');

С другой стороны, вы можете сделать что-то запутанное с временными переменными:

var foo, bar, myObject = {
  foo: (foo = $('foo')),
  bar: (bar = foo.find('bar')),
  baz: bar.data('baz')
};

... потому что результатом выражения присваивания является значение, которое было присвоено. (Не поддерживается постоянная связь между свойствами и переменными.) Но тогда у вас есть эти временные переменные. Вы можете обернуть все это в функцию, чтобы временные переменные были только временно:

var myObject = (function() {
  var foo, bar;
  return {
    foo: (foo = $('foo')),
    bar: (bar = foo.find('bar')),
    baz: bar.data('baz')
  };
})();

... но это становится довольно запутанным.:-)


Re ваш комментарий ниже:

Существуют ли ограничения на то, какие выражения могут быть оценены в значениях свойств?

Нет, обработка выражений для значений инициализаторов свойств точно такая же, как и обработка выражений в других областях области, содержащей инициализатор объекта, частью которого является инициализатор свойства.

Вот пример получения некоторых элементов DOM через jQuery:

var myObject = {
  foo: $('.foo')
};
myObject.bar = myObject.foo.find('.bar');
myObject.baz = myObject.bar.data('baz');

myObject.foo.css("color", "green");
myObject.bar.css("color", "blue");
$("<div>").html(myObject.baz).appendTo(document.body); // "I'm the data"
<div class="foo">
  This is a .foo
  <div class="bar" data-baz="I'm the data">
    This is a .bar inside the .foo
  </div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

Примечание: я бы не стал использовать .data если все, что вы делаете, это доступ data-* атрибуты; Я бы использовал attr вместо. data устанавливает кеш данных для объекта, инициализирует этот кеш со всех data-* Атрибуты объекта, а затем работает из кэша, а не атрибуты. Избыток, если вы просто читаете значение атрибута.

Или используя мою замысловатую функцию:

var myObject = (function() {
  var foo, bar;
  return {
    foo: (foo = $('.foo')),
    bar: (bar = foo.find('.bar')),
    baz: bar.data('baz')
  };
})();

myObject.foo.css("color", "green");
myObject.bar.css("color", "blue");
$("<div>").html(myObject.baz).appendTo(document.body); // "I'm the data"
<div class="foo">
  This is a .foo
  <div class="bar" data-baz="I'm the data">
    This is a .bar inside the .foo
  </div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

Если вы просто пытаетесь объявить объект Javascript с помощью пары пар свойство / значение, вы можете сделать это следующим образом:

var myData = {
    foo: 'val1',
    bar: 'val2',
    baz: 'val3'
};

К которому вы можете получить доступ как:

console.log(myData.foo);   // `val1`    
console.log(myData.bar);   // `val2`
console.log(myData.baz);   // `val3`

Обратите внимание, что Javascript не имеет типа данных, называемого "ассоциативный массив". Большинство разработчиков Javascript используют объект с именами свойств для решения подобных проблем, но объекты Javascript не поддерживают упорядоченный набор свойств - они неупорядочены.


Вы не можете сделать что-то вроде этого:

var myObject = {
  foo: $('foo'),
  bar: $(this.foo).find('bar'),
  baz: $(this.bar).data('baz')
}

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

Что может иметь смысл, это что-то вроде этого:

var myObject = {
    init: function() {
        this.foo = $('foo');
        this.bar = this.foo.find('bar');
        this.baz = this.foo.find('baz');
    }
}

// then, sometime when you know the DOM is ready
myObject.init();

Или, если вы хотите сделать это всего за один вызов функции во время готовности DOM, вы можете пропустить статическое объявление и просто сделать одну функцию, которую вы объявляете и выполняете, когда знаете, что DOM готов:

var myObject = (function() {
    var obj = {};
    obj.foo = $('foo');
    obj.bar = obj.foo.find('bar');
    obj.baz = obj.foo.find('baz');
    return obj;
})();
Другие вопросы по тегам