Javascript,可以在没有eval的情况下传递未声明的方法参数?

时间:2013-07-17 00:05:08

标签: javascript

好的,仅从标题中难以理解。这是一个例子。我想要一个函数来引用一个自动“注入”的变量,即:

function abc() {
   console.log(myVariable);
}

我尝试过:

with({myVariable: "value"}) { abc() } 

但除非在with块中声明了abc,否则这不起作用,即:

with({myVariable: "value"}) { 

    function abc() {
       console.log(myVariable);
    }

    abc();  // This will work

}

所以最后一个部分可以工作,但是可以伪造with语句,还是我必须强制开发人员在with语句中声明他们的函数调用?

基本上我要做的电话是:

doSomething({myVariable: "value"}, function() {
    console.log(myVariable);
});

当然,我知道我可以传递这是一个参数对象,但这不是我想要做的:

doSomething({myVariable: "value"}, function(M) {
    console.log(M.myVariable);
});

此外,我正在尝试避免使用eval:

with({myVariable: "value"}) { 

    eval(abc.toString())(); // Will also work

}

在Javascript中,这是否完全不支持eval?

8 个答案:

答案 0 :(得分:4)

JavaScript没有提供任何直接的方法来实现您正在寻找的语法。将变量注入Lexical Environment的唯一方法是使用eval(或非常相似的Function构造函数)。这个问题的一些答案表明了这一点。其他一些答案建议使用全局变量作为解决方法。但是,每个解决方案都有自己的警告。

除此之外,您唯一的选择是使用不同的语法。最接近原始语法的是将参数从doSomething传递给回调as Aadit M Shah suggested。是的,我知道你说你不想这样做,但它要么是那么丑陋的黑客......


原始答案(当我不完全理解这个问题时写的)

也许您正在寻找的是关闭?像这样:

var myVariable = "value";
function doSomething() {
    console.log(myVariable);
};
doSomething(); // logs "value"

或许这个?

function createClosure(myVariable) {
    return function() {
        console.log(myVariable);
    };
}
var closure = createClosure("value");
closure(); // logs "value"

甚至:

var closure = function(myVariable) {
    return function() {
        console.log(myVariable);
    };
}("value");
closure(); // logs "value"

答案 1 :(得分:2)

尝试:

function doSomething(vars, fun) {
    for (var key in vars) { // set the variables in vars
        window[key] = vars[key];
    }
    fun.call(); // call function
    for (var key in vars) { // remove the variables again. this will allow only the function to use it
        delete window[key];
    }
}

设置可以在fun

内接收的全局变量

JSFiddle:http://jsfiddle.net/shawn31313/MbAMQ/

答案 2 :(得分:2)

警告:提前令人作呕的代码

function callWithContext(func, context, args) {
    var oldProperties = {};

    for(var n in context) {
        if(context.hasOwnProperty(n)) {
            var oldProperty = Object.getOwnPropertyDescriptor(self, n);
            oldProperties[n] = oldProperty;

            (function(n) {
                Object.defineProperty(self, n, {
                    get: function() {
                        if(arguments.callee.caller === func) {
                            return context[n];
                        }

                        if(!oldProperty) {
                            return;
                        }

                        if(oldProperty.get) {
                            return oldProperty.get.apply(this, arguments);
                        }

                        return oldProperty.value;
                    },
                    set: function(value) {
                        if(arguments.callee.caller === func) {
                            context[n] = value;
                        }

                        if(!oldProperty) {
                            return;
                        }

                        if(oldProperty.set) {
                            return oldProperty.get.apply(this, arguments);
                        } else if(!oldProperty.writable) {
                            var fakeObject = {};
                            Object.defineProperty(fakeObject, n, {value: null, writable: false});
                            fakeObject[n] = value; // Kind of stupid, but…
                            return;
                        }

                        oldProperty.value = value;
                    }
                });
            })(n);
        }
    }

    func.apply(this, args);

    for(var n in context) {
        if(context.hasOwnProperty(n)) {
            if(oldProperties[n]) {
                Object.defineProperty(self, n, oldProperties[n]);
            } else {
                delete self[n];
            }
        }
    }
}

