zoukankan      html  css  js  c++  java
  • 你真的懂 export default 吗?

    export default A 和 export { A as default } 乍一看是一样的,但是里面有一些细微的区别比较容易留坑。本文介绍两种写法的不同之处。

    import 语句导入的是引用,不是值

    有导出就必然有导入,我们先明确下 import 语句的工作原理。

    import { A } from './module.js';
    

    显而易见,在上面的代码中,A 和 ./module.js 中的 A 是相同的。再看这段代码:

    const module = await import('./module.js');
    const { A: destructuredA } = await import('./module.js');
    

    在这段代码中,module.A 与 A 是相同的,但是因为 destructuredA 是结构赋值,因此就有一些不同了。

    我们来看下 ./module.js

    // module.js
    export let A = 'initial';
    
    setTimeout(() => {
      A = 'changed';
    }, 500);
    

    导入 ./module.js 的代码为 ./main.js

    // main.js
    import { A as importedA } from './module.js';
    const module = await import('./module.js');
    let { A } = await import('./module.js');
    
    setTimeout(() => {
      console.log(importedA); // "changed"
      console.log(module.A); // "changed"
      console.log(A); // "initial"
    }, 1000);
    

    import 语句导入的是引用,也就是说,当 ./module.js 中 A 的值发生变化的时候,./main.js 中也会跟着变化。解构赋值获得的 A 不会变化是因为解构过程中是使用的值赋值给了新变量,而不是引用。

    值得注意的是,静态语句 import { A } ... 虽然看着像解构赋值,实际上与解构赋值并不相同。

    小结一下:

    // 以下代码获得是引用
    import { A } from './module.js';
    import { A as otherName } from './module.js';
    import * as module from './module.js';
    const module = await import('./module.js');
    // 以下代码获得的是值
    let { A } = await import('./module.js');
    

    export default 语句

    我们修改下 ./module.js

    // module.js
    let A = 'initial';
    
    export { A };
    export default A;
    
    setTimeout(() => {
      A = 'changed';
    }, 500);
    

    同时也修改 ./main.js

    // main.js
    import { A, default as defaultA } from './module.js';
    import anotherDefaultA from './module.js';
    
    setTimeout(() => {
      console.log(A); // "changed"
      console.log(defaultA); // "initial"
      console.log(anotherDefaultA); // "initial"
    }, 1000);
    

    输出结果是 "initial",为什么呢?

    我们知道,我们可以直接 export default 'hello'; 但是却不能 export { 'hello' as A }。规范在这两种语法上有一点不同。export default 后面的将会被作为表达式对待。因此我们可以 export default 'hello';, 甚至可以 export default 1 + 2;。因此,在 export default A 中,A 是作为表达式语句使用的,因此使用的是 A 的值。因此,当 A 的值在 setTimeout 中被改变的时候,export default 出去的值并没有变化。

    小结一下:

    // 引用
    import { A } from './module.js';
    import { A as otherName } from './module.js';
    import * as module from './module.js';
    const module = await import('./module.js');
    // 值
    let { A } = await import('./module.js');
    
    // 导出引用
    export { A };
    export { A as otherName };
    // 导出值
    export default A;
    export default 'hello!';
    

    export { A as default } 语句

    export {} 导出的始终是一个引用,因此:

    // module.js
    let A = 'initial';
    
    export { A, A as default };
    
    setTimeout(() => {
      A = 'changed';
    }, 500);
    

    同样,在先前的 ./main.js 中:

    // main.js
    import { A, default as defaultA } from './module.js';
    import anotherDefaultA from './module.js';
    
    setTimeout(() => {
      console.log(A); // "changed"
      console.log(defaultA); // "changed"
      console.log(anotherDefaultA); // "changed"
    }, 1000);
    

    小结下:

    // 导入引用
    import { A } from './module.js';
    import { A as otherName } from './module.js';
    import * as module from './module.js';
    const module = await import('./module.js');
    // 导入值
    let { A } = await import('./module.js');
    
    // 导出引用
    export { A };
    export { A as otherName };
    export { A as default };
    // 导出值
    export default A;
    export default 'hello!';
    

    export default function 语句

    虽然,前面说过 export default 后面的会被作为表达式使用。但是也有一些例外:

    // module.js
    export default function A() {}
    
    setTimeout(() => {
      A = 'changed';
    }, 500);
    
    // main.js
    import A from './module.js';
    
    setTimeout(() => {
      console.log(A); // "changed"
    }, 1000);
    

    输出 "changed",因为 export default function 有其特殊的语法,在这个语法中,函数是作为引用传递的。

    我们稍微做一下修改:

    // module.js
    function A() {}
    
    export default A;
    
    setTimeout(() => {
      A = 'changed';
    }, 500);
    

    此时控制台输出 ƒ A() {}export 语句不再符合 export default function 语法形式,A 便使用了值传递。

    不仅仅 export default functionexport default class 也是同样的表现。

    为什么呢?

    原因与这些语句当被用作表达式时的表现有关。

    function someFunction() {}
    class SomeClass {}
    
    console.log(typeof someFunction); // "function"
    console.log(typeof SomeClass); // "function"
    

    如果我们将他们变成表达式:

    (function someFunction() {});
    (class SomeClass {});
    
    console.log(typeof someFunction); // "undefined"
    console.log(typeof SomeClass); // "undefined"
    

    function 和 class 语句会在作用域/块中创建标识符,而 function 和 class 语句却不会,尽管他们的函数名和类名可以被使用。

    因此,下面代码中:

    export default function someFunction() {}
    console.log(typeof someFunction); // "function"
    

    如果不进行特殊处理的话,输出的将会是 "undefined"

    小结如下:

    // 导入引用
    import { A } from './module.js';
    import { A as otherName } from './module.js';
    import * as module from './module.js';
    const module = await import('./module.js');
    // 导入值
    let { A } = await import('./module.js');
    
    // 导出引用
    export { A };
    export { A as otherName };
    export { A as default };
    export default function A() {}
    // 导出值
    export default A;
    export default 'hello!';
    

    在早些时候,模块中的默认导出是这样的 export default = A,这样看来,A 被当做表达式会更明显一些。

    欢迎关注公众号“众里千寻”或者在我的网站浏览更多更系统的信息。

  • 相关阅读:
    java foreach遍历的前提条件
    Java中的null值总结
    mybatis不可忽略的细节
    设计模式:创建型模式
    设计模式(四):原型模式
    设计模式(三):建造者模式
    设计模式(二):单例模式(DCL及解决办法)
    设计模式(一):简单工厂、工厂模式、抽象工厂
    定时任务 ScheduledExecutorService
    快速访问GitHub
  • 原文地址:https://www.cnblogs.com/wl-blog/p/15009596.html
Copyright © 2011-2022 走看看