3

私は、いくつかの奇妙なことを可能にするかなり醜いライブラリに取り組んでいます。グラフを使用すると、チェーンのようなスタイルで一連のコレクションをマップでき、システム全体で値を変更するときに変更できます。

終了タイプが JS プリミティブである場合に問題が発生しました。

私の場合、値とオブジェクトを使用してグラフを作成した後、次のようなことができます。

CHAIN.components[0].value = 20; 

componentsセッターとゲッターを使用したグラフのノードに対するフィルター関数です。コンポーネントでフィルタリングされたノードが 1 つしかない場合、ユーザーが設定したデフォルト値は、これを行わなくても使用できます。CHAIN.components.value = 20; しかし、むしろこれ: CHAIN.components = 20;

ここでの問題は、ノードがデフォルト以外の他のメソッドまたはプロパティを持つ可能性があることです(私の場合は on に設定されています) value

Number.prototype をハッキングせずに Number オブジェクトでセッターとゲッターを使用するCHAIN.components方法これは問題です。セットを作成するたびCHAIN.components.func()に Number.prototype に追加してから削除する必要があるからです。funccomponents

この種の行動を達成するための別のアイデアはありますか?

あなたはコードが欲しかったので、ここにあります:

/*jslint nomen: true, sloppy: true*/
GRID.modules.OHM || Object.extend(GRID.modules, ( function() {
    var Node, Nodes, Ohm, num_proto = Number.prototype.__clone(), str_proto = String.prototype.__clone();
    Node = function(uid) {
        var UID = uid;
        this.getUID = function() {
            return UID;
        };
    };
    Nodes = function() {
        var stack = [];
        this.add = function(id, val) {
            var n = new Node(stack.length);
            val.id = id;
            Object.extend(n, val);
            stack.push(n);
            return n.getUID();
        };
        this.getById = function(id) {
            return stack.filter(function(v) {
                var a = id || v.id;
                return (v.id === a);
            });
        };
        this.getByUID = function(UID) {
            return stack[UID];
        };
        this.get = function(callback) {
            !Object.isString(callback) || ( callback = [callback]);
            var f = Object.isFunction(callback) ? callback : (Object.isArray(callback) ? function(k) {
                return (callback.indexOf(k.id) >= 0);
            } : function(k) {
                return true;
            });
            return stack.filter(f);
        };
    };
    Ohm = function(n) {
        var graph = n || (new Nodes()), filters = {}, __nodes = {}, addGS = function(obj, name, conf, binder) {
            var alfa = {};
            Object.extend(alfa, conf);
            if (!alfa.get) {
                alfa.get = function() {
                    var a = this.g.getById(this.p);
                    return a.length === 1 ? a[0] : a;
                }.bind(binder);
            } else {
                alfa.get = alfa.get.bind(binder);
            }
            if (!alfa.set) {
                alfa.set = function(value) {
                    this.g.getById(this.p).forEach(function(k) {
                        Object.extend(k, value);
                        return true;
                    });
                }.bind(binder);
            } else {
                alfa.set = alfa.set.bind(binder);
            }
            Object.defineProperty(obj, name, alfa);
        }, add = function(id, node) {
            if (__nodes.hasOwnProperty(id)) {
                addGS(__nodes, id, {
                    enumerable : true
                }, {
                    t : this,
                    p : id,
                    g : graph
                });
            }
            return graph.add(id, node || {});
        };
        Object.extend(this, {
            add : function() {
                add.apply(this, arguments);
            },
            map : function(name, f, that) {
                var n = name, filterer = ['add', 'map', '__all'];
                n = Object.isFunction(n) ? name.apply(that, arguments.slice(3)) : n;
                if (filterer.indexOf(n.toLowerCase()) >= 0) {
                    console.log("You can't map over a basic property of object !!! Please read the freakin' manual.");
                    return null;
                }
                if (!filters.hasOwnProperty(n)) {
                    filters[n] = new Ohm(graph);
                    addGS(this, n, {
                        get : function() {
                            this.g.get(this.f).forEach(function(v, key, arr) {
                                var temp, binder;
                                if (arr.length !== 1) {
                                    if (!this.filt.hasOwnProperty(v.id)) {
                                        addGS(this.filt, v.id, {
                                            set : function(value) {
                                                this.t.g.getById(this.p).filter(this.t.f).forEach(function(k) {
                                                    Object.extend(k, value);
                                                });
                                            },
                                            get : function() {
                                                var a = this.t.g.getById(this.p).filter(this.t.f);
                                                return a.length === 1 ? a[0] : a;
                                            }
                                        }, {
                                            t : this,
                                            p : v.id
                                        });
                                        (key !== arr.length - 1) || Object.extend(this.filt, this.g.get(this.f));
                                    }
                                } else {
                                    if (Object.isFunction(v.__new__)) {
                                        v.__default = function() {
                                            return Object.extend((new this.__new__(arguments)), this);
                                        };
                                    }
                                    if (!Object.isUndefined(v.__default)) {
                                        temp = this.filt;
                                        this.filt = Object.isFunction(v.__default) ? v.__default.bind(v) : v.__default;
                                        if (Object.isNumber(this.filt) || Object.isString(this.filt)) {
                                            var prot = Object.isNumber(this.filt) ? Number : String;
                                            for (var i in temp) {
                                                if (temp.hasOwnProperty(i) && !prot.prototype.hasOwnProperty(i)) {
                                                    var bin = {
                                                        t : temp,
                                                        m : i,
                                                        p : prot,

                                                    };
                                                    Object.defineProperty(prot.prototype, i, {
                                                        set : function(value) {
                                                            Object.defineProperty(this.p.prototype, this.m, {
                                                                configurable : true, // defaults to false
                                                                writable : false,
                                                                value : 1
                                                            });
                                                            delete this.p.prototype[this.m];
                                                            this.t[this.m] = value;
                                                        }.bind(bin),
                                                        get : function() {
                                                            Object.defineProperty(this.p.prototype, this.m, {
                                                                configurable : true, // defaults to false
                                                                writable : false,
                                                                value : 1
                                                            });
                                                            delete this.p.prototype[this.m];
                                                            return this.t[this.m];
                                                        }.bind(bin),
                                                        enumerable : true,
                                                        configurable : true
                                                    });
                                                }
                                            }
                                        } else {
                                            Object.extend(this.filt, temp);
                                        }
                                    }
                                    if (Object.isNumber(this.filt) || Object.isString(this.filt)) {
                                        var prot = Object.isNumber(this.filt) ? Number : String;
                                        for (var i in v) {
                                            if (v.hasOwnProperty(i) && !prot.prototype.hasOwnProperty(i)) {
                                                var bin = {
                                                    t : v,
                                                    m : i,
                                                    p : prot,

                                                };
                                                Object.defineProperty(prot.prototype, i, {
                                                    set : function(value) {
                                                        Object.defineProperty(this.p.prototype, this.m, {
                                                            configurable : true, // defaults to false
                                                            writable : false,
                                                            value : 1
                                                        });
                                                        delete this.p.prototype[this.m];
                                                        this.t[this.m] = value;
                                                    }.bind(bin),
                                                    get : function() {
                                                        Object.defineProperty(this.p.prototype, this.m, {
                                                            configurable : true, // defaults to false
                                                            writable : false,
                                                            value : 1
                                                        });
                                                        delete this.p.prototype[this.m];
                                                        return this.t[this.m];
                                                    }.bind(bin),
                                                    enumerable : true,
                                                    configurable : true
                                                });
                                            }
                                        }
                                    } else {
                                        Object.extend(this.filt, v);
                                    }
                                }
                            }, this);
                            return this.filt;
                        },
                        set : function(value) {
                            this.g.get(this.f).forEach(function(k) {
                                Object.extend(k, value);
                            });
                        }
                    }, {
                        t : this,
                        f : f,
                        g : graph,
                        filt : filters[n]
                    });
                }
            }
        }, true, true);
        addGS(this, '__all', {
            get : function() {
                var a = this.g.getById();
                Object.extend(__nodes, a.length === 1 ? a[0] : a);
                return __nodes;
            },
            enumerable : true
        }, {
            t : this,
            p : null,
            g : graph
        });
    };
    window['Ω'] = Ohm;
    return {
        OHM : Ohm,
    };
}()));

