javascript 在不使用此关键字的情况下在上下文中进行评估
javascript eval in context without using this keyword
我正在尝试在特定上下文中执行 eval。我发现答案 here 很有用。但是,我在 Chrome 版本 53.0.2785.143 m 中出现以下行为。其他浏览器没试过。我使用的代码如下:
function evalInContext(js, context) {
return function() { return eval(js); }.call(context);
}
console.log(evalInContext('x==3', { x : 3})) // Throws
console.log(evalInContext('this.x==3', { x : 3})) // OK
但是我预计第一次调用 evalInContext
不会抛出。知道为什么会发生这种情况吗?
范围和上下文不一样
变量x
的解析方式与上下文无关。它由范围规则解决:是否有任何闭包定义了该变量?如果没有,请查看全局对象。在任何时候,变量都不能通过查看上下文来解析。
你可以看看this article。
该行为并非eval
所特有,与其他函数相同:
"use strict";
var obj = { x : 3};
var y = 4;
function test() {
console.log(this.x); // 3
console.log(typeof x); // undefined
}
test.call(obj);
function test2() {
console.log(this.y); // 4
console.log(typeof y); // number
}
test2.call(window);
虽然我推荐@trincot 提供的答案,尤其是伟大的 link。我在这里发布我对我遇到的问题的解决方案
function evalInContext(scr, context)
{
// execute script in private context
return (new Function( "with(this) { return " + scr + "}")).call(context);
}
with(this)
表达式允许context
对象的成员变量出现在表达式scr
.
的执行范围内
归功于 this answer 类似问题
这是另一种方法(用 ES6 编写):
(new Function(...Object.keys(context), codeYouWantToExecute))(...Object.values(context));
感谢 Jonas Wilms 的回答。
这是我的解释,因为我最初并不了解它是如何工作的:
let example = new Function('a', 'b', 'return a+ b')
等同于创建函数赋值给example
:
function(a,b) {
return a + b
}
之后,您可以这样调用 example
:example(2,6)
其中 returns 8.
但是假设您不想将 new Function
创建的函数分配给一个变量,而是想立即调用它。
那么你可以这样做:
new Function('a', 'b', 'return a+ b')(2,6)
此 post 顶部的代码片段本质上是做同样的事情。它使用扩展运算符传入 context
对象的键,如下所示:
new Function(key1, key2, key3... lastkey, codeYouWantToExecute)
。然后它调用新创建的函数并使用扩展运算符传递与每个键对应的值。
最后,这里是编译为 ES5 的答案:
(new (Function.bind.apply(Function, [void 0].concat(Object.keys(context), [codeYouWantToExecute])))()).apply(void 0, Object.values(context));
这会计算给定上下文中的表达式,而无需在 vars 前加上 'this'。
有警告但工作正常。
也可以在表达式中加入自定义函数。
只需调用一个 'evaluate' 函数,传入您的表达式、上下文和一个可选函数。
上下文必须是平面结构(无嵌套元素)并且函数必须在字符串表达式中引用为 'fn'。在这种情况下答案打印 11:
var expression = "fn((a+b)*c,2)";
var context = { a: 1, b: 2, c: 3 };
var func = function(x,y){return x+y;};
function evaluate(ex, ctx, fn) {
return eval("var "+JSON.stringify(ctx).replace(/["{}]/gi, "").replace(/:/gi, "=")+", fn="+fn.toString()+";"+ex);
}
var answer = evaluate(expression, context, func);
console.log(answer);
无法评论所以我 post,Joe 的回答很好,但如果字符串包含多个语句(如 x++; y++; x+y;
),y++
部分将永远不会被执行,因为它 returns 在第一个语句中。
通过此修改,将执行所有语句,最后一条语句将是 return 值。
function evalInScopeAndContext(scr, scopeAndContext)
{
// execute script in private context
return (new Function( "with(this) { return eval('" + scr + "'); }")).call(scopeAndContext);
}
我还更改了参数名称,因为该参数同时用作范围和上下文,它们是不同的东西。
警告 1:
使用此函数时,this.x
将等于 x
且无法更改,因为范围的 this
属性 如果存在,将被隐藏。
如果你执行evalInScopeAndContext('this', {a:1, b:2, this:3})
答案将是 {a:1, b:2, this:3}
而不是 3
.
要访问 属性 this
,您必须改写 this.this
,我认为它无法修复。
警告 2:
可以访问和修改外部作用域(从该函数的调用者到 window
),除非被作用域对象隐藏。
因此,如果您打算执行类似 location = 'house';
的操作但忘记在范围对象中插入 location
键,您将执行 window.location = 'house'
并可能造成损坏。
相反,this.location = 'house';
总是安全的,因为 this
总是存在,并且只会将 location
键添加到提供的范围对象中,因此您可能希望始终使用 this.
作为前缀
我正在尝试在特定上下文中执行 eval。我发现答案 here 很有用。但是,我在 Chrome 版本 53.0.2785.143 m 中出现以下行为。其他浏览器没试过。我使用的代码如下:
function evalInContext(js, context) {
return function() { return eval(js); }.call(context);
}
console.log(evalInContext('x==3', { x : 3})) // Throws
console.log(evalInContext('this.x==3', { x : 3})) // OK
但是我预计第一次调用 evalInContext
不会抛出。知道为什么会发生这种情况吗?
范围和上下文不一样
变量x
的解析方式与上下文无关。它由范围规则解决:是否有任何闭包定义了该变量?如果没有,请查看全局对象。在任何时候,变量都不能通过查看上下文来解析。
你可以看看this article。
该行为并非eval
所特有,与其他函数相同:
"use strict";
var obj = { x : 3};
var y = 4;
function test() {
console.log(this.x); // 3
console.log(typeof x); // undefined
}
test.call(obj);
function test2() {
console.log(this.y); // 4
console.log(typeof y); // number
}
test2.call(window);
虽然我推荐@trincot 提供的答案,尤其是伟大的 link。我在这里发布我对我遇到的问题的解决方案
function evalInContext(scr, context)
{
// execute script in private context
return (new Function( "with(this) { return " + scr + "}")).call(context);
}
with(this)
表达式允许context
对象的成员变量出现在表达式scr
.
归功于 this answer 类似问题
这是另一种方法(用 ES6 编写):
(new Function(...Object.keys(context), codeYouWantToExecute))(...Object.values(context));
感谢 Jonas Wilms 的回答。
这是我的解释,因为我最初并不了解它是如何工作的:
let example = new Function('a', 'b', 'return a+ b')
等同于创建函数赋值给example
:
function(a,b) {
return a + b
}
之后,您可以这样调用 example
:example(2,6)
其中 returns 8.
但是假设您不想将 new Function
创建的函数分配给一个变量,而是想立即调用它。
那么你可以这样做:
new Function('a', 'b', 'return a+ b')(2,6)
此 post 顶部的代码片段本质上是做同样的事情。它使用扩展运算符传入 context
对象的键,如下所示:
new Function(key1, key2, key3... lastkey, codeYouWantToExecute)
。然后它调用新创建的函数并使用扩展运算符传递与每个键对应的值。
最后,这里是编译为 ES5 的答案:
(new (Function.bind.apply(Function, [void 0].concat(Object.keys(context), [codeYouWantToExecute])))()).apply(void 0, Object.values(context));
这会计算给定上下文中的表达式,而无需在 vars 前加上 'this'。
有警告但工作正常。
也可以在表达式中加入自定义函数。
只需调用一个 'evaluate' 函数,传入您的表达式、上下文和一个可选函数。
上下文必须是平面结构(无嵌套元素)并且函数必须在字符串表达式中引用为 'fn'。在这种情况下答案打印 11:
var expression = "fn((a+b)*c,2)";
var context = { a: 1, b: 2, c: 3 };
var func = function(x,y){return x+y;};
function evaluate(ex, ctx, fn) {
return eval("var "+JSON.stringify(ctx).replace(/["{}]/gi, "").replace(/:/gi, "=")+", fn="+fn.toString()+";"+ex);
}
var answer = evaluate(expression, context, func);
console.log(answer);
无法评论所以我 post,Joe 的回答很好,但如果字符串包含多个语句(如 x++; y++; x+y;
),y++
部分将永远不会被执行,因为它 returns 在第一个语句中。
通过此修改,将执行所有语句,最后一条语句将是 return 值。
function evalInScopeAndContext(scr, scopeAndContext) { // execute script in private context return (new Function( "with(this) { return eval('" + scr + "'); }")).call(scopeAndContext); }
我还更改了参数名称,因为该参数同时用作范围和上下文,它们是不同的东西。
警告 1:
使用此函数时,this.x
将等于 x
且无法更改,因为范围的 this
属性 如果存在,将被隐藏。
如果你执行evalInScopeAndContext('this', {a:1, b:2, this:3})
答案将是 {a:1, b:2, this:3}
而不是 3
.
要访问 属性 this
,您必须改写 this.this
,我认为它无法修复。
警告 2:
可以访问和修改外部作用域(从该函数的调用者到 window
),除非被作用域对象隐藏。
因此,如果您打算执行类似 location = 'house';
的操作但忘记在范围对象中插入 location
键,您将执行 window.location = 'house'
并可能造成损坏。
相反,this.location = 'house';
总是安全的,因为 this
总是存在,并且只会将 location
键添加到提供的范围对象中,因此您可能希望始终使用 this.
作为前缀