草庐IT

javascript - 从 ES6 模块导入函数表达式或函数声明有什么区别?

coder 2024-07-14 原文

据我了解( see section 16.3.2.1 ),ES6 允许函数/类导出操作数使用不同的语法。区别在于导出的函数是否需要在导入时解释为函数声明,在这种情况下你写:export default function () {} // (a)或作为函数表达式:export default (function () {}); // (b) .

作为一个可能的相关旁注:我读到进口被提升,但我不确定在这种情况下这意味着什么。

以本例为例:
import foo from 'my_module'; // (c)
据我了解,上面的语句会将我导出的函数保存在 foo 中。变量。该变量是否已提升,或者是什么,以及何时提升?

最重要的是,当 foo 有什么区别(在设置 my_module 方面)使用 (a) 导出函数当它使用 (b) 导出时?

最佳答案

你的问题有点复杂,但我会尽力解释一切。

让我们首先确定模块的一般工作方式。一个模块有一组导出的名称,每个名称都指向该模块中的一个局部变量。导出的名称不需要与本地绑定(bind)的名称相同。导出的名称之一可以是 default ,为此有专门用于模块仅导出单个事物的特殊语法(在导出和导入中)。

I read that imports are hoisted, but I'm not really sure what that means in this context:

import { foo } from 'my_module';


是的,进口申报被提升。类似于 varfunction (实际上就像 every other declaration )标识符 foo在模块中的任何语句被执行之前,它从一开始就可用。事实上,绑定(bind)甚至在声明的 var 之前创建。文件。

不同之处在于它们的初始化方式:
  • var s 初始化为 undefined
  • function s 和 function* s 用函数对象
  • 初始化
  • let , constclass es 未初始化
  • 导入的绑定(bind)甚至没有真正初始化,它们被创建为指向导出名称在导入模块中引用的局部变量的指针
  • 导入的模块( import * as … )使用模块对象(其属性也是此类指针)进行初始化

  • When is foo set to refer to my exported function?



    简短的回答:在其他一切之前。

    长答案:它还没有真正设置。它是对您希望保存函数的导入模块中的局部变量的引用。当不是 const 时,局部变量可能会改变- 但我们通常不期望那当然。通常它已经包含该函数,因为 导入的模块在 之前完全评估导入它的模块是。所以如果你担心 var functionName = function() {} vs function functionName() {} 有问题你可能会松口气 - 没有。

    现在回到你的标题问题:

    What is the difference between exporting a function expression and a function declaration in a ES6 module?



    没什么特别的,这两个方面其实没有太大的关系:
  • export声明将导出名称链接到模块范围内的局部变量
  • 模块作用域中的所有变量都像往常一样被提升
  • 函数声明的初始化方式与使用函数表达式赋值的变量声明不同,as usual

  • 当然,仍然没有充分的理由不在任何地方使用更具声明性的函数声明;这在 ES6 模块中与以前没有什么不同。如果有的话,使用函数表达式的理由甚至可能更少,因为声明涵盖了所有内容:
    /* for named exports */
    export function foo() {…}
    
    // or
    function foo() {…}
    export {foo as foo}
    
    /* for default exports */
    export default function foo() {…}
    
    // or
    function foo() {…}
    export {foo as default}
    
    // or
    function foo() {…}
    export default foo;
    
    // or
    export default function() {…}
    

    好吧,最后两个默认导出声明实际上与前两个有点不同。链接到导出名称的本地标识符 default不是 foo ,但是 *default* - 不能重新分配。这在最后一种情况下(没有名称 foo )是有意义的,但在倒数第二个情况下,您应该注意到 foo实际上只是一个本地别名,而不是导出的变量本身。我建议不要使用这种模式。

    哦,在你问之前:是的,最后一个默认导出实际上也是一个函数声明,而不是一个表达式。匿名函数声明。这是 ES6 的新功能 :-)

    So what exactly is the difference between export default function () {} and export default (function () {});



    对于每个目的,它们几乎相同。它们是匿名函数,带有 .name属性(property)"default" , 由那个特殊的 *default* 持有导出名称 default 的绑定(bind)指向匿名导出值。
    它们唯一的区别是提升 - 声明将在模块顶部实例化其函数,只有在模块代码的执行到达语句时才会评估表达式。然而,鉴于没有变量具有可访问的名称,这种行为是不可观察的,除了一个非常奇怪的特殊情况:导入自身的模块。嗯,是的。
    import def from "myself";
    def(); // works and logs the message
    export default function() {
        console.log("I did it!");
    }
    
    import def from "myself";
    def(); // throws a TypeError about `def` not being a function
    export default (function() {
        console.log("I tried!");
    });
    

    无论如何,你真的不应该做这些事情。如果要在模块中使用导出的函数,请在其声明中为其命名。

    In that case, why have both syntaxes?



    发生。这是规范允许的,因为它没有额外的异常(exception)来禁止某些无意义的事情。它不打算使用。在这种情况下,规范甚至明确禁止 functionclass export default 中的表达式语句并将它们视为声明。通过使用分组运算符,您发现了一个漏洞。干得好。不要滥用它。

    关于javascript - 从 ES6 模块导入函数表达式或函数声明有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35223111/

    有关javascript - 从 ES6 模块导入函数表达式或函数声明有什么区别?的更多相关文章

    1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    2. ruby - 在 Ruby 中使用匿名模块 - 2

      假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

    3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

      作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

    4. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

      我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

    5. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

      我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

    6. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    7. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

      为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

    8. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

      我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

    9. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

      在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

    10. ruby-on-rails - active_admin 目录中的常量警告重新声明 - 2

      我正在使用active_admin,我在Rails3应用程序的应用程序中有一个目录管理,其中包含模型和页面的声明。时不时地我也有一个类,当那个类有一个常量时,就像这样:classFooBAR="bar"end然后,我在每个必须在我的Rails应用程序中重新加载一些代码的请求中收到此警告:/Users/pupeno/helloworld/app/admin/billing.rb:12:warning:alreadyinitializedconstantBAR知道发生了什么以及如何避免这些警告吗? 最佳答案 在纯Ruby中:classA

    随机推荐