全局代码的上下文环境数据内容为:javascript
普通变量(包括函数表达式),java 如: var a = 10;函数 |
声明(默认赋值为undefined)this |
函数声明,spa 如: function fn() { }code |
赋值对象 |
thisblog |
赋值ip |
若是代码段是函数体,那么在此基础上须要附加:作用域
参数 |
赋值 |
arguments |
赋值 |
自由变量的取值做用域 |
赋值 |
做用域在函数定义时就已经肯定了,而不是在函数调用时肯定。做用域有上下级的关系,上下级关系的肯定就看函数是在哪一个做用域下建立的。例如,fn做用域下建立了bar函数,那么“fn做用域”就是“bar做用域”的上级。做用域最大的用处就是隔离变量,不一样做用域下同名变量不会有冲突。
做用域只是一个“地盘”,一个抽象的概念,其中没有变量。要经过做用域对应的执行上下文环境来获取变量的值。
一个例子:
for (var i = 0; i < 5; i++) { setTimeout(function() { console.log( i); }, 1000); } console.log(i);
若是咱们约定,用箭头表示其先后的两次输出之间有 1 秒的时间间隔,而逗号表示其先后的两次输出之间的时间间隔能够忽略,代码实际运行的结果该如何描述?
结果是5->5,5,5,5,5。由于i是个自由变量,当循环结束时,i已经等于5了,而setTimeout中的函数还未执行,等其执行时,获取的i为5。
若想输出5->0,1,2,3,4,利用 JS 中基本类型(Primitive Type)的参数传递是按值传递(Pass by Value)的特征便可。
方法一:IIFE
for (var i = 0; i < 5; i++) { (function(j) { // j = i setTimeout(function() { console.log(j); }, 1000); })(i); } console.log( i);
方法二:
var output = function (i) { setTimeout(function() { console.log(i); }, 1000); }; for (var i = 0; i < 5; i++) { output(i); // 这里传过去的 i 值被复制了 } console.log(i);
自由变量:
在A做用域中使用的变量x,却没有在A做用域中声明(即在其余做用域中声明的),对于A做用域来讲,x就是一个自由变量。以下图
如上程序中,在调用fn()函数时,函数体中第6行。取b的值就直接能够在fn做用域中取,由于b就是在这里定义的。而取x的值时,就须要到另外一个做用域中取。到哪一个做用域中取呢?
要到建立这个函数的那个做用域中取值——是“建立”,而不是“调用”,—其实这就是所谓的“静态做用域”。
做用域链:
上面描述的只是跨一步做用域去寻找。
若是跨了一步,还没找到呢?——接着跨!——一直跨到全局做用域为止。要是在全局做用域中都没有找到,那就是真的没有了。
这个一步一步“跨”的路线,咱们称之为——做用域链。
咱们拿文字总结一下取自由变量时的这个“做用域链”过程:(假设a是自由量)
第一步,如今当前做用域查找a,若是有则获取并结束。若是没有则继续;
第二步,若是当前做用域是全局做用域,则证实a未定义,结束;不然继续;
第三步,(不是全局做用域,那就是函数做用域)将建立该函数的做用域做为当前做用域;
第四步,跳转到第一步。