Подтвердить что ты не робот

Javascript: stringify object (включая элементы функции типа)

Я ищу решение для сериализации (и unserialize) объектов Javascript для строки в браузерах, включая членов объекта, которые являются функциями. Типичный объект будет выглядеть следующим образом:

{
   color: 'red',
   doSomething: function (arg) {
        alert('Do someting called with ' + arg);
   }
}

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

JSON.stringify() будет игнорировать элемент 'doSomething', потому что это функция. Я знал, что метод toSource() будет делать то, что я хочу, но он специфичен для FF.

4b9b3361

Ответ 1

Быстрый и грязный способ будет таким:

Object.prototype.toJSON = function() {
  var sobj = {}, i;
  for (i in this) 
    if (this.hasOwnProperty(i))
      sobj[i] = typeof this[i] == 'function' ?
        this[i].toString() : this[i];

 return sobj;

};

Очевидно, это повлияет на сериализацию каждого объекта в вашем коде и может отключить код niave, используя нефильтрованные циклы for in. "Правильный" способ состоял бы в том, чтобы написать рекурсивную функцию, которая добавила бы функцию toJSON ко всем дочерним элементам любого данного объекта, касающимся круговых ссылок и тому подобного. Однако, принимая однопоточный Javascript (нет веб-рабочих), этот метод должен работать и не создавать никаких непредвиденных побочных эффектов.

Аналогичная функция должна быть добавлена ​​в прототип Array для переопределения объекта Object, возвращая массив, а не объект. Другим вариантом будет присоединение одного и пусть оно выборочно возвращает массив или объект в зависимости от собственной природы объектов, но, вероятно, будет медленнее.

function JSONstringifyWithFuncs(obj) {
  Object.prototype.toJSON = function() {
    var sobj = {}, i;
    for (i in this) 
      if (this.hasOwnProperty(i))
        sobj[i] = typeof this[i] == 'function' ?
          this[i].toString() : this[i];

    return sobj;
  };
  Array.prototype.toJSON = function() {
      var sarr = [], i;
      for (i = 0 ; i < this.length; i++) 
          sarr.push(typeof this[i] == 'function' ? this[i].toString() : this[i]);

      return sarr;
  };

  var str = JSON.stringify(obj);

  delete Object.prototype.toJSON;
  delete Array.prototype.toJSON;

  return str;
}

http://jsbin.com/yerumateno/2/edit

Ответ 2

Вы можете использовать JSON.stringify с помощью replacer как:

JSON.stringify({
   color: 'red',
   doSomething: function (arg) {
        alert('Do someting called with ' + arg);
   }
}, function(key, val) {
        return (typeof val === 'function') ? '' + val : val;
});

Ответ 3

Это невозможно без помощи самого объекта. Например, как бы вы сериализовали результат этого выражения?

(function () {
  var x; 
  return {
      get: function () { return x; },
      set: function (y) { return x = y; }
    };
})();

Если вы просто возьмете текст функции, то при десериализации x будет ссылаться на глобальную переменную, а не на закрытие. Кроме того, если ваша функция закрывается над состоянием браузера (например, ссылкой на div), вам придется подумать о том, что вы хотите, чтобы это означало.

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

Ответ 4

Что-то вроде этого...

(function(o) {
    var s = "";
    for (var x in o) {
        s += x + ": " + o[x] + "\n";
    }
    return s;
})(obj)

Примечание: это выражение. Он возвращает строковое представление объекта, переданного в качестве аргумента (в моем примере я передаю переменную с именем obj).

Вы также можете переопределить метод toString прототипа Object:

Object.prototype.toString = function() {
    // define what string you want to return when toString is called on objects
}