JS 作用域链、导入导出

1. JS 的作用域链

作用域在 JS 中表示变量的可访问性和可见性。
JS 作用域有 3 种:1. 全局作用域;2. 函数作用域;3. 块级作用域

下面通过两种情况来解释作用域链:

  1. 简单情况
    在最外层声明一个函数,这个函数拥有全局作用域,同时这个函数里面的变量也拥有了这个函数的作用域,调用这个函数时,会先从这个函数里查找所需的变量,如果没有,则会到外层即全局作用域查找。
    此时作用域链为:fun1的函数作用域 -> 全局作用域
    这是父作用域和调用函数的作用域是同一个的情况下:
let a = 1
function fun1() {
    let a = 2
    console.log(a)
}
fun1() // 输出 2

let a = 1
function fun1() {
    // let a = 2 
    console.log(a)
}
fun1() // 输出 1,因为在 fun1 的作用域内没有找到变量 a,而 fun1 外层就是全局作用域,所以输出全局作用域里的变量 a
  1. 复杂情况
    如果父作用域和调用函数的作用域不在同一个的情况下:
let a = 1       
function fun2() {
    return function fun1() {
        let a = 2
        console.log(a)
    }
}   
fun2()() // 输出 2


let a = 1       
function fun2() {
    let a = 2
    return function fun1() {
        console.log(a)
    }
}   
fun2()() // 还是输出 2

let a = 1   
function fun2() {
    // let a = 2
    return function fun1() {
        console.log(a)
    }
}   
fun2()() // 输出 1

把函数 fun1 声明在函数 fun2 里,此时 fun1 的父作用域是 fun2 的作用域,然后在最外层调用 fun1,可以从第二段代码看到打印的值是 fun1 的父作用域 fun2 里声明的值。

可以得知函数的作用域链和在哪里调用没有关系,只取决于函数在哪里被声明,作用域链为:函数本身作用域 -> 声明函数时所在的作用域,以此往上类推,直到全局作用域。

或者换一种写法来帮助理解:

        let a = 1
        
        function fun1() {
            console.log(a)
        }

        function fun2() {
            let a = 2
            fun1() 
        }
        
        fun2() // 输出1

虽然在 fun2 里调用了 fun1,但 fun1 是在全局作用域被声明的,所以父作用域是全局作用域,当在 fun1 里没有找到变量 a 时,JS 会去 fun1 的父作用域即全局作用域寻找变量 a ,所以输出 1。

  1. 结论:
    可以得出作用域链是由变量或函数被声明时所在的作用域连接而成,且作用域在定义时就已经确定,不会改变,和变量被调用时的作用域没有关系。

2. JS 的导入导出

JS 导入导出有两种,一种 CommonJS 规范的,一种 es6 的,老师面试时问的是 export 和 export default 的区别以及如何 import ,这是 es6 的,我先回答这个。

export 和 export default 的区别
  1. export default

导出:
每一个模块只允许有一个默认导出:

// a.js
export default {
    name: 'csq',
    age: 20
}
// 等同于下面
// a.js
let info = {
    name: 'csq',
    age: 20
}
export default info

导入:
可以使用任意变量名来接收,在 b.js 文件导入上面的 info 对象:

// b.js
import person from './a.js'
console.log(person); // {name: 'csq', age: 20}
  1. export

导出:
每一个模块中可以定义多个命名导出(命名导出和默认导出可以同时存在):

// a1.js
let e1 = 1;
let e2 = 2;
let e3 = 3;
let e4 = 4;
export {e2};
export {e3};
export {e4};
export default e1;

可以直接导出变量表达式:

// a1.js
export let a = 1;

导入:
用 export 导出,只能使用 { } 的形式来接收
如果不需要,可以不在 { } 中定义
必须严格按照导出时候的名称,如果想换个变量名称接收,需要使用 as 来起别名

// b1.js
import  e1, { e2, e4 as aliasE4 } from './a1.js'
CommonJS 的 导入和导出

在 CommonJS 中有一个全局的require()方法,用于加载模块;module.export 和 exports 方法,用于导出模块。

require

require 是赋值过程,require 之后得到一个对象或数字或字符串或者函数等,再把结果赋值给某个变量,是普通的值拷贝传递。
因为是运行时调用,所以比如在项目中动态引入图片时会用到 require
require(url)

module.export 和 exports

Node 为每个模块提供一个 exports 变量,指向 module.exports,module.exports 初始值是一个空对象,等同于:

const exports = module.exports = {};

所以如果直接给 exports 赋值,会切断exports和 module.exports的关联关系;require 导出的内容是 module.exports 的内容,所以还是尽量都用 module.exports 导出:

// output.js
let a = 100;

console.log(module.exports); //能打印出结果为:{}
console.log(exports); //能打印出结果为:{}

exports.a = 200; // 把 module.exports 的内容给改成 {a : 200}

exports = '指向其他内存区'; // 把exports的指向指走

//input.js
var a = require('/utils');
console.log(a) // 输出 {a : 200}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。