在Internet Explorer中实现Mozilla的toSource()方法

14

有人在Internet Explorer和其他非Gecko浏览器中实现了Mozilla的Object.toSource()方法吗?我正在寻找一种将简单对象序列化为字符串的轻量级方式。

8个回答

9
考虑以下内容:(在使用FireFox 3.6时)
javascript:
  x=function(){alert('caveat compter')};
  alert(['JSON:\t',JSON.stringify(x),'\n\ntoSource():\t',x.toSource()].join(''));

显示如下内容:

JSON:

toSource():(function () {alert("谨慎使用计算机");})

甚至包括:

javascript:
x=[];x[3]=x;
alert('toSource():\t'+x.toSource());
alert('JSON can not handle this at all and goes "infinite".');
alert('JSON:\n'+JSON.stringify(x));

显示如下:

toSource(): #1=[, , , #1#]

并且随之而来的是“进入无限循环”的信息,这是JSON递归深入的结果。

这些示例强调了在JSON表示中明确排除的表达细微差别,这些差别可以通过toSource()得到呈现。

要复制Gecko toSource()原语的相同结果,对于所有情况编写程序并不容易,因为它非常强大。

以下是一个复制toSource()功能必须成功处理的一些“移动目标”:

javascript:
function render(title,src){ (function(objRA){
    alert([ title, src,
        '\ntoSource():',objRA.toSource(),
        '\nJSON:',JSON.stringify(objRA)     ].join('\n'));
    })(eval(src));
}
render('Simple Raw Object source code:',
    '[new Array, new Object, new Number, new String, ' +
        'new Boolean, new Date, new RegExp, new Function]'  );

render( 'Literal Instances source code:',
    '[ [], 1, true, {}, "", /./, new Date(), function(){} ]'    );

render( 'some predefined entities:',
    '[JSON, Math, null, Infinity, NaN, ' +
        'void(0), Function, Array, Object, undefined]'      );

展示如下:

    简单的原始对象源代码:
    [new Array, new Object, new Number, new String, 
                new Boolean, new Date, new RegExp, new Function]
toSource(): [[], {}, (new Number(0)), (new String("")), (new Boolean(false)), (new Date(1302637995772)), /(?:)/, (function anonymous() {})]
JSON: [[],{},0,"",false,"2011-04-12T19:53:15.772Z",{},null]

然后显示:

    字面量实例源代码:
    [ [], 1, true, {}, "", /./, new Date(), function(){} ]
toSource(): [[], 1, true, {}, "", /./, (new Date(1302638514097)), (function () {})]
JSON: [[],1,true,{},"",{},"2011-04-12T20:01:54.097Z",null]

最后:

    一些预定义的实体:
    [JSON, Math, null, Infinity, NaN, void(0), 
                        Function, Array, Object, undefined]
toSource(): [JSON, Math, null, Infinity, NaN, (void 0), function Function() {[native code]}, function Array() {[native code]}, function Object() {[native code]}, (void 0)]
JSON: [{},{},null,null,null,null,null,null,null,null]

如果翻译是“要使用”的话,前面的分析是重要的;如果只是为了简单的人类消费,查看对象的内部,则不太严格。作为一种表示形式,JSON 的一个主要特征是在环境之间传输一些结构化信息“以便使用”。

toSource() 函数的质量是程序的指称语义的因素,影响但不限于:
往返计算、最小固定点属性和反函数。

  • 代码转换的重复是否会安静地达到静态状态?
  • 是否 obj.toSource() == eval(eval(eval(obj.toSource()).toSource()).toSource()).toSource()?
  • 考虑 obj == eval(obj.toSource()) 是否有意义?
  • 撤销转换是否导致不仅是相似的对象,而是完全相同的对象?
    这是一个充满深刻含义的问题,在克隆操作对象时尤其如此。

等等等等...

请注意,当 obj 包含已执行的代码对象(例如 (new Function ...)())时,上述问题具有更重要的意义。


5
如果您需要序列化带有循环引用的对象,您可以使用Douglas Crockford提供的JSON.js扩展中的cycle.js。您可以在https://github.com/douglascrockford/JSON-js上找到它。这类似于toSource(),但不会序列化函数(但可能可以通过使用函数的toString方法进行适应)。

5
如果您不需要完全匹配Firefox的序列化格式,可以使用在http://json.org列出的JavaScript JSON序列化/反序列化库之一。使用标准方案(如JSON)可能比模仿专有的Gecko格式更好。

1
如果JSON足以满足原始问题的需求,那么就不需要反编译函数、允许循环引用等功能。 - Nickolay

