ES6导出调用内部函数的函数时,内部函数仅可被内部访问的技术原理
我目前在用基于Gulp、Babel7和Webpack4的ZURB Foundation 6.4框架,正在尝试ES6的export/import特性。以下是我的代码:
导出模块代码(./lib/eCommerceLogic.js)
// JS exporting function consoletest(){ console.log("HelloRequire!") } function commonAJAXCall(){ return $.get('https://jsonplaceholder.typicode.com/todos/1', { }) .then((response) => { response = JSON.stringify(response) console.log(response) console.log("AJAX happened") return response }) } export async function productnamesGetter(){ let productNameElements = document.getElementsByClassName('customProductCardName') await commonAJAXCall() console.log("result is ", productNameElements) }
导入模块代码
// JS importing import * as myModule from './lib/eCommerceLogic.js' $("#AJAXproductnames").on("click", function(){ myModule.productnamesGetter() })
我尝试直接调用未导出的consoletest和commonAJAXCall时会提示未定义错误,这符合预期,但我想从技术层面理解:ES6是怎么实现让导出的productnamesGetter能访问这些内部函数,而外部却无法直接调用它们的?
这背后核心是ES6模块的文件级私有作用域和函数闭包的结合,再加上Webpack这类模块打包器的静态分析与封装机制,具体拆解来看:
模块作用域的隔离性
每个ES6模块都是一个独立的、封闭的文件级作用域——模块里定义的所有未导出的变量、函数,都只存在于这个作用域内部,外部模块无法直接访问。就像你的consoletest和commonAJAXCall,它们是eCommerceLogic.js模块的"私有成员",模块作用域就像一道墙,把它们和外部完全隔离开。导出函数的闭包特性
你导出的productnamesGetter是在这个模块作用域内定义的函数,它天生就拥有对当前模块作用域的访问权(这就是闭包的本质:函数能记住并访问它定义时所在的词法作用域)。所以当productnamesGetter被导出到外部调用时,它依然能"拉取"模块内部的commonAJAXCall和consoletest来使用——这些内部函数的作用域并没有因为productnamesGetter被导出而消失,反而被闭包完整保留了下来。静态分析与绑定导出
ES6模块是静态的(编译时就确定导出和导入关系),Webpack和Babel在处理的时候,会把模块编译成一个封装好的立即执行函数(IIFE),模块的私有成员都被包裹在这个函数的作用域里,而导出的成员会被作为这个函数的返回值暴露出去。外部模块导入的其实是这些暴露出来的绑定,根本碰不到函数内部的私有变量/函数。
举个简化的编译后逻辑(模拟Webpack的处理):
var eCommerceLogicModule = (function() { // 模块私有作用域 function consoletest(){ console.log("HelloRequire!") } function commonAJAXCall(){ /* ... */ } async function productnamesGetter(){ /* ... */ } // 导出的内容作为返回值暴露 return { productnamesGetter: productnamesGetter }; })(); // 外部导入时拿到的是这个返回对象 const myModule = eCommerceLogicModule; myModule.productnamesGetter(); // 能正常调用,因为它闭包了私有函数 myModule.consoletest(); // 报错,因为返回对象里没有这个属性
简单说就是:模块把私有成员藏在自己的作用域里,导出的函数靠着闭包"带着"对这些私有成员的访问权,而外部只能拿到导出的函数本身,碰不到背后的私有成员。
内容的提问来源于stack exchange,提问作者Narktor

