函数闭包浅谈

很多时候,总是为了排期,完成任务。然而,停下来回头想想,发现javascript还没系统的重新捋过,既然知道了问题所在,那静下心来,重新过一下把。我们先从js闭包开始。


《javascript权威指南》关于闭包的第一段话:

和其他大多数现代编程语言一样,javascript也采用词法作用域,也就是说,函数的执行依赖于变量作用域,这个作用域是在函数定义时决定的,而不是函数调用时决定的。为了实现这种词法作用域,javascript函数对象的内部状态不仅包含函数的代码逻辑,还必须引用当前的作用域链。函数对象可以通过作用域链互相关联起来,函数体内部的变量都可以保存在函数作用域内,这种特征在计算机科学文献中成为闭包。

这段话,可了解到,想要功课闭包这个关,先要解决作用域和作用域链。那首先来看看作用域和作用域链是什么。


1.作用域

作用域分为变量作用域函数作用域

1.1变量作用域

一个变量的作用域是程序源代码中定义这个变量的区域。

这句话的重点在定义上,而不是在调用上。举个例子:

var a = 12;
function func1() {
alert(a);
};
function func2() {
var a = 13;
func1();
}
func2();复制代码

上述代码,最后会打印出什么结果呢?

console结果


咦?12? 怎么不是13?
这个就是为什么把定义标粗的原因了。 首先调用func2代码,执行func2里的代码后(a的作用域范围在func2内)。再调用func1(),为什么不是弹出13呢? 因为在func1被定义的时候,全局变量a的作用域就包括了func1,也包括func2内。因此,在没有优先级的影响下,最后alert 12。如果弹出的结果是13,那就说明,变量的作用域链产生是在函数的调用时产生,而不是在函数的定义时产生。显然,这是错的


1.2.函数作用域

块级作用域。花括号内的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的。Javascript没有块级作用域,取而代之的使用函数作用域,变量在声明他们的函数体内级这个函数体嵌套的任意函数体内都是有定义的。


2.作用域链

作用域链是一个对象列表或者链表,这组对象定义了这段代码『作用域』中的变量。
当定义一个函数时,他实际上保存了一个作用域链。当调用这个函数时,他创建一个对象存储他的局部变量,并将这个对象添加保存至作用域链上,同时创建一个更长的表示函数调用作用域的链。

对于嵌套函数而言,每次调用外部函数时,内部函数都会重新定义一遍,但是每次调用的外部函数时,作用域链都是不同的。这也就解释为什么下面这个栗子:

var func_a_para = 0;
function func_a() {
    var func_a_para = 1;
    return function b() {
        func_a_para++;
        console.log(func_a_para);
    };
}
var a = func_a();
var b = func_a();

a();  // func_a_para = 2
a();  // func_a_para = 3
b();  // func_a_para = 2复制代码

3.闭包

上述代码也是闭包。函数的执行用到了作用域链,这个作用域链是在函数定义的时候创建的。因此我们看看这个函数在调用的时候的作用域链是什么样子:

var func_a_para = 0;
function func_a() {
    var func_a_para = 1;
    return function b() {
        func_a_para++;
        console.log(func_a_para);
    };
}
console.dir(func_a());复制代码

作用域

可看到,b函数的作用域链上有两个对象。一个是局部变量,另一个是全局对象。从这两个对象里,我们都能找到func_a_para这个变量:分别是1和0。从这个顺序我们也能看到,在作用域链上,我们先找到局部变量的func_a_para,因此不会再取全局对象中的参数了。这也就是为什么局部变量的优先级高于全局变量的原因。

从《2.作用域链》中代码示例中,我们可以发现一个现象:闭包可以获取并保存局部变量,并将这些变量绑定到定义他们的外部调用函数中。


抄一个栗子:来自《javascript权威指南》

function addPrivateProperty(o, name, predicate) {
    var value;
    o['get' + name] = function () {return value;}

    o['set' + name] = function (v) {
        if (predicate && !predicate(v)) {
            throw Error('set' + name + ':invalid value ' + v);
        }
        else {
            value = v;
        }
    }
}

// 调用
var o = {};
addPrivateProperty(o, 'Name', function (x) {return typeof x === 'string';})

o.setName('Frank');

console.log(o.getName()); // Frank
o.setName(o); // Error复制代码

再贴一个比我说的好的关于闭包的文章链接:shuaihua.cc/article/148…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值