闭包
闭包:函数中又定义了一个新的函数,外部函数将内部函数返回 。
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
- 全局变量:
- 位置:一对script标签内,函数外。
- 作用范围:全局,整个页面。
- 生命周期:随着页面的关闭而销毁。
- 局部变量:
- 位置:一对函数花括号内,函数内。
- 作用范围:只能在函数内使用。
- 生命周期:从函数开始执行到函数执行完毕而销毁。
闭包的用途:
闭包的用途有两个:
- 让函数外部能够得到内部的变量。
// 让函数外部能够得到函数内部的变量
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
- 让函数内部的变量始终保存在内存中。
// 让函数内部的变量始终保存在内存中
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
闭包的缺点
闭包可以让函数外部拿到函数内部的变量,也能将函数内部的变量始终保存在内存中。
这是闭包的优点,也是它的缺点。
因为一旦滥用闭包就会让内存消耗变大,情况严重时可能会造成内存泄露。
解决办法
使用面向对象的思想进行代码的编写,将需要使用的函数和变量尽量封装到一个对象中去。
// 面试题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"
// 面试题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'
// 面试题3:
function getSum(num1){ // num1 = 5
return function(num2){ // num2 = 10
return num1+num2;
}
}
let num = getSum(5)(10);
console.log(num) // 15
// 面试题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