22

グローバル関数と変数を動的に使用する場合は、次を使用できます。

window[functionName](window[varName]);

ローカルスコープの変数に対して同じことを行うことは可能ですか?

このコードは正しく動作しますが、現在 eval を使用しており、他の方法を考えようとしています。

var test = function(){
    //this = window
    var a, b, c; //private variables

    var prop = function(name, def){
        //this = window
        eval(name+ ' = ' + (def.toSource() || undefined) + ';');    

        return function(value){
            //this = test object
            if ( !value) {
                return eval('(' + name + ')');
            }
            eval(name + ' = value;')
            return this;
        };

    };

    return {
        a:prop('a', 1),
        b:prop('b', 2),
        c:prop('c', 3),
        d:function(){
            //to show that they are accessible via to methods
            return [a,b,c];
        }
    };
}();

>>>test
Object
>>>test.prop
undefined
>>>test.a
function()
>>>test.a()
1 //returns the default
>>>test.a(123)
Object //returns the object
>>>test.a()
123 //returns the changed private variable
>>>test.d()
[123,2,3]
4

4 に答える 4

8

あなたの質問に答えるには、いいえ、を使用せずにローカルスコープで動的変数検索を行う方法はありませんeval()

最良の代替手段は、「スコープ」を通常のオブジェクト [リテラル] (つまり、"{}") にし、そこにデータを貼り付けることです。

于 2009-03-01T00:35:58.273 に答える
2

evalを使わなくても、実際にできる と思います!

が間違っている可能性があるので、私がそうである場合は修正してくださいvar

function (a, b, c) { ...

それ以外の

function () { var a, b, c; ...

argumentsつまり、これらの変数/引数は、関数の呼び出しで値が指定されている場合、関数のオブジェクトと一緒にバインドされます。つまり、次のようになります。

function foo (bar) {
    arguments[0] = 'changed...';
    console.log(bar); // prints 'changed...'

    bar = '...yet again!';
    console.log(arguments[0]); // prints '..yet again!'
}

foo('unchanged'); // it works (the bound is created)
// logs 'changed...'
// logs '...yet again!'

foo(undefined); // it works (the bound is created)
// logs 'changed...'
// logs '...yet again!'

foo(); // it doesn't work if you invoke the function without the 'bar' argument
// logs undefined
// logs 'changed...'

そのような状況 (機能する場合) で、呼び出された関数のオブジェクトを何らかの方法で保存/保存すると、オブジェクトargumentsからスロットに関連する引数を変更でき、変更argumentsは変数自体に自動的に反映されます。

// using your code as an example, but changing it so it applies this principle
var test = function (a, b, c) {
    //this = window
    var args = arguments, // preserving arguments as args, so we can access it inside prop
        prop = function (i, def) {
            //this = window

            // I've removed .toSource because I couldn't apply it on my tests
            //eval(name+ ' = ' + (def.toSource() || undefined) + ';');
            args[i] = def || undefined;   

            return function (value) {
                //this = test object
                if (!value) {
                    //return eval('(' + name + ')');
                    return args[i];
                }

                //eval(name + ' = value;');
                args[i] = value;
                return this;
            };
        };

    return {
        a: prop(0, 1),
        b: prop(1, 2),
        c: prop(2, 3),
        d: function () {
            // to show that they are accessible via to methods
            return [a, b, c];
        }
    };
}(0, 0, 0);

値を引数として関数に渡すことができるという事実に悩まされている場合は、いつでも別の無名関数でラップできます。これにより、引数として渡された最初の定義済みの値に実際にアクセスできなくなります。つまり、次のようになります。

var test = (function () {
    // wrapping the function with another anomymous one
    return (function (a, b, c) {
        var args = arguments, 
            prop = function (i, def) {
                args[i] = def || undefined;   

                return function (value) {
                    if (!value) {
                        return args[i];
                    }

                    args[i] = value;
                    return this;
                };
            };

        return {
            a: prop(0, 1),
            b: prop(1, 2),
            c: prop(2, 3),
            d: function () {
                return [a, b, c];
            }
        };
    })(0, 0, 0);
})();

完全な動的アクセスの例

関数自体 ( arguments.callee) を文字列として取得し、正規表現を使用してそのパラメーターをフィルター処理することで、すべての引数変数名を配列にマップできます。

var argsIdx = (arguments.callee + '').replace(/function(\s|\t)*?\((.*?)\)(.|\n)*/, '$2').replace(/(\s|\t)+/g, '').split(',')

arguments配列内のすべての変数を使用して、各関数のスロット インデックスに対応する変数名を知ることができます。これを使用して、関数(この場合はprop )を宣言して、変数に読み書きします。

function prop (name, value) {
    var i = argsIdx.indexOf(name);

    if (i === -1) throw name + ' is not a local.';
    if (arguments.hasOwnProperty(1)) args[i] = value;

    return args[i];
}

質問の例のように、各変数をプロパティとして動的に追加することもできます。

argsIdx.forEach(function (name, i) {
    result[name] = prop.bind(null, name);
});

最後に、名前で変数を取得するメソッドを追加できます (デフォルトではすべて)。true最初の引数として渡された場合、すべての変数を識別子でハードコーディングした配列を返し、変数が変更されていることを証明します。

function props (flgIdent) {
    var names = [].slice.call(arguments.length > 0 ? arguments : argsIdx);

    return flgIdent === true ? [a, b, c, d, e, f] : names.map(function (name) {
        return args[argsIdx.indexOf(name)];
    });
} 

propおよびprops関数は、返されたオブジェクト内のメソッドとして使用可能にすることができます。最終的には、次のようになります。

var test = (function () {
    return (function (a, b, c, d, e, f) { 
        var argsIdx = (arguments.callee + '').replace(/function(\s|\t)*?\((.*?)\)(.|\n)*/, '$2').replace(/(\s|\t)+/g, '').split(','), 
            args = arguments, 
            result = {
                prop: function (name, value) {
                    var i = argsIdx.indexOf(name);

                    if (i === -1) throw name + ' is not a local.';
                    if (arguments.hasOwnProperty(1)) args[i] = value;

                    return args[i];
                },
                props: function (flgIdent) {
                    var names = [].slice.call(arguments.length > 0 ? arguments : argsIdx);

                    return flgIdent === true ? [a, b, c, d, e, f] : names.map(function (name) {
                        return args[argsIdx.indexOf(name)];
                    });
                }
            };

        args.length = argsIdx.length;
        argsIdx.forEach(function (name, i) {
            result[name] = result.prop.bind(null, name);
        });

        return result;
    })(0, 0, 0, 0, 0, 0);
})();

結論

関数のローカル スコープ変数を eval なしで読み書きすることは不可能ですが、それらの変数が関数の引数であり、値が与えられている場合、それらの変数識別子を関数のargumentsオブジェクトにバインドし、オブジェクト自体から間接的に読み書きすることができます。 arguments.

于 2020-02-07T01:58:49.693 に答える