1、JS高级程序设计关于这些个问题的解释:
1、执行环境及作用域:
执行环境(execution context,也有称之为执行上下文)是JS语言中最重要的一个概念。
Javascript没有代码块作用域的概念,局部作用域是针对函数来说的。
执行环境定义了变量或者函数有权访问到的其他数据,决定了他们的各自行为。
每个执行环境都有一个与之关联的变量对象(variable object),执行环境中定义的所有变量和函数都保存在这个对象中。无法访问,但是解析器在后台执行中要用到。
全局执行环境是最外围的执行环境。根据ECMAScript实现的宿主不同,表示执行环境的对象也不一样。Web浏览器中,全局执行环境是window对象,因此全局函数和全局变量都是作为window的对象和方法。
某个执行环境中的所有代码执行完毕后将被销毁,全局执行环境在关闭网页的时候才会被销毁。
每个函数都有自己的执行环境。当执行流进入函数的时候,函数的环境被推入到一个环境栈中。在函数执行以后,栈将其环境弹出,把控制权交给之前的执行环境。
当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。
作用域链的作用是:是保证对执行环境有权访问的所有变量和函数的有序访问。作用域的前端始终是当前执行的代码所在的环境变量的变量对象。如果这个环境是一个函数,则将其活动对象(activation object)作为变量对象。作用域链中的下一个变量对象来自于包含环境。全局执行环境始终是作用域链中最后一个对象。
标识符解析始终是沿着作用域链一级一级地搜索标识符的过程,前端开始,逐级向上,直到找到标识符为止,找不到就会报错。
函数的作用域链包含着两个对象:它自己的变量对象(其中定义了arguments),和全局环境的变量对象。
js作用域分函数作用域和全局作用域。
当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。
作用域链的作用是:是保证对执行环境有权访问的所有变量和函数的有序访问。作用域的前端始终是当前执行的代码所在的环境变量的变量对象。如果这个环境是一个函数,则将其活动对象(activation object)作为变量对象。作用域链中的下一个变量对象来自于包含环境。全局执行环境始终是作用域链中最后一个对象。
标识符解析始终是沿着作用域链一级一级地搜索标识符的过程,前端开始,逐级向上,直到找到标识符为止,找不到就会报错。
函数的作用域链包含着两个对象:它自己的变量对象(其中定义了arguments),和全局环境的变量对象。
2、延长作用域链:
虽然js中的执行环境总共就两种:全局和函数(局部),但是还是有其他的办法来延长作用域链。就是在作用域的前端添加一个临时增加的变量对象。
具体使用:try...catch语句块的catch块;和with语句。
对于with语句来说,会将指定的对象添加到作用域链中。
function buildUrl(){
var qs = "?debug=ture";
with(location){
var url = href + qs;
}
return url;
}
3、没有块级作用域:
js中没有块级作用域,只有全局作用域和函数作用域。注意在if语句||for语句中定义的变量会被添加到全局变量中。
可以使用自执行语句来模拟块级作用域。
使用var声明的变量会被添加到最接近的环境中,函数内部就是函数作用域。如果初始化没有使用var,就会被添加到全局作用域中。
4、查询标识符:
沿着作用域链向上查找。
闭包以及为什么要使用闭包:
闭包与匿名函数不要搞混。
闭包:就是指有权访问另一个函数作用域中的变量的函数。创建闭包最常见的方式,就是在一个函数作用域内创建另一个函数。并且返回另一个函数。
需要闭包的原因:当使用函数被调用时,会创建一个执行环境(上下文环境)以及相应的作用域链,使用arguments和其他命名参数的值来初始化函数的活动对象(activation object)。函数执行过程就需要在作用域链中查找变量,查找过程不能向下,所以就需要闭包来读取函数内部的变量。闭包会让外层函数的变量一直存在于内存中。直到都不调用为止。
外层函数的变量对象(活动对象)存在于内层函数的作用域链中。
闭包的问题:
1、过度使用闭包会造成内存占用过多。
2、作用域链的这种配置引出一个问题,就是闭包只能取得包含函数中任何变量的最后一个值,闭包保存的是整个变量对象,而不是某个特殊的变量。
这里打印的0~9是console.log打印的。最终保存的结果i都是10。
以上函数,在for循环中使用闭包,这样最后读取到的i是最后一个值。存在风险。改用如下的方式,将当前值保存下来,用一个自执行的闭包函数来保存变量,之后结果传出。
js权威指南解释作为补充:
函数的执行依赖于变量作用域,这个作用域在函数定义的时候决定的,不是在函数调用的时候决定的。
古老解释对于闭包:函数对象通过作用域链来相互关联,函数体内的变量都可以保存在函数作用域内。
闭包可以捕捉到局部变量(和参数),并一直保存下来。
闭包的应用场景以及优缺点:
应用场景:1、闭包的典型框架应该就是jquery了。
2、闭包是javascript语言的一大特点,主要应用闭包场合主要是为了:设计私有的方法和变量(模块设计,自执行闭包)。
3、这在做框架的时候体现更明显,有些方法和属性只是运算逻辑过程中的使用的,不想让外部修改这些属性,因此就可以设计一个闭包来只提供方法获取。
优点:1. 逻辑连续,当闭包作为另一个函数调用的参数时,避免你脱离当前逻辑而单独编写额外逻辑。
2. 方便调用上下文的局部变量。
3. 加强封装性,第2点的延伸,可以达到对变量的保护作用。
缺点:闭包有一个非常严重的问题,那就是内存浪费问题,这个内存浪费不仅仅因为它常驻内存,更重要的是,对闭包的使用不当会造成无效内存的产生。
关于闭包,就我自己的习惯而言,能不用就不用,如果非用不可,那就想办法保持闭包对象的数量很少甚至唯一。
关于this:
this对象在运行时是基于函数的执行环境绑定的:在全局函数中,this等于window,当函数被当作某个对象的方法调用的时候,this等于那个对象,非对象的函数调用,this都会指向window。也就相当于说,谁调用这个函数,this就指向谁。
匿名函数的执行环境具有全局性。这时候this对象指向window。
每当创建一个函数的时候,函数在被调用的时候自动获取两个特殊的变量,this和arguments。