JS闭包

作者: 饥人谷光仔 | 来源:发表于2018-10-25 00:52 被阅读3次

闭包

定义

MDN定义

javascriptkit

词法作用域

作用域链

  • 函数在执行的过程中,先从自己内部找变量
  • 如果找不到,再从创建当前函数所在的作用域(词法作用域)去找, 以 此往上
  • 注意找的是变量的当前的状态

作用域链的博客

函数连同它作用域链上的要找的这个变量,共同构成闭包

一般情况下使用闭包主要是为了

  • 封装数据

  • 暂存数据

一个典型的闭包案例

function car(){
  var speed = 0
  function fn(){
    speed++
    console.log(speed)
  }
  return fn
}

var speedUp = car()
speedUp()   //1
speedUp()   //2

在说闭包时先了解什么是立即执行语句和立即执行函数以及他们之间的区别,请移步文末参考博客

当函数内部没有执行以下的代码时

function fn(){
    speed++
    console.log(speed)
    }
return fn

在代码执行完成后,函数内部的局部变量speed就会被销毁,由于全局标量speedUp一直存在(除非关闭当前页面,否则全局变量一直存在),那么函数内部的作用域就没有办法被销毁,里面有东西一直被使用,这点与浏览器的垃圾回收机制相仿,当我们执行speedUp(),他会在函数的词法作用域下去寻找,函数里面又返回了一个fn,因而形成闭包,简单的理解为

var speed = 0
function fn(){
     speed++
     console.log(speed)
}

这一段代码形成一个闭包,如果不return fn,那函数内部的局部变量就会被销毁。

我们可以看看上述代码利用立即执行语句和立即执行函数可以怎么演变:

function car(){
  var speed = 0
  function fn(){
    speed++
    console.log(speed)
  }
  return fn
}

var speedUp = car()

//1
function car(){
  var speed = 0
  return function (){
    speed++
    console.log(speed)
  }
}

var speedUp = car()

//2
function car(speed){
  return function (){
    speed++
    console.log(speed)
  }
}

var speedUp = car(3)

//3
function car(){
  var speed = arguments[0]
  return function (){
    speed++
    console.log(speed)
  }
}

var speedUp = car()

//4
function car(){
  var speed = 0
  return function (){
    speed++
    console.log(speed)
  }
}

//5 car可以不写,则为匿名函数 
var speedUp = (function car(speed){
    return function (){
    speed++
    console.log(speed)
  }
}
)(3)

闭包的相关案例

如下代码输出多少?如果想输出3,那如何改造代码?

var fnArr = [];
for (var i = 0; i < 10; i ++) {
  fnArr[i] =  function(){
    return i
  };
}
console.log( fnArr[3]() ) // 10

同等演变

假设只有两层循环:

var fnArr = []
for (var i = 0; i < 2; i ++) {
    fnArr[i] =  (function(j){
       return function(){
         return j
        } 
      })(i)
}
fnArr[3]()

//1
var fnArr = [] 
fnArr[0] = (function(j){
       return function(){
         return j
        } 
      })(0)
}
fnArr[1] = (function(j){
       return function(){
         return j
        } 
      })(1)
}
fnArr[3]()

//2
var a = (function(j){
       return function(){
         return j
        } 
      })(0)
}
var b = (function(j){
       return function(){
         return j
        } 
      })(1)
}
b()

//3
var a = (function(j){
       return function(){
         return j
        } 
      })(0)
}
function fn2(j){
       return function(){
         return j
        }
}
var b = fn2(1)

//4
var a = (function(j){
       return function(){
         return j
        } 
      })(0)
}
function fn2(j){
       return function(){
         return j
        }
    return f
}
var b = fn2(1)

//5
var a = (function(j){
       return function(){
         return j
        } 
      })(0)
}
function fn2(j){
    var j = arguments[0]
    function f(){
         return j
        }
    return f
}
var b = fn2(1)

改造后(立即执行语句,演变过程)

var fnArr = []
for (var i = 0; i < 10; i ++) {
  fnArr[i] =  (function(j){
    return function(){
      return j
    } 
  })(i)
}
console.log( fnArr[3]() ) // 3

var fnArr = []
for (var i = 0; i < 10; i ++) {
  (function(i){
    fnArr[i] =  function(){
      return i
    } 
  })(i)
}
console.log( fnArr[3]() ) // 3

var fnArr = []
for (let i = 0; i < 10; i ++) {
  fnArr[i] =  function(){
    return i
  } 
}
console.log( fnArr[3]() ) // 3

