web前端之JavaScrip中的闭包


闭包--笔试-11

javascript 复制代码
function fun() { 
	var n = 9; 
	// js 中强行给一个未声明的变量赋值,
	// 程序不会报错
	// 并且会自动在全局创建此变量
	add = function() { 
		n++;
	}; 
	return function() { 
		console.log(n); 
	}; 
};

// 把 fun() 执行的结果赋值给 fn 变量
var fn = fun();

// 此处调用的是全局的 add 函数,
// 因为全局的 add 函数作用域链引用着 fun 函数作用域对象
// 所以修改的是 fun 里面变量的值
add();
fn(); // 10

// 把 fun() 执行的结果赋值给 fn2 变量
// 注意:这里的 fn2 所引用的是 fun() 执行后的地址
// 所以 fn 和 fn2 变量使用的地址是不同,结果也不相同
var fn2 = fun();
fn2(); // 9
add();
add();
fn2(); // 11
fn(); // 10
add();
fn(); // 10

defineReactive函数,利用闭包封装Object.defineProperty()

javascript 复制代码
function defineReactive(data, key, val) {
	Object.defineProperty(data, key, {
		// 可枚举
		enumerable: true,
		// 可以被配置,比如可以被 delete
		configurable: true,
		// getter  
		get() { 
			return val;
		},
		// setter
		set(newValue) {
			if (val === newValue) return false;
			val = newValue;
		}
	});
};
let obj = {};
defineReactive(obj, 'a', 10); // 设置 a 属性
console.log(obj.a); // 10 访问 a 的值
obj.a = 100; // 改变 a 的值
console.log(obj.a); // 100 访问改变后 a 的值

闭包--节流函数--笔试-10

1、定义

节流函数的作用是在限定的时间内函数只执行一次。
1、按钮提交(可以避免重复提交,当然不只这种方法,将按钮设置为不可用也可以)。
2、scroll、mousehover、mousemove 等触发频率高的时候。
主要的原理就是在闭包内设置一个标记,在限定的时间内这个 flag 设置为 true,函数再次点击则让执行,setTimeout 函数执行以后将 flag 设置为 flase,就可以继续执行 。


2、html

html 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=GBK">
	</head>
	<body>
		<div class="box" id="div_box" >
			<button onclick="fn1()">test</button>
		</div>
	</body>
</html>

3、JavaScript

javascript 复制代码
function throttle(fn, delay) {
	var flag = false;
	var timer = null;
	
	return function() {
		// 将参数转成数组
		var args = [].slice.call(arguments, 0); 
		var context = this;
		
		// 如果在限定的时间内 flag 是 true 则直接返回,不让执行
		if(flag) return false;
		// 函数正在控制中
		flag = true; 
		// 执行函数
		fn.apply(context, args);
		// 清除定时器
		clearTimeout(timer); 
		timer = setTimeout(function() {
			// 延时时间过了以后,放开函数控制
			flag = false; 
		}, delay);	
	}
}

function fn() {
	console.log(123);
}

// 绑定节流函数
var fn1 = throttle(fn, 2000);  

闭包的定义

定义-01

闭包是指有权访问另一个函数作用域中的变量的函数。


定义-02

闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。


定义-03

闭包可以让一个函数访问并操作其声明时的作用域中的变量和函数,即使声明时的作用域消失了,也可以调用。


定义-04

闭包是一个定义在其它函数(父函数)里面的函数,它拥有对父函数里面变量的访问权。闭包有三个作用域的访问权。自身的作用域、父作用域和全局作用域。


JavaScript闭包的9大经典使用场景

1、返回值(最常用)

这个很好理解就是以闭包的形式将 name 返回。

javascript 复制代码
function fn() {
    var name = "hello";
    return function (){
        return name;
    };
};
var fnc = fn();
console.log(fnc()); // hello

2、函数赋值

在闭包里面给fn函数设置值,闭包的形式把name属性记忆下来,执行会输出 hello。

javascript 复制代码
var fn;
function fnc() {
    var name = "hello";
    // 将函数赋值给fn
    fn = function (){
        return name;
    };
};
fnc(); // 要先执行进行赋值,
console.log(fn2()); // 执行输出fn2

3、函数参数

用闭包返回一个函数,把此函数作为另一个函数的参数,在另一个函数里面执行这个函数,最终输出 hello。

javascript 复制代码
function fn(){
    var name = "hello";
    return function callback() {
        return name;
    };
};
var fun = fn(); // 执行函数将返回值(callback函数)赋值给fnc
function func(f) {
    // 将函数作为参数传入
    console.log(f()); // 执行函数,并输出
};
func(fun); // 执行输出fun

