作用域
1 | <script> |
- 在JS中,函数嵌套是非常普遍的,在函数嵌套中,对变量进行如下的寻找方式:首先在函数内寻找,寻找不到,则往函数外层寻找,……,直到全局(window)区域
声明变量var
- var是在函数运行的上下文中,声明一个变量,如果不加var,则是一个赋值操作,但不要狭隘的理解为–>声明了一个全局变量【见下例】
1 | <script> |
以window.xxx引用全局变量,寻找不到,作为某个属性不存在,返回undefined 【window.a】
直接以xxx引用某个变量,寻找不到,则是报xxx is not defined错误的 【a】
1 | <script> <script> |
JS代码自上而下执行,但是,JS代码在整体运行时分为【词法分析期】和【运行期】两部分
自上而下执行之前,先有一个【词法分析】过程,以上面的结果为例:
1 | 第一步:分析t1() |
词法分析
语法分析,分析3样东西:
第一步:先分析参数
第二步:在分析变量声明
第三步:分析函数声明
一个函数能使用的局部变量,就从上面的3步分析而来
具体步骤:
第一步:函数运行前的一瞬间,生成Active Object(活动对象),下称AO
第二步:把函数声明的参数,形成AO的属性,值全是undefined,如果有实参,则接收,并形成AO相应的属性的值
第三步:分析变量声明!如var age,如果AO上还没有age属性,则添加AO属性,值是undefined;如果AO上已经有age属性,则不做任何影响
第四步:分析函数声明,如 function foo(){ },则把函数赋给AO.foo属性,如果此前foo属性已经存在,则被无情的覆盖
1 | <script> |
函数声明与函数表达式
函数可以赋值给变量,也可以作为参数来传递
JS被称为披着C外衣的lisp语言,lisp是一种强大的函数式语言
1 | function t1(){} |
作用域链
argumengs详解
是一个长得很像数组的对象
内容是函数运行时的实参列表
1 | //arguments收集“所有”的实参,即使没有与之相对应的形参 |
函数运行期内,有三个关键的对象:
AO ——> 本函数AO上没有某属性,则继续去外层函数的AO上找,直到全局对象,叫做 作用域链
arguments ——> 每个函数都有自己的callee,但不会向外层函数接着找arguments的相关属性,即不形成链
this ——> 也不形成链
this详解
- 在JS中函数有4种调用方式
1 | alert(window.x); //输出 undefined |
闭包
在大部分语言中,t1被调用执行,则申请内存并把其局部变量push入栈。t1函数执行完毕,内部的局部变量,随着函数的退出而销毁,因此age = 20的局部变量已经消息。
但是在JS中,age = 20这个变量,却被t2捕捉,即使t1执行完毕,通过t2,依然能访问该变量
像这种情况:返回的函数,并非孤立的函数,甚至把其周围的变量环境,形成了一封闭的“环境包”,共同返回,所以叫闭包。
一句话概括:函数的作用域取决于声明时,而不取决于调用时!