js高级学习5 模块化编程 节流防抖 第一阶段总结
Posted lin-fighting
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js高级学习5 模块化编程 节流防抖 第一阶段总结相关的知识,希望对你有一定的参考价值。
模块化编程
最早起的模块化 高级单例设计模式
解决变量命名限制
1 IFFE 利用闭包的保存机制
(function(){
let A = 1
})()
(function(){
let A = 'A'
})()
2 基于对象进行分组,每个对象都是Object的实例 ->单例设计模式
let a = {
A: 1
}
let b = {
A: 'A'
}
基于单独的实例,实现信息的分组,可以避免污染。
a和b可以称为是命名空间,把描述同一事物的属性和方法放在相同的命名空间中。
使用闭包模拟模块
let A = (function(){
let a = 1
return { a }
})()
let B = (fcuntion(){
let a = 2 + A.a //作用域链,会从外部继续找A
return { a }
})()
最早期的模块。(闭包+对象)
问题:需要注意导出顺序。
AMD
按需加载(require.js)
使用方法:
// define相当于定义一个模块
// A.js
define(function(){
return {
a: 1
}
})
// B.js ,饮用A模块的写法
define(['A'],function(A){
return {
a: A.a + 1
}
})
//使用
require(['A', 'B'], function(A, B){
return A.a + B.a
})
自己模拟实现AMD
//自己实现的AMD
let factories = {}
//define第一个参数是模块名
function define(moduleName, factoy){
factories[moduleName] = factoy
//存到全局对象
}
//
function require(modules, callback){
modules = modules.map(item=>{
//从全局对象取出并且执行,拿到每个模块返回的结果
return factories[item]()
})
callback(...modules) //执行callback。并且将每个module传入
}
define('A', ()=>({a:1}))
define('B', ()=>({a: 2}))
require(['A', 'B'], (A,B)=>{
console.log(A.a+B.a); //3
return A.a+B.a
})
AMD的实现原理大概就是这样。
问题:所有的依赖模块都需要提前导入。
CMD && COMMONJS
node.js下的模块规范。
在哪用到,在哪导入。
A.js
const a = 1
module.exports = { a } //导出的是对象
B.js
const A = require('./A.js')
consoe.log(A.a) // 1
浏览器不支持commons,但是webpack支持,webpack是基于node运行的,他可以将commonjs打包成浏览器支持的代码,因为webpack自己内部实现了一套CMD规范。
简单实现
const fs = require("fs")
const myRequrie = moduleName => {
//读取文件的内容
const content = fs.readFileSync(moduleName, "utf8")
//关键实现, exports用于当导出是exports={}的时候
// eslint-disable-next-line no-new-func
const fn = new Function(
"exports",
"module",
"require",
"__dirname",
`${content}\\n return module.exports`
)
const module = {
exports: {}
}
fn(module.exports, module, myRequrie, __dirname)
return module.exports
}
const req = myRequrie("./1.js")
console.log(req)
思路就是通过读取改文件的内容,比如
const req = 123
module.exports = {req}
将文件的内容读取出来,然后放到fn函数去,再去执行,fn函数应该是
function fn(exports, module, require, __diranme){
const req = 123
module.exports = { req }
return module.exports
}
然后执行,执行的时候对module.exports进行一个赋值。再返回,
const req = myRequrie("./1.js") // req=> { req: 123}
console.log(req) // { req: 123 }
这样commonjs就简易实现了,当然原本的commonjs有更复杂的东西,比如缓存等等
通过我们的简易实现我们可以看到commonjs是同步运行的,也相当于把其他文件的代码放到改文件去执行,给module.epxorts赋值再返回。
ES6MODUle
webpack支持,浏览器也支持。
A.js
const a = 1
export {a} //导出的并不是对象,而是建立与一块地址的连接。
import { a } from './A.js/. 导入的是从连接的地址拿到的值
import是在编译的时候就能确定的,而commonjs是在运行的时候才能确定的。
节流防抖
防抖:只执行一次
节流:将发生时间的频率降低
防抖:
- 简单的防抖:点击后发送事件,在发送完成后才能再发送。
function debounce(fn){
let isRun. =false
return function(...args){
if(isRun) return
isRun.= true
fn.apply(this, args).then(res=>isRun = fakse)
}
}
btn.onclick = debounce(xxx)
每执行一次isRun就为true,点击之后就不会出发请求。等请求完成后再将isRun设为false.
- 事件执行很多遍,但是我们真正执行的可以只执行一次
function debounce(fn){
let num = 0
return function(...args){
if(num > 100){
fn.apply(this,args)
num = 0 //重置为0
. } else {
num+=1
}
}
}
如上,只有当点击100次之后才会执行fn。
- 通过时间控制只执行一次
function debounce(fn, n, immediate) { // imediate最开始触发还是最后面触发
let timer;
return function (...args) {
if (timer) {
clearTimeout(timer)
timer = null
}
const _selft = this
let result;
let now = !timer && immediate //是否最开始触发
timer = setTimeout(() => {
timer = null
if(!immediate){
result = fn.apply(_selft, args) //最后触发
}
}, n)
if(now)result = result = fn.apply(_selft, args) //最开始触发
return result
}
}
btn.onclick = debounce(fn, 2000)
如上,只有当停止触发点击事件的时候,才会触发两秒后的setTimeout,因为你不停点击的时候,setTimeout会不断被销毁创建销毁创建。
第三个参数immediate可以控制函数在开始的时候触发还是最后面触发,如果传入true,表示一开始触发一次,在之后的n秒内不再触发。
节流
节流的思路是在n秒内只能触发一次,不管你点击多少次。
上代码:
// 节流
// 思路:记录上次点击的时候与这次点击的时间,如果超过n秒,证明可以立即执行。否则就判断有没有定时器,没有的话创建一个定时器,时间为n-(现在-上次),比如n是5,现在是2,上次是0,也就是说定义一个3s的定时器。
//在3s内不管再怎么点击都无效。3s后固定执行一次。再将现在的时间记录起来。下次再点击的时候,如果继续这样判断。
function throttle(fn, n) {
let timer;
let pre = 0
return function (...args) {
let result;
let now = Date.now()
let remaining = n - (now - pre) //距离上次点击是否超过限时
if (remaining <= 0) {
//两者点击时间大于n,立即执行 //第一次点击必定执行
result = fn.apply(this, args)
pre = now // 将这次触发的时间记录
} else if (!timer) {
//没超时,而且还没有定时器
//没设置过定时器
timer = setTimeout(() => {
result = fn.apply(this, args)
timer = null
pre = Date.now()
}, remaining)
} else {
//没超时,有定时器,啥也不做
}
return result
}
}
如上,如果在一定时间内同时点击多次,那么只有一个setTimeout生效。达到控制触发频率的效果。
第一阶段总结
对闭包的理解?
大函数执行返回小函数?大错特错。
从以下角度:堆栈内存,垃圾回收机制,实际应用,设计模式,源码等入手。
- 浏览器代码执行的地方是在栈内存,函数在执行的时候会产生一个私有上下文,当私有上下文中的变量被其下级上下文所引用的时候,该上下文不会被浏览器的垃圾回收机制(标记清除+引用计数),这种可以保护私有变量,产生不会回收的上下文的机制就叫做闭包。最常见的就是for循环累计数的应用,像单例模式,柯里化函数,compose组合函数都是利用闭包的保护机制。像redux源码中的store.surcible中返回的销毁函数也是利用了闭包。
以上是关于js高级学习5 模块化编程 节流防抖 第一阶段总结的主要内容,如果未能解决你的问题,请参考以下文章