Javascript 递归JSON.stringify实现

Javascript 递归JSON.stringify实现,javascript,recursion,Javascript,Recursion,我正在努力学习Javascript中的递归,所以我想我应该重写本机的JSON.stringify函数,使用递归来挑战自己。我的代码几乎可以正常工作了: var my_stringify = function(obj){ value = obj[ Object.keys(obj)[0] ]; index = Object.keys(obj)[0]; delete obj[ Object.keys(obj)[0] ]; // The value is just a

我正在努力学习Javascript中的递归,所以我想我应该重写本机的
JSON.stringify
函数,使用递归来挑战自己。我的代码几乎可以正常工作了:

var my_stringify = function(obj){        
  value = obj[ Object.keys(obj)[0] ];
  index = Object.keys(obj)[0];

  delete obj[ Object.keys(obj)[0] ];

  // The value is just a simple string, not a nested object
  if (typeof value === 'string'){
    if (Object.keys(obj).length !== 0){
      // Continue recursion ..
      return '"' + index + '":"' + value + '",' + my_stringify(obj);
    }

    // This would be the base case with a string at the end. Stop recursion.
    return '"' + index + '":"' + value + '"}';
  }
  // The value is actually a nested object
  else{     
    if (Object.keys(obj).length !== 0){
    // Continue recursion ..
      return '"' + index + '":{' + my_stringify(value) + ',' + my_stringify(obj);
    }
    // This is the base case with a nested object at the end. Stringify it and end recursion.
    return '"' + index + '":{' + my_stringify(value) + '}';  
  }
}
除了我的答案中的第一个
{
不见了之外,我不知道如何修复这个bug

例如,
my_stringify({foo:'bar'})
返回
“foo”:“bar”}
而不是
{foo:“bar”}

另外,我知道我正在完全销毁原始对象,是否有任何方法可以在不删除任何内容的情况下(如
obj.slice(1)
)向递归发送原始对象的简化版本


任何建议都将不胜感激

您需要将递归视为深入对象而不实际更改对象。看起来您正试图使用递归在对象内部横向移动

我已经编写了一个stringify版本,它处理基本对象(没有数组或函数)

这是你的电话号码

代码如下:

var my_stringify2 = function (obj) {
    var objKeys = Object.keys(obj);
    var keyValueArray = new Array();
    for (var i = 0; i < objKeys.length; i++) {
        var keyValueString = '"' + objKeys[i] + '":';
        var objValue = obj[objKeys[i]];
        keyValueString = (typeof objValue == "string") ? 
            keyValueString = keyValueString + '"' + objValue + '"' : 
            keyValueString = keyValueString + my_stringify2(objValue);
        keyValueArray.push(keyValueString);
    }
    return "{" + keyValueArray.join(",") + "}";
}
结果是正确的json

{"foo":"bar","bar":"foo","foobar":{"foo":"bar","bar":"foo"}} 
如果希望完全避免for循环,可以执行以下操作

在本例中,您像普通一样传递对象,但递归地传递一个键数组,从每个属性的键数组中移除一个元素

有点复杂,所以我添加了注释

var my_stringify2 = function (obj, objKeys) {
    var str = "";
    // keys haven't been loaded, either first pass, or processing a value of type object
    if (objKeys == undefined) { 
        objKeys = Object.keys(obj);
        str = "{"
    } else {
        // if keys array exists and is empty, no more properties to evaluate, return the end bracket
        if (objKeys.length == 0) {
            return "}";
        // array exists and isn't empty, that means it's a property and not the first property, add a comma    
        } else {
            str = ",";
        }
    }
    // add the property name
    str += '"' + objKeys[0] + '":';
    // get the value
    var objValue = obj[objKeys[0]];
    // if the value type is string, add the string, if it's an object, call this function again, but leave the objKeys undefined
    str +=
        (typeof objValue == "string") ? 
        '"' + objValue + '"' : 
         my_stringify2(objValue);    
    // remove the first element fromt the keys array
    objKeys.splice(0,1);
    //call the function for the next property
    return str + my_stringify2(obj, objKeys);
}

从根本上说,您是通过切断第一个属性进行字符串化,将其字符串化,然后递归对象的其余部分。不管怎么说,递归的唯一原因是当存在嵌套对象时,否则您应该只遍历属性。这样做,您就很难判断您是否位于对象的开头,并且应该用字符串返回缺少的
{

在半伪代码中(留给您一些自己做的工作),您需要这样的东西:

var my_stringify = function(obj) {
    // check first for null / undefined / etc and return
    var myJSON = "{";
    // iterate through all the properties of the object
    for (var p in obj) {
        if (obj.hasOwnProperty(p)) {
            // check to see if this property is a string, number, etc
            if (//...) {
                myJSON += // the JSON representation of this value using p and obj[p]
            }
            if (// test for nested object) {
                myJSON += my_stringify(obj[p]);    // this is recursion!
            }
            if (// test for arrays) {
                // arrays also need special handling and note that they might
                // include objects or other arrays - more chances for recursion!
            }
            // note: functions should be ignored, they aren't included in JSON
        }
    }
    return myJSON + "}";
}

我不同意@Bergi的说法,即常规的旧递归不适合这种情况。正如我在评论中所说,通过将索引作为参数传递给函数,可以避免使用
for
循环。这是一种非常常见的技术,可以防止您需要复制或修改数据结构

下面是我对这种实现的尝试。正如您所看到的,它非常简单(令我自己惊讶的是,它可以工作!):

函数jsonify(obj,idx){ var json,objStr=toString.call(obj); //处理字符串 如果(objStr=='[对象字符串]'){return'+obj+'''} idx=idx | | 0 //句柄数组 if(objStr=='[object Array]'){ 如果(idx>=对象长度){ //下面的代码确保我们永远不会超过数组的末尾, //所以我们可以假设这是一个空数组 返回“[]” } //JSONify在idx处的值 json=jsonify(obj[idx]) if(idx输出[9,“九”,“键”:[],“键2”:{“子键”:3.333}]
有很多方法可以加强这一点(我肯定也有一些漏洞),但你知道了。

我在采访中被问到这个问题,这就是我想到的。 可理解的递归方法:

​
函数字符串化(输入){
var-arrVals=[];
Object.keys(输入).forEach(函数(键名){
设val=input[keyName];
if(typeof val!=“未定义”&&typeof val!=“函数”){
arrVals.push(getQuotedString(键名)+“:”+getString(val));
}
});
返回'{'+arrVals.join(',')+'}';
}
函数getString(val){
开关(val类型){
大小写“string”:
返回getQuotedString(val);
打破
案件编号:
“布尔”大小写:
返回val;
打破
案例“对象”:
如果(val==null){
返回“null”;
}
if(数组isArray(val)){
让arrString=[]
for(设i=0;i}
旧问题的新答案

这里有一些非常糟糕的答案,即使在最简单的例子下也会失败。这个答案旨在详尽地回答这个问题,并演示这样的方法在处理各种数据类型和

角落案例

此函数对一个非空数据的<代码>构造函数< /代码>属性进行简单的实例分析,并相应地编码。它管理了许多不可能考虑的角情况,例如

  • JSON.stringify(未定义)
    返回
    undefined
  • JSON.stringify(null)
    返回
    'null'
  • JSON.stringify(true)
    返回
    'true'
  • JSON.stringify([1,2,未定义,4])
    返回
    '[1,2,null,4]'
  • var my_stringify = function(obj) { // check first for null / undefined / etc and return var myJSON = "{"; // iterate through all the properties of the object for (var p in obj) { if (obj.hasOwnProperty(p)) { // check to see if this property is a string, number, etc if (//...) { myJSON += // the JSON representation of this value using p and obj[p] } if (// test for nested object) { myJSON += my_stringify(obj[p]); // this is recursion! } if (// test for arrays) { // arrays also need special handling and note that they might // include objects or other arrays - more chances for recursion! } // note: functions should be ignored, they aren't included in JSON } } return myJSON + "}"; }
    function jsonify(obj, idx) {
      var json, objStr = toString.call(obj);
    
      // Handle strings
      if(objStr == '[object String]') { return '"' + obj + '"' }
    
      idx = idx || 0
    
      // Handle arrays
      if(objStr == '[object Array]') {
        if(idx >= obj.length) {
          // The code below ensures we'll never go past the end of the array,
          // so we can assume this is an empty array
          return "[]"
        }
    
        // JSONify the value at idx
        json = jsonify( obj[idx] )
    
        if(idx < obj.length - 1) {
          // There are items left in the array, so increment the index and
          // JSONify the rest
          json = json + "," + jsonify( obj, idx + 1 )
        }
    
        // If this is the first item in the array, wrap the result in brackets
        if(idx === 0) { return "[" + json + "]" }
    
        return json
      }
    
      // Handle objects
      if(obj === Object(obj)) {
        var keys = Object.keys(obj)
        var key = keys[idx]
    
        // JSONify the key and value
        json = '"' + key + '":' + jsonify( obj[key] )
    
        if(idx < keys.length - 1) {
          // There are more keys, so increment the index and JSONify the rest
          return json + "," + jsonify( obj, idx + 1 )
        }
    
        // If this is the first key, wrap the result in curly braces
        if(idx === 0) { return "{" + json + "}" }
    
        return json
      }
    
      return obj.toString() // Naively handle everything else
    }
    
    var items = [ 9, "nine", { "key": [], "key2": { "subkey": 3.333 } } ]
    
    console.log("OUTPUT", jsonify(items))
    // => OUTPUT [9,"nine","key":[],"key2":{"subkey":3.333}]
    
    // we really only care that JSON.parse can work with our result
    // the output value should match the input value
    // if it doesn't, we did something wrong in our stringifier
    const test = data => {
      return console.log(JSON.parse(stringifyJSON(data)))
    }
    
    test([1,2,3])     // should return [1,2,3]
    test({a:[1,2,3]}) // should return {a:[1,2,3]}