Java教程

JavaScript中的(模块化)导入与导出

本文主要是介绍JavaScript中的(模块化)导入与导出,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

JavaScript是没有作用域(局部)和命名空间这一概念的,而JavaScript又是脚本(解释型)语言,当js引擎在解析它时,将其放在「一起」自上而下开始解释(生成ast,然后生成字节码,最后编译成机器码),这就在解释时会遇到一个显然的问题:符号覆盖(symbol overhead)「在编译型语言中就是符号冲突,符号冲突在编译时就会报错,而符号覆盖并没有任何错误提示,能正常解释执行,显然这是无法接受的」

因此,在工程实践中,JavaScript的模块化就显得十分必要。

在没有模块化的JS世界中:

//index.html
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>demo</title>

		<script src="alex.js" type="text/javascript"></script>
		<script src="lib.js" type="text/javascript"></script>
				
	</head>
	<body>
	</body>
</html>

//lib.js
let value = 'value'
//bob.js
alert(value)

bob.js中显然没有定义过变量value,但是能正常输出变量值value,这是因为在index.html中先加载了alex.js文件后加载的bob.js文件,因为js并没有命名空间这一概念,所以能正常输出(等价于将所有js文件集中到同一个文件中)。

显然,这样是无法展开正常的编码的,各个文件(模块)无法做到有效隔离(全是各种变量污染)

(于是各种骚操作就来了...)

//lib.js
let mod = (function () {
	
	let mod = {}
	mod.value = 'value'
	mod.say_sm = function(){
		alert('say sm')
	}
	return mod
	
})()
//bob.js
alert(mod.value)
mod.say_sm()

正常输出valuesay sm

直接在lib.js中返回一个对象mod,JavaScript中是存在函数作用域的,所以通过立即调用函数,返回一个对象,这样其他js文件就能直接使用这个对象了。(在这个立即调用函数内部可以定义各种变量和函数,直接将其给到这个mod对象即可),这是个一个妥协的方式,本质上并没有避免变量污染(mod这个变量有可能污染其他js文件中的同名变量mod),并且在html文件中存在先后依赖关系(被依赖的文件必须先被加载),这在存在大量js文件的情况下...

JavaScript自诞生起直到2015年都不支持模块化,直到ES6标准时才在语言层面支持模块化,而此前业界通常采用制定「行业规范」的方式来「实现(模拟)」模块化(module)。

  • 模块化规范
  • 语言的支持

业界流行的规范为CommonJS,这份规范被实现得完整通用。

ES6对模块化的支持

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>demo</title>

		<script src="lib.js" type="module"></script>
		
		<script src="bob.js" type="module"></script>
		
	</head>
	<body>
	</body>
</html>

//lib.js
let value = 'value'
function say_sm(){
	
	alert('say sm')
}

export{
	value,
	say_sm
}
//bob.js
import {value,say_sm} from './module/lib04.js'
alert(value)
say_sm()

如上所示,ES6中引入2个关键字来支持模块化,importexport,这是js的关键字而非函数,作用于js 的解释期(AST),而非运行期。(因此不能支持条件导入)

let flag = 1
if(flag == 1){
  import {value,say_sm} from './module/lib04.js'
}
//解释期报错,因为if条件需要等到执行期才能判断,在编译为AST时是无法判断的,而import在编译期就要开始生成代码。故直接parse语法错误

nodejsCommonJS的支持

//lib.js
let value = 'value'
function say_sm(){
    console.log(value)
}

module.exports.value = value
module.exports.say_sm = say_sm
//bob.js
const lib = require('./lib.js')
console.log(lib.value)
lib.say_sm()

以上就是nodejs中的模块化支持(非语言层面的支持),其中,每个js文件就是一个模块,在nodejs中,有一个全局对象module,它是Module类的一个实例,这个实例有一个属性,exports,在也是一个对象,在module实例被创建时默认初始化为{}(一个无属性的对象)(为了「彻底」支持commonjs规范,module实例中还有一个属性exports,这个值被直接赋值为module.exports,即exports = module.exports

函数require直接返回一个对象,这个对象并非一个新对象,而是直接返回lib.js模块中的对象module中的属性(对象)exports

image.png

所以,nodecommonjs的支持本质还是在做「对象共享」这一件事。

一个js文件被加载时,node便实例化了一个module对象,然后创建了一个对象exports对象

let module = new Module()
module.exports = {}

这个module.exports对象位于堆内存的某个地址处address_x

当其他模块引用lib.js时(require),直接返回address_x地址值(对象)

image.png

其他js模块require同一个lib.js时,本质是在共享module.exports对象,因此,任何一个模块对exports对象的修改都会反应到其他js文件中

//bob.js
const lib = require('./lib.js')
lib.value = 'bob'
//在其他js文件中require('./lib.js'),value的值也被修改为bob

ES6中对模块的支持则不存在这一问题,其机制并非通过共享对象来实现的,而是通过Binding Env Block机制来AST期间实现

image.png

以上,就是基本的Js中的导入与导出

这篇关于JavaScript中的(模块化)导入与导出的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!