我正在阅读JavaScript语言的教科书。在我研究闭幕主题时,我提出了以下问题。
考虑这个功能:
function foo() {
extractPropsToCurrentContext({'prop1' : 'hello', 'prop2' : 123});
}
我想要上面代码的结果,等于:
function foo() {
var prop1 = 'hello';
var prop2 = 123;
}
所以,我的问题是,如何实现函数extractPropsToCurrentContext(/ * Object * /)?
为了澄清,我想将对象的这些属性提取到执行上下文中,而不是在“this”指针下。 (因此,那些提取的道具应该在该函数内私有。)
要澄清的另一件事是,不能假设将使用'new'调用foo。 (比如新的foo())
更新
我的意思是,我们是否有可能使用任何 hacky技巧来绕过浏览器的限制,让更接近到我们想要的结果?就像多年前一样,我们发明了跨域的JSONP,长时间推送消息等等?
答案 0 :(得分:4)
我想将对象的这些属性提取到执行上下文
execution context实际上包含三件事:
ThisBinding
,您说您不想改变或扩展。
VariableEnvironment
,它包含声明的变量和函数。这就是你的平等代码会改变的。您可以使用以下hack更改它:
function getVariableDeclaration(obj) {
return "var " + Object.keys(obj).map(function(name) {
return name + " = " + JSON.stringify(obj[name]);
}).join(",\n ") + ";";
}
function foo() {
eval(getVariableDeclaration({'prop1' : 'hello', 'prop2' : 123}));
debugger;
}
foo();
但是,这仅适用于非严格模式。有关详细信息,请查看§10.4.2。此外,此hack目前仅限于JSON可序列化值,如果您需要分配任意值,它将变得更加丑陋 - 必须在您想要更改的环境中使用eval
。
LexicalEnvironment
,它包含当前标识符绑定(并且可能在执行in contrast to the VariableEnvironment
期间更改)。这不是您可能想要的,但可以通过with
声明轻松修改:
function foo() {
with ({'prop1' : 'hello', 'prop2' : 123}) {
debugger;
}
}
foo();
正如您所看到的,严格模式禁止对(非全局)执行上下文进行任何更改,因为这会使绑定成为非静态和不可优化的。大多数代码也变得难以理解,因此通常认为这是一种不好的做法。
从学术观点(理解语言的运作方式)思考这些黑客是很好的,但你不应该在生产中使用它们。无论你想到什么需要这样的技术,都有更好的解决方案。
答案 1 :(得分:1)
只是想分享我的尴尬解决方案。我们的想法是从父函数的其余部分构造新函数并调用它
在eval
。
function cc(fnname, ctxname) {
'use strict';
var __evalfn = function () {
'use strict';
var __fn = __fnname,
__ctx = __ctxname,
__fnStr = __fn.toString(),
__cccall = 'return eval(cc("'+__sfnname+'", "'+__sctxname+'"));',
__cccallPos = __fnStr.indexOf(__cccall),
__newFn = '(function () { var ',
__decl = [], __restBody;
if (__cccallPos === -1) { throw new Error("Can't find cc call"); }
__restBody = __fnStr.slice(__cccallPos+__cccall.length);
for (var __k in __ctx) {
__decl.push(__k + '=' + __sctxname + '['+JSON.stringify(__k)+']');
}
__newFn += __decl.join(',') + ';';
__newFn += __restBody;
__newFn += ')()';
return eval(__newFn);
},
__evalStr = __evalfn.toString()
.replace(/__fnname/g, fnname)
.replace(/__sfnname/g, JSON.stringify(fnname))
.replace(/__ctxname/g, ctxname)
.replace(/__sctxname/g, JSON.stringify(ctxname));
return '('+__evalStr+')()';
}
var fn = function () {
var a=10, ctx = {'b':10, 'c':'hello'};
return eval(cc("fn", "ctx"));
console.log(a,b,c);
return a+b;
};
> fn();
> 10 10 "hello"
> <- 20
要求您将return eval(cc("fn", "ctx"));
放入您的函数中。 fn
是变量保持函数的名称,您可以在其中使用cc
。 ctx
是包含新绑定的对象的名称。 cc
的一个优点是它允许ctx
中的任何值,从数字和字符串到使用用户定义的构造函数创建的对象。