そして今デモ:

var c = new Ω();
c.add('ann', {
__default : 58,
blah : 98,
ceva : function()
{
    console.log('asd');
}
});
c.add('ann2',{
    __default: function(){
       console.log('hello');
    },
    abc: 78,
    dce: function(){
       console.log(' world');
    }
};
c.add('b2', {
__new__ : function() {
    this.init = function() {
        this.id = 86;
    };
    this.mer = function() {
        console.log(this);
    };
},
els : 'asadar'
});
c.map('b2', function(k) {
return k.id === 'b2';
});
c.map('ann', function(k) {
return k.id === 'ann';
});
c.map('ann2', function(k) {
return k.id === 'ann2';
});
console.log(c.ann); // returns 58 ( the __default value )
console.log(c.ann.blah); // returns 98
console.log(c.ann.blah.blah); // undefined
console.log(c.ann2);  // function()
c.ann2(); // prints out 'hello'
c.ann2.cde(); // prints out 'world'
c.ann2 = 60;
console.log(c.ann2); // 60
console.log(c.ann2.cde()); // prints out 'world'

このコードは機能しますが、Number または String プロトタイプを使用しなければならない部分が気になります。これを行う別の方法はありますか?

その理由は、PHP では実行できるが JS では実行できないと誰かが言ったことを実行するためです。この男は最近、私と一緒に WebGL シェーダーで作業していて、複数の効果を FBO と組み合わせて使用​​するために 700 行のコードを書かなければならないことを嫌っていました。 PHPで書かれたこのような同様のツールで彼を連れて行った100。はい、私はプリミティブ プロトタイプのアクセサーがハックであることを知っていますが、チェーン エンド オブジェクトがプリミティブである場合、valueOf を使用せずにどうすればそれを変更できますか?

4

2 に答える 2

4

OK、添付のコードを読みました (ただし、すべてを理解することはできませんでした)。いくつかのコメントが欠けていますが、私はそれについて愚痴をこぼしません。誰も読んだり理解したりすることを期待していないので、私自身のコードは良くありません:-)

