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 模块化编程 节流防抖 第一阶段总结的主要内容,如果未能解决你的问题,请参考以下文章

用lodash使用防抖节流

闭包与防抖节流

JS函数节流和防抖

js的节流和防抖

js的防抖、节流

js面试题之手写节流函数和防抖函数