一、变量的作用域
变量的作用域无非就是两种:全局变量和局部变量。
Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
在函数外部自然无法读取函数内的局部变量。
注意,函数内部声明变量的时候,一定要使用var命名。如果不用的话,实际上声明了一个全局变量!
执行上下文(Execution Context)(执行环境)
每次当控制器转到可执行代码的时候,就会进入一个执行上下文。执行上下文可以理解为当前代码的执行环境,它会形成一个作用域。
全局环境:JavaScript代码运行起来会首先进入该环境
函数环境:当函数被调用执行时,会进入当前函数中执行代码
因此在一个JavaScript程序中,必定会产生多个执行上下文,JavaScript引擎会以堆栈(堆为树装结构,栈为先进后出)的方式来处理它们。栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。
当代码在执行过程中,会生成一个执行上下文,放入栈中,而处于栈顶的上下文执行完毕之后,就会自动出栈
对执行上下文总结
1.单线程
2.同步执行,只有栈顶的上下文处于执行中,其他上下文需要等待
3.全局上下文只有唯一的一个,它在浏览器关闭时出栈
4.函数的执行上下文的个数没有限制
5.每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。
作用域
作用域是一种规则,在代码预编译阶段就确定了,规定了变量与函数的可被访问的范围。全局变量拥有全局作用域,局部变量则拥有局部作用域。 js是一种没有块级作用域的语言(包括if、for等语句的花括号代码块或者单独的花括号代码块都不能形成一个局部作用域),所以js的局部作用域的形成有且只有函数的花括号内定义的代码块形成的,既函数作用域
作用域链
作用域链是作用域规则的实现,通过作用域链的实现,变量在它的作用域内可被访问,函数在它的作用域内可被调用。
作用域链是一个只能单向访问的链表,这个链表上的每个节点就是执行上下文的变量对象(代码执行时就是活动对象),单向链表的头部(可被第一个访问的节点)始终都是当前正在被调用执行的函数的变量对象(活动对象),尾部始终是全局活动对象。
作用域链的形成
1.什么是闭包?
函数对象可以通过作用域链相互关联起来,函数体内的数据(变量和函数声明)都可以保存在函数作用域内,这种特性在计算机科学文献中被称为“闭包”。既函数体内的数据被隐藏于作用于链内,看起来像是函数将数据“包裹”了起来。
但是你只需要知道应用的两种情况即可——函数作为返回值
js的垃圾回收机制可以粗略的概括为:如果当前执行上下文执行完毕,且上下文内的数据没有其他引用,则执行上下文被推出,其内数据等待被垃圾回收。而当我们在其他执行上下文通过闭包对执行完的上下文内数据仍然进行引用时,那么被引用的数据则不会被垃圾回收。
闭包是什么?
闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
专业文献上的“闭包”定义非常抽象,很难理解
可以这样理解:闭包就是能够读取其他函数内部变量的函数。
也可以把闭包简单理解成“定义在一个函数内部的函数”。
闭包是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。闭包可以用在许多地方。它的最大用处有两个,一个是可以读取函数内部的变量,另一个是让这些变量的值始终保持在内存中。
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题。
那用闭包又有什么坏处?
- 增加了内存的消耗。
- 某些浏览器上因为回收机制的问题,有内存泄漏,内存溢出风险。
- 增加了代码的复杂度,维护和调试不便。
内存溢出 ,是指程序在申请内存时,没有足够的内存空间供其使用,出现溢出;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。内存泄露会最终会导致内存溢出。内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。
闭包用途很多,可以很好地区分开各个作用域,避免变量的混淆
用闭包有什么好处?
- 延长作用域链
- 生成预编译函数
这一点是借用jquery中的说法,实际上就是通过闭包把外层函数提供的参数保存起来,在闭包运行的时候就可以得到预先指定的参数
- 处理变量不能即时传递的问题
可以很好地区分开各个作用域