导航
[[深刻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 = 1var c = 2function x() { var a = 10 // 全局中也有a,然而函数作用域申明的a不会影响全局 var b = 100 c = 20 // 然而函数外部能批改全局的变量,(作用域链外部能批改内部的变量)}x()console.log(a) // 1console.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的执行程序
bgcolor=orange>async (1)加载:是异步加载,加载不阻塞页面;(2)执行:加载完立刻执行,不能保障各js的执行程序 <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,不是函数参数传入的,而是拜访的父级作用域的objconsole.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属性不会净化全局变量a32. 缩小了全局作用域内的变量数量: - 这里只有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') // 11console.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.jsdefine(function () { // ----------------- define(function(){...}) 定义一个模块return 'string b'})c.jsdefine(['./b.js'], function(res) { // --- define(['a'], function(res){...}) 定义一个有依赖的模块,c 依赖模块 breturn 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 = 1exports.xxx = valuemodule.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...