2013-04-11 6 views
3

В JavaScript я хочу сделать следующее:значения по умолчанию для вложенных Javascript хэшей

var pi = {}; pi[0]['*']['*'] = 1;

, конечно, это бросает «Не удается прочитать свойство„*“неопределенной» ошибка. Ясно, что я могу определить p [0] = {}, но это какая-то боль, поскольку я буду придерживаться множества разных значений в разных атрибутах, например.

pi[2]['O']['I-GENE'] = 1;

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

default array values

, но это не значит, что мне нужна инициализация других хэшей по умолчанию.

Это действительно кажется, что я пытаюсь сделать, это работает против спецификации ECMAScript, которая указывает, что неопределенные атрибуты объекта (который является хэш в JavaScript) должен возвращать неопределенными, как упомянуто здесь:

Set default value of javascript object attributes

который интересно включает в себя опасную работу.

В других местах, где я пытаюсь использовать вложенные хэши, как это я окажусь писать длинные биты некрасивого кода, как это:

function incrementThreeGramCount(three_grams,category_minus_two,category_minus_one,category){ 
    if(three_grams[category_minus_two] === undefined){ 
     three_grams[category_minus_two] = {}; 
    } 
    if(three_grams[category_minus_two][category_minus_one] === undefined){ 
     three_grams[category_minus_two][category_minus_one] = {}; 
    } 
    if(three_grams[category_minus_two][category_minus_one][category] === undefined){ 
     three_grams[category_minus_two][category_minus_one][category] = 0; 
    } 
    three_grams[category_minus_two][category_minus_one][category]++; 
} 

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

Возможно, мне стоит написать свой собственный Hash класс ... или с помощью Прототипы:

http://prototypejs.org/doc/latest/language/Hash/

или чужого:

http://www.daveperrett.com/articles/2007/07/25/javascript-hash-class/

или MooTools:

http://mootools.net/docs/more/Types/Hash

Argh, так много вариантов - хотелось бы знать, лучшие практики здесь ...

+0

Хей - это напоминает мне о спорном PR на MooTools в прошлом году: https://github.com/mootools/mootools-core/pull/2191 - Object.get/set methods (not .prototype), который сделал это автоматически. Не приземлился, и Даниэль прекратил вносить свой вклад, поскольку: D - его реализация была вполне разумной - https://github.com/csuwldcat/mootools-core/blob/master/Source/Types/Object.js#L23-L43 - может легко работать w/o mootools –

ответ

1

Я хотел бы сделать что-то вроде этого:

Object.prototype.get = function(v){ 
    if(!this[v]){ 
     this[v] = {} 
    } 

    return this[v]; 
} 

, а затем, вместо того, чтобы object["x"]["y"].z = 666 использование object.get("x").get("y").z = 666 ,

+0

это выглядит классно, но разве у него нет опасных побочных эффектов для других библиотек, которые могут полагаться на поведение по умолчанию, которое возвращает «undefined»? Мне бы очень понравился этот мод, но он ограничен локальным кодом, поэтому класс Hash кажется более безопасным, нет? –

+0

ну, есть шанс, что некоторые lib также определяют метод 'get', поэтому попробуйте использовать более уникальное имя, например' get_or_initialize' ... или рассмотрите меньшее подробное имя, например '_' :) – IProblemFactory

+0

ah получил вас - извините, я пропустил, что вы рекомендовали изменить синтаксис на .get («x») - да, я хотел бы придерживаться ['x'] настолько менее подробного - я думаю, что ._ ('x') тоже неплохо. И любой пользовательский класс Hash Class I тоже должен будет изменить синтаксис - я думаю, я не могу легко переопределить оператор [], например Object.prototype. [] = Function (v) {... –

2

Это можно сделать, используя ES6 proxies. Вы определяете прокси-сервер для объекта с помощью обработчика get. Когда get выполняется с ключом с значением undefined или для которого объект не имеет собственного свойства, вы устанавливаете его на новый прокси-сервер, который использует тот же обработчик get и возвращает этот новый прокси.

Кроме того, это будет работать без необходимости синтаксисе скобки:

var obj = ...; 
obj.a.b.c = 3; 

К сожалению, будучи функцией ES6, их поддержка ограничена Firefox, и они могут быть включены в Chrome с экспериментальным флагом.

+1

К сожалению, прокси-объекты в настоящее время поддерживаются только firefox. Возможно, вы можете добавить это как примечание. – C5H8NNaO4

+0

Они поддерживаются в Chrome stable с флагом «Включить экспериментальный JavaScript», поскольку они были в V8 в течение очень долгого времени. Я уверен, что когда спецификация будет завершена, они будут включены в следующий стабильный толчок. –

+0

Ницца, Thx Я не знал, что хром поддерживает прокси-серверы как экспериментальные +1 – C5H8NNaO4

0

Вы можете написать простой помощник, чтобы сделать это. например

function setWDef() { 
    var args = [].slice.call(arguments); 
    var obj = args.shift(); 
    var _obj = obj; 
    var val = args.pop(); 
    if (typeof obj !== "object") throw new TypeError("Expected first argument to be of type object"); 
    for (var i = 0, j = args.length - 1; i < j; i++) { 
     var curr = args[i]; 
     if (!_obj[curr]) _obj[curr] = {}; 
     _obj = _obj[curr]; 
    } 
    _obj[args.pop()] = val; 
    return obj; 
} 

Теперь просто установите значение с помощью функции

var pi ={} 
console.log (setWDef(pi,"0","*","a*",1)) //{"0": {"*": {"a*": 1}}} 

Heres деморолик на JSBin