顺便说一句,这是可怕的骇人听闻的;不要使用它。但是ew, it actually works

答案 3 :(得分:2)

很久以前我问过类似的问题:Is it possible to achieve dynamic scoping in JavaScript without resorting to eval?

简短的回答是否定的,如果不诉诸eval,就无法实现动态范围。答案很长,您不需要

JavaScript不支持动态作用域,但这不是问题,因为您可以创建它们所属函数的自由变量参数。

我认为这是最好的解决方案:

function doSomething(context, callback) {
    callback(context);
}

doSomething({myVariable: "value"}, function(M) {
    console.log(M.myVariable);
});

但是,由于您不想编写正式参数,下一个最好的方法是使用this代替:

function doSomething(context, callback) {
    callback.call(context);
}

doSomething({myVariable: "value"}, function() {
    console.log(this.myVariable);
});

另一个选择是操作程序的形式参数列表,如下所示:

function inject(func, properties) {
    var args = [], params = [];

    for (var property in properties) {
        if (properties.hasOwnProperty(property)) {
            args.push(properties[property]);
            params.push(property);
        }
    }

    return Function.apply(null, params.concat("return " + func.toString()))
        .apply(null, args);
}

现在我们可以使用这个inject方法将属性注入函数,如下所示:

function doSomething(context, callback) {
    var func = inject(callback, context);
    func();
}

doSomething({myVariable: "value"}, function() {
    console.log(myVariable);
});

请参阅演示:http://jsfiddle.net/sDKga/1/

注意: inject函数将创建一个全新的函数,它与原始函数的词法范围不同。因此,具有自由变量和部分应用函数的函数将无法按预期工作。仅将inject普通功能一起使用。

Function构造函数有点像eval,但它更安全。当然,我建议您只使用形式参数或this。然而,设计决定是您的选择。

答案 4 :(得分:1)

我不明白为什么你不能只传递信息或定义一个全局,但我认为这将是最好的。

说,我在模块制造商/跑步者工作,允许草率/危险代码执行而不干扰主机环境。这提供了重新定义变量的机会,可以作为对象传递。

这确实使用了eval(技术上的Function()),但它可以在“use strict”中运行,所以它不会太疯狂/聪明。 它不会留下文物。 它也不会让全局性受到伤害。

它仍然是一项正在进行中的工作,我需要在保证安全性之前解决一些细微的细节,所以不要将它用于堡垒诺克斯或任何东西,但它的工作和稳定性足以执行要求的操作。

在ch28,FF22,IE10中测试:

