作用域
要理解闭包,首先要理解变量的作用域。作用域分为全局作用域和函数作用域(ES5)。在作用域链上,函数内部可以获取外部的变量,但是函数外部不能获取函数内部的变量。
闭包就是可以在函数外部获取内部变量的一种方法。
闭包最大的特点就是可以记住他所诞生的环境,本质是链接函数内外部。
闭包的用途
-
读取函数内部的变量,这些变量始终在内存中(因为引用关系一直存在)。-----所以在不用变量之后需要手动释放变量。
-
能够封装对象的私有属性和方法
注意
-
因使用闭包使函数中的变量始终在内存中,内存消耗大,所以不能滥用,否则会造成页面的性能问题,在ie中可能会导致内存泄露。
-
如果用封装的话,不要任意修改私有属性。
闭包形成的三个条件
-
函数嵌套
-
访问所在的作用域的函数
-
在所在作用域外被调用
闭包的十种表示方式
- 返回值
这是一种最常见的表现形式。
var fn = function(){
var name = 'nxl'
return function(){
return name
}
}()
console.log(fn())
- 函数赋值
将内部函数赋值给一个外部的对象。
var fn2;
var fn = function() {
var name = 'nxl'
var a = function(){
return name
}
fn2 = a
}
fn()
console.log(fn2())
- 函数参数
把函数当作参数传给另一个函数。
function fn2(f){
console.log(f())
}
function fn() {
var name = 'nxl'
var a = function() {
return name
}
fn2(a)
}
fn()
- IIFE
function fn2(f){
console.log(f())
}
(function(){
var name = 'nxl'
var a = function() {
return name
}
fn2(a)
})()
- 循环赋值
function foo(){
var arr = []
for(var i = 0; i< 10; i++) {
arr[i] = (function(n){
return function(){
return n
}
})(i)
}
return arr
}
var bar = foo()
console.log(bar[3]())
getter
和setter
var getValue, setValue
(function() {
var num = 0
getValue = function() {
return num
}
setValue = function(n) {
if(typeof n === 'number') {
num = n
}
}
})()
console.log(getValue())
setValue(10)
console.log(getValue())
- 迭代器
计数器
var add = function() {
var num = 0
return function() {
return ++num
}
}()
console.log(add())
console.log(add())
console.log(add())
迭代数组
function setUp(arr) {
var i = 0
return function() {
return arr[i++]
}
}
var next = setUp(['1', '2', '3', '4'])
console.log(next())
console.log(next())
console.log(next())
console.log(next())
- 区分首次
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))
- 缓存机制
// 未加入缓存
function mult() {
var sum = 0
for(var i = 0; i < arguments.length; i++) {
sum += arguments[i]
}
return sum
}
console.log(mult(1,2,3,5,3,2,3))
console.log(mult(1,2,3,5,3,2,3))
// 加入缓存
var mult = function() {
var cache = {}
var calculate = function() {
var sum = 0
for(var i = 0; i< arguments.length; i++) {
sum += arguments[i]
}
return sum
}
return function() {
var args = Array.prototype.join.call(args, ',')
if(args in cache) {
return cache[args]
} else {
return cache[args] = calculate.apply(null, arguments)
}
}
}
console.log(mult(1,2,3,5,3,2,3))
console.log(mult(1,2,3,5,3,2,3))
- img图片上传
// 低版本浏览器在进行数据上报,会丢失30%左右的数据
var report = function(src) {
var img = new Image()
img.src = src
}
report('http://xxx.com/getUserInfo')
// 用闭包来做图片上传
var report = function(src) {
var imgs = []
return function(src) {
var img = new Image()
imgs.push(img)
img.src = src
}
}
report('http://xxx.com/getUserInfo')
关于闭包的练习题
- example 1
function funA() {
var a = 10
return function () {
console.log(a)
}
}
var b = funA()
b()
- example 2
function outerFn() {
var i = 0
function innerFn() {
i++
console.log(i)
}
return innerFn
}
var inner = outerFn()
inner()
inner()
inner()
var inner2 = outerFn()
inner2()
inner2()
inner2()
- example 3
var i = 0
function outerFn() {
function innerFn() {
i++
console.log(i)
}
return innerFn
}
var inner1 = outerFn()
var inner2 = outerFn()
inner1()
inner2()
inner1()
inner2()
- example 4
function fn() {
var a = 3
return function () {
return ++a
}
}
console.log(fn()());
console.log(fn()());
var newFn = fn()
console.log(newFn())
console.log(newFn())
console.log(newFn())
- example 5
function outerFn() {
var i = 0
function innerFn() {
i++
console.log(i);
}
return innerFn
}
var inner1 = outerFn()
var inner2 = outerFn()
inner1()
inner2()
inner1()
inner2()
- example 6
(function () {
var m = 0
function getM() { return m }
function setA(val) { m = val }
window.g = getM
window.f = setA
})()
f(100)
console.info(g());
- example 7
function a() {
var i = 0
function b() { console.log(++i) }
return b
}
var c = a()
c()
c()
- example 8
function f() {
var count = 0
return function () {
count++
console.info(count);
}
}
var t1 = f()
t1()
t1()
t1()
- example 9
var add = function (x) { // x=1
var sum = 1
var tmp = function (x) { //x=2 x=3
sum = sum + x // 3 6
return tmp
}
tmp.toString = function () {
return sum
}
return tmp
}
console.log(add(1)(2))
console.log(add(1)(2)(3))
- example 10
var lis = document.getElementsByTagName('li')
for (var i = 0; i < lis.length; i++) {
(function (i) {
lis[i].onclick = function () {
console.log(i);
}
})(i) // //事件处理函数中闭包的写法
}
- example 11
function m1() {
var x = 1
return function () {
console.log(++x);
}
}
m1()()
m1()()
m1()()
var m2 = m1()
m2()
m2()
m2()
- example 12
var fn = (function () {
var i = 10
function fn() {
console.log(++i);
}
return fn
})()
fn()
fn()
- example 13
function love1() {
var num = 223
var me1 = function () {
console.log(num);
}
num++
return me1
}
var loveme1 = love1()
loveme1()
loveme1()
- example 14
function fun(n, o) {
console.log(o);
return {
fun: function (m) {
return fun(m, n)
}
}
}
var a = fun(0)
a.fun(1) // 执行fun(1,0)
a.fun(2) // 执行fun(2,0)
a.fun(3) // 执行fun(3,0)
var b = fun(0).fun(1).fun(2).fun(3)
// 1 fun(0)----------// n=0 o=undefined 打印undefined 返回fun(m,0)函数
// 2 fun(0).fun(1)---------- // 执行fun(1,0) n=1 o=0 打印0 返回fun(m,1)
// 3 fun(0).fun(1).fun(2)---------- // 执行fun(2,0) n=2 o=1 打印1 返回fun(m, 2)
// 3 fun(0).fun(1).fun(2).fun(3)---------- // 执行fun(3,0) n=3 o=2 打印2返回fun(m, 2)
var c = fun(0).fun(1) // 执行fun(1,0) n=1 o=0 打印undefined 0 返回fun(m,1)
c.fun(2) // 执行fun(2,1) n=2 o=1 打印1 返回fun(m,2)
c.fun(3) // 执行fun(3,1) n=3 o=1 打印1 返回fun(m,3)
- example 15
function fn() {
var arr = [];
for (var i = 0; i < 5; i++) {
// (function (i) {
arr[i] = function () {
return i;
}
// })(i)
}
return arr;
}
var list = fn();
for (var i = 0, len = list.length; i < len; i++) {
console.log(list[i]());
}
- example 16
function fn() {
var arr = [];
for (var i = 0; i < 5; i++) {
arr[i] = (function (i) {
return function () {
return i;
};
})(i);
}
return arr;
}
var list = fn();
for (var i = 0, len = list.length; i < len; i++) {
console.log(list[i]());
}
参考:
深入理解闭包(视频)
学习Javascript闭包(Closure)
JS 中的闭包是什么?
闭包,看这一篇就够了——带你看透闭包的本质,百发百中
MDN闭包