首页 > 编程语言 >JavaScript学习笔记:模块

JavaScript学习笔记:模块

时间:2023-05-21 13:13:47浏览次数:33  
标签:function square return JavaScript 笔记 js 模块 calc

前言

在js编程中,模块指的是按照一定格式将代码以功能拆分后作为独立文件存在的一个实体。
早期的JS并没有规定模块应该如何设计,核心语言也没有针对模块提供相关支持。
早期的代码使用IIFE来实现一个模块, 它是通过向全局对象添加属性来实现与其他模块来交互的。

(function() {
  var calc = {
    add: function(a,b) {return a+b;},
    subtract: function(a,b) {return a-b;},
    multiply: function(a,b) {return a*b;},
    divide: function(a,b) {return a/b;}
  };

  window..calc = calc;
}());

这样的代码对全局对象造成了很大的污染,一个属性一旦被模块占用,就不能另作他用了。
为了解决这样的问题,出现了很多定义模块的规范与实现,著名的有CommonJS,AMD,CMD。

开源社区提出的浏览器模块化规范与实现

CommonJS 与 node.js

CommonJS是node.js的模块化实现使用的规范,它是同步引入文件的。因为node.js是服务器软件,其代码文件在本地,读取文件与解析代码的时间可以忽略不计,所以使用该该规范没有问题。
但是浏览器不然,受限于网络传输的速度慢、不稳定的问题,同步引入文件是不现实的,更何况解析引擎并没有为模块做任何支持。所以浏览器环境的实现使用异步方式引入模块文件,将业务代码写在回调函数里,在文件引入代码就绪后再执行回调函数里的业务代码。

AMD 与 Require.js

客户端流行的规范是AMD,其流行的实现是Require.js。它给全局对象添加了definerequire两个函数属性用于定义与引入模块,还解决了模块依赖的问题,也就是JS文件引入的先后顺序的问题。
Require.js核心代码定义在全局变量requirejs中,通过给其定义config属性来配置模块名与代码文件路径的关系,还支持将不使用AMD规范的模块定义为可以被require方法导入的模块。

// 配置模块与文件的关系
require.config({
   baseUrl : '/js',
   paths : { 
     //当百度的jquery没有加载成功后,会加载本地js目录下的jquery 
     jquery : ['http://libs.baidu.com/jquery/2.0.3/jquery', '/public/js/jquery'], 
     jqueryUI : 'http://cdn.bootcss.com/jqueryui/1.11.4/jquery-ui',
     calc: 'calc' 
   } 
});
//  模块calc calc.js
define(function() {
  var calc = {
    add: function(a,b) {return a+b;},
    subtract: function(a,b) {return a-b;},
    multiply: function(a,b) {return a*b;},
    divide: function(a,b) {return a/b;}
  };
  return calc;
});

// square模块 square.js 依赖calc模块
define(['calc'], function(calc) {
  function square (number){
    return calc.multiply(number, nember);
  };
  return {square: square};
});

// 业务代码,计算平方,并将结果显示到网页
require(['square', 'jquery'], function(square, jq) {
  jq('<a></a>').text(square(2)).appendTo(jq(document.body).first());
});

CMD 与 sea.js

基于AMD规范的Require.js对模块的依赖是前置的,通过参数的方式提前告诉Require.js要引入那些模块。
而基于CMD的sea.js则推崇依赖就近,不提前指定要依赖的模块,直接使用require()方法引入模块,在通过调用模块所在函数的toString()获取代码的字符串,解析字符串取得要依赖的模块。
这个“依赖前置”与“依赖就近”是模块定义层面的,而不是代码执行层面的,二者都会在模块的业务代码执行前加载所需的依赖。
只不过AMD的依赖是提前指定的,执行业务代码的时候,直接引入依赖即可,这即是“依赖前置”。
而CMD的依赖是在业务代码中需要依赖的时候,就近指定的,在执行业务代码前再解析一次获取需要的依赖,这就是“依赖就近”。这意味着写在if/else里的依赖不会按照代码逻辑引入,而是全部被引入。
虽然sea.js的require()方法在功能上与Require.js的不同,但是sea.js也有与Require.js的require()功能一致的方法,叫做require.async(),使用方法与之一样,业务逻辑作为回调函数被异步执行,这也意味着require.async()指定的依赖不会被解析,不会在业务代码执行前引入。
模块的导出方式与Require.js也不同,除了使用return关键字,还可以是给传入回调函数的参数对象指定属性名的方式,这种导出方式与基于CommonJS实现的Node模块是一样的。