function Module(strCode, blnPreventExtensions, objWhitelist, objExtend) {
   var  __proto__=self.__proto__, pbu=self.__proto__, str=strCode, om=[].map, wasFN=false,
    params = {Object:1}, fnScrubber, natives= [ Object, Array, RegExp, String, Boolean, Date] ,
    nativeSlots = [],
        preamble = "'use strict';" ,
                inherited="__defineGetter__,__defineSetter__,__proto__,valueOf,constructor,__lookupGetter__,__lookupSetter__",
        late = inherited + 
                Object.getOwnPropertyNames(__proto__||{}) + Object.getOwnPropertyNames(window);
    late.split(",").sort().map(function(a) {
        this[a] = 1;
    }, params);

        preamble+=";var "+inherited+";";

        //turn functions into strings, but note that a function was passed
         if(str.call){wasFN=true; str=String(str); delete params.Object; }

           objExtend=objExtend||{};
           var vals=Object.keys(objExtend).map(function(k){ return objExtend[k]; })


        // build a usable clone of Object for all the new OOP methods it provides:
        var fakeOb=Object.bind();
        (Object.getOwnPropertyNames(Object)||Object.keys(Object)).map(function(a){
                 if(Object[a] && Object[a].bind){this[a]=Object[a].bind(Object); } return this;
        },fakeOb)[0];



    //allow "eval" and "arguments" since strict throws if you formalize them and eval is now presumed safe.
        delete params.eval;
        delete params.arguments;
        params.hasOwnProperty=undefined;
        params.toString=undefined;
                params['__proto__']={};
                __proto__=null;


        Object.keys(objWhitelist||{}).map(function ripper(a,b){
                b=this[a];
                if(typeof b!=='object'){ 
                    delete this[a];
                }
         }, params);

        // var ok=Object.keys.bind(Object);

    // prevent new prototype methods from being added to native constructors:
    if (blnPreventExtensions) {
        natives.forEach(function(con, i) {
                       var proto=con.prototype;
                       Object.getOwnPropertyNames(proto).map(function(prop){ 
                              if(proto[prop] && proto[prop].bind ){ this[prop]=proto[prop];} 
                        }, nativeSlots[i] = {});
                         delete con.constructor;
                         delete con.prototype.constructor;  
        }); //end con map()
    } /* end if(blnPreventExtensions) */


    //white-list harmless math utils and prevent hijacking:
    delete params.Math;
    if(blnPreventExtensions){Object.freeze(Math);}

    //prevent literal constructors from getting Function ref (eg: [].constructor.constructor, /./.constructor.constructor, etc...):
    Function.prototype.constructor = null;


    try {
                //generate a private wrapper function to evaluate code:
        var response = Function(
                   Object.keys(objExtend) + (vals.length?",":"") +
           Object.keys(params).filter(/./.test, /^[\w\$]+$/), // localize most globals
           preamble + " return " + str.trim() // cram code into a function body with global-blocking formal parameters
        );


               // call it with a blank this object and only user-supplied arguments:
               if (blnPreventExtensions) {  //( user-land code must run inside here to be secure)
                       response = response.apply({}, vals.concat(fakeOb)).apply({}, [].slice.call(arguments,4) ); 
               }else{
                       response = response.apply({}, vals.concat(fakeOb)); 
               }

    } catch (y) {
        response = y + "!!";
    } /* end try/catch */


    if (blnPreventExtensions) {
        om.call(natives, function(con, i) {
                        var pro=con.prototype;

                        //remove all proto methods for this con to censor any additions made by unsafe code:
                        Object.getOwnPropertyNames(pro).map(function(a){ try{delete pro[a];}catch(y){}});

                        //restore all original props from the backup:
            var bu = nativeSlots[i];
            om.call(Object.keys(bu), function(prop){ con.prototype[prop]=bu[prop]; }, bu);

        }); //end con map()
    } /* end if(blnPreventExtensions) */

       //restore hidden Function constructor property:
       Function.prototype.constructor = Function;
    return response;
} /* end Module() */

/////////////////////////////////////////////////////////////


function doSomething(context, fn){
    console.log(myVariable);
    return myVariable;
}


//use 1:
 alert(    Module(doSomething, true, {console:1}, {myVariable: "value123"} )    );// immed

//use2:
var fn=Module(doSomething, false, {console:1}, {myVariable: "value123"} );// as function
alert(fn);
alert(fn());

再次,我认为OP最好不要在以后做事情,但为了全面和灵感,我真诚地把它放在那里。

答案 5 :(得分:0)

您需要使用call()来构建上下文,如:

var f=function(){
    console.log(this.foo);
};

f.call({foo:'bar'})

将打印“bar”

答案 6 :(得分:0)

如果您愿意在eval()中使用doSomething(),则可以避免在调用该函数时使用function abc() { console.log(myVariable); } // Prints "value" callWith({ myVariable: "value" }, abc); function callWith(context, func) { for(var i in context) eval('var ' + i + ' = context[i];'); eval('(' + func.toString() + ')')(); }

{{1}}

查看this post

答案 7 :(得分:0)

看一下goog.partial,向上滚动一下,看看它的作用说明:

以下是它的实现:

var b = goog.partial(alert, 'Hello world!');
b();//alerts "Hello world!"

在示例中,它使用参数“Hello world!”传递函数alert。但你可以用多个参数传递你自己的函数。

这允许您创建一个变量,该变量指向始终使用某个参数调用的函数。要在函数中使用未命名的参数,可以使用参数:

function test(){
  console.log(arguments);//["hello","world"]
}

test("hello","world");