js作用域和闭包

104 阅读2分钟

一、理解作用域

作用域负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套严格的规则,确定当前执行的代码对这些标识符的访问权限。

二、词法作用域

词法作用域是由你在写代码时将变量和块作用域写在哪里决定的。 image.png

2.1 欺骗词法

2.1.1 eval

严格模式下,则无法修改所在的作用域

function foo(str,a){ 
        eval(str)   //欺骗,eval调用var b = 3 ,b相当于在foo内部创建新变量,并且遮蔽了b=2这个变量
        console.log(a,b);   //1,3
}
var b = 2
foo("var b = 3", 1)

2.1.2 with

with通常被当作重复引用同一个对象的多个属性快捷方式,不需重复引用对象本身。 image.png

三、函数作用域

属于这个函数作用域的全部变量可以在整个函数范围内使用及复用。

function foo(a){ 
        b = a + c(a * 2)
        console.log(b * 3);  
}	
function c(a){
        return a - 1
}
var b 		
foo(2)   //15
console.log(b); //此时b全局都能访问到,应该放到foo()里才最安全

函数若不想显示调用,要自动运行

四、块作用域

五、提升

函数作用域和块作用域的行为是一样的,可以总结为:任何声明在某个作用域内的变量,都将附属于这个作用域。
var a = 2,JavaScript 实际上会将其看成两个声明:var a; 和 a = 2;。第一个定义声明是在编译阶段进行的。第二个赋值声明会被留在原地等待执行阶段。
只有函数声明本身会被提升,而赋值或其他运行逻辑会留在原地。

foo()    //函数声明提升
function foo(){
        console.log(a);  //undefined     变量声明提升
        var a = 2;
}

函数表达式却不会被提升。

foo()    //提升
var foo = function bar(){
        console.log(a);  // foo is not a function
        var a = 2;
}

5.1函数优先

函数会首先被提升,然后才是变量。 看函数在哪里调用

foo()    
var foo 
function foo(){
        console.log(1); //第2个出现  1
}
foo = function(){
        console.log(2);  //foo is not a function
}

function foo(){
        console.log(3);   //第1个出现  3
} 
var foo 
function foo(){
        console.log(1);    //第3个出现 1 
}
foo = function(){
        console.log(2);  //第1个出现  2  
}
function foo(){
        console.log(3);   //第2个出现  3
} 
foo()  

六、作用域闭包

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

好处

image.png 解决循环时

var buttons = document.querySelectorAll("button")
for (var i = 0; i < buttons.length; i++) {
        this.buttons[i].index = i                 //把i值赋值给index
        buttons[i].onclick = function(){
                console.log(this.index);              //0 1 2
        }
}

这种会创建更多的构造函数

var buttons = document.querySelectorAll("button")
for (var i = 0; i < buttons.length; i++) {
        (function(i){                  //调用三次fn
                buttons[i].onclick = function(){
                        console.log(i);              //0 1 2
                }
        })(i)            //把外面的i传递给function里
}