是否有可能在JavaScript中实现动态范围而无需使用eval? [英] Is it possible to achieve dynamic scoping in JavaScript without resorting to eval?
问题描述
JavaScript具有词法作用域,这意味着从函数内访问的非局部变量将被解析为定义时该函数的父作用域中存在的变量。这与动态作用域形成对比,在动态作用域中,从函数内访问的非局部变量在被调用时被解析为该函数调用范围中存在的变量。
JavaScript has lexical scoping which means that non-local variables accessed from within a function are resolved to variables present in the parents' scope of that function when it was defined. This is in contrast to dynamic scoping in which non-local variables accessed from within a function are resolved to variables present in the calling scope of that function when it is called.
x=1
function g () { echo $x ; x=2 ; }
function f () { local x=3 ; g ; }
f # does this print 1, or 3?
echo $x # does this print 1, or 2?
上述程序以词法范围的语言打印1然后打印2,然后打印3然后打印1在动态范围的语言中。由于JavaScript是词法范围的,它将打印1然后2,如下所示:
The above program prints 1 and then 2 in a lexically scoped language, and it prints 3 and then 1 in a dynamically scoped language. Since JavaScript is lexically scoped it will print 1 and then 2 as demonstrated below:
var print = x => console.log(x);
var x = 1;
function g() {
print(x);
x = 2;
}
function f() {
var x = 3;
g();
}
f(); // prints 1
print(x); // prints 2
虽然JavaScript不支持动态范围我们可以使用 eval
实现它,如下所示:
Although JavaScript doesn't support dynamic scoping we can implement it using eval
as follows:
var print = x => console.log(x);
var x = 1;
function g() {
print(x);
x = 2;
}
function f() {
// create a new local copy of `g` bound to the current scope
// explicitly assign it to a variable since functions can be unnamed
// place this code in the beginning of the function - manual hoisting
var g_ = eval("(" + String(g) + ")");
var x = 3;
g_();
}
f(); // prints 3
print(x); // prints 1
我想知道是否还有另一个在不诉诸 eval
的情况下实现相同结果的可行方法。
I would like to know if there exists another possible way to achieve the same result without resorting to eval
.
编辑这是我试图实现而不使用 eval
:
This is what I'm trying to implement without using eval
:
var print = x => console.log(x);
function Class(clazz) {
return function () {
var constructor;
var Constructor = eval("(" + String(clazz) + ")");
Constructor.apply(this, arguments);
constructor.apply(this, arguments);
};
}
var Rectangle = new Class(function () {
var width, height;
constructor = function (w, h) {
width = w;
height = h;
};
this.area = function () {
return width * height;
};
});
var rectangle = new Rectangle(2, 3);
print(rectangle.area());
我知道这不是一个很好的例子,但一般的想法是使用动态范围来创建闭包。我认为这种模式有很大的潜力。
I know that it's not a very good example but the general idea is to use dynamic scoping to create closures. I think this pattern has a lot of potential.
推荐答案
属性查找属于原型链,与动态范围非常匹配。只需传递你自己的动态范围变量环境,而不是使用Javascript的词法范围。
Attribute lookup falls through the prototype chain, which matches quite well to dynamic scopes. Just pass your own environment of dynamically-scoped variables to use around instead of using Javascript's lexical scoping.
// Polyfill for older browsers. Newer ones already have Object.create.
if (!Object.create) {
// You don't need to understand this, but
Object.create = function(proto) {
// this constructor does nothing,
function cons() {}
// and we assign it a prototype,
cons.prototype = proto;
// so that the new object has the given proto without any side-effects.
return new cons();
};
}
// Define a new class
function dyn() {}
// with a method which returns a copy-on-write clone of the object.
dyn.prototype.cow = function() {
// An empty object is created with this object as its prototype. Javascript
// will follow the prototype chain to read an attribute, but set new values
// on the new object.
return Object.create(this);
}
// Given an environment, read x then write to it.
function g(env) {
console.log(env.x);
env.x = 2;
}
// Given an environment, write x then call f with a clone.
function f(env) {
env.x = 3;
g(env.cow());
}
// Create a new environment.
var env = new dyn();
// env -> {__proto__: dyn.prototype}
// Set a value in it.
env.x = 1;
// env -> {x: 1} // Still has dyn.prototype, but it's long so I'll leave it out.
f(env.cow());
// f():
// env -> {__proto__: {x: 1}} // Called with env = caller's env.cow()
// > env.x = 3
// env -> {x: 3, __proto__: {x: 1}} // New value is set in current object
// g():
// env -> {__proto__: {x: 3, __proto__: {x: 1}}} // caller's env.cow()
// env.x -> 3 // attribute lookup follows chain of prototypes
// > env.x = 2
// env -> {x: 2, __proto__: {x: 3, __proto__: {x: 1}}}
console.log(env.x);
// env -> {x: 1} // still unchanged!
// env.x -> 1
这篇关于是否有可能在JavaScript中实现动态范围而无需使用eval?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!