SoFunction
Updated on 2025-02-28

Sharing of Require calling js instance in JavaScript

When I first started writing JavaScript functions, it usually looks like this:

function fun1() {
 // some code here
}
function fun2() {
 // some other code here
}
...

All functions are written in the global environment, the projects are small, and there are usually no conflict problems.

But after more code, I gradually found that the function name (English vocabulary) was a bit insufficient. So the concept of namespace was introduced and modular code began.

Functions under namespace

Under the namespace, my code is written like this:

var com = com || {};
 =  || {};
.module1 = (function() {
 // some code here
 return {
 func1: func1,
 ...
 };
}());
.module2 = (function() {
 // some other code here
 return {
 func1: func1,
 ...
 };
}());
...

Based on the principle of object-oriented, I usually write this way to execute functions:

.module1.({},['arg1',arg2]);
...

Of course, in order to type less characters, I will also import 1 public API interface in the closure:

(function($, mod1) {
 // some code here
 mod1.({},['arg1',arg2]);
}(jQuery, .module1));
...
 

At this point, the possibility of code conflicts is already very small, but the problem of code dependence, multi-script file management and blocking are gradually emerging - the namespace method is beginning to emerge.

So Require.js2 appears.

First, let’s understand the concept of module 3:

A module is different from a traditional script file in that it defines a well-scoped object that avoids polluting the global namespace. It can explicitly list its dependencies and get a handle on those dependencies without needing to refer to global objects, but instead receive the dependencies as arguments to the function that defines the module.

Simply put, there are two points: 1. The module scope is self-organized and does not pollute the global space; 2. The module indicates the dependency relationship, and the dependency is imported through parameter passing, without reference through global object - dependency also does not pollute the global space.

Define modules

Unlike the old-fashioned namespace method above, the module is defined using the global method, and the form is as follows:

define(id?, dependencies?, factory); // ? Indicates optional 

I will divide the module into two types.

Dependless module

If a module does not rely on other modules, it is very simple to define, such as the module hello is placed in a file:

define(function() {
 // some code here
 return {
 // some public api
 };
});
 

Dependable modules

Modules with dependencies should be a little more complicated. When defining, we need to list the dependencies of modules:

define(['jquery'], function($) { // For example, this module, the code execution depends on jQuery. The jquery module code will be loaded first and executed, and then the dependency module will be passed into the callback function as a parameter of $. The callback function will register the execution result as a module // maybe some code here
 return {
 // some public api
 };
});
 

Here, the 'jquery' in the dependency is the path of the module relative to baseUrl, equivalent to the module ID.

Now, look at the code that imports public APIs in the closure written above, and compare them with the define function:

(function($, mod1) {
 // some code here
 mod1.({},['arg1',arg2]);
}(jQuery, .module1));

In this code, I also imported jQuery. In the closure, I also accessed jQuery through the external parameter passed in $. It can be said that its "definition dependencies" method is very similar to the define method. The difference is that the jquery imported by define is not a global variable, so it will not pollute the global environment.

About module name

The define function has three parameters. The first id is the module name. The format of this name is to remove the file format relative to the path of baseUrl. For example, baseUrl is a js directory and a module is placed in js/libs/. If the name is defined like this:

define('libs/hi', ['jquery'], function($){......});
 

The advantage of such a definition form is that modules cannot conflict because files with the same name are not allowed under the same directory. But it is also recommended that we do not set the module name, because after setting the module name of ‘libs/hi’, the module must be placed in the file in the js/libs directory. If you want to move the location, the module name must be changed accordingly. As for the module name generated during later optimization, it is already another matter.

Using module

After defining various modules with "dependence" and "no dependencies", how should we use them? Provides a function, require (equivalent to requirejs).

The require function loads dependencies and executes callbacks. Unlike define, it does not register the callback result 4 as a module:

require(['jquery'], function($) { // This function loads the jquery dependency and executes the callback code ($);
});
 

Let's give a simple example. I have a folder with the following file structure:


 js/
  
  
  
 

Here, it has been registered as an AMD module, and the HTML file is referenced as follows:

