是否可以在不使用eval的情况下在JavaScript中实现动态作用域?

JavaScript具有词法作用域,这意味着从函数内部访问的非局部变量在定义时将解析为该函数的父级作用域中存在的变量。这与动态作用域相反,在动态作用域中,从函数内部访问的非局部变量在调用时将解析为该函数的调用范围中存在的变量。


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,如下所示:


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以下方法实现它:


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



SMILET
浏览 516回答 3
3回答

拉风的咖菲猫

属性查找贯穿整个原型链,该原型链与动态范围非常匹配。只需传递您自己的动态范围变量环境即可使用,而不是使用Javascript的词法作用域。// 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 classfunction 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中,只要您使用以下内容:函数声明语句或函数定义表达式,则局部变量将具有词法范围。函数构造函数,然后局部变量将引用全局范围(顶级代码)this 是JavaScript中唯一具有动态作用域并通过执行(或调用)上下文设置的内置对象。因此,要回答您的问题,在JS中this,该语言已经是动态作用域的功能,您甚至无需模仿其他功能。

明月笑刀无情

为什么没有人说this?您可以通过将变量绑定到上下文来将变量从调用范围传递到被调用函数中。function called_function () {   console.log(`My env ${this} my args ${arguments}`, this, arguments);   console.log(`JS Dynamic ? ${this.jsDynamic}`);}function calling_function () {   const env = Object.create(null);   env.jsDynamic = 'really?';   ...    // no environment   called_function( 'hey', 50 );   // passed in environment    called_function.bind( env )( 'hey', 50 );也许值得一提的是,在严格模式下,默认情况下,所有函数都不会发送“环境”(this为null)。在非严格模式下,全局对象是被this调用函数的默认值。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript