js 闭包(closure)

定义
A closure is the combination of a function and the lexical environment 
within which that function was declared.
This environment consists of any local variables that were in-scope 
at the time the closure was created. 

闭包 = 函数 + 环境
环境包含了函数内能访问到的所有局部变量
示例1
function makeFunc() {
    var bar = 'bar'
    function printBar() {
        console.log(bar)
    }
    // printBar是一个闭包
    return printBar
}

let foo = makeFunc()
foo()
// some

// 和上面类似,传递参数
function makeAdder(x) {
    return function (y) {
        return x + y
    }
}

let add2 = makeAdder(2)
add2(5)
// 7
示例2 模拟私有方法
function makeCounter() {
  // 私有变量
  var privateCounter = 0
  // 私有方法
  function changeBy(val) {
    privateCounter += val
  }
  return {
    increment: function() {
      changeBy(1)
    },
    decrement: function() {
      changeBy(-1)
    },
    value: function() {
      return privateCounter
    }
  }
}

let counter1 = makerCounter()
let counter2 = makerCounter()

counter1.increment()
counter1.increment()
counter1.value()    // 2
counter2.value()    // 0
示例3
var name = 'window'

var obj1 = {
    name: 'my obj',
    getNameFunc: function() {
        console.log(obj1 === this)    // true
        return function() {
        console.log(global === this)  // true
            return this.name
        }
    }
}

obj1.getNameFunc()()    // undefined,没有输出'window'

var obj2 = {
    name: 'my obj',
    getNameFunc: function() {
        var that = this
        return function() {
            return that.name
        }
    }
}

obj1.getNameFunc()()    // 'my obj'
  • 全局变量 this global
var a = 1
this.b = 2
global.c = 3

// a不属于this和global
this === global     // false
console.log(this.a, this.b, this.c)     // undefined, 2, undefined
console.log(global.a, global.b, global.c)   // undefined, undefined, 3
示例4 循环中创建闭包的常见错误
  • 考虑以下情况(详见参考)
<html>
  <body>
    <p id="help">Helpful notes will appear here</p>
    <p>E-mail: <input type="text" id="email" name="email"></p>
    <p>Name: <input type="text" id="name" name="name"></p>
    <p>Age: <input type="text" id="age" name="age"></p>

    <script>
    function showHelp(help) {
      document.getElementById('help').innerHTML = help;
    }

    function setupHelp() {
      var helpText = [
          {'id': 'email', 'help': 'Your e-mail address'},
          {'id': 'name', 'help': 'Your full name'},
          {'id': 'age', 'help': 'Your age (you must be over 16)'}
        ];

      for (var i = 0; i < helpText.length; i++) {
        var item = helpText[i];
        document.getElementById(item.id).onfocus = function() {
          showHelp(item.help);
        }
      }
    }

    setupHelp();
    </script>
  </body>
</html>
  • node模拟上述情况
function makeFuncs() {
    let funcs = []
    // var:i的作用域在makeFuncs函数中
    for(var i = 0; i < 3; i++) {
        funcs.push(() => i)
        // funcs.push(function() {return i}) // 等价
    }
    return funcs
}

let funcs = makeFuncs()
funcs[0]()  // 3,不是1
funcs[1]()  // 3,不是2
funcs[2]()  // 3
  • 解决方法
// 方法1,使用工厂函数(factory),返回一个闭包,保留了当时的i
const makeI = function(i) {
  return () => i
}
// const makeI = i => () => i // 等价

function makeFuncs() {
  let funcs = []
  for (var i = 0; i < 3; i++) {
    // makeI立即执行,返回一个闭包,保留了当时的i
    funcs.push(makeI(i))
  }
  return funcs
}

// 方法2,在函数表达式中保留当时的i
function makeFuncs() {
  let funcs = []
  for (var i = 0; i < 3; i++) {
    (function () {
      var k = i
      funcs.push(() => k)
    })()
  }
  return funcs
}

// 方法3,使用let绑定块级作用域
// let:i的作用域在for循环中;var:i的作用域在makeFuncs函数中
function makeFuncs() {
    let funcs = []
    for(let i = 0; i < 3; i++) {
        funcs.push(() => i)
    }
    return funcs
}

参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值