第十章深入理解闭包

146 阅读3分钟

第十章深入理解闭包

理解闭包

作用域:全局作用域和函数作用域

闭包就是fn2,既能够读取其它函数内部变量的函数

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

var a = 123;
function fn1(){
    console.log(a);
    var b = 234;
    function fn2(){
    console.log(b);
}
	return fn2;
}
var result = fn1();
result();//b is not defined

闭包的用途

1.通过闭包制作计数器

function a(){
    var start = 0;
    function b(){
    return start++;
	}
	return b;
}
var inc = a();
console.log(inc());
console.log(inc());
console.log(inc());
// 释放当前的变量
inc = null;

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('mjj');
p1.setAge(18);
console.log(p1.getAge());
p1 = null;

闭包的注意点

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

2.变量用完不会被销毁,函数用完会销毁,私有属性会被存储起来

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

4.闭包需要三个条件:1.函数嵌套 ;2.访问所在的作用域 ;3.在所在作用域外被调用

立即执行函数(IIFE)

IIFE: ()是表达式 跟在函数后面 表示调用函数 fn()

立即执行函数:定义函数之后,立即调用该函数,这种函数叫做立即执行函数

简称:自执行函数

//1.常用的两种写法
(function(){

})()
!(function(){

}());
//2.通常情况下,写自执行函数的时候
!(function(){})();

自执行函数的应用

计数器

// 1.全局变量,会污染这个全局的变量
	var a = 0;
    function add(){
    	return ++a;
    }
    console.log(add());
    console.log(add());
    console.log(add());
    console.log(add());
    console.log(add());
// 2.自定义属性 有些代码可能会无意修改add.count
    function add(){
    	return ++add.count;
    }
    add.count = 0;
    console.log(add());
    console.log(add());
    console.log(add());
    console.log(add()); 
			
// 立即执行函数也叫闭包,可以封装私有的属性,同时可以减少对全局变量的污染
   var add = (function (){
   		// 私有属性
       var count = 0;
       return function (){
       		return ++count;
   	   }
   })();

    var add2 = (function(){
        var count = 1;
    })();
    console.log(add);
    console.log(add());
    console.log(add());
    console.log(add());
    console.log(add());
    console.log(add());
    console.log(add());

对循环和闭包的理解错误

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

解决方案:

1.使用闭包解决循环中变量的问题,相当于把变量保存在内存中,,每次执行的时候从内存中获取

function foo(){
    var arr = [];
    for(var i = 0; i < 10; i++){
        arr[i] = (function(n){//第一种写法
        	return function(){
        		return n;
        	}
		})(i); 

        (function(n){//第二种写法
            arr[n] = function(){
            	return  n;
            }
        })(i);
	}
	return arr;
} 

var bar = foo();
console.log(bar);
console.log(bar[0]());
console.log(bar[1]());
console.log(bar[2]());
console.log(bar[3]());
console.log(bar[4]()); 

2.解决方法 let块级作用域 ES6

function foo(){
    var arr = [];
    for(let i = 0; i < 10; i++){
    arr[i] = function(){
    	return i;
    }
}
	return arr;
}
var bar = foo();
console.log(bar[0]());
console.log(bar[1]());
console.log(bar[2]());
console.log(bar[3]());

闭包的10种表示形式

1.返回值 最常见的一种形式

var fn = function(){
    var name = 'mjj';
    return function(){
    	return name;
	}
}
var fnc = fn();
console.log(fnc());

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

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

3.函数参数

function fn2(f) {
	console.log(f());
}

function fn() {
    var name = 'mjj';
    var a = function() {
    	return name;
    }
    fn2(a);
}
fn();

4.IIFE

function fn3(f) {
	console.log(f());
}

(function() {
    var name = 'alex';
    var a = function() {
    	return name;
    }
    fn3(a);
})(); 

5.循环赋值

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

6.getter和setter函数来将要操作得变量保存在函数内部,防止暴露在外部

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

7.迭代器(计数器)

var add = function() {
	var num = 0;
	return function() {
		return ++num;
	}
}();
console.log(add());
console.log(add()); 

//['alex','mjj','阿黄']
function setUp(arr) {
	var i = 0;
	return function() {
		return arr[i++];
	}
}
var next = setUp(['alex', 'mjj', '阿黄']);
console.log(next());
console.log(next());
console.log(next()); 

8.区分首次

var firstLoad = (function() {
	var list = [];
	return function(id) {
		if (list.indexOf(id) >= 0) {
        	//list已经有id
        	return false;
        } else {
        	// 首次调用
        	list.push(id);
        	return true
        }
    }
})();
console.log(firstLoad(10));
console.log(firstLoad(10));
console.log(firstLoad(30));
console.log(firstLoad(40));
console.log(firstLoad(40)); 

9.缓存机制

9.1 未加入缓存

function mult(){
	// arguments
	var sum = 0;
	for(var i = 0; i < arguments.length; i++){
		sum = sum + arguments[i];
	}
	return sum;
}
			
console.log(mult(1,2,3,1,1,2,3)); //求和
console.log(mult(1,2,3,1,1,2,3)); //求和 
console.log(mult(1,2,3,1,1,2,3,4)); //求和 

9.2 有缓存机制

// 模拟一个对象的key 看该对象中是否有相同的key,如果有则直接获取value返回
{
    key:value
    1,2,3,1,1,2,3: 18,
    1,2,3,1,1,2,3,4: 22
} 
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 (){
		// 对cache对象进行操作
		// 1,2,3,1,1,2,3
		var args = Array.prototype.join.call(arguments,',');
        if(args in cache){
        	return cache[args];
        }
            console.log(cache);
            return cache[args] = calculate.apply(null,arguments);
    }
}();
console.log(mult(1,2,3,1,1,2,3));
console.log(mult(1,2,3,1,1,2,3));
console.log(mult(1,2,3,1,1,2,3,10,20));
console.log(mult(1,2,3,1,1,2,3,10,20,1)); 

10.img图片对象上报

new Image() 进行数据上报

低版本浏览器在进行数据上报会丢失30%左右的数据

// 使用闭包来做图片上传
var report = function (src){
    var imgs = [];
    return function (src){
        var img = new Image();
        imgs.push(img);
        img.src = src;
    }
}();
report('http://xxx.com/getUserInfo');