そうです、ネイティブプロトタイプを拡張する部分が怖いです。私が理解していることから、数値/文字列を返す前に、Number または String プロトタイプでアクセサー プロパティを定義します。取得時と設定時の両方で、アクセサー プロパティはデータ プロパティ (???) によって上書きされ、値を保存/返す前にプロパティ全体が削除されます。これは、プリミティブ値のカスタム プロパティを許可する巧妙なハックのようですが、次のようになります。

  • 衝突の危険性が高いです。this値をルックアップ テーブルのキーとして使用することで ( と区別(5).xするため)、それを下げることは可能かもしれませんが(3).x、それでも完全に回避することはできません。
  • アクセス/設定時に自分自身を削除するプロパティは、非常に直感的ではありません。それを避けてください。
  • プリミティブのプロトタイプのアクセサー プロパティを任意に変更すると、コストがかかります。これは、4 つのパフォーマンス コントラを組み合わせたものです。これを最適化できるエンジンはありません。そして、あなたはそれらをかなり頻繁に使用しているようです。

これが本当に必要な場合 (理由がわかりませんでした)、ルックアップ テーブルを持つバリアントを使用します。衝突を減らす必要があります (コードがそれらをどのように処理したかはわかりません)。定義されているアクセサー プロパティは変更されないため、より永続的です (ただし、リークする可能性があります)。