//  模块calc calc.js
define(function(require, export, module) {
  var calc = {
    add: function(a,b) {return a+b;},
    subtract: function(a,b) {return a-b;},
    multiply: function(a,b) {return a*b;},
    divide: function(a,b) {return a/b;}
  };
  return calc;
});

// square模块 square.js 依赖calc模块
define(function(require, export, module) {
  export.square = function (number){
    var calc = require('calc');
    return calc.multiply(number, nember);
  };
  // 除了给export添加属性,还可以为module.export指定一个对象来导出模块
  // 实际上export参数是module参数的export属性的一个引用,实际导出的还是module.export,所以直接给export赋值是错误的做法
  // 导出必须是同步的,所以不能写在回调函数里
  module.export = {
    square: function (number){
      var calc = require('calc');
      return calc.multiply(number, nember);
    },
  };
});

// sea.js没有定义全局的require函数,使用use方法来在使用模块
seajs.use(['square', 'jq'], function(square) {
  jq('<a></a>').text(square(2)).appendTo(jq(document.body).first());
});

Node.js 的 模块

Node使用CommonJS规范实现自己的模块功能。
它规定了每个模块是一个单独的文件,定义了一个全局的exports对象(module.exports)用于导出,定义了全局的require()函数用于导入。

  // 模块calc calc.js
  var calc = {
    add: function(a,b) {return a+b;},
    subtract: function(a,b) {return a-b;},
    multiply: function(a,b) {return a*b;},
    divide: function(a,b) {return a/b;}
  };
  exports.calc = calc;
  // exports.ok = "OK"; // 导出更多值
  // module.exports = {calc: calc} //只导出一个对象值

// square模块 square.js 依赖calc模块
let calc = require('./calc.js');
function square (number){
  return calc.multiply(number, nember);
};
module.exports =  {square};

// 业务代码,计算平方,并将结果打印
let square = require('./square.js');
console.log(square(2));

ES6 module 官方实现

概念

ES6的模块也规定一个模块定义在一个文件中,每个模块都有自己独立的上下文,模块中定义的变量的作用域只在其定义的模块文件,但模块内可以访问全局作用域内定义的变量,这与函数体内的行为一致。
ES6新增了import(default),export/from关键字用于导入与导出。导出的值可以是常量、变量、函数或类。
使用export可以导出一个或多个值,导出一个值,将其加在声明以前即可,导出多个值,将标识符包裹在花括号里以逗号分隔。
使用export default来导出一个值,称为默认导出。
默认导出可以导出字面量,而export不能,这可以简化模块的代码。

定义与导出值

// 模块calc calc.js
  export const calc = {
    add: function(a,b) {return a+b;},
    subtract: function(a,b) {return a-b;},
    multiply: function(a,b) {return a*b;},
    divide: function(a,b) {return a/b;}
  };
  let a = 0, b = {}, c = function() {};
  export {a,b,c}; // 导出三个值,分别是0,对象,函数
  
  export default {a,b,c}; // 导出一个值,有三个属性

导入值

导入是可以选择性的导入需要的值,或将所有值导入并在哦为一个对象的属性值。
承接导入值的标识符是一个常量,不可修改。

import {calc} from './calc.js';
import {a,b,c} from './calc.js';
import * as obj from './calc.js'; // {a, b, c}

// 同时导入默认值与命名值
import defaultObj, {a,b,b} from './calc.js';

// 为导出值重命名
import {default as defaultObj, a1 as a, b, c} from './calc.js';

再导出

将一个模块的导出值导出,不需要先使用import导入,直接使用export/from即可。

export {calc} from './calc.js';

在网页中使用模块

为script标签的type属性指定'module'值即可将脚本文件以模块的方式解析与执行。
模块代码默认会在HTML文档解析完成后执行,就像给script标签指定了defer属性一样。
要改变这个默认行为,可以给模块的script标签指定async属性,这样代码将在加载完毕后立即执行,而不管HTML文档是否解析完成。
defer与async本身就是布尔值,添加就代表以true值生效,不需要指定属性值。

<script src="./calc.js" async></script>

import()动态导入

