共计 12728 个字符,预计需要花费 32 分钟才能阅读完成。
导航
[[深刻 01] 执行上下文](https://juejin.im/post/684490…
[[深刻 02] 原型链](https://juejin.im/post/684490…
[[深刻 03] 继承](https://juejin.im/post/684490…
[[深刻 04] 事件循环](https://juejin.im/post/684490…
[[深刻 05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490…
[[深刻 06] 隐式转换 和 运算符](https://juejin.im/post/684490…
[[深刻 07] 浏览器缓存机制(http 缓存机制)](https://juejin.im/post/684490…
[[深刻 08] 前端平安](https://juejin.im/post/684490…
[[深刻 09] 深浅拷贝](https://juejin.im/post/684490…
[[深刻 10] Debounce Throttle](https://juejin.im/post/684490…
[[深刻 11] 前端路由](https://juejin.im/post/684490…
[[深刻 12] 前端模块化](https://juejin.im/post/684490…
[[深刻 13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490…
[[深刻 14] canvas](https://juejin.im/post/684490…
[[深刻 15] webSocket](https://juejin.im/post/684490…
[[深刻 16] webpack](https://juejin.im/post/684490…
[[深刻 17] http 和 https](https://juejin.im/post/684490…
[[深刻 18] CSS-interview](https://juejin.im/post/684490…
[[深刻 19] 手写 Promise](https://juejin.im/post/684490…
[[深刻 20] 手写函数](https://juejin.im/post/684490…
[[react] Hooks](https://juejin.im/post/684490…
[[部署 01] Nginx](https://juejin.im/post/684490…
[[部署 02] Docker 部署 vue 我的项目](https://juejin.im/post/684490…
[[部署 03] gitlab-CI](https://juejin.im/post/684490…
[[源码 -webpack01- 前置常识] AST 形象语法树](https://juejin.im/post/684490…
[[源码 -webpack02- 前置常识] Tapable](https://juejin.im/post/684490…
[[源码 -webpack03] 手写 webpack – compiler 简略编译流程](https://juejin.im/post/684490…
[[源码] Redux React-Redux01](https://juejin.im/post/684490…
[[源码] axios ](https://juejin.im/post/684490…
[[源码] vuex ](https://juejin.im/post/684490…
[[源码 -vue01] data 响应式 和 初始化渲染 ](https://juejin.im/post/684490…
前置常识
js 中省略每行结尾的 ; 分号时,须要留神的问题
- <font color=red>() 小括号结尾的前一条语句,小括号前必须加分号,或者在前一条语句完结时加分号 </font>
- <font color=red>[] 中括号结尾的前一条语句,中括号前必须加分号,或者在前一条语句完结时加分号 </font>
js 中省略每行结尾的 ; 分号,须要留神的问题:- () 小括号结尾的前一条语句,小括号前必须加分号,或者在前一条语句完结时加分号
- [] 中括号结尾的前一条语句,中括号前必须加分号,或者在前一条语句完结时加分号
例子:(1) () 小括号结尾的前一条语句,小括号前要加 ';' 分号,或者前一条语句结尾加分号
var a = 1
(function() { // 报错:Uncaught TypeError: 1 is not a function
console.log(a)
})()
解决办法:var a = 1
;(function() { <---------
// 在 () 前加分号
// 或者在 var a = 1; 结尾加分号
console.log(a)
})()
(2) [] 中括号结尾的前一条语句,须要在 [] 后面加上 ';' 分号,或者前一条语句结尾加分号
var a = 1
[1,2,3].forEach(item => console.log(item)) // Uncaught TypeError: Cannot read property 'forEach' of undefined
解决办法:var a = 1
;[1,2,3].forEach(item => console.log(item)) <---------
作用域
- 作用域:指变量存在的范畴
-
作用域分为:<font color=red> 全局作用域,函数作用域,eval</font>
<script> var a = 1 var c = 2 function x() { var a = 10 // 全局中也有 a,然而函数作用域申明的 a 不会影响全局 var b = 100 c = 20 // 然而函数外部能批改全局的变量,(作用域链外部能批改内部的变量)} x() console.log(a) // 1 console.log(c) // 20 ;(function() {// 留神分号是必须的,因为 ()[] 结尾的前一条语句开端,或者 ()[] 结尾加 ; console.log(c) // 20 })() console.log(b) // 报错,b is not defined 函数内部不能拜访函数外部的变量 </script>
浏览器解析 js 文件的流程
浏览器加载 javascript 脚本,次要通过 script 标签
来实现
- (1) 浏览器一边下载 html 文件,一边开始解析
就是说:不等到 html 下载实现,就开始解析
- (2) 解析过程中,遇到 script 标签就暂定解析,把网页的渲染控制权交给 javascript 引擎
- (3) 如果 script 标签援用了
内部脚本
,则下载脚本并执行
;如果没有
就间接执行
script 标签内的代码 -
(4) javascript 引擎
执行结束
,控制权交还渲染引擎
,恢復解析 html 网页
浏览器解析 js 文件流程
- html 是一边下载,一边解析的
- script 标签会阻塞 html 解析,如果耗时较长,就会呈现浏览器假死景象
-
script 标签之所以会阻止 html 解析,阻止渲染页面,是因为 js 可能会批改 DOM 树和 CSSOM 树,造成简单的线程比赛和控制权竞争的问题
js 同步加载,js 异步加载
- <font color=red> 同步加载:阻塞页面 </font>
- <font color=red> 异步加载:不会阻塞页面 </font>
script 标签 异步加载的形式 async defer
defer:是提早的意思
-
(1) script 标签搁置在 body 底部
- 严格说不算异步加载,然而这也是常见的通过扭转 js 加载形式来晋升页面性能的一种形式
-
(2) <font color=red>defer 属性 </font>
异步加载,不阻塞页面,在 DOM 解析实现后才执行 js 文件
程序执行,不影响依赖关系
-
(3) <font color=red>async 属性 </font>
异步加载,加载不阻塞页面,然而 async 会在异步加载实现后,立刻执行,如果此时 html 未加载完,就会阻塞页面
留神:异步加载,加载不会阻塞页面,执行会阻塞页面
不能保障各 js 文件的执行程序
-
defer(1)加载:是异步加载,加载不阻塞页面;(2)执行:要 DOM 渲染完才执行,能保障各 js 的执行程序 -
async(1)加载:是异步加载,加载不阻塞页面;(2)执行:加载完立刻执行,不能保障各 js 的执行程序 bgcolor=orange>
-
<font color=red>defer,async,放在 body 底部,三种办法哪种好?</font>
- 最稳当的方法还是把
<script>
写在<body>
底部,没有兼容性问题,没有白屏问题,没有执行程序问题
- 最稳当的方法还是把
tree 命令 – 生成目录构造
tree [<Drive>:][<Path>] [/f] [/a]
tree 命令生成目录构造
tree [<Drive>:][<Path>] [/f] [/a]
/f:显示每个目录中文件的名称
/a:应用文本字符而不是图形字符连贯
例子:C:.
│ index.html
│
├─a
│ └─b
│ ccc.js
│
└─requirejs
a.js
b.js
c.js
立刻调用的函数表达式 IIFE
IIFE Immediately-invoked Function Expressions 立刻调用的函数表达式
(function(){...})()
,(function(){...}())
- 需要:在函数定义后,立刻调用该函数
-
呈现问题:<font color=red> 如果间接在函数前面加上括号去调用,就会报错 </font>
报错:function(){}();
- 报错起因:<font color=red>function 关键词呈现在行首,会被 js 解析成语句(即函数的定义),不应该以圆括号结尾,所以报错 </font>
-
如何解决:IIFE 利用立刻调用的函数表达式去解决
- 即让其成为一个表达式,而不是语句
- <font color=red> 语句不能以圆括号结尾,然而表达式能够 </font>
-
须要把握的知识点:
(1) IIFE 如何传参
(2) 多个 IIFE 一起时,分号不能省略
(3) IIFE 不会净化全局变量,因为不必为函数命名
(4) IIFE 能够造成一个独自的作用域名,则能够封装一些内部无奈读取的变量
(5) IIFE 的两种写法
IIFE 立刻调用的函数表达式 须要了解的几个方面:(1) IIFE 如何传参 - (作为模块时,依赖项就是通过参数传递实现的) (2) 多个 IIFE 一起时,分号不能省略 (3) IIFE 不会净化全局变量,因为不必为函数命名 (4) IIFE 能够造成一个独自的作用域名,则能够封装一些内部无奈读取的变量 (5) IIFE 的两种写法 --------------- 案例:const obj = {name: 'woow_wu7'}; (function(params) { // params 形参 console.log(obj) // 这里拜访的 obj,不是函数参数传入的,而是拜访的父级作用域的 obj console.log(params) })(obj); // obj 是实参 // (1) // 留神:这里开端的分号是必须的,因为是两个 IIFE 间断调用 // 打印:都是 {name: 'woow_wu7'} // (2) // IIFE 的两种写法:// 1. (function(){...})() // 2. (function(){...}()) // 下面的 (1)(2) 都会被 js 了解为表达式,而不是语句 // 表达式能够以圆括号结尾,函数定义语句不能以圆括号结尾 // (3) // 因为 function 没有函数名,防止了变量名净化全局变量 (function(params2){console.log(obj) console.log(params2) }(obj))
前端模块化
模块的概念
- 将一个简单程序的各个局部,依照肯定的
规定 (标准)
封装不同的块(不同的文件)
,并组合在一起 - <font color=red> 块 外部的变量和办法是公有的,只会向外裸露一些接口,通过接口与内部进行通信 </font>
非模块化存在的问题
- 对全局变量的净化
- 各个 js 文件外部变量相互批改,即只存在全局作用域,没有函数作用域
- 各个模块如果存在依赖关系,依赖关系含糊,很难分清谁依赖谁,而依赖又必须前置
-
难以保护
<!DOCTYPE html> <html lang="en"> <head> <script> var me = 'changeMe'; console.log(window.me, '全局变量未被批改前的 window.me') // changeMe </script> <script src="./home.js"></script> <!-- var home = 'chongqing' --> <script src="./me.js"></script> <!-- var me = 'woow_wu7' --> <script src="./map.js"></script> <!-- var map = 'baidu' --> </head> <body> <script> console.log(window.me, '全局变量被批改后的 window.me') // 'woow_wu7' 阐明净化了全局变量 console.log(map, '模块内变量 map 被批改前') // baidu var map = 'Amap' console.log(map, '别的模块内 mao 竟然被批改了') // Amap 阐明模块内的变量被批改了,因为只有全局作用域 </script> </body> </html>
模块化的益处
能够先记忆一下,有个概念
- 更好的拆散:防止一个 html 搁置多个 script,只需引入一个总的 script
- 防止命名抵触:模块内的变量不会影响到模块外,即各个模块能够有雷同命令的变量
- 更好的解决依赖:每个模块只用放心本人所依赖的模块,不必思考其余模块的依赖问题,<font color=red> 如果在一个 html 中引入很多 script,各个模块 (script) 的依赖关系是很难分分明的 </font>
- 更利于保护
模块化须要解决的问题
- 模块的平安装置,即不能净化任何模块外的代码
- 惟一的标识每个模块
- 优雅的裸露 api,不能减少任何全局变量
- 能够援用其余依赖
<font color=red> 模块化不同计划比照 </font>
IIFE,CommonJS 标准,AMD,CMD,ES6 的模块化计划
commonjs -------------------------------- node.js 应用的标准
AMD:Asynchronous Module Definition ----- 异步模块定义
CMD:Common Module Definition ----------- 通用模块定义
-
commonjs 用于服务端,同步加载 —————————— node.js 应用的规范 -
AMD 和 CMD 次要用于浏览器端,异步加载 -
ES6 的模块计划:用于浏览器和服务器,通用计划,动态化 -
AMD 依赖前置,依赖必须一开始写好,提前加载依赖 (依赖前置,提前执行) ——– RequireJS -
CMD 依赖就近,须要应用的时候,才去加载依赖 (依赖就近,延时执行) ————- seajs -
ES6 模块是动态化的,在 (编译时) 就能确定模块得依赖关系,输出,输入;而 AMD 和 CMD 只能在运行时能力确定 -
2021/3/23 补充
- ES6 的模块化计划 =>
动静更新
- CommonJS =>
模块输出的是 (值的缓存),不存在动静更新
-
ES6 的模块中
- export {a, b}; export 前面的花括号并不是对象,而是一个接口,这个接口中有 a 和 b 两个变量
-
CommonJS 中
-
module.export = {a: 1}; 导出的就是一个对象
es6 模块 ------- export var foo = 'bar'; setTimeout(() => foo = 'baz', 500); 下面代码输入变量 foo,值为 bar,500 毫秒之后变成 baz
-
- ES6 的模块化计划 =>
模块化的倒退历程
(1)原始阶段 – 只用函数作用域
var a1 = 'a1'
function a1() {}
function a2() {}
毛病:函数名会净化全局变量
(2)对象封装
var a3 = 'a3'
var a1 = {a2: function() {}
a3: function() {}
}
长处:1. a1 对象的 a3 属性不会净化全局变量 a3
2. 缩小了全局作用域内的变量数量:- 这里只有 a3,a1 ------- 2 个
- 而全用函数:---------- 3 个
毛病:1. 还是会净化全局变量
2. 内部能够批改 a1 的属性,即会裸露所有属性并且能够被内部批改
(3)用 IIFE(立刻调用的函数表达式) 实现模块化
IIFE Immediately-invoked Function Expressions
-
IIFE 实现的模块化能解决的问题:
-
<font color=red> 在其余中央都要不能批改模块外部的变量 </font>
- 不能在其余中央批改模块内的变量,阐明每个模块都有本人的(<font color=red> 独自的作用域 </font>),<font color=red> 内部无法访问 </font>
- 函数就具备(<font color=red> 函数作用域 </font>),函数内部无法访问函数外部的变量
-
<font color=red> 模块内的变量不能净化全局变量 </font>
- 即模块内的变量的作用域不能是(全局作用域),则能够用函数来解决(函数作用域)
-
什么叫不能净化全局变量:
- 即不能变量笼罩,模块内的变量不能笼罩全局的变量,从而影响全局变量
- <font color=red> 防止间接应用函数,函数名净化全局变量 </font>
- <font color=red>IIFE 实现的模块化,依赖其余模块,可用传参来解决 </font>
- <font color=red> 模块须要裸露的办法和变量,都能够挂载都 window 上 </font> =>
衡量全局变量净化问题,能够应用特殊符号防止
用 IIFE(立刻调用的函数表达式) 实现模块化 须要解决的问题:(1) 各个模块中定义的变量不能在模块外被批改,只能在该模块内批改,则每个模块须要造成独自的作用域 (2) 模块内的变量不能净化全局变量 => 即不能在同一个作用域,用函数能够解决 ------ 未解决以上问题前的模块:<!DOCTYPE html> <html lang="en"> <head> <script> var me = 'changeMe'; console.log(window.me, '全局变量未被批改前的 window.me') // changeMe </script> <script src="./home.js"></script> <!-- var home = 'chongqing' --> <script src="./me.js"></script> <!-- var me = 'woow_wu7' --> <script src="./map.js"></script> <!-- var map = 'baidu' --> </head> <body> <script> console.log(window.me, '全局变量被批改后的 window.me') // 'woow_wu7' 阐明净化了全局变量 console.log(map, '模块内变量 map 被批改前') // baidu var map = 'Amap' console.log(map, '别的模块内 mao 竟然被批改了') // Amap 阐明模块内的变量被批改了 </script> </body> </html> ------ IIFE 实现的模块化:<!DOCTYPE html> <html lang="en"> <head> <script> (function(window,$) { var me = 'changeMe'; console.log(me) // changeMe window.me = me })(window, jquery) // 该模块依赖 jquery // 须要裸露的变量,能够挂载到 window 对象上 </script> </head> <body> <script> console.log(me, '内部无法访问,报错') // me is not defined </script> </body> </html>
-
(4)CommonJS 标准
- <font color=red>Nodejs 采纳 CommonJS 标准,次要用于服务端,同步加载 </font>
-
同步加载
- nodejs 次要用服务端,加载的模块文件个别都存在硬盘上,加载起来比放慢,不必思考异步加载的形式
- 但如果是浏览器环境,要从服务器加载模块,就必须采纳异步形式,所以就有了 AMD CMD 计划
-
<font color=red>module 示意以后模块,module.exports 是对外的接口,require 一个模块其实就是加载 module.exports 属性 </font>
- 留神:在 node.js 中 moudle.exports 和 exports 的区别?
nodejs 中 moudle.exports 和 exports 的区别 案例:--- modu.js --- const a = 11; module.exports = a ---------------------------------- module.exports 裸露模块 --- modu2.js --- const b = 22; exports.bVar = b ------------------------------------ exports 裸露模块 --- index.js --- const a = require('./modu.js') ---------------------- require 引入模块 const b = require('./modu2.js') console.log(a, 'a') // 11 console.log(b, 'b') // {bVar: 22} console.log(b.bVar, 'b.bVar') // 22
(5) AMD – Asynchronous Module Definition 异步模块定义 <font color=red>// RequireJS</font>
- AMD 用于浏览器端,异步加载,依赖前置
-
<font color=red> 浏览器端不能应用 commonjs 同步加载计划 </font>
- <font color=red> 是因为浏览器端加载 js 的文件在服务器上,须要的工夫较长,同步加载会阻塞页面的加载和渲染 </font>
- <font color=red> 而对于服务器端,文件则在硬盘中,加载和读取都非常快,所以能够同步加载,不必思考加载形式 </font>
-
RequireJS
RequireJS (1) 目录构造:C:. │ index.html │ └─requirejs b.js c.js (2) 例子 b.js define(function () {// ----------------- define(function(){...}) 定义一个模块 return 'string b' }) c.js define(['./b.js'], function(res) {// --- define(['a'], function(res){...}) 定义一个有依赖的模块,c 依赖模块 b return res + 'c' }); index.html <!DOCTYPE html> <html lang="en"> <head> <script src="https://cdn.bootcss.com/require.js/2.3.6/require.min.js"></script> </head> <body> <script> require(['./requirejs/c.js'], function(res) { // 引入模块,并应用模块裸露的值,res 就是模块 c 裸露的值 console.log(res, 'res') }) </script> </body> </html>
(6) CMD – Common Module Definition 通用模块定义 <font color=red>// seajs</font>
- CMD 用于浏览器端,异步加载,依赖就近,即应用时才会去加载
-
CMD 是 SeaJS 在推广过程中对模块定义的规范化产出
// 定义没有依赖的模块 define(function(require, exports, module){ var value = 1 exports.xxx = value module.exports = value }) // 定义有依赖的模块 define(function(require, exports, module){var module2 = require('./module1') // 引入依赖模块(同步) require.async('./module2', function (m3) {// 引入依赖模块(异步) }) exports.xxx = value // 裸露模块,也能够用 module.exports }) // 引入应用模块 define(function (require) {var a = require('./module1') var b = require('./module2') })
(7) ES6 中的模块计划
- <font color=red>ES6 的模块化计划作为通用计划,能够用于浏览器端和服务器端 </font>
-
<font color=red>ES6 中的模块化计划,设计思维是动态化,即在编译时就能确定模块的依赖关系,输出变量,输入变量;而 CommonJS 和 AMD 和 CMD 都只有在运行时能力确定依赖关系,输出和输入 </font>
CommonJS 模块就是对象,输出时 (引入模块) 必须查找对象属性
- CommonJS 是运行时加载,因为只有运行时能力生成对象,从而失去对象,才能够拜访对象的值
- ES6 模块不是对象,而是通过 exports 显示输入的代码,通过 import 输出
-
ES6 的模块,默认采纳严格模式
// CommonJS 模块 let {stat, exists, readFile} = require('fs'); (1) 本质上是整体加载模块 fs,在 fs 对象上再去读取 stat,exists 等属性 (2) 像 CommonJS 这种加载形式成为运行时加载,因为只有运行时能力失去这个对象 // ES6 模块 import {stat, exists, readFile} from 'fs'; (1) 本质是从 fs 模块加载 3 个办法,其余办法不加载 - 称为编译时加载或者动态加载 (2) ES6 在编译时实现加载,而不须要像 CommonJS,AMD,CMD 那样运行时加载,所以效率较高 - 这会导致没法援用 ES6 模块自身,因为它不是对象 // ES6 模块的益处 (1) 动态加载,编译时加载 ----- 效率较高,能够实现 (类型查看) 等只能靠 (动态剖析) 实现的性能 (2) 不再须要 (对象) 作为(命名空间),将来这些性能能够通过模块提供
-
export 命令
- 模块的性能次要由两个命令形成:import 和 export
- <font color=red>export 能够输入变量,函数,类 </font>
- export 输入的变量,就是变量原本的名字,然而能够通过 <font color=red>as</font> 关键字来重命名
// 报错 export 1; // 报错 var m = 1; export m; // 写法一 export var m = 1; // 写法二 var m = 1; export {m}; // 写法三 var n = 1; export {n as m}; // 报错 function f() {} export f; // 正确 export function f() {}; // 正确 function f() {} export {f};
-
模块的整体加载
- 除了指定加载某个输入值,还能够应用整体加载。
- 即用 <font color=red>*</font> 指定一个对象,所有输入值都加载到这个对象下面
-
export default
- import 须要直到函数名或变量命,否则无奈加载,– export default 指定模块的默认输入
- <font color=red>export default 其实时输入 default 的变量,所以他前面不能跟变量的申明语句 </font>
// 正确 export var a = 1; // 正确 var a = 1; export default a; ----------------> export default a : 意思是把变量 a 赋值给 default 变量 // 谬误 export default var a = 1 // 正确 export default 42; ---------------> 留神:能够将值赋值给 default 变量,对外的接口是 default // 报错 export 42; -----------------------> 没有指定对外的接口
-
<font color=red>export 与 import 的复合写法 </font>
export {foo, bar} from 'my_module'; // 能够简略了解为 import {foo, bar} from 'my_module'; export {foo, bar};
import()函数 – 反对动静加载模块
- 因为 ES6 模块化计划是动态加载,即编译时就能确定依赖关系,输出,输入;不必等到运行时
- <font color=red> 那如何做到 动静加载 ?</font>
- <font color=red>import(specifier) ———- specifier:说明符 </font>
-
<font color=red>import()返回一个 promise</font>
- import()相似于 Node 的 require 办法,区别次要是前者是异步加载,后者是同步加载。
- 它是 <font color=red> 运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块。</font>
import() 语法 (1) 按需加载:在须要的时候,再加载某个模块 (2) 条件加载:能够依据不同的条件加载不同的模块,比方 if 语句中 (3) 动静的模块门路:容许模块门路动静生成 import(f()).then(...); // 据函数 f()的返回值,加载不同的模块。(4) import()加载模块胜利当前,这个模块会作为一个对象,当作 then 办法的参数。// 因而,能够应用对象解构赋值的语法,获取输入接口。// import('./myModule.js').then(({export1, export2}) => {...}); (5) 如果模块有 default 输入接口,能够用参数间接取得。// import('./myModule.js').then(myModule => {console.log(myModule.default)}); // 下面 myModule 模块具备 defalut 接口,所以能够用 (参数.default) 获取 (6) 同时加载多个模块 Promise.all([import('./module1.js'), import('./module2.js'), import('./module3.js'), ]) .then(([module1, module2, module3]) => {···});
材料
具体 (模块化) 真的写得好:https://juejin.im/post/684490…
超残缺 (模块化):https://juejin.im/post/684490…
js 中哪些状况不能省略分号:https://blog.csdn.net/BigDrea…
ES6 模块化计划:http://es6.ruanyifeng.com/#do…
我的语雀:https://www.yuque.com/woowwu/…
模块化参考资料 https://juejin.cn/post/684490…