3

私は最近、OPがオブジェクトのプロパティへのパスを見つけたいというこの質問が出されているのを見たので、実際に解決策を書くのに十分な時間がないと述べて、疑似コードで答えました。しかし、その質問は私にとって非常に興味深いものだったので、とにかく解決策を書こうとしました。これが私がこれまでに思いついたものです:

function isEmpty(obj) {
    for (var prop in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, prop)) {
            return false;
        }
    }
    return true;
}

function Node(obj, parent, searchTarget) {
    this.parent = parent;
    this.obj = obj;
    this.searchTarget = searchTarget;

    this.searchNode = function() {
        if(this.obj == this.searchTarget) {

            //return this.reconstructPathRecursive();
        }

        if (!isEmpty(this.obj)) {
            var children = [];

            for (prop in this.obj) {
                if (this.obj.hasOwnProperty(prop)) {
                    children.push(new Node(this.obj[prop], this, searchTarget));
                }
            }

            var path;
            for(var i = 0, len = children.length; i < len; i++) {
                path = children[i].searchNode();
                if(path) return path;
            }
        }
    }

    this.reconstructPathRecursive = function() {
        var path = [this], curObj = this.parent;

        while (curObj != undefined) {
            path.push(curObj);
            curObj = curObj.parent;
            if(curObj == undefined) break;
        }

        return path;
    }

    this.findPath = function() {
        return this.searchNode();
    }
}

var myObj = {
    nullRoot: "gotcha!",
    path1: {
        myFunc: function() {
            alert("Success!");
        }
    }
}

function findFunctionPath(obj, func) {
    return new Node(obj, undefined, func).findPath();
}
var thisFunc = myObj.path1.myFunc;
    console.log("--");

console.log(findFunctionPath(myObj, thisFunc));

アイデアはthis.searchNode()、オブジェクトの各プロパティを表す Node オブジェクトを呼び出すというものです。searchNode()結果の各プロパティ ノードで自身を呼び出し、現在のオブジェクトを各子ノードの として渡しますparent。検索する関数が見つかったら、 を呼び出しますreconstructPathRecursive()。これは、各ノードの親プロパティを使用して、ほとんどそれを行います。

ただし、「最大コール スタック サイズを超えました」というメッセージが表示されます。このライブ テストを実行するとエラーが発生します。どういうわけか誤って無限ループを書いたということだと思います。私の論理のどこに欠陥があり、その無限ループはどこに忍び込んだのでしょうか? console.logが何度も呼び出されていることを示していsearchNodeますが、オブジェクトが空でない場合にのみ呼び出しており、オブジェクトにそれ自体への参照をどこにも与えていません (私は考えていません...)。私は本当にここで困惑しています。

編集:isEmpty Node 関数からグローバル関数に変更するためにコードを多少更新してthis.objsearchNode()関数内で呼び出すことができるようにしました。以前は、参照されるオブジェクトではなく、ノード (常に少なくとも 2 つのプロパティを持つため、無限ループが発生します) でのみ呼び出されていました。それは修正されましたが、エラーは続きます。

別の編集:別のエラーを見つけて修正しました(Satyajitの回答を参照)。ただし、まだ無限ループを取得しています。

4

2 に答える 2

2

プロパティnullRootは文字列であり、javascript オブジェクトではありません。その上で関数を実行するisEmptyと、false が返されることはなく、無限ループにスローされます。ただし、その値として「gotcha」を入れることはほとんど予言的です。

于 2012-04-27T20:07:19.560 に答える
1

これは失敗します

var arr = [];
arr[0] = arr;

それは子供として自分自身を含んでいるからです。このNode関数は、親が子と同じであるノードの無制限のリストを作成することになります。

循環オブジェクトグラフを処理するには、既にアクセスしたものを追跡し、再アクセスしないようにする必要があります。オブジェクトセットがないため、JavaScriptではこれは困難です。

訪問したオブジェクトのリストを保持して、そこに表示されるかどうかを確認するかobj、特別なオブジェクトプロパティ(ブレッドクラム)を使用して、オブジェクトにアクセスしたかどうかを確認することができます。適切にクリーンアップしないか、他の誰かがそのプロパティを使用するか、他のコードがEcmaScript 5フリーズを使用すると、ブレッドクラムは失敗します。


編集:

また、子の中に実際にパスを見つけたときに、子ループから抜け出す必要があります。

path = children[i].searchNode();

する必要があります

 path = children[i].searchNode();
 if (path) { return path; } 

編集:

Satyajitが指摘しているように、"gotcha"はプロパティ値です。

それ以来"gotcha"[0] === "g""g"[0] === "g"あなたがサイクルに到達するまでにそれほど時間はかかりません。

私がする時

alert("gotcha".hasOwnProperty(0));
for (var k in "gotcha") { alert(k); }

最近のChromeでは、アラート「true」、「0」、「1」、...、「5」が表示されます。

これは、セクション15.5.5.2で概説されているように、最新のブラウザの標準的な動作です。

文字列オブジェクトは、他のネイティブECMAScriptオブジェクト(8.12.1)で使用される[[GetOwnProperty]]内部メソッドのバリエーションを使用します。この特別な内部メソッドは、Stringオブジェクトの個々の文字に対応する名前付きプロパティへのアクセスを追加するために使用されます。

これらのプロパティは、そのセクションの箇条書き9により列挙可能です。

于 2012-04-27T19:33:47.997 に答える