// Let's call this
// PRIMITIVE PROXIES
// as they proxy real objects behind primitive values
var proxy = _.map( { // some map that works on Objects
    string: String.prototype,
    number: Number.prototype
}, function closure(type, proto) {
    var table = {};
    function setupProperty(prop) {
        if (prop in proto) return; // ah, we already proxied this kind of object
        Object.defineProperty(proto, prop, {
            configurable:true, // for deleting
            get: function getter() {
                // "this" is the primitive value
                if (!this in table)
                    return undefined;
                return table[this][prop]; // get prop from obj
            },
            set: function setter(val) {
                if (this in table)
                    table[this][prop] = val; // pass val to obj
            }
        });
    }
    return {
        create: function createProxy(prim, obj) {
            if (prim in table) // we already did create a proxy on this primitive
                return; // let's abort. You might continue to overwrite
            table[prim] = obj;
            Object.getOwnPropertyNames(obj).forEach(setupProperty);
            return prim; // the new "proxy"
        },
        move: function moveName(from, to) {
            if (to in table) return false;
            table[to] = table[from];
            delete table[from];
            return true;
        }
    };
});
proxy.create = function(prim, obj) {
    return proxy[typeof prim].create(prim, obj);
};
proxy.move = function(from, to) {
    return proxy[typeof from].create(from, to);
};
// USAGE:
// proxy.create works just like Object.extend
> var c = {ann: 58},
>     o = {blah: 98};
> proxy.create(c.ann, o);
> 58..blah
98
> c.ann.blah
98
> (58).blah = 60;
> o
{blah: 60}
> var num = c.ann; // 58
> c.ann.blah = function(){return "Hello"};
> num.blah()
"Hello"
> proxy.move(c.ann, c.ann = 78);
> c.ann
78
> (58).blah
undefined
> c.ann.blah()
"Hello"
> // getters/setters for properties are global:
> c.ann.blub = "something"; // does not work, there is no getter
> c.ann.blub
undefined
> proxy.create(58, {blub: "foo"})
> c.ann.blub // still returns
undefined
> c.ann.blub = "bar"; // but can be set now
> (58).blub + (78).blub
"foobar"
> // infinite lookup loops are possible:
> proxy.create("loop", {x:"loop"});
> "loop" === "loop".x
true
> "loop".x.x.x.….x
"loop"

ただし、絶対に回避できないことが 1 つあります。

オブジェクトとは異なり、プリミティブ値は一意ではありません。彼らにはアイデンティティがありません。

、またはとc.ann区別することはできません。そのため、両方にプロパティがあるかどうかが決まります。これは、API を構築するための適切な前提ではありません。58"loop""loop".x

Numberそのため、andStringオブジェクトを使用することをお勧めします。それらに(m)メソッドがないように見えるため、(私の前の回答に示されているように)それらをサブクラス化する必要はありません。したがって、簡単に構築できます。

c.ann = new Number(58);
c.ann.blah = 98;
return c;

typeofオペレーターにとっては、ほとんど違いはないはずです。値を使用する例をいくつか追加していただけ__defaultますか?

しかし、チェーン エンド オブジェクトがプリミティブである場合、valueOf を使用せずにどのように異なるものにすることができますか?

この単純な質問に答えるには: 彼の言う通り、JavaScript ではネイティブ プロトタイプをハックせずには実行できません。そして、あなたは正しいです、ハックはかなり醜いです:-)

于 2012-11-28T23:30:53.177 に答える
1

プリミティブな数にしないで、数のように振る舞うオブジェクトにします。オブジェクトを使用Numberしてカスタム プロパティで拡張することもできますが ( https://stackoverflow.com/a/9338486/1048572Numberを参照)、一般にサブクラス化しないのはなぜですか? 数値を返すvalueOfメソッドを保持するだけで済みます。

function MyNumber(n) {
    this.value = Number(n);
}
MyNumber.prototype = Object.create(Number.prototype, {constructor:{value:MyNumber}});
MyNumber.prototype.valueOf = function() {
    return this.value;
};
// OK, we have to overwrite those methods as they don't work on subclasses of Number
["toExponential", "toFixed", "toLocaleString", "toPrecision", "toString"].forEach(function(name) {
    MyNumber.prototype[name] = function() {
        return Number.prototype[name].apply(new Number(this.value), arguments);
    };
});

// now extend the prototype with other properties, e.g. chaining methods

もちろん、components特定の数値 (または数値) を MyNumber に変換するセッターを作成する必要がある場合があります。

var actualNumber = new MyNumber;
Object.defineProperty(MyFabulousChainingThing, "components", {
    get: function() { return actualNumber; },
    set: function(x) { actualNumber.value = Number(x); }
});
于 2012-11-28T13:48:08.003 に答える