javascript作用链域-JavaScript 作用域链

范围

作用域是变量和函数的可访问范围,它控制着变量和函数的可见性和生命周期。 在JavaScript中,变量的作用域有全局作用域和局部作用域。

纯 JavaScript 作用域非常容易理解。 在一些类似 C 的编程语言中,花括号内的每段代码都有自己的作用域,变量在声明它们的代码段(称为块)之外是不可见的。 这就是JavaScript很容易被初学者误解的地方。 JavaScript 没有块级作用域,只有函数级作用域:变量在函数体及其声明它们的子函数中可见。

未在函数中声明或声明时不使用 var 的变量是具有全局作用域的全局变量,并且 window 对象的所有属性都具有全局作用域; 它们可以在代码中的任何地方访问,在函数内部声明并用 var 修改。 变量是局部变量,只能在函数体内使用。 即使函数的参数不使用var,它们仍然是局部变量。

变量a=3; //全局变量 function fn(b){ //局部变量 c=2; //全局变量 var d=5; //局部变量 function subFn(){var e=d; //父函数局部变量对子函数可见 for(var i=0;i){console.write(i);}alert(i);//3,在for循环中声明,在中仍然可见循环外的函数,无块效应 Domain}}alert(c); //在函数中声明但不加var修饰,仍然是全局变量

只要明白JavaScript没有块作用域javascript作用链域,简单的JavaScript作用域就很容易理解,还有一点很容易让初学者感到困惑。 JavaScript 变量是函数式的、可解析的或提前声明的。 别名有很多,但他们说的是一件事,JavaScript实际上是解释执行的,但不是一步步解释执行的。 在实际解释和执行之前,JavaScript解释器会预先解析代码并提前解释变量和函数声明。 这意味着我们可以在函数声明语句之前调用函数,这对于大多数人来说是常见的,但是对于变量和解析乍一看会很奇怪

控制台.log(a); //undefinedvar a=3;console.log(a); //3console.log(b); //未捕获的ReferenceError:b未定义

var a=3 的声明; 上面代码中的在执行前就已经被预解析了(但是赋值语句不会被执行),所以第一次会是undefine,不会报错。 赋值语句执行后为3。上面的代码除最后一句外,与下面的代码效果相同。

var a;console.log(a); //undefineda=3;console.log(a); //3但是

如果这就是全部,那么 JavaScript 作用域问题就非常简单了,但由于函数子函数引起的问题,作用域远不止那么简单。 重要的是 - 执行环境或运行时上下文(好极客):执行上下文定义变量或函数可以访问的其他数据并确定它们各自的行为。 每个执行环境都有一个与之关联的变量对象(variable object,VO)。 执行环境中定义的所有变量和函数都会存储在这个对象中,解析器在处理数据时只会访问这个内部对象。 目的。

全局执行环境是最里面的执行环境。 在Web浏览器中,全局执行环境是window对象,因此所有全局变量和函数都创建为属性并放大window对象。 每个函数都有自己的执行环境。 当执行流进入函数时,函数环境会被扔进函数栈中。 函数执行完毕后,执行环境会被出栈并销毁,并保存在其中。 程序中的所有变量和函数定义都被急剧破坏,控制权返回到之前的执行环境。 在应用程序退出(浏览器关闭)之前,全局执行环境不会被破坏。

作用域链

当代码在环境中执行时,会创建变量对象的作用域链(scope chain,不称为sc),以确保对执行环境有权访问的变量和函数的有序访问。 作用域的第一个对象仍然是当前执行代码所在环境的变量对象(VO)

函数 a(x,y){var b=x+y;返回 b;}

当函数a创建时,它的作用域链就被全局对象充满了,全局对象里有所有的全局变量

如果执行环境是一个函数,那么它的激活对象(activation object,AO)作为作用域链的第一个对象,第二个对象是包含环境,下一个对象是包含环境的包含环境。 。 。 。 。

函数a(x,y){var b=x+y;返回b;}var总计=a(5,10);

此时vartotal=a(5,10); 该语句的作用域链如下

函数运行时对标识符的分析是沿着作用域链逐级查找的过程,从第一个对象开始javascript作用链域,逐级回溯,直到找到同名的标识符,找到后不再继续遍历。 如果找不到,请报告错误。

我们看一下闭包

之前的博客已经总结过:只要有调用内部函数的可能,JavaScript就需要保留引用的函数。 并且JavaScript运行时需要跟踪所有引用这个内部函数的变量,直到最后一个变量被丢弃,JavaScript垃圾收集器才能释放相应的显存空间。 回想起来,我明白了很多。 父函数定义的变量位于子函数的作用域链中。 如果子函数不被销毁,则其作用域链中的所有变量和函数都将被维护,不会被销毁。

for(var i=0;i){elements[i].onclick=function(){alert(i);}}

这是下一篇博客中提到的经典错误。 每次元素点击警报时,都是长度。 这段代码中绑定到元素的点击事件处理程序的作用域链是这样的

由于内部函数的原因(点击事件处理程序可能随时被调用),它的作用域链无法被销毁(更何况在这个例子中,i在全局作用域中,只能在页面卸载时被销毁) ,而i的值在循环执行后仍然保留为length值,因此每次触发onclick时都会触发alert length。

for(var i=0;i){(function(n){elements[n].onclick=function(){alert(n);}})(i);}

为什么这有效? 这时onclick引用的变量就变成了n,而且由于函数是立即执行的,所以每个onclick函数在作用域链中都会维护一个对应的n(0~length-1)。 这个时候就这样了。

终于

事实上,了解了执行环境和作用域链之后,闭包就变成了显而易见的东西,但是却不能被滥用。 从计数器的例子可以看出,闭包会让子函数维护其作用域链的所有变量和变量。 在函数和显存方面,内存消耗非常大。 使用时,尽量销毁父函数不再使用的变量。