在ES2020,引入了import()操作符用于异步地引入模块脚本,它接收一个可以可以转为字符串的值,返回一个Promise对象。
import()看起来是一个函数调用,但是它实际上是一个操作符,浏览器的全局对象并没有定义一个import属性。

import('./calc.js').then(function() {});
async suare(n) {
  let calc = await import('./calc.js');
  return calc.multiply(n, n);
}

import.meta.url

获取模块代码文件的绝对路径。可以方便地访问同路径下的其他资源。

标签:function,square,return,JavaScript,笔记,js,模块,calc
From: https://www.cnblogs.com/chaihuibin/p/17415688.html

相关文章

  • 为知笔记服务器迁移
    举个例子:原有的老服务器的为知笔记的docker启动脚本是dockerrun--namewiz1--restart=always-it-d-v/mnt/wizdata:/wiz/storage-v/etc/localtime:/etc/localtime-p80:80-p9268:9269/udpwiznote/wizserver那么把原来老服务器的docker映射出来的数据路径目录,也就......
  • 计算机图形学入门——GAMES101第一课笔记
    一、光栅化将三维空间的几何形体显示在屏幕上,就是光栅化(Rasterization)。 虎书中有这么一段话: Theprocessoffindingallthepixelsinanimagethatareoccupiedbyageometricprimitiveiscalledrasterization;即光栅化就是找到所有被几何原型所占据的所有像素点......
  • C#学习笔记 -- 对象初始化语句、索引器、访问器的修饰符
    1、对象初始化语句扩展语法有如下两种扩展语法,第一种当类中没有声明构造器或者声明了无参构造器才能用第二种当类中声明了有参构造器才能用newExampleClass{FieldOrProp=InitProp,FieldOrProp=InitProp,...};newExampleClass(ArgList){FieldOrProp=I......
  • Makefile学习笔记
    ​目录一、概述1.1 Makefile介绍1.2规则1.3核心1.4示例1.5定义命令1.6 make是如何工作的1.7、makefile中使用变量1.8让make自动推导1.9、另类风格的makefile1.10、清空目标文件的规则二、Makefile总述2.1、Makefile里有什么?2.2、 makefile文件名2.3、引用其......
  • OverTheWire攻关过程-Leviathan模块1
    我们打开lv0,查看信息然后我们打开lv0-lv1,查看信息一样的信息但是我们发现,我们没有找到相关的文件开始查看这些隐藏的文件发现信息太多使用grep命令匹配结果找到<DT><AHREF="http://leviathan.labs.overthewire.org/passwordus.html|Thiswillbefixedlater,thepasswordfor......
  • JavaScript基础知识笔记
    JavaScript是属于Web的编程语言,对网页行为进行编程。参考教程:https://www.w3school.com.cn/js/index.asphttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Language_overviewJavaScript能够改变HTML内容document.getElementById("demo").innerHTML="Hell......
  • C#学习笔记 -- 类的属性
    属性属性代表类实例或类中数据项的成员,使用属性就像写入或读取一个字段,语法相同,从语法上无法区分他们(0)属性的特征是命名的类成员有类型可以被赋值和读取与字段不同,属性是一个函数成员不一定为数据存储分配内存执行代码属性是两个匹配的、命名的......
  • GO性能优化指南笔记
    今天的学习内容时关于GO性能优化的问题。今天的讲师讲的关于GO在项目实操过程中的各种优化案例可以说是目前来说我见过的最好的了,以下我从几个方面说明一下1,GO语言在编程过程都要注意的问题:简单性(以能理解并运行为第一要素,拒绝冗杂的代码,以简单清晰的逻辑编辑代码)可读性(代码是......
  • 树相关知识点--零碎笔记
    深入理解前中后序二叉树的前中后序遍历是什么?前中后序遍历,即二叉树结构的前中后位置前序遍历-即刚刚进入一个节点的时候中序遍历-即进入节点之后未离开节点之前后序遍历-即即将离开第一个节点的时候前中后序是遍历二叉树过程中处理每一个节点的三个特殊时间点前--刚刚进入一个二......
  • redis-cli 使用lua脚本笔记
    前言众所周知,redis可以执行lua脚本,至于为什么要用lua脚本来操作redis,自行百度咯先来讲一下最简单的方式,关于如何在javaspringboot里用lua脚本,请查看我另一篇文章:https://www.cnblogs.com/daen/p/17418024.html更为详细的资料请参考以下文章https://blog.csdn.net/jiayibingd......