Js总的采取词法作用域,也就是静态作用域。词法作用域是作用域在词法分析阶段就确定了,不会改变,即变量的作用域是在定义的时候而不是执行的时候确定的作用域。作用域最大的用处就是隔离变量,不同作用域下同名变量是不会有冲突的
词法作用域分析1
var value = 1;
function foo() {
console.log(value);//1
}
function bar() {
var value = 2;
foo();
}
bar();
在执行bar()的时候,当执行到foo()函数的,当需要输出value的时候,会先在他的内部寻找是否有这个值,如果没有就返回错误,寻找他的上一级作用域,在此例中是全局的1,如果按照动态作用域分析的话,会输出2.
全局作用域:在代码的任何地方都能访问到的对象拥有全局作用域,全局作用域的变量是全局对象的属性,不论在什么函数中都可以直接访问
1 .最直接形式,最外层函数体声明的变量
2 .window的所有属性都具有全局作用域
3 .没有使用var声明的变量都具有全局作用域,成为全局变量,所以声明局部变量必须使用var
局部作用域:局部变量的优先级高于全局变量(或者应该叫做函数作用域)
1 .函数体内使用var声明的变量具有局部作用域,成为局部变量
2 .函数的参数也具有局部作用
var a=3; // 全局变量
function fn(b){ // 局部变量
c=2; // 全局变量
var d=5; // 局部变量
function subFn(){
var e=d; // **父函数的局部变量对子函数可见**
for(var i=0;i<3;i++){
console.write(i);
}
alert(i);// 3, 在for循环内声明,循环外function内仍然可见,没有块作用域 但是使用let声明就不一样了。
}
}
fn()
console.log(c); // 在function内声明但不带var修饰,仍然是全局变量
块级作用域
1 . let,const定义的在{}内更加深入的一些变量
作用域链[[Scope]]内部属性,该属性包含了函数被创建的作用域中的对象的集合,这个集合被称为函数的作用域链,他决定了哪些数据能被函数访问(服务于函数,理解为一个数组,而不是真的一条链)
1 .当一个函数创建之后,他实际上保存一个作用域链,并且作用域链会被创建次函数的作用域中可访问的数据对象填充。
function fun03()
{
var a = 10;
return function(){
a*= 2 ;
return a ;
};
}
var f = fun03();
f();
var x = f();
console.log(x); //40
var g = fun03();
var y = g();
console.log(y); //20
1 .分析
var a;
function f(){
a=100;
}
f()
console.log(a)//100执行函数过后,里面的覆盖了外面的全局变量
var a;
function fun03()
{
var a = 10;
a=100
}
fun03()//100只是覆盖了里面的局部的a的值,外面的还是undefiend
console.log(a)//undefined
var a;
function fun03()
{
var a = 10;
return function(){
a*= 2 ;
return a;
};
}
var g = fun03();
var y = g();
console.log(y); //20 输出的是整个函数的值,因为里面返回的是一个函数,而且现在a的值仍然是undefiend.和上面的原理一样。
var f = fun03();
f();
var x = f();
console.log(x);//40上面的值被执行了两遍
function test(){
var a=b=c=d=e=0;
//除了直接接触var的a,剩下的都变成了全局变量
}
4 .参数和局部变量重名的时候,优先级是相等的。
var a=10;
function f(){
console.log(a)
var a=20;
}
f()//不传参的时候为undefined
var a=10;
function f(a){
console.log(a)
var a=20;
}
f(a)//传参以后10
后来
1 .在解释的过程中,js是严格按照作用域的机制来执行,所以js解释器只需要通过静态分析就可以确定每个变量,函数的作用域。
2 .每个解释器在执行函数的时候,都会创建一个执行环境,这个虚拟环境中先创建一个调用对象,在这个调用对象中存储着当前作用域中所有的局部变量,参数,嵌套函数,外部作用域以及父级引用。
3 .js解释器通过作用域链把多个嵌套函数作用域连接在一起,并借助这个链帮助了解释器检索变量的值,这个作用域链相当于一个索引表,并通过编号来存储嵌套关系。
作用域的优先级
1 .局部变量优先于全局变量。
2 .如果函数体内存在同名的参数函数和局部参数,函数参数优先
3 .函数体内有局部变量和全局变量,那么局部变量优先。
4 .内层函数可以访问外层函数的局部变量,而外层函数却不能访问内层函数的变量,这就是变量作用域链。
5 .闭包函数根据定义作用域来确定自己包含变量的作用域和作用域链
6 .变量是对象的属性,全局变量是window对象的属性,局部变量是调用对象的变量。
管理作用域
1 .js引擎会在页面加载后创建一个全局的执行上下文,然后每执行一个函数都会创建一个对应的执行上下文,最终机那里一个执行上下文栈,当前起作用的执行上下文在堆栈的最底部
2 .每个执行上下文都有一个与之相关联的作用域链,用于解析标识符。作用域链包含一个或者多个变量对象,这些对象定义执行了上下文作用域内的标识符
3 .全局执行上下文的作用域链中只有一个变量对象,他定义了js中所有可用的全局变量和函数。当函数被创建的时候,js引擎会把创建时执行上下文的作用域链赋给函数的内部属性[[scope]],这个内部属性无法内直接访问。当函数执行的时候,js引擎会创建一个活动对象,并在初始化的时候给this,argumets,命名参数,和该函数的所有局部变量赋值。活动对象出现在执行上下文作用域链的顶端,紧接着后面的是函数[[scope]]属性中的对象。
4 .在执行代码的时候,js引擎会通过搜索执行上下文的作用域链来解析诸如变量和函数名这样的标识符,解析标识符的过程从作用域链顶端开始,自上而下的开始进行。
5 .标识符在作用域链中的位置越深,查找和访问他所需的时间就越长,如果作用域管理不当,就会给脚本执行带来负面影响
6 .不过以上原则对于V8不是很适用,因为他的优化早已对这个无感了
7 .使用局部变量:局部变量是js中读取最快的标识符。因为他们存在于执行函数活动的对象中,解析符只需要查找作用域链中的单个对象。
8 .增长作用域链:with和try-catch会增长作用链,所以不要在catch中执行过多的代码,来将其带来的性能影响降到最小
高效的数据存取
1 .数据在脚本中存储的位置直接影响脚本执行的总耗时。
2 .字面量读取值和局部变量读取值的开销差异十分的小,可以忽略不计
3 .真正的差异是在于从数组或者对象中读取数据,一个需要索引,一个需要属性值来查询数据存储的位置。
4 .始终将那些需要频繁存取的值存储到局部变量中。在数据村塾时,将函数中使用超过一次的兑现该属性或数组元素存储为局部变量
5 .在处理HTMLCollection对象这样的dom方法的返回值,使用局部变量特别重要,实际上每次存取这个对象,都是在对DOM文档进行动态查询。