好的,仅从标题中难以理解。这是一个例子。我想要一个函数来引用一个自动“注入”的变量,即:
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?
答案 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
答案 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");