<script src="js/" data-main="js/main"></script>

The data-main attribute value will be checked, here is js/main. According to the settings, it will load the files in the js directory.

In the file, I only do one thing, use the jQuery method to get the width of the current window:

require(['jquery'], function($) {
 var w = $(window).width();
 (w);
});

Executing code is that simple.

Non-AMD specification modules

But things are far from as beautiful as we thought. AMD is just a community norm, not a standard, and before it appeared, there were already all kinds of popular libraries, not to mention the code we wrote ourselves early on, so we will definitely encounter a bunch of non-AMD-specific modules. In order to enable them to load and automatically identify and load dependencies, we have two options: 1. Put a function called define on them all; 2. Use the configuration option provided by shim to save the country in a curve.

For example, I am using jQuery version 1.4.1 for some reason, and jQuery is registered as an AMD module since version 1.7. If I want to use it in, I need to do shim first:

({
 shim: {
  'jquery-1.4.1': { // <= This is the path relative to   exports: 'jQuery' // <= This value needs to be paid a little attention, and will be mentioned later  },
  'libs/': { // <= jQuery plugin   deps: ['jquery-1.4.1'] //No exports are needed, because we are just enhancing the jQuery function  }
 },
});
require(['jquery-1.4.1', 'libs/'], function($){
 ($.debounce);
});

After writing shim, I found that the names such as jquery-1.4.1 and libs/ are a bit long. Here we have two options. One is to directly open and modify the js file name, or use the provided configuration item paths to specify the corresponding real file path to the module ID:

({
 paths: {
  'jquery': 'jquery-1.4.1', // <= Module jquery points to js/jquery-1.4. File  'debounce': 'libs/'
 },
 shim: {
  'jquery': {
   exports: '$'
  },
  'debounce': {
   deps: ['jquery']
  }
 }
});
require(['jquery', 'debounce'], function($){
 ($.debounce);
});

In this way, it will be much more convenient to quote.

In addition, you need to pay attention to the exports item in shim, which is closer to imports, that is, importing global variables. If we change the exports value to a non-global variable name, it will cause the object passed in the callback to become undefined. For example:

({
 paths: {
  'jquery': 'jquery-1.4.1',
 },
 shim: {
  'jquery': {
   exports: 'hellojQuery' // Here I set the exports value to a value other than jQuery/$  }
 }
});
require(['jquery'], function($){
 ($);// Here, undefined will be displayed});
 

The same applies to other modules when doing shim, for example, underscore requires exports to be _.

Benefits

After saying so much, what are the benefits?

Parallel loading

We know that the <script></script> tag will block the page. When loading, all subsequent files have to wait until they are loaded and executed before they can start loading and execution. The modules can be downloaded in parallel, and modules without dependencies can also be executed in parallel, greatly speeding up page access.

Don't worry about dependence

When we define modules, we have decided on the dependencies of the modules - c depends on b, b depends on a. When I want to use the functions of the c module, I just need to specify c in the dependency of the require function:

require(['c'], function(c) {...});

As for the c-dependent module, the dependency module of the c-dependent module... etc., we will help us manage it.

In the traditional script approach, we must specify the order of all dependencies:

<script src="js/"></script>
 <script src="js/"></script>
 <script src="js/"></script>

In other words, in traditional script methods, we are most likely to rely on memory or checking module content to determine dependencies - it is too inefficient and brain-consuming.

Reduce global conflicts

Through the define method, we have reduced the global variables a lot, so that the probability of code conflicts is extremely small - There is a saying in the JavaScript world that global variables are devils. Think about it, it is a good thing that we can reduce the number of devils.

About global variables

One thing to note is that there are not only define and require global variables in the environment. Many libraries expose variables to the global environment. Taking jQuery as an example, after version 1.7, although it registered itself as an AMD module, it also exposed jQuery and $ to the global environment. So in the following code, although we did not pass a reference to the callback function, jQuery/$ also exists:

require(['jquery'], function(){
 (jQuery);
 ($);
});
 

The above example sharing of Require calling js in JavaScript is all the content I share with you. I hope you can give you a reference and I hope you can support me more.