JS中的模块化

这篇博客探讨了JavaScript中的模块化开发,通过一个实例解释了如何使用立即执行函数表达式(IFE)创建闭包,以及如何通过`require`和`exports`暴露公共接口。文章详细阐述了`modules`变量的作用,它是模块私有的,用于存储和挂载模块。作者还分析了模块定义和实现的过程,展示了如何在模块间进行调用,并强调了闭包在保持词法作用域中的变量存活中的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天看YDKJS时,明白了一个以前一直疑惑的东西,那就是JS中的模块开发,之前也使用过require.s进行模块化的开发,但是一直不能明白其中具体的原理,看了YDKJS的讲解后,真的是豁然开朗,在此记录其中一个实例的代码,并进行解析。

	var myModules = (function  foo(){
		var modules = {};
		function get(name){
			return modules[name];
		}
		function define(name, deps, impl){
			for(var i = 0; i < deps.length; i++){
				deps[i] =  modules[ deps[i] ];
			}
			modules[name] = impl.apply( impl, deps );
		}
		return {
			get: get,
			define: define
		}
	})();
	
	myModules.define('foo',[],function(){
		function hello(val){
			console.log('hello',val);
		}
		return {
			hello: hello
		}
	});
	myModules.define('test',['foo'],function(foo){
		function useHello(val){
			var log = "I am from test" + val;
			foo.hello( log );
		}
		return {
			useHello: useHello
		}
	})	
	myModules.get('test').useHello('hello  world');

说明:

  1. IFFE
    在整个myModules 定义的最外层,使用了立即执行函数表达式var myModules =(function foo(){...})(),这样做的目的是为了形成一个闭包,使用myModules 保存函数表达式function foo(){...}其中的词法作用域。

  2. 暴露公共接口
    使用return {get: get, define: define },是为了将公共的接口getdefine暴露出来,可以在myModules上调用这些方法。同时因为这两个方法的内部都使用了变量modules,所以modules就会在内存中被保存起来,类似于myModules的私有变量。

  3. 私有变量modules
    变量modules类似于myModules 的私有变量,不通过myModules 是无法访问到这个变量的,且其会一直保存在内存中。使用这个变量,结合define方法,将所有定义的模块都挂载到modules上。

  4. 模块定义的方法

    function define(name, deps, impl){
    	for(var i = 0; i < deps.length; i++){
    		deps[i] =  modules[ deps[i] ];
    	}
    	modules[name] = impl.apply( impl, deps );
    }
    

    name: 定义的模块名称
    deps: 模块对应的依赖
    impl: 模块的实现

    先是使用循环根据依赖deps中的依赖模块名,从modules中得到具体的挂载在modules上的模块数组。
    然后是下面这句很精髓的话:
    modules[name] = impl.apply( impl, deps );
    deps依赖模块数组作为参数传递给该模块的实现方法,并使用apply调用当前模块的实现方法。那么此时我们可以发现,实际挂载到modules上的并不是模块具体的实现方法,而是模块的返回值。但是模块的返回值是一个对象,这个对象中的值有可以引用了模块中定义的方法变量,如此一来,模块暴露出来的方法和变量中涉及的词法作用域中的变量就会被保存起来,而不会在内存中消失。其实还是应用了所谓的闭包。

  5. 模块定义的实现
    我们来看看具体的模块定义和实现

    myModules.define('foo',[],function(){
       function hello(val){
       	console.log('hello',val);
       }
       return {
       	hello: hello
       }
    });
    

    我们定义了foo模块,在foo模块中,我们暴露了hello方法,实际上此时 myModules内部的modules是这样的

    modules = {
    	foo:{
    		hello: hello
    	}
    }
    

    可以看到modules上挂载的foo模块实际上是foo模块定义时的返回值,但是这个返回值中引用的方法hello此时会一直保存在内存中。

  6. 模块间调用

    	myModules.define('test',['foo'],function(foo){
    		function useHello(val){
    			var log = "I am from test" + val;
    			foo.hello( log );
    		}
    		return {
    			useHello: useHello
    		}
    	})
    	
    	myModules.get('test').useHello('hello  world');
    

    我们在模块test中调用了foo模块。注意,在test模块的实现中,我们加入了参数foo, 那么实际定义的过程如下

    impl.apply( impl,  modules['foo'] );
    

    等价于

    impl.apply( impl,  { hello: hello } );
    

    所以,我们在test模块的实现中,可以直接使用变量foo,就像真的在调用foo模块一样,然后后我们再将test模块中的返回值挂载到modules

    modules['test'] = impl.apply( impl,  { hello: hello } );
    

    以上是我个人对于js中的模块化的理解,如有错误,欢迎各位老铁指正。

    参考资料:
    [1]: https://github.com/Alizwell/You-Dont-Know-JS/blob/1ed-zh-CN/scope %26 closures/ch5.md

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值