• sprinty
    2019-11-18
    ESModule 根据 import 构建依赖树,所以在代码运行前名字就是已经存在于上下文,然后在运行模块最顶层代码,给名字绑定值,就出现了‘变量提升’的效果。

    作者回复: Yes! 满分答案👍

    
     15
  • Y
    2019-11-18
    老师,关于这边文章的中心,我能总结成这个意思吗。
    export default function(){}。这个语法本身没有任何的问题。但是他看似导出一个匿名函数表达式。其实他真正导出的是一个具有名字的函数,名字的default。

    作者回复: 是的。不过,这算是题解。中心还是模块装载执行和标识符绑定全过程来着😄
    标识符和值绑定是“声明”语法处理的核心,而六种声明是js静态语法的核心。而静态语法,也就是这一整篇“语言如何构建”的核心了🤓

    
     5
  • 许童童
    2019-11-18
    为什么在 import 语句中会出现“变量提升”的效果?
    如老师所说,在代码真正被执行前,会先进行模块的装配过程,也就是执行一次顶层代码。所以如果import了一个模块,就会先执行模块内部的顶层代码,看起来的现象就是“变量提升”了。

    作者回复: 😃👍

    
     4
  • 海绵薇薇
    2019-11-22
    hello 老师好,感谢老师之前的回答,有醍醐灌顶之效。

    下面是读完这篇文章和下面评论之后的观点,不知是否有误,望指正,一如既往的感谢:)

    1.

    function a() {} // 函数声明,在六种声明内

    function () {} // 报错,以function 开头应该是声明,但是又没有名字

    (function() {}) // 函数表达式(这是一个正真的匿名函数(function() {}).name 为 “”),即使是具名函数(function a() {}),当前作用域也找不到a,因为这不是声明

    var a = function() {} // 函数定义,这里的function() {} 也是表达式,只是赋给了变量a,所以有了区别,也有了名字a.name为a,称作函数定义

    var b = function c() {} // 函数定义,函数function c() {} 也是表达式,只是赋值给了变量b,但是b.name却为c,和上面存在的区别,但也是函数定义

    2.

    导出的是"名字",我理解为名字就像一个绳子,后面拴的牛是会变的。这就是为什么import {a} from '../a.js' 这个a会变,虽然当前模块不能赋值给a。
    展开

    作者回复: 对哒!赞+2

    
     2
  • leslee
    2019-12-14
    是否可以理解为,一个具有了名字的函数表达式就可以称为函数定义

    作者回复: Yes. 这样理解没错。

    
     1
  • Marvin
    2019-11-20
    export default v=>v 这种,箭头函数是特例吗?

    作者回复: 有点特殊,但就处理逻辑(以及目的)上来说,也并不算是特例。

    其实“函数定义(Function Definition)”这个概念出现得比较奇怪。

    仔细分析一下就明白了,你想,“函数声明(Function Declaration)”是静态语义的,它在执行期的结果是empty,所以它必须是具名的才能导出,因为“声明(6种)”的目的都是具名,而export原则上只能“导出一个名字”。所以,由于“函数定义(Function Definition)”没有名字,所以它不能按函数声明来处理。

    然后,由于“函数表达式(Function Expression)”是动态语义的,有执行语义(也就是执行结果返回不是empty),得到一个运行期概念上的“闭包”。但这并不是最关键处,最关键的地方在于函数表达式没名字——即使是具名的函数表达式,它的名字也只能闭包内有影响。由于它没有名字一个可供导出的名字,所以也不能直接直接用作export的对象。

    那么到底在概念上该怎么说这个东西呢?ECMAScript在这里就加了这么一层概念,叫“函数声明(Function Declaration)”,一方面它是有静态语义的,它声明了某个东西;另一方面,它的名字又是迟绑定的,需要到了执行期根据“name = FunctionExpression”中的`name`来确认。

    在这种情况下,其实“函数定义(Function Definition)”就是“函数表达式”的一层概念封装:它又有在外层(或被关联的对象)中的名字,它又是表达式;它的执行结果又是闭包,又是实例。

    所以箭头函数看起来是特例,但用在导出语法的“这个位置”时,概念上却仍然是“封装了一层的‘箭头函数表达式’”,仍然还是“函数定义”。

     2
     1
  • leslee
    2019-11-19
    第三个结论推导过程的中间语法定义的引用那里(markdown '>' 符号表示的引用)读得不是很通顺, 有点迷....

    作者回复: 这是因为类似于:

    obj = {
      f: function() {
      },
      ...
    }

    这样位置中的匿名函数,在ECMAScript中都是称为“匿名函数定义”,而不是“匿名函数表达式”。所有在语法上记为“x = functionExpression”的,在处理上都与一般表达式有不同,这是一个非常非常小的细节,但在引擎层面,加入了好大一段逻辑呢。

    真正的匿名函数表达式,是下面这样的:
    > (1 + function() {})

    就是:把它直接用在一个表达式计算过程中,而不是把它用来赋值(或绑定,或引用)给另一个东西。这种情况下,它才是按匿名函数表达式来处理的。

    这几讲都是讲JavaScript的静态语言特性的,所以“词法分析以及对应的引擎处理”是要点,在词法分析阶段,关键在于“不能为它(函数、函数表达式、函数定义等等)创建闭包”。因为在静态处理阶段,还没有“闭包”这个概念,所以好多东西处理起来跟我们平常的理解不同,这就是根由了。

    
     1
  • 万籁无声
    2019-11-18
    感觉没有抓住主题思想在表达什么,可能是我层次太低了

    作者回复: 正好,刚写完“Y”同学的留言,你不妨看看,应该正好能回答你的疑问。

    (万恶的极客时间没有提供分留言链接的功能,产品同学要打手板心5次 🤔)

    
     1
  • 张旭.cheri
    2020-01-31
    console.log((function(){}).name)); // ""
    这行多了一个反括号

    作者回复: 多谢。我晚些请编辑处理一下~ ^^.

    
    
  • 情诗和你
    2019-12-29
    ExportDeclaration: export defaultAnonymousFunctionDefinition;
    这部分的排版是乱的。
    还有一部分斜体的字符串没有正确转换

    作者回复: 多谢指出。我已经反馈给编辑同学了~

    
    
  • 晓小东
    2019-12-19
    老师,我又来了,怕您看不到我的问题,接上一个问题,函数声明标识符不应该放入词法环境用中,本来我想函数声明标识符放入词法环境,来验证函数声明提升优先级高于var ,因为标识符的查找先从词法环境中查找,再到变量环境,再到上级作用域,从而实现声明的优先级。老师对于函数声明的优先级,你怎么看。

    作者回复: 关于这个问题,其实还挺好玩儿的,因为它涉及到`execute_context.VariableEnvironment`这个东西怎么用的问题。

    首先,其实词法环境(LexicalEnvironment)与变量环境(VariableEnvironment)并没有一个所谓优先级的问题。在实现上,它们之间是一个使用env.outer来衔接起来的链,所以所谓查找顺序,本质上就是二者谁在链的外层的问题。——然而,从实际实现的角度上,二者并不需要强调谁在外层,这种关系不是必须的(它们只需要衔接在一起就可以了)。

    除了在函数或全局初始化需要一个表来指示“哪些东西是var和函数名”之外,事实上区分var/let/const之间的必要性是不大的。并且即使是在这种情况下,引擎也并不需要VariableEnvironment这个东东的参与,因为在它们初始化时,引擎是可以访问来自源代码的ParserNode的。也就是说,它可以直接访问原始的信息,而不必依赖VariableEnvironment这个列表。

    VariableEnvironment这个东西,以及LexicalEnvironment,它们都是给运行期的上下文用的,也只在运行期才有意义。——更进一步的,只有对全局和函数,在它们的执行期才有意义(对函数来说,是它被调用的时候)。

    为什么呢?就目前而言,VariableEnvironment其实只在一种情况下被用到。——就是当全局或函数内出现eval('var x...')这样的代码的时候。因为只有在这种情况下,在相应的变量环境中,才会需要执行上下文去访问变量环境列表,并动态地向中间插入一个新的名字。由于事实上var变量只能全局和函数有用,所以四种执行上下文(Global/Function/Module/Eval)中,虽然都有这两个成员,但其实Module.VariableEnvironment是没有用的,而Eval.VariableEnvironment受限于是否是在严格模式(当处在非严格模式时,它指向外层的——例如函数的VariableEnvironment;当处在严格模式时,它将自己创建一个,以隔离开对外部环境的影响)。

    所以,本质上你来看VariableEnvironment这个东西的时候,不是要去“检查”它有什么样的优先级,而是直接看到“它有什么用,它怎么用”。再一次强调,对于单向链表访问来说,所谓“优先级”就是谁在链尾的问题;但即使如此,它对VariableEnvironment的使用来说也没有什么意义,因为VariableEnvironment归ExecuteContext使用,而ExecuteContext根本不care这个顺序。

    
    
  • 晓小东
    2019-12-18
    1. 老师问你一个问题, 我在看别人博客发现函数声明怎么放入的词法环境中(multiply 标识)不是说函数声明 是varDecls, 应该放入 VariableEnvironment, 在执行上下文创建中, 如下:(是正确的的吗)
    2. 怎么解读ECMA规范(整理架构比如目录结构), 本来想查阅文档来验证, 但不知所措,后期可不可以教教我们怎么翻阅ECMA规范文档, 来查阅某个东西。

    GlobalExectionContext = {

      ThisBinding: <Global Object>,

      LexicalEnvironment: {
        EnvironmentRecord: {
          Type: "Object",
          // 标识符绑定在这里
          a: < uninitialized >,
          b: < uninitialized >,
          multiply: < func >
        }
        outer: <null>
      },

      VariableEnvironment: {
        EnvironmentRecord: {
          Type: "Object",
          // 标识符绑定在这里
          c: undefined,
        }
        outer: <null>
      }
    }
    展开

    作者回复: 他这个写法是错误的。细节上有不少错误~ :(

    ECMAScript确实比较难读(是真的难),我也许今后会出一个专题来讲这个……但这个真不保证呵,因为想了解这个的人太少太少了。

    关于你的第一个问题,你打开ECMAScript规范,查找一下VariableEnvironment这个关键词,看一下它在哪些地方用的,怎么用的,就可以明白了。——之所以这么讲,是因为它只有24处,很少,通读一下也没什么,反倒有利于你熟悉文档。^^.

     1
    
  • 穿秋裤的男孩
    2019-11-29
    可以这样理解吗?
    静态解析期:export只导出名字到某个名字表,import从名字表获取映射关系。
    执行期:执行代码,为名字赋值。

    作者回复: 是的。这个“执行期”在用户代码之前。

    
    
  • 穿秋裤的男孩
    2019-11-29
    所谓模块的装配过程,就是执行一次顶层代码而已。

    这边的顶层代码是指什么呢?模块装配不是在静态解析期进行的吗?为什么还会执行代码?还是这边指的执行并不是一般意义上的执行呢?

    作者回复: ```
    // t.mjs
    console.log("here =>", typeof f);
    import f from './f.mjs';

    // f.mjs
    export default function() {}
    console.log('NOW');

    // test
    > node --experimental-modules t.mjs
    NOW
    here => function
    ```

    想想,
     1. 为什么`here`为什么是function呢?import语句还没有到呢。
     2. 为什么`NOW`在`here`之前?这是哪个时候的执行过程?

    
    
  • 穿秋裤的男孩
    2019-11-29
    老师,下面这句话我不是很懂,希望给解惑:

    ------
    因此,该匿名函数初始化时才会绑定给它左侧的名字“default”,这会导致import f from ...之后访问f.name值会得到“default”这个名字。
    ------
    根据这句话我实际运行了下代码,如下:
    // a.js
    export default function() {};

    // b.js
    import a from './a.js';
    console.log(a.name); // 打印为空字符串“”;

    // 疑惑点
    按照您的解释,这边不是应该打印default吗?很奇怪
    展开

    作者回复: 不知道你的引擎的情况,我这里显示会是default。

    ```
    // f.mjs
    export default function() {}

    // t.mjs
    import f from './f.mjs';
    console.log(f.name);

    // test
    > node --experimental-modules t.mjs
    default
    ```

     1
    
  • 海绵薇薇
    2019-11-28
    Hello 老师好:)

    函数定义

    var a = function foo() {

        console.log(foo)

    }

    当前上下文没有标识符foo,但是foo函数内却可以拿到该标识符,所以foo这个标识符应该是声明了,但是不在当前作用域,那么可以简单理解为

    var a = eval('\
        let foo;\
        foo = function (){\
            console.log(foo)\
        }\
    ')

    可以这么理解吗?
    展开

    作者回复: 是的。这样没问题。除了缺一咪咪的严谨之外,你的理解是对的。^^.

    
    
  • 陆昱嘉
    2019-11-20
    “声明(6种)”的目的都是具名,“函数声明(Function Declaration)”必须是具名的才能导出。
    既然这样,不就是这个函数声明function(){}具名导出给default这个名了吗?
    现在又说
    “它并不是导出了一个匿名函数表达式,而是导出了一个匿名函数定义。”
    不是函数表达式理解,为什么非说导出“函数定义”而不是“函数声明”呢?
    函数声明,函数表达式,和函数定义之间的区别联系是什么,搞晕了。
    展开

    作者回复: 这个问题请参考一下在评论区给Marvin的回复就好了。^^.

    
    
  • westfall
    2019-11-18
    export default function x() { }
    导出一个具名函数表达式
    export default function() { }
    不是导出一个匿名函数表达式,而是一个匿名函数定义

    作者回复: 第一个也不是“(具名)函数表达式”。因为如果它是表达式(expression),那么它的执行结果就是一个实例,而实例是无法导出的。只有它是一个“函数定义”,它才能在“静态语法分析之后、代码执行之前”被导出。

     4
    
  • Wiggle Wiggle
    2019-11-18
    文中多次出现“最顶层代码”,那什么是最顶层代码呢?

    作者回复: 这个细述起来有点复杂,因为有好些细节的地方都能被称为“顶层的(代码)”。简而言之,你把一个.js文件中所有在全局的声明语句去掉,剩下的就是顶层代码了。

    顶层这个概念其实挺重要的,因为tc39现在有一份提案在推,就是top-level await。这个在引擎实现的层面上,以及应用的层面上都挺关键的。

     4
    
  • 可可
    2019-11-18
    没有JavaScript基础的人表示,听起来很吃力啊,估计要先学学JavaScript,然后在多读几遍本专栏才行。

    作者回复: 呵呵,其实真讲JavaScript的地方并不特别的多。就好象讲数据结构总需要一门基础语言一样,那个基础语言其实不重要,重要的是数据结构。
    除了简单的语法和一些惯例之外,这门课真在讲“JavaScipt怎么用”的地方并不多。^^.

     1
    
我们在线,来聊聊吧