JavaScript vars存储在哪里?

Dmi*_*try 9 javascript lisp lambda ecmascript-6

我正在玩普通的lisp,并意识到与Lisp不同,所有局部变量都是参数(通过lambda)lambda或通过参数(通过let).

换句话说,他们总是遵循IIFE惯用语:

((x, y, z) => {
   /* I has variables */
})(1, 2, 3);
Run Code Online (Sandbox Code Playgroud)

例如

((lambda (x y z)
    ; I has variables
) 1 2 3)
Run Code Online (Sandbox Code Playgroud)

要么

(let ((x 1)(y 2)(z 3))
    ; I has variables
)
Run Code Online (Sandbox Code Playgroud)

在JavaScript中,vars"感觉"像setq,但是如果没有声明局部变量影响它,则setq变异全局范围,在JavaScript中,vars不会改变全局范围,无论如何.

假设我想在Lisp中执行此操作:

(function() {
    var x = 1;
    var y = 2;
    var z = 3;

    /* woo, I has 3 vars */
})();
/*note that the vars no longer exist */
Run Code Online (Sandbox Code Playgroud)

如果我尝试这样做:

; progn is like lambda but never has arguments and automatically iifes itself.
; eg (progn (setq x 1)) is ((lambda () (setq x 1))
(progn
    (setq x 1)
    (setq y 2)
    (setq z 3)
    ; Woo I has 3 vars
)
; oops, I polluted global scope :(
Run Code Online (Sandbox Code Playgroud)

为了获得这种类似JavaScript的感觉,我最终会做类似的事情

; wait, we're writing smalltalk now? 
; [ 
;     | x y z | 
;     x := 1.
;     y := 2.
;     z := 3.
;     "I has three vars..."
; ] value.
(let
    ((x)(y)(z))

    (setq x 1)
    (setq x 2)
    (setq x 3)
)
Run Code Online (Sandbox Code Playgroud)

奇怪的是,Lisp似乎没有与JavaScript var/let/const并行; 在上面的例子意义上(是吗?我对Lisp不是很熟悉......).

我的问题是; vars实际存储在哪里?它们不作为参数传递,并且它们没有在参数中显式声明......但是它们必须存储在某个地方,而某个地方不是全局范围......

T.J*_*der 10

JavaScript变量是名为LexicalEnvironment对象的对象的"绑定" .当执行进入可以拥有自己的绑定的范围时(例如输入函数时),将创建一个新的LexicalEnvironment并使用参数,本地和本地声明的函数进行填充.

这是一个规范构造; JavaScript引擎如何实际实现它取决于引擎,只要它忠实地复制规范的语义.规范中没有任何内容可以让我们直接访问该对象.(特别是:如果没有关闭变量,它们可以很好地在堆栈上实现并通过在退出时重置堆栈指针来清理.)

请注意,这就是闭包在JavaScript中的工作方式,因此阅读闭包可以让您更深入地了解变量的存储位置.简而言之:当您创建函数时,该函数对活动的LexicalEnvironment对象(创建时)具有(稍微间接)引用.由于对象包含变量的绑定,因此函数可以通过对象访问它们.

一个具体的例子可能有所帮助; 看评论:

// A function that returns a function that closes over its
// local variable
function f() {
  // When this function is called, a *LexicalEnvironment* object
  // is created and populated with an `a` variable
  // (and a few other things)
  var a = Math.random();
  
  // If we create a function, it gets a reference to the object,
  // and so it can access that variable
  return function() {
    return a;
  };
}

// Create a lexical environment containing a variable, get back
// a function with access to it
var f1 = f();

// Do it again
var f2 = f();

// Now we have *two* separate lexical environment objects (well,
// more, but two related to `f`). They both continue to exist
// as long as there's something referring to them (like all other
// objects). Our `f1` and `f2` each refer to one of them, so they
// still exist and `f1` and `f2` can use the `a` on each of them:
console.log(f1());
console.log(f2());

// Now we release the functions, which release the lexical
// environment object they had references to
f1 = f2 = undefined;
Run Code Online (Sandbox Code Playgroud)


Rai*_*wig 5

在Common Lisp中,为变量赋值setq不会像这样声明变量.这种块范围没有机制,提到变量将在最内部块范围内创建它.

你提到的letlambda已经.

注意lambda&aux变量:

(lambda (&aux (x 1) (y 2) (z 3))

  ; x and y and z are variables here...

  )
Run Code Online (Sandbox Code Playgroud)

这些辅助变量需要在lambda参数列表中声明,但不是调用函数的参数的一部分.

例:

CL-USER 61 > ((lambda (x &aux (y (* x x)) (z 12))
                (+ y x z))
              5)
42
Run Code Online (Sandbox Code Playgroud)

你的榜样

CL-USER 63 > (let (x y z)

               (setq x 1)
               (setq y 2)
               (setq z 3)

               (+ x y z))
6
Run Code Online (Sandbox Code Playgroud)

是一个受欢迎的选择,特别是因为几年前它通常被写为:

CL-USER 72 > (prog (x y z)

               (setq x 1)
               (setq y 2)
               (setq z 3)

               (return (+ x y z)))
6
Run Code Online (Sandbox Code Playgroud)

prog提供,局部变量和标签体.在标签体中,我们可以通过go构建本地标签和跳转.

简介:这意味着您可以拥有块本地或函数局部变量,但您必须首先在Common Lisp中声明它们.在某些其他语言中,您可以在某个范围的正文中执行此操作.

当您以这种方式输入块时,词汇变量列表是已知且已修复的.语言实现不需要扫描正文以获取新变量,也不需要提供某种方法来使用新变量扩展当前词法环境.