node.js - javascript作用域的问题,就是内嵌函数访问外层函数变量的问题,求大神指导我?
大家讲道理
大家讲道理 2017-04-11 09:12:11
0
15
685

众所周知,在javascript中,内嵌函数,可以访问所有外层函数的变量,这个是由著名的js作用域链实现的,但是我只是从语法特性的角度去理解这个知识点的,不知道里面具体原理是什么呢?
就比如,我们在chrome中去尝试这个访问:

我们发现:height变量压根不存在,肯定为not available,但是局部变量textVar和count也是not available;color作为全局变量可以访问到,值就是red。
下面是firefox里面的测试结果:

firefox中,height变量height is not defined很好理解,但是textVar和count爆出错误variable has been optimized out是什么意思?

我们稍微修改下代码:

这次,我们特意在内嵌函数里面访问下count变量,count变量的值在Watch里面正确读取到了,但是textVar依旧没有被访问到,相对于上面那个测试,是不是很奇怪?js解释引擎到底怎么处理类似逻辑的?
下面是firefox里面的测试结果:

这个测试结果和chrome测试结果保持一致,就不累述了。

为了进一步研究,我们写nodejs测试,用webstorm对nodejs代码调试:

看了一下Watchers,发现nodejs简直丧心病狂,全局变量color都变得不可访问了。
仿照上面的套路,我们在函数里面显式访问textVar,结果是:

显式访问textVar变量,textVar在Watchers里面访问到了。


很多小伙伴认为我在讨论闭包问题,我在此申明下,我讨论的是js作用域链问题:

不是闭包吧,还是一样的测试结果!!!我讨论的是js作用域链的问题,探究的是js作用域的底层实现,虽然是个小美工,但是想跟你们多学点东西嘛。

大家讲道理
大家讲道理

光阴似箭催人老,日月如移越少年。

全員に返信(15)
黄舟

这个是执行栈的问题。

上面,return了一个方法赋给了一个全局变量。作用域还是在的,只不过这个返回方法的执行栈脱离了原本方法的执行栈,这就是闭包。Chrome的watch是基于当前栈的,所以找不到。建议还是学习下闭包。

下面那个例子,也是这个问题。。
在内部执行的方法中,是没有b的,这只是执行栈没有用到,但是作用域链上是有的。

也可以看到b
只要你改下Call Stack就好了,在外部执行栈内,还是有b的。

いいねを押す +0
阿神

得亏这么个问题你写了 3 屏幕的字,认真学学闭包你就明白了。

いいねを押す +0
迷茫

感觉问题没有描述清楚啊。没看懂。

いいねを押す +0
刘奇

http://blog.sina.com.cn/s/blog_bfde779f0...
这是我得闭包笔记,你看看。。。。

いいねを押す +0
Ty80

你在调试环境里看到的已经是引擎优化后的结果了。用console.log把你期望看到的变量打印出来,你就都明白了

いいねを押す +0
黄舟

感觉题主很有探索精神啊!这应该是引擎的闭包优化以减少内存占用吧。

function a() {
    var aa = {}, bb = {}, cc = {}; // ... 此处省略一千个变量
    var b = function() {
         bb = {};
    };
    return b;
}
var b = a();

setInterval(b, 30);

在以上代码中,如果b一直被调用,那它所处的闭包就会一直存在,但我们在b中用到的只有bb而没有用到其它诸如aa,cc的变量,所以只需保留bb即可。因为即使保留了aa,cc将来也永远不会被使用,会导致不必要的内存浪费。js引擎的实现只是做了必要的优化而已,并没有违背js规范。

いいねを押す +0
Ty80

这个是被JS引擎优化掉的变量,与ES标准里的作用域链没有关系,ES标准写的作用域链就是各种教程说的那样。
如果我们在一段代码里没有使用一个变量,那么即使在其执行环境里js引擎不让这段代码可以访问这个变量对JS的执行来说并不会产生任何的影响。
而事先隐藏某些变量可以减小js引擎查找一个变量的时间,这种优化付出的代价也不会很多。由于变量声明提升的原因,JS引擎在执行一段代码前总需要扫描一遍整个代码。
做这种优化的同时还可顺便减小(可能存在的)闭包占用的内存,虽然你写了闭包,但是真的也会有些局部变量不可能在访问的,这个时候JS引擎就可以释放这个局部变量占用的内存减小闭包的代价。

いいねを押す +0
巴扎黑

当函数执行以后,变量声明的上下文就会被释放掉,如果在这个作用域中声明的变量如果没有再被引用,就会被当成垃圾回收掉。而如果这个作用域中的变量会被引用,例如console.log(age,count)中的count,它定义时的上下文已经不存在了,但是通过一个闭包来访问count,所以count还会存在内存中,这就是为什么同一个作用域下的变量,其他都已经不可访问,count依然可以访问的原因,只有等count不会再被引用才会释放掉。

いいねを押す +0
迷茫

这是尾调用的问题。好像叫tail call。

いいねを押す +0
阿神

没有研究过watch的具体实现方式

应该和前面几位说的差不多,属于浏览器/解析器上的优化,

当前scope中没有使用到的变量,会被优化掉

いいねを押す +0
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート