下述内容为个人学习的复习记录,主要是对一些网络资料的摘抄记录。以及一些自己的总结,可能会有一些错误,如有发现,欢迎指正!!!
学习内容主要来至:github.com/stephentian…
立即执行函数表达式(IIFE)
一、基本概念
- IIFE: Imdiately Invoked Function Express(立即执行函数表达式) e.g.
/* 以下待会会自动执行输出'Hello World' */
;(function (str) {
console.log(str)
})('Hello World')
二、括号的意义
1. 包裹 function 的括号
- 该括号主要是为了将
function(){}
转换成表达式 - 该括号也可用
~
、+
等符号来替代如 - 该括号不能去掉,因为形如
function(){}
不是一个表达式
e.g.
~function () {
console.log(123)
}()
+function () {
console.log(123)
}()
2. 第二个括号
- 第二个括号主要是为了能够执行表达式
三、参数的意义
;(function (str) {
console.log(str)
})('Hello World')
1. 实参
- 函数中的实参与形参会一一对应如上述例子中的 str 会对应的被赋值为
Hello World
2. 形参
- 普通形参
- 即第二个括号内传入的参数
- 特殊形参
undefined
- 防止 undefined 被更改,IE6 等低版本浏览器的 undefined 值可以被更改
- 压缩代码可以压缩 undefined
模块
一、模块的几种写法
1. 原始写法
- 模块就是实现特定功能的一组方法
- 将不同函数(以及记录状态的变量)简单的放在一起,就算是一个模块
e.g.
function m1 () {
/* code */
}
function m2 () {
/* code */
}
- 缺点:
- 污染了全局变量
- 模块成员之间看不出直接关系
2. 对象写法
- 把所有的模块成员都放到一个对象里
e.g.
var module = {
_count: 0,
m1: function () {
/* code */
},
m2: function () {
/* code */
}
}
- 缺点:
- 暴露了模块成员,内部状态可以直接被该部修改
3. 立即执行函数写法
- 使用立即执行函数,将要暴露的模块成员返回出来,其余的不返回,起到保护内部成员的作用
e.g.
var module = (function () {
var _count: 0
var m1 = function () {
/* code */
}
var m2 = function () {
/* code */
}
return {
m1: m1,
m2: m2
}
})()
4. 放大模式
- 每次添加模块的时候,都进行命名空间的声明,这样可以不用考虑代码的执行顺序
- 要注意的点
- 每次往命名空间挂载功能模块时要先判断命名空间是否初始化,如果没有则进行初始化
- 功能挂在完毕后要将新的命名空间返回,将命名空间进行更新
e.g.
var module = (function (module) {
if (typeof module === 'undefined') {
module = {}
}
module.m1 = function () {
console.log('m1 function')
}
return module
})(module)
var module = (function (module) {
if (typeof module === 'undefined') {
module = {}
}
module.m2 = function () {
console.log('m2 function')
}
return module
})(module)
5. 宽放大模式
- 在实参中使用
||
进行取巧,使用window.module||(window.module = {})
,如果 window 中命名空间 module 被实例化,则直接传入 module,否则执行后续的表达式(window.module={})
并返回 window.module 的引用
e.g.
;(function (module) {
if (typeof module === 'undefined') {
module = {}
}
module.m2 = function () {
console.log('m2 function')
}
return module
})(window.module || (window.module = {}))
- 优点: 可以切割成多个文件进行加载,不必考虑文件加载的先后顺序(因为每次模块添加功能模块时都是检查 window 中是否创建了对应的命名空间,如果创建了会直接使用对应的命名空间)
模块书写注意内容
- 使用立即执行函数表达式时最好在前面加上
;
以结束上一个代码段,否则会出现合并的问题;如 a 文件中有一句console.log(1)
;而 b 文件为一个开头无分号的立即执行函数表达式,那么合并后代码将变成
console.log(1)(function () {
/* code */
})()
那么这将会被当作如下执行
console.log(1)(function () {
/* code */
})()
代码将报错
模块的规范化
一、CommonJS 规范(同步加载模块)
- 使用
require
方法来同步加载(遇到 require 语句将会将对应的模块文件加载进来,后续代码执行都需要等待模块去不加载完毕后才能执行)其他模块;通过exports
或module.exports
来导出需要暴露的接口
e.g.
/* 模块导入 */
require('Koa')
require('./module.js')
/* 模块导出 */
exports.m1 = function () {}
module.exports = someValue
- 优点
- 简单易使用
- 服务器端模块便于复用
- 缺点
- 同步加载不适合浏览器环境,因为其加载速度会受网络影响。会影响浏览器资源的异步加载
- 不能非阻塞的加载多个模块
二、AMD(异步加载模块)
- 使用异步加载
- 所有依赖模块的语句都会放到一个回调函数中,等待模块加载完毕才会执行
e.g.
// 定义模块
define('module', ['dep1', 'dep2'], function (d1, d2) {/* code */});
// 加载模块
require(["module","../app"],function(module,app){/* code */}
- 优点
- 适合在浏览器中加载模块(异步加载)
- 可以并行加载多个模块
- 缺点
- 提高开发成本、代码阅读和书写困难、模块定义语句不通畅
- 不符合通用的模块发思维方式
- Tips: 实现 AMD 的规范代表是 require.js
三、CMD(异步加载模块)
-
在 CMD 中一个模块就是一个文件
-
定义模块使用全局函数 define,接收一个 factory 参数,factory 可以为函数\对象\字符串
-
当 factory 是函数的时候,他有三个参数
- require: 函数,接收模块作为唯一参数,用来获取其他模块的接口
- exports: 对象,用来向外部提供接口
- module: 对象,存储了与当前模块相关联的一些属性和方法
-
优点
- 依赖很近,延迟执行
- 容易在 Node.js 中运行
-
缺点
- 依赖 SPM 打包,模块加载逻辑偏重
四、UMD
- 是 AMD 和 CommonJS 的糅合
- 先判断是否支持 Node.js 的 exports 是否存在,存在则使用 CommonJS 规范,否则再判断 AMD 模式是否支持(即 define 是否存在),存在则使用 AMD 规范
五、ES6 规范
- 在语言标准层面上实现模块功能,实现简单,可以取代 CommonJS 和 AMD 规范,成为浏览器服务器通用解决方案
- ES6 模块规范设计思想:尽量静态化、编译时确定模块依赖关系,以及输入输出的变量(CommonJS 和 AMD 模块都只能在运行时确定这一内容)
e.g.
// 导入
import "app.js"
import React from "react"
// 导出
export function app(){/* code */}
export const count=0
export default...
- 优点
- 容易进行静态分析
- 面向未来的ES标准
- 缺点
- 原生浏览器端还没有实现该标准
- 全新的命令字,新版的Node.js才支持
六、CommonJS和ES6的区别
- CommonJS使用
require/exports
;ES6使用import/export
CommonJS
- 模块中的基本数据类型被引入时会被复制,另一个模块可以对导出的基本类型值进行重新赋值,不影响其他模块的使用
- 模块中的复杂数据类型被引入时会被浅拷贝,两个模块引用的对象指向同一个内存空间,当一个模块对其中的值进行更改会影响到另一个模块的执行
- require加载某一模块时会执行该模块文件的代码,重复引入不会导致重复执行,会直接取用缓存中的值
ES6
- ES6模块中的值属于动态只读引用
- import引入的值会产生一个动态只读引用,模块遇到import时会对引入的变量生成一个动态只读的引用,执行时可以根据这个引用去取值,不能修改该值。引入变量的原始值发生变化时,加载的变量也会产生变化;上述的变量对基本数据类型和复杂数据类型都成立
注意的点
- 不论是用什么规范,require和exports都是必须的,因为编写的import/export都是通过require/exports来执行的