Массовое объявление 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') }
Там есть пара проблем:
$('foo')
ищет элементы с тегомfoo
- здесь нетfoo
HTML-тег Возможно, вы имели в виду#foo
или же.foo
(или возможноfoo
был предназначен для заменыdiv
или жеspan
или что угодно).Ваш
foo
значение свойства уже является объектом jQuery, нет необходимости переносить его в другое$()
вызов.this
не имеет особого значения в инициализаторе объекта, это так же, какthis
вне инициализатора объекта, а не ссылки на инициализируемый объект. Предложение Малка также не будет работать, потому что на момент инициализации свойств,myObject
ему еще не было присвоено значение. Вот как оценивается ваше общее выражение выше:Перед любым пошаговым кодом в области видимости, переменная
myObject
создается со значениемundefined
,Когда выражение достигается при пошаговом выполнении кода, запустите выражение присваивания, оценивая правую часть (бит после
=
).Чтобы запустить выражение инициализатора объекта, новый объект создается, но нигде не сохраняется.
Каждый инициализатор свойства в инициализаторе объекта обрабатывается в порядке исходного кода (да, это гарантируется спецификацией), например так:
Оцените выражение, определяющее его свойство.
Создайте свойство объекта, используя заданный ключ и оцененное значение.
Когда инициализатор объекта завершен, результирующее значение (ссылка на объект) присваивается
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;
})();