4、IIFE(自执行函数)

直接在自执行函数里面将封装的函数fn1传给fn2,作为参数调用同样可以获得结果hello。

javascript 复制代码
(function (){
	var name = "hello";
    var fn1 = function (){
        return name;
    };
    // 直接在自执行函数里面调用fn2,将fn1作为参数传入
    fn2(fn1);
})();
function fn2(f){
    // 将函数作为参数传入
    console.log(f()); // 执行函数,并输出
};

5、循环赋值

如果不采用闭包的话,会有不一样的情况。

javascript 复制代码
// 每秒执行1次,分别输出1-10
for(var i = 1; i <= 10; i++){
    (function (j){
        // j来接收
        setTimeout(function (){
            console.log(j);
        }, j * 1000);
    })(i); // i作为实参传入
}

6、getter和setter

第一次输出 hello 用setter以后再输出 world ,这样做可以封装成公共方法,防止不想暴露的属性和函数暴露在外部。

javascript 复制代码
function fn() {
	var name = 'hello',
    setName = function (n){
        name = n;
    },
    getName = function (){
        return name;
    };

    // 将setName,getName作为对象的属性返回
    return {
        setName,
        getName
    };
};
var fn1 = fn(); // 返回对象,属性setName和getName是两个函数
console.log(fn1.getName()); // getter
fn1.setName('world'); // setter修改闭包里面的name
console.log(fn1.getName()); // getter

7、迭代器(执行一次函数往下取一个值)

null

javascript 复制代码
var arr = ['aa','bb','cc'];
function incre(arr) {
    var i = 0;
    return function (){
		// 这个函数每次被执行都返回数组arr中 i下标对应的元素
		return arr[i++] || '数组值已经遍历完';
    }
}
var next = incre(arr);
console.log(next()); // aa
console.log(next()); // bb
console.log(next()); // cc
console.log(next()); // 数组值已经遍历完

8、首次区分(相同的参数,函数不会重复执行)

null

javascript 复制代码
var fn = (function (){
	var arr = []; // 用来缓存的数组
	return function (val){
		if(arr.indexOf(val) == -1) { // 缓存中没有则表示需要执行
			arr.push(val); // 将参数push到缓存数组中
			console.log('函数被执行了', arr);
			//这里写想要执行的函数
      	} else {
			console.log('此次函数不需要执行');
		}
		console.log('函数调用完打印一下,方便查看已缓存的数组:', arr);
	};
})();
fn(10);
fn(10);
fn(1000);
fn(200);
fn(1000);

9、缓存

比如求和操作,如果没有缓存,每次调用都要重复计算,采用缓存已经执行过的去查找,查找到了就直接返回,不需要重新计算。

javascript 复制代码
var fn = (function (){
	var cache = {}; // 缓存对象
	var calc = function (arr){ // 计算函数
		var sum = 0;
		// 求和
		for(var i = 0; i < arr.length; i++){
			sum += arr[i];
		}
		return sum;
	};

	return function (){
		var args = Array.prototype.slice.call(arguments,0); // arguments转换成数组
		var key = args.join(","); // 将args用逗号连接成字符串
		var result, 
			tSum = cache[key];
		if(tSum){ // 如果缓存有   
			console.log('从缓存中取:', cache); // 打印方便查看
			result = tSum;
		} else {
			// 重新计算,并存入缓存同时赋值给result
			result = cache[key] = calc(args);
			console.log('存入缓存:', cache); // 打印方便查看
		}
		return result;
	}
})();
fn(1, 2, 3, 4, 5);
fn(1, 2, 3, 4, 5);
fn(1, 2, 3, 4, 5, 6);
fn(1, 2, 3, 4, 5, 8);
fn(1, 2, 3, 4, 5, 6);

链接

大千世界出品

相关推荐
GISer_Jing2 小时前
前端面试通关:Cesium+Three+React优化+TypeScript实战+ECharts性能方案
前端·react.js·面试
落霞的思绪3 小时前
CSS复习
前端·css
咖啡の猫5 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲7 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5818 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路8 小时前
GeoTools 读取影像元数据
前端
ssshooter9 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
你的人类朋友9 小时前
【Node.js】什么是Node.js
javascript·后端·node.js
Jerry9 小时前
Jetpack Compose 中的状态
前端
dae bal10 小时前
关于RSA和AES加密
前端·vue.js