作用域与作用域链-食铁獸

1. 什么是作用域?


通常来说一段程序代码中使用的变量和函数并不总是可用的,限定其可用性的范围即作用域,作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少名字冲突

js的作用域是靠函数来形成的,作用域控制着变量与函数的可见性和生命周期。

全局作用域
1.1 函数外定义变量有全局作用

var a=1   //函数外定义变量有全局作用
function f(){
    var s=2  
    return s
}
console.log(a)   // 1
console.log(f())   // 2
console.log(s)    //error

1.2 未定义直接赋值的变量自动声明为拥有全局作用域

var a=1   //未定义直接赋值的变量自动声明为拥有全局作用域
function f(){
    s=2  
    return s
}
console.log(a)   // 1
console.log(f())   // 2
console.log(s)    //2

1.3 windows对象的属性具有全局作用

局部作用域

局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,所以在一些地方会把这种作用域成为函数作用域。

ES6引入了块级作用域,明确允许在块级作用域中声明函数,let和const命令都涉及块级作用域。

块级作用域允许声明函数只在使用大括号的情况下成立。

var a=2     //全局作用域
function f(){  //{}之间的是局部作用域
    var a=3
    var b=4
    function f1(){
        var c=1
        // 找不到 a,b 向上查找
        console.log(a,b,c)  // 3  4  1
        function f11(){
            console.log(a)  // 3
            console.log(c)  // 1
        }
        f11()
    }
    f1()
}

f()
var a=1
for (let i=0;i<4;i++){
    console.log(i)
}
console.log(i)      //error

2. 执行上下文o(´^`)o


不得不提的执行上下文 |ू・ω・` )

执行上下文就是为了提供数据的(变量和函数)

当运行了js代码,默认产生一个全局的执行上下文,当调用一个函数时,就会产生一个局部的执行上下文,局部的执行上下文会压在全局的执行上下文上面,压栈,当函数调用完后,局部的执行上下文,就会出栈,当js代码执行完后,全局的执行上下文也会出栈

举个栗子

//全局执行上下文开始
var a=2     //全局执行上下文 a=2 压栈
function f(){  //局部执行上下文 开始
    var a=3         //局部执行上下文  a=3 压栈
    var b=4         //局部执行上下文  b=4 压栈
    function f1(){
        var c=1        //局部执行上下文  c=1 压栈
        // 找不到 a,b 向上查找
        console.log(a,b,c)  // 3  4  1
        function f11(){
            console.log(a)  // 3
            console.log(c)  // 1
        }
        f11()

    }
    f1()// f1()执行完毕,c=1 出栈
    
}

f()// f1()执行完毕,a=2,b=4 出栈,局部执行上下文现在结束了
console.log(c)  //全局执行上下文没有找到,error

3. 作用域链


全局执行上下文件包含全局变量,函数

局部执行上下文包含内部变量,函数,arguments,父级函数的执行上下文。

如果找一个数据,在一个局部执行上下文中,内部没有找到,就去父级函数的执行上下文中找,如果父里也没有,就是父的父中的执行上下文中找,直到找到全局的执行上下文,如果还找不到,就会报错。这个链就是作用域链。

举栗子

var m=7
var a=2     
function f(){  
    var a=3         
    var b=4         
    function f1(){
        var c=1        
        // 找不到 a,b 向上查找
        console.log(a,b,c)  // 3  4  1
        function f11(){
            // 向上找a,c
            console.log(a)  // 3
            console.log(c)  // 1
            console.log(m)  // 7
 // f11()=>f1()=>f()=>全局,终于找到了(~ ̄▽ ̄)~ 
        }
        f11()
    }
    f1()
}
console.log(c)  //error ,向上找不到,不能向下找 ̄へ ̄
f()

4. 闭包


提到作用域就不得不提到闭包,简单来讲,闭包外部函数能够读取内部函数的变量。

优点:闭包可以形成独立的空间,永久的保存局部变量。

缺点:保存中间值的状态缺点是容易造成内存泄漏,因为闭包中的局部变量永远不会被回收

ES6之前, 通常我们实现的模块就是利用了闭包. 闭包依赖的结构有个鲜明的特点, 即: 一个函数在词法作用域之外执行

5. 踩个作用域经典的坑


var div = document.getElementsByTagName("div")
for(var i=0,len=div.length;i<len;i++){
  div[i].onclick = function(){
      console.log(i)
  }
}

上述代码的本意是每次点击div, 打印div的索引, 实际上打印的却是 len 的值. 我们来分析下原因.

点击div时, 将会执行 console.log(i) 语句, 显然 i 变量不在 click 事件的局部作用域内, 浏览器将沿着 scope chain 寻找 i 变量, 在 index1 的地方, 即 for循环开始的地方, 此处定义了一个 i 变量, 又 js 没有块作用域, 故 i 变量并不会在 for循环块执行完成后被销毁,又 i的最后一次自加使得 i = len, 于是浏览器在scope chain index=1索引的地方停下来了, 返回了i的值, 即len的值.

为了解决这个问题, 我们将根据症结, 对症下药, 从作用域入手, 改变click事件的局部作用域, 如下:

var div = document.getElementsByTagName("div")
for(var i=0,len=div.length;i<len;i++){
  (function(n){
      div[n].onclick = function(){
      console.log(n)
    }
  })(i)
}

en,没问题了。

end


作用域和作用域链是很基础的内容,希望本文能给你带来帮助,感谢阅读(〃‘▽’〃)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值