Неожиданное поведение JavaScript, связанное со словарями на Кордове [дубликат]

Вы можете использовать Reflection для этого: (из моей библиотеки - это получает имена и значения)

public static Dictionary DictionaryFromType(object atype)
{
    if (atype == null) return new Dictionary();
    Type t = atype.GetType();
    PropertyInfo[] props = t.GetProperties();
    Dictionary dict = new Dictionary();
    foreach (PropertyInfo prp in props)
    {
        object value = prp.GetValue(atype, new object[]{});
        dict.Add(prp.Name, value);
    }
    return dict;
}

Эта вещь не будет работать для свойств с индексом - для этого (она становится громоздкой ):

public static Dictionary DictionaryFromType(object atype, 
     Dictionary indexers)
{
    /* replace GetValue() call above with: */
    object value = prp.GetValue(atype, ((indexers.ContainsKey(prp.Name)?indexers[prp.Name]:new string[]{});
}

Кроме того, чтобы получить только общедоступные свойства: ( см. MSDN в BindingFlags enum )

/* replace */
PropertyInfo[] props = t.GetProperties();
/* with */
PropertyInfo[] props = t.GetProperties(BindingFlags.Public)

Это работает с анонимными типами, слишком! Чтобы просто получить имена:

public static string[] PropertiesFromType(object atype)
{
    if (atype == null) return new string[] {};
    Type t = atype.GetType();
    PropertyInfo[] props = t.GetProperties();
    List propNames = new List();
    foreach (PropertyInfo prp in props)
    {
        propNames.Add(prp.Name);
    }
    return propNames.ToArray();
}

И это примерно то же самое только для значений, или вы можете использовать:

GetDictionaryFromType().Keys
// or
GetDictionaryFromType().Values

Но это немного медленнее, я бы представьте себе.

89
задан georgeawg 1 June 2018 в 14:06
поделиться

6 ответов

Спасибо за комментарий, tec. Мне удалось найти существующую неподтвержденную ошибку Webkit, которая объясняет эту проблему: https://bugs.webkit.org/show_bug.cgi?id=35801 (EDIT: теперь исправлено!) [/ ​​G1]

Похоже, что есть некоторые дебаты относительно того, какая часть ошибок и насколько она исправлена. Мне кажется, что это плохое поведение. Это особенно беспокоило меня, потому что, по крайней мере, в Chrome это происходит, когда код находится в сценариях, которые выполняются немедленно (до загрузки страницы), даже когда консоль открыта, всякий раз, когда страница обновляется. Вызов console.log, когда консоль еще не активна, приводит только к ссылке на объект, находящийся в очереди, а не на вывод, который будет содержать консоль. Поэтому массив (или любой объект) не будет оцениваться до тех пор, пока консоль не будет готова. Это действительно случай ленивой оценки.

Однако есть простой способ избежать этого в вашем коде:

var s = ["hi"];
console.log(s.toString());
s[0] = "bye";
console.log(s.toString());

Вызывая toString, вы создаете представление в памяти которые не будут изменены следующими операциями, которые консоль будет читать, когда она будет готова. Вывод консоли немного отличается от непосредственного передачи объекта, но он кажется приемлемым:

hi
bye
49
ответ дан Eric Mickelsen 15 August 2018 в 16:10
поделиться
  • 1
    На самом деле, с ассоциативными массивами или другими объектами, это может быть реальной проблемой, поскольку toString не производит ничего ценного. Есть ли легкая обстановка для объектов вообще? – Eric Mickelsen 30 October 2010 в 20:00
  • 2
    JSON.stringify () – draeton 4 January 2011 в 04:29
  • 3
    @draeton: Круто. – Eric Mickelsen 4 January 2011 в 05:08
  • 4
    webkit посадил патч для этого несколько месяцев назад – antony.trupe 9 October 2012 в 05:01
  • 5
    сделайте следующее: console.log (JSON.parse (JSON.stringify (s)); – Lee Comstock 11 April 2018 в 09:58

Вы можете клонировать массив с помощью Array#slice:

console.log(s); // ["bye"], i.e. incorrect
console.log(s.slice()); // ["hi"], i.e. correct

Функция, которую вы можете использовать вместо console.log, которая не имеет этой проблемы, выглядит следующим образом:

console.logShallowCopy = function () {
    function slicedIfArray(arg) {
        return Array.isArray(arg) ? arg.slice() : arg;
    }

    var argsSnapshot = Array.prototype.map.call(arguments, slicedIfArray);
    return console.log.apply(console, argsSnapshot);
};

В случае объектов, к сожалению, лучшим методом является отладка сначала с помощью браузера, отличного от WebKit, или для записи сложной функции для клонирования. Если вы работаете только с простыми объектами, где порядок ключей не имеет значения, а функций нет, вы всегда можете:

console.logSanitizedCopy = function () {
    var args = Array.prototype.slice.call(arguments);
    var sanitizedArgs = JSON.parse(JSON.stringify(args));

    return console.log.apply(console, sanitizedArgs);
};

Все эти методы, очевидно, очень медленные, поэтому еще больше так как с нормальным console.log s, вы должны отключить их после завершения отладки.

5
ответ дан Domenic 15 August 2018 в 16:10
поделиться

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

console.log(JSON.stringify(the_array));
2
ответ дан justinsAccount 15 August 2018 в 16:10
поделиться
  • 1
    Может подтвердить. Это буквально самое худшее при попытке выйти из ReactSyntheticEvents. Даже JSON.parse(JSON.stringify(event)) не получает правильной глубины / точности. Заявления отладчика - единственное реальное решение, которое я нашел, чтобы получить правильное представление. – CStumph 27 July 2015 в 23:35

Похоже, что Chrome на фазе «pre compile» заменяет любой экземпляр «s» указателем на фактический массив.

. Один из способов - клонирование массива, запись вместо него следующей копии:

var s = ["hi"];
console.log(CloneArray(s));
s[0] = "bye";
console.log(CloneArray(s));

function CloneArray(array)
{
    var clone = new Array();
    for (var i = 0; i < array.length; i++)
        clone[clone.length] = array[i];
    return clone;
}
0
ответ дан Shadow Wizard 15 August 2018 в 16:10
поделиться
  • 1
    Это хорошо, но поскольку это мелкая копия, все еще есть возможность более тонкой проблемы. А как насчет объектов, которые не являются массивами? (Это настоящая проблема сейчас.) Я не думаю, что то, что вы говорите «pre compile», является точной. Кроме того, в коде есть ошибка: clone [clone.length] должен быть клонирован [i]. – Eric Mickelsen 31 October 2010 в 17:54
  • 2
    Нет ошибки, я выполнил ее, и все было в порядке. clone [clone.length] точно так же, как и clone [i], поскольку массив начинается с длины 0, а также итератор цикла i. Во всяком случае, не уверен, как он будет вести себя со сложными объектами, но IMO стоит попробовать. Как я уже сказал, это не решение, это проблема вокруг проблемы. – Shadow Wizard 1 November 2010 в 11:17
  • 3
    @Shadow Wizard: Хорошая точка: clone.length всегда будет равна i. Он не будет работать для объектов. Возможно, существует решение с «для каждого». – Eric Mickelsen 1 November 2010 в 17:57
  • 4
    Объекты, которые вы имеете в виду? var s = {param1: "hi", param2: "как вы? & quot; }; если это так, я просто протестировал и когда у вас есть ["param1"] = "bye"; он работает нормально, как ожидалось. Можете ли вы, пожалуйста, разместить пример «он не будет работать для объектов» ?? Я посмотрю и попытаюсь подняться на этот. – Shadow Wizard 2 November 2010 в 11:27
  • 5
    @Shadow Wizard: Очевидно, что ваша функция не сможет клонировать свойства и не будет работать ни на каких объектах без свойства length. Ошибка webkit затрагивает все объекты, а не только массивы. – Eric Mickelsen 3 November 2010 в 15:19

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

Он реализует только методы log, warn и error, вам нужно будет добавить еще несколько, чтобы он был взаимозаменяемым с регулярным console.

var fixedConsole;
(function($) {
    var _freezeOne = function(arg) {
        if (typeof arg === 'object') {
            return $.extend(true, {}, arg);
        } else {
            return arg;
        }
    };
    var _freezeAll = function(args) {
        var frozen = [];
        for (var i=0; i<args.length; i++) {
            frozen.push(_freezeOne(args[i]));
        }
        return frozen;
    };
    fixedConsole = {
        log: function() { console.log.apply(console, _freezeAll(arguments)); },
        warn: function() { console.warn.apply(console, _freezeAll(arguments)); },
        error: function() { console.error.apply(console, _freezeAll(arguments)); }
    };
})(jQuery);
1
ответ дан wrygiel 15 August 2018 в 16:10
поделиться

Из объяснения Эрика это связано с тем, что console.log() находится в очереди, и он печатает более позднее значение массива (или объекта).

Могут быть 5 решений:

1. arr.toString()   // not well for [1,[2,3]] as it shows 1,2,3
2. arr.join()       // same as above
3. arr.slice(0)     // a new array is created, but if arr is [1, 2, arr2, 3] 
                    //   and arr2 changes, then later value might be shown
4. arr.concat()     // a new array is created, but same issue as slice(0)
5. JSON.stringify(arr)  // works well as it takes a snapshot of the whole array 
                        //   or object, and the format shows the exact structure
15
ответ дан 太極者無極而生 15 August 2018 в 16:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: