闭包

106 阅读4分钟

闭包 **** > 闭包:函数中又定义了一个新的函数,外部函数将内部函数返回 。 > > 在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。 > * 全局变量: > 1. 位置:一对script标签内,函数外。 > 2. 作用范围:全局,整个页面。 > 3. 生命周期:随着页面的关闭而销毁。 > * 局部变量: > 1. 位置:一对函数花括号内,函数内。 > 2. 作用范围:只能在函数内使用。 > 3. 生命周期:从函数开始执行到函数执行完毕而销毁。 闭包的用途: > 闭包的用途有两个: > > * 让函数外部能够得到内部的变量。 javascript // 让函数外部能够得到函数内部的变量 function outside(){ // 局部变量num1 var num1 = 10 // inside就是outside的闭包函数 // 连接了函数内外 function inside(){ return num1 } return inside } // 相当于:var fn = function inside(){ ... } var fn = outside() console.log(fn); //inside函数:function inside(){ return num1 } var fn1 = fn() console.log(fn1); //10 > * 让函数内部的变量始终保存在内存中。 javascript // 让函数内部的变量始终保存在内存中 function fn1(){ var num1 = 10 add = function(){ num1++ console.log(num1); console.log("add"); } function fn2(){ console.log(num1); } return fn2 } var a = fn1() console.log(a); //fn2函数 console.log(add()); //11 add undefind:(add()) add() //12 add add() //13 add a() //13 > 闭包的缺点 > 闭包可以让函数外部拿到函数内部的变量,也能将函数内部的变量始终保存在内存中。 > > 这是闭包的优点,也是它的缺点。 > > 因为一旦滥用闭包就会让内存消耗变大,情况严重时可能会造成内存泄露。 > 解决办法 > 使用面向对象的思想进行代码的编写,将需要使用的函数和变量尽量封装到一个对象中去。 javascript // 面试题1: var name = "window" var obj = { name:"obj", // getName:function(){}为obj对象中的属性 getName:function(){ return function(){ return this.name } } } // obj.getName()执行结果是返回的getName函数:function(){return this.name}。 // obj.getName()()执行时,function(){return this.name}变成普通函数,this指向window。 console.log( obj.getName()()) // "window" javascript // 面试题2: var name = 'window'; var obj = { name:'obj', getName:function(){ // 若此处变为箭头函数()=>{},this就指向了window,结果就会变成'window' // this指向对象本身obj,锁定this指向 var _this = this return function(){ // 若此处变为箭头函数()=>{},且不锁定this,this就指向了obj,结果就会变成'obj' // this指向已经被锁定为obj return _this.name; } } }; alert(obj.getName()()); // 'obj' javascript // 面试题3: function getSum(num1){ // num1 = 5 return function(num2){ // num2 = 10 return num1+num2; } } let num = getSum(5)(10); console.log(num) // 15 javascript // 面试题4: function fn(n,o){ //fn1 console.log(n,o); // 返回一个对象,对象的花括号没有作用域 return { fn:function(m){ //fn2 return fn(m,n); //fn3 } } } var a = fn(0); a.fn(1); a.fn(2); a.fn(3); var b = fn(0).fn(1).fn(2).fn(3); var c = fn(0).fn(1); c.fn(2); c.fn(3); // 首先将a,b,c分解 var a = fn(0); a.fn(1); a.fn(2); a.fn(3); 可以分解为: //0赋值给 fn(n,o); n=0,o=undefined var a = fn(0); //0 undefined // a调用对象fn2, 给m传值1,返回fn3,fn3(m,n)=>(1,0) 此处fn3变成全局变量,fn3中的n与fn1中的n相等 a.fn(1) // 1 0 // 同上,给m传值2,fn3中m为2,n为fn1中的0 a.fn(2) // 2 0 // 给m传值3,fn3中m为3,n为fn1中的0 a.fn(3) // 3 0 var b = fn(0).fn(1).fn(2).fn(3); 可以分解为: var a = fn(0) // 0 undefined // a调用对象fn2,m传值1,n往上找找到fn1中的n等于0,n的值变成0 var b = a.fn(1) // 1 0 // b调用对象fn2,m传值2,此时因为是在变量a的基础上调用,fn1的n现在为1 var c = b.fn(2) // 2 1 // c调用对象fn2,m传值3,此时因为是在变量b的基础上调用,fn1的n现在为2 c.fn(3) // 3 2 var c = fn(0).fn(1); c.fn(2); c.fn(3); 可以分解为: var a = fn(0) //0 undefined // a调用对象fn2,m传值1,n往上找找到fn1中的n等于0,n的值变成0 var b = a.fn(1); //1 0 // b调用对象fn2,m传值2,此时因为是在变量a的基础上调用,fn1的n现在为1 b.fn(2); //2 1 // c调用对象fn2,m传值3,此时还是在变量a的基础上调用,fn1的n现在还是1 b.fn(3); //3 1