3
你可以像这样做:
Object.prototype.getSource = function() {
    var output = [], temp;
    for (var i in this) {
        if (this.hasOwnProperty(i)) {
            temp = i + ":";
            switch (typeof this[i]) {
                case "object" :
                    temp += this[i].getSource();
                    break;
                case "string" :
                    temp += "\"" + this[i] + "\"";    // add in some code to escape quotes
                    break;
                default :
                    temp += this[i];
            }
            output.push(temp);
        }
    }
    return "{" + output.join() + "}";
}

1
我不是JavaScript专家,但Object.prototype是被禁止的!请参见:http://erik.eae.net/archives/2005/06/06/22.13.54/也许最好将其实现为自由函数。 - jk.
2
修改对象原型并不是一个好主意。另外,字符串类型需要进行更多的转义,如 \t\n\r 等。 - AnthonyWJones
4
只要你懂JavaScript编程而且不使用像jQuery之类的库(为了提高速度而明确选择被破坏),修改Object原型是完全可行的想法。 - Phrogz

1
为了进一步理解:当您发送某个东西以进行处理时,接收者必须接收并能够处理它。因此,这段代码将起到作用 - 改编自Eliran Malka之前的答案。
// SENDER IS WRAPPING OBJECT TO BE SENT AS STRING
// object to serialize
var s1 = function (str) {
    return {
        n: 8,
        o: null,
        b: true,
        s: 'text',
        a: ['a', 'b', 'c'],
        f: function () {
            alert(str)
        }
    }
};
// test
s1("this function call works!").f();
// serialized object; for newbies: object is now a string and can be sent ;)
var code = s1.toString();

// RECEIVER KNOWS A WRAPPED OBJECT IS COMING IN
// you have to assign your wrapped object to somevar
eval('var s2  = ' + code);
// and then you can test somevar again
s2("this also works!").f();

请注意使用eval。如果您拥有所有被传输的代码:可以自由使用它(尽管它也可能有缺点)。如果您不知道源代码来自何处:这是不可取的。

0

您不必使用toSource();只需将要序列化的代码包装在返回JSON结构的函数中,并使用function#toString()代替:

var serialized = function () {
    return {
        n: 8,
        o: null,
        b: true,
        s: 'text',
        a: ['a', 'b', 'c'],
        f: function () {
            alert('!')
        }
    }
};
serialized.toString();

请查看在 jsFiddle 上的实时演示


0

0

另请参阅JavaScript数据格式化/漂亮打印机。我认为该程序以有效的JS格式导出,因此可以使用eval将其取回。

[编辑]实际上不是这样!它适用于快速转储,但不适用于真正的序列化。 我进行了改进,结果如下:

function SerializeObject(obj, indentValue)
{
  var hexDigits = "0123456789ABCDEF";
  function ToHex(d)
  {
    return hexDigits[d >> 8] + hexDigits[d & 0x0F];
  }
  function Escape(string)
  {
    return string.replace(/[\x00-\x1F'\\]/g,
        function (x)
        {
          if (x == "'" || x == "\\") return "\\" + x;
          return "\\x" + ToHex(String.charCodeAt(x, 0));
        })
  }

  var indent;
  if (indentValue == null)
  {
    indentValue = "";
    indent = ""; // or " "
  }
  else
  {
    indent = "\n";
  }
  return GetObject(obj, indent).replace(/,$/, "");

  function GetObject(obj, indent)
  {
    if (typeof obj == 'string')
    {
      return "'" + Escape(obj) + "',";
    }
    if (obj instanceof Array)
    {
      result = indent + "[";
      for (var i = 0; i < obj.length; i++)
      {
        result += indent + indentValue +
            GetObject(obj[i], indent + indentValue);
      }
      result += indent + "],";
      return result;
    }
    var result = "";
    if (typeof obj == 'object')
    {
      result += indent + "{";
      for (var property in obj)
      {
        result += indent + indentValue + "'" +
            Escape(property) + "' : " +
            GetObject(obj[property], indent + indentValue);
      }
      result += indent + "},";
    }
    else
    {
      result += obj + ",";
    }
    return result.replace(/,(\n?\s*)([\]}])/g, "$1$2");
  }
}

indentValue 可以是 null、""、" "、"\t" 或其他任何值。如果为 null,则不缩进,输出相对紧凑的结果(可以使用更少的空格...)。

我可以使用数组来堆叠结果然后将它们连接起来,但除非您有巨大的对象,否则字符串连接应该足够好了...
也无法处理循环引用...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接