1. 执行上下文环境及作用域
(1)执行上下文:执行上下文的定义---在执行代码之前,把将要用到的变量都事先拿出来,有的直接赋值了(this和函数声明),有的先用undefined占个位(变量和函数表达式)。
处于活动状态的执行上下文环境只有一个。函数每被调用一次,都会产生一个新的执行上下文环境。当执行流进入一个函数时,这个函数的环境就会被推入一个环境栈中。在函数执行之后,环境栈将其环境弹出。即函数执行完之后就会将其执行上下文环境销毁(还有种情况就是闭包,闭包执行完之后,他的执行上下文环境不会被销毁)。
(2)全局执行环境 是最外围的一个执行环境,在web浏览器中,全局执行环境是window对象。
(3)作用域:函数在定义的时候,就已经确定了函数体内部自由变量的作用域。js没有块及作用域,除了全局作用域外,只有函数才能创建作用域。作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
(4)作用域链:作用域有上下级关系,上下级关系的确定就看函数在哪个作用域下创建的,当代码在一个环境中执行,会创建变量对象的一个作用域链。当访问变量时,会一级一级向上寻找变量定义,直到找到他。若一直寻找到全局作用域还找不到就会报 'xxx is not defined'的错误。
作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。例:
var num = 1; function changeNum(){ if(num===1){ // 可以在函数内部访问num,是因为可以再作用域链中找到他 num=2; } else{ num=1; } } changNum(); alert(num) // 2
局部环境中开始时会先在自己的变量对象中搜索变量名和函数名,搜索不到的话就搜索上一级作用域链。
2. 延长作用域链
try catch语句的catch块;with语句,这两个语句会在作用域链的前端添加一个变量对象。
function buildUrl() { var qs = "?debug=true"; with(location){ var url = href + qs; } return url; }
with 语句接收的是 location 对象,因此其变量对象中就包含了 location 对象的所有属性和方法,而这个变量对象被添加到了作用域链的前端。
with 语句中引用变量 href 时(实际引用的是 location.href),with 语句内部,定义了一个名为 url 的变量,因而 url 就成了函数执行环境的一部分,所以可以作为函数的值被返回。
3. 没有块级作用域
js有作用域,函数作用域,但是没有块级作用域(ES6新增了块级作用域 let const 声明)。js除了全局作用域外,只有函数能创建作用域
if 语句 for语句 中的变量声明会将变量添加到当前的执行环境
(1)声明变量
使用 var 声明的变量会自动被添加到最接近的环境中。如果初始化变量,没有用var声明,该变量会自动被添加到全局环境中。
function add(num1, num2) { var sum = num1 + num2; // 这里如果有var声明则是被添加到函数内部执行环境中,所以后面访问不到 return sum; } var result = add(10, 20); //30 alert(sum); //由于 sum 不是有效的变量,因此会导致错误 function add(num1, num2) { sum = num1 + num2; // 这里没有用var声明,sum变量被添加到全局作用域,所以后面可以访问到sum的值 return sum; } var result = add(10, 20); //30 alert(sum); //30
(2)查询标识符
当在某个环境中为了读取或写入而引用一个标识符时,必须通过搜索来确定该标识符实际代表什么。搜索过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符。如果在局部环境中找到了该标识符,搜索过程停止,变量就绪。如果在局部环境中没有找到该变量名,则继续沿作用域链向上搜索。搜索过程将一直追溯到全局环境的变量对象。如果在全局环境中也没有找到这个标识符,则意味着该变量尚未声明。