# 闭包

参考资料 视频1 (opens new window) 视频2 (opens new window)

# 1.什么是闭包

先看一段代码

function bag(){
	var book = '背包里的书';
}
console.log(book);
// book is not defined

执行上下文:全局环境、函数环境、Eval环境

关于执行上下文,详细情况请看 作用域 文档

现在想要在全局环境下调用book,该怎么做?

当想要在全局环境下调用函数环境的内部变量的想法,就叫做闭包

(全局作用域下调用函数作用域的内部变量或函数)

function bag(){
	var book = '背包里的书';
	function bagTag(){
		// 在这里,是能够输出book的值的
		console.log(book);
	}
	return bagTag;
}
var result = bag();
result();	// 成功输出了book

闭包就是bagTag,能够读取其他函数内部变量的函数

闭包最大的特点:可以记住诞生的环境,比如bagTag记住了其诞生的环境是bag,所以在bagTag中可以得到bag中的内部变量

# 2.闭包的用途

# 2.1 量封装在函数中,不暴露在全局作用域中

读取函数内部的变量,这些变量始终在内存中

// 不使用闭包的计数器
var start = 0;	// 变量暴露在全局作用域下
function count(){
    return start++;
}
for(var i = 0; i < 10; i++){
    console.log(count());
}
// 使用闭包的计数器
function count(){
    var start = 0;	// 变量在函数作用域内部
    function rt() {
        return start++;
    }
    return rt;	// 返回闭包函数
}
var result = count();
for(var i = 0; i < 10; i++){
    console.log(result());
}
// 因为返回的闭包函数是存储在 result 这个对象之中
// 所以会存在内存泄漏、占用的问题,可以在调取完变量之后,将其置空
result = null;

# 2.2 闭包能够封装对象的私有属性和方法

function Person(name){
    var age;	//私有属性
    // 私有方法——闭包,
    function setAge(n){
        age = n;
    }
    function getAge(){
        return age;
    }
	return {
		name: name,
        setAge: setAge,
        getAge: getAge
	}
}
var p1 = Person('张三');
p1.setAge(18);
console.log(p1.getAge());
p1 = null;

# 3.闭包注意点

  1. 使用闭包使得函数中的变量始终在内存中内存消耗很大,所以不能滥用闭包,否则会造成页面的性能问题,在IE中可能导致内存泄漏

# 4.闭包总结

闭包需要三个条件:

  1. 函数嵌套
  2. 访问所在的作用域
  3. 在所在作用域外被调用

每个父函数调用完成,都会形成新的闭包,父函数中的变量会始终在内存中,相当于缓存,小心内存的消耗问题

# 5.立即执行函数 IIFE

在JavaScript中 () 跟在函数后面,表示调用函数 fn()

立即执行函数: 需要定义函数之后,立即调用该函数

如果function关键字出现在行首,一律解释成函数声明语句,函数声明语句需要函数名,为了匿名函数没有函数名,所以可以使用()将行首取代掉

for(var i = 0; i < 5; i++) {
	setTimeout(function(){
		console.log(i++);
	},4000);
}

// 因为 setTimeout 是异步操作,所以会被列入任务队列中等待同步操作执行完毕再执行
// 输出结果是 5 6 7 8 9
// 但是 如果要输出 0 1 2 3 4,该怎么修改?
// 那就是要让setTimeout操作立即执行

for(var i = 0; i < 5; i++) {
    (function(x){
        setTimeout(function(){
            console.log(x++);
        },4000);
    })(i);
}
// (function(x){})(i)
// (function(x){}(i))	W3C建议该种

# 6.闭包的十种场景

# 6.1 返回值

最常见

var fn = function(){
	var name = '张三';
    return function(){
        return name;
    }
}
var fnname = fn();
console.log(fnname());

# 6.2 函数赋值

一种变形的形式是将内部函数赋值给一个外部的变量

var fn2;
var fn = function(){
	var name = '张三';
	var a = function () {
		return name;
	}
	fn2 = a;
}
fn();
console.log(fn2());

# 6.3 函数参数

var fn2 = function (f) {
	console.log(f());
}
function fn(){
	var name = '张三';
	var a = function () {
		return name;
	}
	fn2(a);
}
fn();

# 6.4 IIFE

function fn2(f){
    console.log(f());
}
(function () {
    var name = '张三';
    var a = function () {
        return name;
    }
    fn2(a);
})();

# 6.5 循环赋值

function foo(){
	var arr = [];
    for(var i = 0; i < 10; i++){
        (function(i){
            arr[i] = function(){
                return i;
            }
        })(i);
    }
}
var bar = foo();

# 6.6 getter 和 setter

var getValue,setValue;
(function(){
	var num = 0;
	getValue = function(){
		return num;
	}
	setValue = function(v){
		if(typeof v === 'number'){
			num = v;
		}
	}
})();
setValue(233);
console.log(getValue());

# 6.7 迭代器

计数器

var add = function () {
	var num = 0;
    var name = ['张三', '李四', '王五']
	return function () {
		return name[num++];
	}
}();
console.log(add());
console.log(add());
console.log(add());

# 6.8 区分首次

var firstLoad = (function(){
    var list = [];
	return function(id){
        if(list.indexOf(id) >= 0){
            return false;
        } else {
            list.push(id);
            return true;
        }
    }
})();
console.log(firstLoad(10);)
console.log(firstLoad(10);)

# 6.9 缓存机制

var mult = function () {
    //缓存对象
    var cache = {};
    var calculate = function () {
        var sum = 0;
        for (var i = 0; i < arguments.length; i++) {
            sum = sum + i;
        }
        return sum;
    }
    return function () {
        var args =  Array.prototype.join.call(arguments,',');
        if(args in cache){
            return chche[args];
        }
        return cache[args] = calculate.apply(null,arugments);
    }
}();
console.log(mult(1,2,3,1,1,2,3));

# 6.10 img对象图片上报

# 7.总结

更新时间: 6/28/2021, 11:57:55 PM