封装一个 Car 对象

var Car = (function(){
   var speed = 0;
   function set(s){
       speed = s
   }
   function get(){
      return speed
   }
   function speedUp(){
      speed++
   }
   function speedDown(){
      speed--
   }
   return {
      setSpeed: setSpeed,
      get: get,
      speedUp: speedUp,
      speedDown: speedDown
   }
})()
Car.set(30)
Car.get() //30
Car.speedUp()
Car.get() //31
Car.speedDown()
Car.get()  //30

如下代码输出多少?如何连续输出 0,1,2,3,4

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

输出结果为:delayer:5(连续输出5个),执行setTimeout时,代码会挂到任务队列中区,待i遍历完成之后执行,而此时i = 5,所以输出delayer:5(连续输出5个)

修改后

for(var i=0; i<5; i++){
  (function(j){
    setTimeout(function(){
      console.log('delayer:' + j )
    }, 0)//1000-1000*j    
  })(i)
}

或者

for(var i=0; i<5; i++){
  setTimeout((function(j){
    return function(){
      console.log('delayer:' + j )
    }
  }(i)), 0)    
}

如下代码输出多少?

function makeCounter() {
  var count = 0

  return function() {
    return count++
  };
}

var counter = makeCounter()
var counter2 = makeCounter();

console.log( counter() ) // 0
console.log( counter() ) // 1

console.log( counter2() ) // 0
console.log( counter2() ) // 1

补全代码,实现数组按姓名、年纪、任意字段排序

var users = [
  { name: "John", age: 20, company: "Baidu" },
  { name: "Pete", age: 18, company: "Alibaba" },
  { name: "Ann", age: 19, company: "Tecent" }
]

users.sort(byName) 
users.sort(byAge)
users.sort(byField('company'))

解答

function byName(user1, user2){
  return user1.name > user2.name
}

function byAge (user1, user2){
  return user1.age > user2.age
}

function byFeild(field){
  return function(user1, user2){
    return user1[field] > user2[field]
  }
}
users.sort(byField('company'))

写一个 sum 函数,实现如下调用方式

console.log( sum(1)(2) ) // 3
console.log( sum(5)(-1) ) // 4

解答

function sum(a) {
  return function(b) {
    return a + b
  }
}

函数柯里化-只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数

参考

Javascript-立即调用函数表达式

立即执行函数: (function(){...})() 与 (function(){...}()) 有什么区别?

Scoping & Hoisting

什么是立即执行函数?有什么作用?

Javascript闭包(Closure)

相关文章

  • php之闭包函数(Closure)

    php闭包函数(Closure) JS闭包 js和php闭包使用和区别

  • JS闭包

    JS闭包 闭包练习

  • JS闭包问题(二)

    在之前的JS闭包问题(一)文章中大概介绍了一下JS闭包,同时讲了闭包与变量之间的问题,今天我们继续聊闭包,聊聊闭包...

  • JS闭包大结局(JS闭包系列3)

    在上一篇中再谈JS闭包(JS闭包系列2),我详细的介绍了JS中的变量作用域相关的概念,结合第一节关于JS闭包(JS...

  • JS闭包入门

    最近有看到朋友的面经里提到的JS闭包的问题,就想研究研究,以下是我对JS闭包的简单理解。 到底什么是JS闭包? 定...

  • 学习JavaScript闭包和作用域笔记

    JS JavaScript闭包和作用域 闭包 JavaScript高级程序设计中对闭包的定义:闭包是指有权访问另外...

  • 再谈JS闭包(JS闭包系列2)

    这篇文章,来继续谈谈Javascript闭包的剩余问题。因为在上一篇文章中关于JS闭包(JS闭包系列1)主要简单的...

  • 简单的聊一下闭包

    js中的闭包 闭包是学习js中永远也绕不过去的一个坎,那么,今天我们就去一段简单的代码开始聊一聊闭包 什么是闭包 ...

  • 浅谈闭包

    js中的闭包 闭包是学习js中永远也绕不过去的一个坎,那么,今天我们就去一段简单的代码开始聊一聊闭包 什么是闭包 ...

  • 闭包??

    什么闭包,闭包有什么用?http://js.jirengu.com/pogadikofa/1/闭包是在某个作用域内...

网友评论

    本文标题:JS闭包

    本文链接:https://www.haomeiwen.com/subject/mfgftqtx.html