基础总结JS(2):作用域与闭包

一、作用域

1、简单来说:就是由内向外搜索变量,内部找到了,就不再搜索;内部可以访问到外部的变量,外部不能访问内部的。
有一个执行环境栈,最底层是全局环境,然后是父执行环境,后是子执行环境,每次执行完都弹出相应的执行环境,后进的先出。

window.name = '小芳';
console.log(name);
function outer() {
    var name = '小刚';
    console.log(name);
    function inner() {
        var name = '小明';
        console.log(name);
    }
    inner();
}  
outer(); 
// '小芳'
// '小刚' 
// '小明'

2、没有块级作用域

if (true) {
    var sum = 10;
}
console.log(sum);  // 10

若有块级作用域,应提示:Uncaught ReferenceError: sum is not defined,所以变量sum被添加到了全局环境里

二、闭包

这个概念困扰了我好几年,偶然看到一篇文章,醍醐灌顶,贴出地址:「每日一题」JS 中的闭包是什么?

1、「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。闭包不是包,英文Closure,是翻译的问题。

n. 关闭;终止,结束
vt. 使终止

function init() {
    var name = "Mozilla"; // name 是一个被 init 创建的局部变量
    function displayName() { // displayName() 是内部函数,一个闭包
        alert(name); // 使用了父函数中声明的变量
    }
    displayName();
}
init();

name + displayName 就是一个闭包

2、闭包和 有没有return无关,return只是为了让外部能访问到,挂在window上也可以。

function init() {
    var name = "Mozilla"; 
    return function() {  // return只为外部能访问,和闭包无关
        alert(name); 
    }
}
init()();
function init() {
    var name = "Mozilla"; 
    window.displayName = function() { 
        alert(name); 
    }
}
init();
displayName();

3、闭包只是个定义,不懂,照样可以写,闭包只是作用域的副产品,和作用域访问顺序是一致的,没有创新。
4、闭包会造成内存泄漏?错!内存泄漏是不再使用的变量不能被回收所导致的,而闭包是我们还在使用的,闭包中未释放的局部变量和在全局声明一个变量,影响是一样的;只是IE浏览器引用计数的垃圾回收机制造成循环引用导致的内存泄漏,不是闭包的锅。

三、闭包的实际使用

示例来源:曾探的《JavaScript设计模式与开发实践》

1、解决异步事件导致的问题

经典例子:5个div节点,逐个点击,弹出对应的索引值。

<body>
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
  
<script type="text/javascript">

var nodes = document.getElementsByTagName('div');

for(var i=0,len=nodes.length;i<len;i++) {
    nodes[i].onclick=function(){
        console.log(i);
    }
}
</script>

</body>

点击后发现,都是5,并没有弹出0,1,2,3,4。因为onclick是异步触发,当事件触发时,for循环已执行完毕,i 的值是 5, 事件函数顺着作用域链向外查找到全局变量 i,所以输出 5。

<body>
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
  
<script type="text/javascript">

var nodes = document.getElementsByTagName('div');

for(var i=0,len=nodes.length;i<len;i++) {
    (function(index){
        nodes[index].onclick=function(){
            console.log(index);
        }
    })(i);
}
</script>

</body>

使用闭包把每次循环的 i 值都封闭起来,当在事件函数中顺着作用域链中从内到外查找变量 i 时,会先找到被封闭在闭包环境的 i 值,就会输出 0,1,2,3,4。

setTimeout也一样。

2、封装变量,把不需要暴露在全局的变量封装成“私有的”

例子:计算乘机,使用缓存机制,把计算结果保存下来。

var cache = {};
var mult = function(){
    var args = Array.prototype.join.call(arguments, ',');
    if(cache[args]){
    	console.log('这是缓存结果');
        return cache[args];
    } //如果缓存中有,则返回结果,不再计算
    var result = 1;
    for(var i=0,len=arguments.length;i<len;i++){
        result = result*arguments[i];
    }
    return cache[args] = result;
}
console.log(mult(2,3,4)); 
console.log(cache);       
console.log(mult(2,3,4)); 
// 24
// {'2,3,4': 24}
// '这是缓存结果'
// 24

cache暴露在全局中,很容易被修改,使用闭包封闭起来

var mult = (function(){
    var cache = {};
    return function() {
        var args = Array.prototype.join.call(arguments, ',');
        if(cache[args]){
            console.log('这是缓存结果');
            return cache[args];
        }
        var result = 1;
        for(var i=0,len=arguments.length;i<len;i++){
            result = result*arguments[i];
        }
        return cache[args] = result;
    }
})();
console.log(mult(2,3,4));
console.log(mult(2,3,4));  
console.log(cache);     
// 24
// '这是缓存结果'
// 24
// Uncaught ReferenceError: cache is not defined

3、延续局部变量的寿命

例子:img对象用于数据上报时,低版本浏览器会丢失30%数据。

var report= function(src){
    var img = new Image();
    img.src = src;
}
report('https://www.xxx.com/home/hit/v.gif?mod=xxx...');

原因:img是report的局部变量,report调用完,img随即销毁,此时可能还没来得及发出HTTP请求,就丢失了,使用闭包将img封闭起来,就能解决:

var report= (function(){
    var imgs = [];
    return function(src) {
        var img = new Image();
        imgs.push(img);
        img.src = src;
    }
})();
report('https://www.baidu.com/home/hit/v.gif?mod=xxx...');
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值